Merge pull request #33365 from backstage/rugvip/opaque-api-ref-type
frontend-plugin-api: convert ApiRef to an opaque type
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/frontend-app-api': patch
|
||||
---
|
||||
|
||||
Frontend apps now respect an explicit `pluginId` on `ApiRef`s when deciding which plugin owns an API factory.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/core-plugin-api': patch
|
||||
---
|
||||
|
||||
Updated `createApiRef` to preserve the direct config call without deprecation warnings while staying compatible with the new frontend API ref typing.
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'@backstage/frontend-plugin-api': patch
|
||||
---
|
||||
|
||||
Added a builder form for `createApiRef` in the new frontend system and deprecated the direct `createApiRef({ ... })` call in favor of `createApiRef().with({ ... })`. The builder form now also preserves literal API ref IDs in the resulting `ApiRef` type.
|
||||
|
||||
The `createApiRef().with({ ... })` form can also use an explicit `pluginId` to declare API ownership without encoding the plugin ID into the API ref ID, while keeping that metadata internal to runtime handling.
|
||||
@@ -28,7 +28,6 @@ import { ComponentType } from 'react';
|
||||
import { ConfigApi } from '@backstage/frontend-plugin-api';
|
||||
import { configApiRef } from '@backstage/frontend-plugin-api';
|
||||
import { createApiFactory } from '@backstage/frontend-plugin-api';
|
||||
import { createApiRef } from '@backstage/frontend-plugin-api';
|
||||
import { DiscoveryApi } from '@backstage/frontend-plugin-api';
|
||||
import { discoveryApiRef } from '@backstage/frontend-plugin-api';
|
||||
import { ErrorApi } from '@backstage/frontend-plugin-api';
|
||||
@@ -256,7 +255,8 @@ export { configApiRef };
|
||||
|
||||
export { createApiFactory };
|
||||
|
||||
export { createApiRef };
|
||||
// @public
|
||||
export function createApiRef<T>(config: ApiRefConfig): ApiRef<T>;
|
||||
|
||||
// @public
|
||||
export function createComponentExtension<
|
||||
|
||||
@@ -19,9 +19,15 @@ import { createApiRef } from './ApiRef';
|
||||
describe('ApiRef', () => {
|
||||
it('should be created', () => {
|
||||
const ref = createApiRef({ id: 'abc' });
|
||||
expect(ref.$$type).toBe('@backstage/ApiRef');
|
||||
expect(ref.id).toBe('abc');
|
||||
expect(String(ref)).toBe('apiRef{abc}');
|
||||
expect(() => ref.T).toThrow('tried to read ApiRef.T of apiRef{abc}');
|
||||
expect(ref.T).toBeNull();
|
||||
});
|
||||
|
||||
it('should not accept pluginId in the core createApiRef config', () => {
|
||||
// @ts-expect-error pluginId is not supported in core-plugin-api
|
||||
createApiRef<string>({ id: 'abc', pluginId: 'test' });
|
||||
});
|
||||
|
||||
it('should reject invalid ids', () => {
|
||||
|
||||
@@ -14,5 +14,23 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { createApiRef } from '@backstage/frontend-plugin-api';
|
||||
export type { ApiRefConfig } from '@backstage/frontend-plugin-api';
|
||||
import {
|
||||
createApiRef as createFrontendApiRef,
|
||||
type ApiRef,
|
||||
type ApiRefConfig,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
|
||||
const createFrontendApiRefCompat = createFrontendApiRef as <T>(
|
||||
config: ApiRefConfig,
|
||||
) => ApiRef<T>;
|
||||
|
||||
/**
|
||||
* Creates a reference to an API.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function createApiRef<T>(config: ApiRefConfig): ApiRef<T> {
|
||||
return createFrontendApiRefCompat<T>(config);
|
||||
}
|
||||
|
||||
export type { ApiRefConfig };
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
import {
|
||||
AppTreeApi,
|
||||
type ApiRef,
|
||||
appTreeApiRef,
|
||||
coreExtensionData,
|
||||
createExtension,
|
||||
@@ -166,10 +167,13 @@ describe('createSpecializedApp', () => {
|
||||
"factories": Map {
|
||||
"core.featureflags" => {
|
||||
"factory": {
|
||||
"api": ApiRefImpl {
|
||||
"config": {
|
||||
"id": "core.featureflags",
|
||||
},
|
||||
"api": {
|
||||
"$$type": "@backstage/ApiRef",
|
||||
"T": null,
|
||||
"id": "core.featureflags",
|
||||
"pluginId": "app",
|
||||
"toString": [Function],
|
||||
"version": "v1",
|
||||
},
|
||||
"deps": {},
|
||||
"factory": [Function],
|
||||
@@ -178,10 +182,13 @@ describe('createSpecializedApp', () => {
|
||||
},
|
||||
"core.app-tree" => {
|
||||
"factory": {
|
||||
"api": ApiRefImpl {
|
||||
"config": {
|
||||
"id": "core.app-tree",
|
||||
},
|
||||
"api": {
|
||||
"$$type": "@backstage/ApiRef",
|
||||
"T": null,
|
||||
"id": "core.app-tree",
|
||||
"pluginId": "app",
|
||||
"toString": [Function],
|
||||
"version": "v1",
|
||||
},
|
||||
"deps": {},
|
||||
"factory": [Function],
|
||||
@@ -190,10 +197,13 @@ describe('createSpecializedApp', () => {
|
||||
},
|
||||
"core.config" => {
|
||||
"factory": {
|
||||
"api": ApiRefImpl {
|
||||
"config": {
|
||||
"id": "core.config",
|
||||
},
|
||||
"api": {
|
||||
"$$type": "@backstage/ApiRef",
|
||||
"T": null,
|
||||
"id": "core.config",
|
||||
"pluginId": "app",
|
||||
"toString": [Function],
|
||||
"version": "v1",
|
||||
},
|
||||
"deps": {},
|
||||
"factory": [Function],
|
||||
@@ -202,10 +212,13 @@ describe('createSpecializedApp', () => {
|
||||
},
|
||||
"core.route-resolution" => {
|
||||
"factory": {
|
||||
"api": ApiRefImpl {
|
||||
"config": {
|
||||
"id": "core.route-resolution",
|
||||
},
|
||||
"api": {
|
||||
"$$type": "@backstage/ApiRef",
|
||||
"T": null,
|
||||
"id": "core.route-resolution",
|
||||
"pluginId": "app",
|
||||
"toString": [Function],
|
||||
"version": "v1",
|
||||
},
|
||||
"deps": {},
|
||||
"factory": [Function],
|
||||
@@ -214,10 +227,13 @@ describe('createSpecializedApp', () => {
|
||||
},
|
||||
"core.identity" => {
|
||||
"factory": {
|
||||
"api": ApiRefImpl {
|
||||
"config": {
|
||||
"id": "core.identity",
|
||||
},
|
||||
"api": {
|
||||
"$$type": "@backstage/ApiRef",
|
||||
"T": null,
|
||||
"id": "core.identity",
|
||||
"pluginId": "app",
|
||||
"toString": [Function],
|
||||
"version": "v1",
|
||||
},
|
||||
"deps": {},
|
||||
"factory": [Function],
|
||||
@@ -359,6 +375,150 @@ describe('createSpecializedApp', () => {
|
||||
expect(app.apis.get(testApiRef)).toEqual({ value: 'owner' });
|
||||
});
|
||||
|
||||
it('should select the API factory from an explicitly owned plugin on conflict', () => {
|
||||
const testApiRef = createApiRef<{ value: string }>().with({
|
||||
id: 'shared.api',
|
||||
pluginId: 'owner',
|
||||
});
|
||||
|
||||
const app = createSpecializedApp({
|
||||
features: [
|
||||
makeAppPlugin(),
|
||||
createFrontendPlugin({
|
||||
pluginId: 'other-before',
|
||||
extensions: [
|
||||
ApiBlueprint.make({
|
||||
params: defineParams =>
|
||||
defineParams({
|
||||
api: testApiRef,
|
||||
deps: {},
|
||||
factory: () => ({ value: 'other' }),
|
||||
}),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
createFrontendPlugin({
|
||||
pluginId: 'owner',
|
||||
extensions: [
|
||||
ApiBlueprint.make({
|
||||
params: defineParams =>
|
||||
defineParams({
|
||||
api: testApiRef,
|
||||
deps: {},
|
||||
factory: () => ({ value: 'owner' }),
|
||||
}),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
expect(app.errors).toEqual([
|
||||
expect.objectContaining({
|
||||
code: 'API_FACTORY_CONFLICT',
|
||||
message: expect.stringContaining("API 'shared.api'"),
|
||||
}),
|
||||
]);
|
||||
|
||||
expect(app.apis.get(testApiRef)).toEqual({ value: 'owner' });
|
||||
});
|
||||
|
||||
it('should reject unsupported opaque ApiRef versions', () => {
|
||||
const testApiRef = {
|
||||
$$type: '@backstage/ApiRef',
|
||||
version: 'v0',
|
||||
id: 'shared.api',
|
||||
pluginId: 'owner',
|
||||
T: null as unknown as { value: string },
|
||||
toString() {
|
||||
return 'apiRef{shared.api}';
|
||||
},
|
||||
} as ApiRef<{ value: string }, 'shared.api'> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
readonly version: 'v0';
|
||||
readonly pluginId: 'owner';
|
||||
};
|
||||
|
||||
expect(() =>
|
||||
createSpecializedApp({
|
||||
features: [
|
||||
makeAppPlugin(),
|
||||
createFrontendPlugin({
|
||||
pluginId: 'other-before',
|
||||
extensions: [
|
||||
ApiBlueprint.make({
|
||||
params: defineParams =>
|
||||
defineParams({
|
||||
api: testApiRef,
|
||||
deps: {},
|
||||
factory: () => ({ value: 'other' }),
|
||||
}),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
createFrontendPlugin({
|
||||
pluginId: 'owner',
|
||||
extensions: [
|
||||
ApiBlueprint.make({
|
||||
params: defineParams =>
|
||||
defineParams({
|
||||
api: testApiRef,
|
||||
deps: {},
|
||||
factory: () => ({ value: 'owner' }),
|
||||
}),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
).toThrow("Invalid opaque type instance, got version 'v0', expected 'v1'");
|
||||
});
|
||||
|
||||
it('should not infer app ownership from core-prefixed API ids', () => {
|
||||
const testApiRef = createApiRef<{ value: string }>({ id: 'core.shared' });
|
||||
|
||||
const app = createSpecializedApp({
|
||||
features: [
|
||||
makeAppPlugin(),
|
||||
createFrontendPlugin({
|
||||
pluginId: 'other-before',
|
||||
extensions: [
|
||||
ApiBlueprint.make({
|
||||
params: defineParams =>
|
||||
defineParams({
|
||||
api: testApiRef,
|
||||
deps: {},
|
||||
factory: () => ({ value: 'other' }),
|
||||
}),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
createFrontendModule({
|
||||
pluginId: 'app',
|
||||
extensions: [
|
||||
ApiBlueprint.make({
|
||||
params: defineParams =>
|
||||
defineParams({
|
||||
api: testApiRef,
|
||||
deps: {},
|
||||
factory: () => ({ value: 'app' }),
|
||||
}),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
expect(app.errors).toEqual([
|
||||
expect.objectContaining({
|
||||
code: 'API_FACTORY_CONFLICT',
|
||||
message: expect.stringContaining("API 'core.shared'"),
|
||||
}),
|
||||
]);
|
||||
|
||||
expect(app.apis.get(testApiRef)).toEqual({ value: 'other' });
|
||||
});
|
||||
|
||||
it('should allow API overrides within the same plugin', () => {
|
||||
const testApiRef = createApiRef<{ value: string }>({ id: 'test.api' });
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ import {
|
||||
import { ApiFactoryRegistry, ApiResolver } from '@backstage/core-app-api';
|
||||
import {
|
||||
createExtensionDataContainer,
|
||||
OpaqueApiRef,
|
||||
OpaqueFrontendPlugin,
|
||||
} from '@internal/frontend';
|
||||
|
||||
@@ -401,14 +402,14 @@ function createApiFactories(options: {
|
||||
const apiFactory = apiNode.instance?.getData(ApiBlueprint.dataRefs.factory);
|
||||
if (apiFactory) {
|
||||
const apiRefId = apiFactory.api.id;
|
||||
const ownerId = getApiOwnerId(apiRefId);
|
||||
const ownerId = getApiOwnerId(apiFactory.api);
|
||||
const pluginId = apiNode.spec.plugin.pluginId ?? 'app';
|
||||
const existingFactory = factoriesById.get(apiRefId);
|
||||
|
||||
// This allows modules to override factories provided by the plugin, but
|
||||
// it rejects API overrides from other plugins. In the event of a
|
||||
// conflict, the owning plugin is attempted to be inferred from the API
|
||||
// reference ID.
|
||||
// conflict, the owning plugin is inferred from the explicit pluginId or
|
||||
// legacy plugin-prefixed API reference ID.
|
||||
if (existingFactory && existingFactory.pluginId !== pluginId) {
|
||||
const shouldReplace =
|
||||
ownerId === pluginId && existingFactory.pluginId !== ownerId;
|
||||
@@ -455,14 +456,19 @@ function createApiFactories(options: {
|
||||
|
||||
// TODO(Rugvip): It would be good if this was more explicit, but I think that
|
||||
// might need to wait for some future update for API factories.
|
||||
function getApiOwnerId(apiRefId: string): string {
|
||||
function getApiOwnerId(apiRef: { id: string }): string {
|
||||
if (OpaqueApiRef.isType(apiRef)) {
|
||||
const { pluginId } = OpaqueApiRef.toInternal(apiRef);
|
||||
if (pluginId) {
|
||||
return pluginId;
|
||||
}
|
||||
}
|
||||
|
||||
const apiRefId = apiRef.id;
|
||||
const [prefix, ...rest] = apiRefId.split('.');
|
||||
if (!prefix) {
|
||||
return apiRefId;
|
||||
}
|
||||
if (prefix === 'core') {
|
||||
return 'app';
|
||||
}
|
||||
if (prefix === 'plugin' && rest[0]) {
|
||||
return rest[0];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { ApiRef } from '@backstage/frontend-plugin-api';
|
||||
import { OpaqueType } from '@internal/opaque';
|
||||
|
||||
export const OpaqueApiRef = OpaqueType.create<{
|
||||
public: ApiRef<unknown> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
versions: {
|
||||
readonly version: 'v1';
|
||||
readonly pluginId?: string;
|
||||
};
|
||||
}>({
|
||||
type: '@backstage/ApiRef',
|
||||
versions: ['v1'],
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './OpaqueApiRef';
|
||||
@@ -15,4 +15,5 @@
|
||||
*/
|
||||
|
||||
export * from './routing';
|
||||
export * from './apis';
|
||||
export * from './wiring';
|
||||
|
||||
@@ -24,7 +24,12 @@ export type PluginWrapperApi = {
|
||||
};
|
||||
|
||||
// @public
|
||||
export const pluginWrapperApiRef: ApiRef<PluginWrapperApi>;
|
||||
export const pluginWrapperApiRef: ApiRef<
|
||||
PluginWrapperApi,
|
||||
'core.plugin-wrapper'
|
||||
> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
|
||||
// @public
|
||||
export const PluginWrapperBlueprint: ExtensionBlueprint<{
|
||||
|
||||
@@ -31,7 +31,9 @@ export type AlertApi = {
|
||||
};
|
||||
|
||||
// @public
|
||||
export const alertApiRef: ApiRef<AlertApi>;
|
||||
export const alertApiRef: ApiRef_2<AlertApi, 'core.alert'> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
|
||||
// @public
|
||||
export type AlertMessage = {
|
||||
@@ -46,7 +48,9 @@ export type AnalyticsApi = {
|
||||
};
|
||||
|
||||
// @public
|
||||
export const analyticsApiRef: ApiRef<AnalyticsApi>;
|
||||
export const analyticsApiRef: ApiRef_2<AnalyticsApi, 'core.analytics'> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
|
||||
// @public
|
||||
export const AnalyticsContext: (options: {
|
||||
@@ -187,9 +191,10 @@ export type ApiHolder = {
|
||||
};
|
||||
|
||||
// @public
|
||||
export type ApiRef<T> = {
|
||||
id: string;
|
||||
T: T;
|
||||
export type ApiRef<T, TId extends string = string> = {
|
||||
readonly $$type?: '@backstage/ApiRef';
|
||||
readonly id: TId;
|
||||
readonly T: T;
|
||||
};
|
||||
|
||||
// @public
|
||||
@@ -212,7 +217,9 @@ export type AppLanguageApi = {
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export const appLanguageApiRef: ApiRef<AppLanguageApi>;
|
||||
export const appLanguageApiRef: ApiRef_2<AppLanguageApi, 'core.applanguage'> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
|
||||
// @public
|
||||
export interface AppNode {
|
||||
@@ -285,7 +292,9 @@ export type AppThemeApi = {
|
||||
};
|
||||
|
||||
// @public
|
||||
export const appThemeApiRef: ApiRef<AppThemeApi>;
|
||||
export const appThemeApiRef: ApiRef_2<AppThemeApi, 'core.apptheme'> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
|
||||
// @public
|
||||
export interface AppTree {
|
||||
@@ -305,12 +314,17 @@ export interface AppTreeApi {
|
||||
}
|
||||
|
||||
// @public
|
||||
export const appTreeApiRef: ApiRef_2<AppTreeApi>;
|
||||
export const appTreeApiRef: ApiRef_2<AppTreeApi, 'core.app-tree'> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
|
||||
// @public
|
||||
export const atlassianAuthApiRef: ApiRef<
|
||||
OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
|
||||
>;
|
||||
export const atlassianAuthApiRef: ApiRef_2<
|
||||
OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi,
|
||||
'core.auth.atlassian'
|
||||
> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
|
||||
// @public
|
||||
export type AuthProviderInfo = {
|
||||
@@ -348,20 +362,28 @@ export type BackstageUserIdentity = {
|
||||
};
|
||||
|
||||
// @public
|
||||
export const bitbucketAuthApiRef: ApiRef<
|
||||
OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
|
||||
>;
|
||||
export const bitbucketAuthApiRef: ApiRef_2<
|
||||
OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi,
|
||||
'core.auth.bitbucket'
|
||||
> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
|
||||
// @public
|
||||
export const bitbucketServerAuthApiRef: ApiRef<
|
||||
OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
|
||||
>;
|
||||
export const bitbucketServerAuthApiRef: ApiRef_2<
|
||||
OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi,
|
||||
'core.auth.bitbucket-server'
|
||||
> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
|
||||
// @public
|
||||
export type ConfigApi = Config;
|
||||
|
||||
// @public
|
||||
export const configApiRef: ApiRef<ConfigApi>;
|
||||
export const configApiRef: ApiRef_2<Config, 'core.config'> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export interface ConfigurableExtensionDataRef<
|
||||
@@ -415,8 +437,22 @@ export function createApiFactory<Api, Impl extends Api>(
|
||||
instance: Impl,
|
||||
): ApiFactory<Api, Impl, {}>;
|
||||
|
||||
// @public @deprecated
|
||||
export function createApiRef<T>(config: ApiRefConfig): ApiRef<T> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
|
||||
// @public
|
||||
export function createApiRef<T>(config: ApiRefConfig): ApiRef<T>;
|
||||
export function createApiRef<T>(): {
|
||||
with<const TId extends string>(
|
||||
config: ApiRefConfig & {
|
||||
id: TId;
|
||||
pluginId?: string;
|
||||
},
|
||||
): ApiRef<T, TId> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
};
|
||||
|
||||
// @public
|
||||
export function createExtension<
|
||||
@@ -869,7 +905,9 @@ export interface DialogApiDialog<TResult = void> {
|
||||
}
|
||||
|
||||
// @public
|
||||
export const dialogApiRef: ApiRef_2<DialogApi>;
|
||||
export const dialogApiRef: ApiRef_2<DialogApi, 'core.dialog'> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
|
||||
// @public
|
||||
export type DiscoveryApi = {
|
||||
@@ -877,7 +915,9 @@ export type DiscoveryApi = {
|
||||
};
|
||||
|
||||
// @public
|
||||
export const discoveryApiRef: ApiRef<DiscoveryApi>;
|
||||
export const discoveryApiRef: ApiRef_2<DiscoveryApi, 'core.discovery'> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
|
||||
// @public
|
||||
export type ErrorApi = {
|
||||
@@ -901,7 +941,9 @@ export type ErrorApiErrorContext = {
|
||||
};
|
||||
|
||||
// @public
|
||||
export const errorApiRef: ApiRef<ErrorApi>;
|
||||
export const errorApiRef: ApiRef_2<ErrorApi, 'core.error'> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export const ErrorDisplay: {
|
||||
@@ -1285,7 +1327,12 @@ export interface FeatureFlagsApi {
|
||||
}
|
||||
|
||||
// @public
|
||||
export const featureFlagsApiRef: ApiRef<FeatureFlagsApi>;
|
||||
export const featureFlagsApiRef: ApiRef_2<
|
||||
FeatureFlagsApi,
|
||||
'core.featureflags'
|
||||
> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
|
||||
// @public
|
||||
export type FeatureFlagsSaveOptions = {
|
||||
@@ -1317,7 +1364,9 @@ export type FetchApi = {
|
||||
};
|
||||
|
||||
// @public
|
||||
export const fetchApiRef: ApiRef<FetchApi>;
|
||||
export const fetchApiRef: ApiRef_2<FetchApi, 'core.fetch'> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type FrontendFeature =
|
||||
@@ -1390,27 +1439,36 @@ export type FrontendPluginInfoOptions = {
|
||||
};
|
||||
|
||||
// @public
|
||||
export const githubAuthApiRef: ApiRef<
|
||||
OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
|
||||
>;
|
||||
export const githubAuthApiRef: ApiRef_2<
|
||||
OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi,
|
||||
'core.auth.github'
|
||||
> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
|
||||
// @public
|
||||
export const gitlabAuthApiRef: ApiRef<
|
||||
export const gitlabAuthApiRef: ApiRef_2<
|
||||
OAuthApi &
|
||||
OpenIdConnectApi &
|
||||
ProfileInfoApi &
|
||||
BackstageIdentityApi &
|
||||
SessionApi
|
||||
>;
|
||||
SessionApi,
|
||||
'core.auth.gitlab'
|
||||
> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
|
||||
// @public
|
||||
export const googleAuthApiRef: ApiRef<
|
||||
export const googleAuthApiRef: ApiRef_2<
|
||||
OAuthApi &
|
||||
OpenIdConnectApi &
|
||||
ProfileInfoApi &
|
||||
BackstageIdentityApi &
|
||||
SessionApi
|
||||
>;
|
||||
SessionApi,
|
||||
'core.auth.google'
|
||||
> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
|
||||
// @public @deprecated
|
||||
export type IconComponent = ComponentType<{
|
||||
@@ -1430,7 +1488,9 @@ export interface IconsApi {
|
||||
}
|
||||
|
||||
// @public
|
||||
export const iconsApiRef: ApiRef_2<IconsApi>;
|
||||
export const iconsApiRef: ApiRef_2<IconsApi, 'core.icons'> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
|
||||
// @public
|
||||
export type IdentityApi = {
|
||||
@@ -1443,16 +1503,21 @@ export type IdentityApi = {
|
||||
};
|
||||
|
||||
// @public
|
||||
export const identityApiRef: ApiRef<IdentityApi>;
|
||||
export const identityApiRef: ApiRef_2<IdentityApi, 'core.identity'> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
|
||||
// @public
|
||||
export const microsoftAuthApiRef: ApiRef<
|
||||
export const microsoftAuthApiRef: ApiRef_2<
|
||||
OAuthApi &
|
||||
OpenIdConnectApi &
|
||||
ProfileInfoApi &
|
||||
BackstageIdentityApi &
|
||||
SessionApi
|
||||
>;
|
||||
SessionApi,
|
||||
'core.auth.microsoft'
|
||||
> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
|
||||
// @public @deprecated
|
||||
export const NavItemBlueprint: ExtensionBlueprint_2<{
|
||||
@@ -1515,7 +1580,12 @@ export type OAuthRequestApi = {
|
||||
};
|
||||
|
||||
// @public
|
||||
export const oauthRequestApiRef: ApiRef<OAuthRequestApi>;
|
||||
export const oauthRequestApiRef: ApiRef_2<
|
||||
OAuthRequestApi,
|
||||
'core.oauthrequest'
|
||||
> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
|
||||
// @public
|
||||
export type OAuthRequester<TAuthResponse> = (
|
||||
@@ -1532,22 +1602,28 @@ export type OAuthRequesterOptions<TOAuthResponse> = {
|
||||
export type OAuthScope = string | string[];
|
||||
|
||||
// @public
|
||||
export const oktaAuthApiRef: ApiRef<
|
||||
export const oktaAuthApiRef: ApiRef_2<
|
||||
OAuthApi &
|
||||
OpenIdConnectApi &
|
||||
ProfileInfoApi &
|
||||
BackstageIdentityApi &
|
||||
SessionApi
|
||||
>;
|
||||
SessionApi,
|
||||
'core.auth.okta'
|
||||
> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
|
||||
// @public
|
||||
export const oneloginAuthApiRef: ApiRef<
|
||||
export const oneloginAuthApiRef: ApiRef_2<
|
||||
OAuthApi &
|
||||
OpenIdConnectApi &
|
||||
ProfileInfoApi &
|
||||
BackstageIdentityApi &
|
||||
SessionApi
|
||||
>;
|
||||
SessionApi,
|
||||
'core.auth.onelogin'
|
||||
> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
|
||||
// @public
|
||||
export type OpenIdConnectApi = {
|
||||
@@ -1555,9 +1631,12 @@ export type OpenIdConnectApi = {
|
||||
};
|
||||
|
||||
// @public
|
||||
export const openshiftAuthApiRef: ApiRef<
|
||||
OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
|
||||
>;
|
||||
export const openshiftAuthApiRef: ApiRef_2<
|
||||
OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi,
|
||||
'core.auth.openshift'
|
||||
> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export interface OverridableExtensionDefinition<
|
||||
@@ -1845,7 +1924,12 @@ export type PluginHeaderActionsApi = {
|
||||
};
|
||||
|
||||
// @public
|
||||
export const pluginHeaderActionsApiRef: ApiRef_2<PluginHeaderActionsApi>;
|
||||
export const pluginHeaderActionsApiRef: ApiRef_2<
|
||||
PluginHeaderActionsApi,
|
||||
'core.plugin-header-actions'
|
||||
> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export interface PluginOptions<
|
||||
@@ -1887,7 +1971,12 @@ export type PluginWrapperApi = {
|
||||
};
|
||||
|
||||
// @public
|
||||
export const pluginWrapperApiRef: ApiRef_2<PluginWrapperApi>;
|
||||
export const pluginWrapperApiRef: ApiRef_2<
|
||||
PluginWrapperApi,
|
||||
'core.plugin-wrapper'
|
||||
> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
|
||||
// @public
|
||||
export const PluginWrapperBlueprint: ExtensionBlueprint_2<{
|
||||
@@ -1993,7 +2082,12 @@ export interface RouteResolutionApi {
|
||||
}
|
||||
|
||||
// @public
|
||||
export const routeResolutionApiRef: ApiRef_2<RouteResolutionApi>;
|
||||
export const routeResolutionApiRef: ApiRef_2<
|
||||
RouteResolutionApi,
|
||||
'core.route-resolution'
|
||||
> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
|
||||
// @public
|
||||
export type SessionApi = {
|
||||
@@ -2031,7 +2125,9 @@ export interface StorageApi {
|
||||
}
|
||||
|
||||
// @public
|
||||
export const storageApiRef: ApiRef<StorageApi>;
|
||||
export const storageApiRef: ApiRef_2<StorageApi, 'core.storage'> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
|
||||
// @public
|
||||
export type StorageValueSnapshot<TValue extends JsonValue> =
|
||||
@@ -2121,7 +2217,12 @@ export interface SwappableComponentsApi {
|
||||
}
|
||||
|
||||
// @public
|
||||
export const swappableComponentsApiRef: ApiRef_2<SwappableComponentsApi>;
|
||||
export const swappableComponentsApiRef: ApiRef_2<
|
||||
SwappableComponentsApi,
|
||||
'core.swappable-components'
|
||||
> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type TranslationApi = {
|
||||
@@ -2142,7 +2243,9 @@ export type TranslationApi = {
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export const translationApiRef: ApiRef<TranslationApi>;
|
||||
export const translationApiRef: ApiRef_2<TranslationApi, 'core.translation'> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type TranslationFunction<
|
||||
@@ -2332,13 +2435,16 @@ export const useTranslationRef: <TMessages extends { [key in string]: string }>(
|
||||
};
|
||||
|
||||
// @public
|
||||
export const vmwareCloudAuthApiRef: ApiRef<
|
||||
export const vmwareCloudAuthApiRef: ApiRef_2<
|
||||
OAuthApi &
|
||||
OpenIdConnectApi &
|
||||
ProfileInfoApi &
|
||||
BackstageIdentityApi &
|
||||
SessionApi
|
||||
>;
|
||||
SessionApi,
|
||||
'core.auth.vmware-cloud'
|
||||
> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
|
||||
// @public @deprecated
|
||||
export function withApis<T extends {}>(
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createApiRef, ApiRef } from '../system';
|
||||
import { createApiRef } from '../system';
|
||||
import { Observable } from '@backstage/types';
|
||||
|
||||
/**
|
||||
@@ -51,6 +51,7 @@ export type AlertApi = {
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const alertApiRef: ApiRef<AlertApi> = createApiRef({
|
||||
export const alertApiRef = createApiRef<AlertApi>().with({
|
||||
id: 'core.alert',
|
||||
pluginId: 'app',
|
||||
});
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ApiRef, createApiRef } from '../system';
|
||||
import { createApiRef } from '../system';
|
||||
import { AnalyticsContextValue } from '../../analytics/types';
|
||||
|
||||
/**
|
||||
@@ -151,6 +151,7 @@ export type AnalyticsApi = {
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const analyticsApiRef: ApiRef<AnalyticsApi> = createApiRef({
|
||||
export const analyticsApiRef = createApiRef<AnalyticsApi>().with({
|
||||
id: 'core.analytics',
|
||||
pluginId: 'app',
|
||||
});
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ApiRef, createApiRef } from '../system';
|
||||
import { createApiRef } from '../system';
|
||||
import { Observable } from '@backstage/types';
|
||||
|
||||
/** @public */
|
||||
@@ -31,6 +31,7 @@ export type AppLanguageApi = {
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export const appLanguageApiRef: ApiRef<AppLanguageApi> = createApiRef({
|
||||
export const appLanguageApiRef = createApiRef<AppLanguageApi>().with({
|
||||
id: 'core.applanguage',
|
||||
pluginId: 'app',
|
||||
});
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import { ReactNode } from 'react';
|
||||
import { ApiRef, createApiRef } from '../system';
|
||||
import { createApiRef } from '../system';
|
||||
import { Observable } from '@backstage/types';
|
||||
|
||||
/**
|
||||
@@ -82,6 +82,7 @@ export type AppThemeApi = {
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const appThemeApiRef: ApiRef<AppThemeApi> = createApiRef({
|
||||
export const appThemeApiRef = createApiRef<AppThemeApi>().with({
|
||||
id: 'core.apptheme',
|
||||
pluginId: 'app',
|
||||
});
|
||||
|
||||
@@ -117,4 +117,7 @@ export interface AppTreeApi {
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const appTreeApiRef = createApiRef<AppTreeApi>({ id: 'core.app-tree' });
|
||||
export const appTreeApiRef = createApiRef<AppTreeApi>().with({
|
||||
id: 'core.app-tree',
|
||||
pluginId: 'app',
|
||||
});
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { ApiRef, createApiRef } from '../system';
|
||||
import { createApiRef } from '../system';
|
||||
import type { Config } from '@backstage/config';
|
||||
|
||||
/**
|
||||
@@ -29,6 +29,7 @@ export type ConfigApi = Config;
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const configApiRef: ApiRef<ConfigApi> = createApiRef({
|
||||
export const configApiRef = createApiRef<ConfigApi>().with({
|
||||
id: 'core.config',
|
||||
pluginId: 'app',
|
||||
});
|
||||
|
||||
@@ -173,6 +173,7 @@ export interface DialogApi {
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const dialogApiRef = createApiRef<DialogApi>({
|
||||
export const dialogApiRef = createApiRef<DialogApi>().with({
|
||||
id: 'core.dialog',
|
||||
pluginId: 'app',
|
||||
});
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { ApiRef, createApiRef } from '../system';
|
||||
import { createApiRef } from '../system';
|
||||
|
||||
/**
|
||||
* The discovery API is used to provide a mechanism for plugins to
|
||||
@@ -50,6 +50,7 @@ export type DiscoveryApi = {
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const discoveryApiRef: ApiRef<DiscoveryApi> = createApiRef({
|
||||
export const discoveryApiRef = createApiRef<DiscoveryApi>().with({
|
||||
id: 'core.discovery',
|
||||
pluginId: 'app',
|
||||
});
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ApiRef, createApiRef } from '../system';
|
||||
import { createApiRef } from '../system';
|
||||
import { Observable } from '@backstage/types';
|
||||
|
||||
/**
|
||||
@@ -86,6 +86,7 @@ export type ErrorApi = {
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const errorApiRef: ApiRef<ErrorApi> = createApiRef({
|
||||
export const errorApiRef = createApiRef<ErrorApi>().with({
|
||||
id: 'core.error',
|
||||
pluginId: 'app',
|
||||
});
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
/* We want to maintain the same information as an enum, so we disable the redeclaration warning */
|
||||
/* eslint-disable @typescript-eslint/no-redeclare */
|
||||
|
||||
import { ApiRef, createApiRef } from '../system';
|
||||
import { createApiRef } from '../system';
|
||||
|
||||
/**
|
||||
* Feature flag descriptor.
|
||||
@@ -121,6 +121,7 @@ export interface FeatureFlagsApi {
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const featureFlagsApiRef: ApiRef<FeatureFlagsApi> = createApiRef({
|
||||
export const featureFlagsApiRef = createApiRef<FeatureFlagsApi>().with({
|
||||
id: 'core.featureflags',
|
||||
pluginId: 'app',
|
||||
});
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ApiRef, createApiRef } from '../system';
|
||||
import { createApiRef } from '../system';
|
||||
|
||||
/**
|
||||
* A wrapper for the fetch API, that has additional behaviors such as the
|
||||
@@ -46,6 +46,7 @@ export type FetchApi = {
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const fetchApiRef: ApiRef<FetchApi> = createApiRef({
|
||||
export const fetchApiRef = createApiRef<FetchApi>().with({
|
||||
id: 'core.fetch',
|
||||
pluginId: 'app',
|
||||
});
|
||||
|
||||
@@ -41,6 +41,7 @@ export interface IconsApi {
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const iconsApiRef = createApiRef<IconsApi>({
|
||||
export const iconsApiRef = createApiRef<IconsApi>().with({
|
||||
id: 'core.icons',
|
||||
pluginId: 'app',
|
||||
});
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { ApiRef, createApiRef } from '../system';
|
||||
import { createApiRef } from '../system';
|
||||
import { BackstageUserIdentity, ProfileInfo } from './auth';
|
||||
|
||||
/**
|
||||
@@ -51,6 +51,7 @@ export type IdentityApi = {
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const identityApiRef: ApiRef<IdentityApi> = createApiRef({
|
||||
export const identityApiRef = createApiRef<IdentityApi>().with({
|
||||
id: 'core.identity',
|
||||
pluginId: 'app',
|
||||
});
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import { Observable } from '@backstage/types';
|
||||
import { ApiRef, createApiRef } from '../system';
|
||||
import { createApiRef } from '../system';
|
||||
import { AuthProviderInfo } from './auth';
|
||||
|
||||
/**
|
||||
@@ -126,6 +126,7 @@ export type OAuthRequestApi = {
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const oauthRequestApiRef: ApiRef<OAuthRequestApi> = createApiRef({
|
||||
export const oauthRequestApiRef = createApiRef<OAuthRequestApi>().with({
|
||||
id: 'core.oauthrequest',
|
||||
pluginId: 'app',
|
||||
});
|
||||
|
||||
@@ -40,6 +40,8 @@ export type PluginHeaderActionsApi = {
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const pluginHeaderActionsApiRef = createApiRef<PluginHeaderActionsApi>({
|
||||
id: 'core.plugin-header-actions',
|
||||
});
|
||||
export const pluginHeaderActionsApiRef =
|
||||
createApiRef<PluginHeaderActionsApi>().with({
|
||||
id: 'core.plugin-header-actions',
|
||||
pluginId: 'app',
|
||||
});
|
||||
|
||||
@@ -47,6 +47,7 @@ export type PluginWrapperApi = {
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const pluginWrapperApiRef = createApiRef<PluginWrapperApi>({
|
||||
export const pluginWrapperApiRef = createApiRef<PluginWrapperApi>().with({
|
||||
id: 'core.plugin-wrapper',
|
||||
pluginId: 'app',
|
||||
});
|
||||
|
||||
@@ -65,6 +65,7 @@ export interface RouteResolutionApi {
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const routeResolutionApiRef = createApiRef<RouteResolutionApi>({
|
||||
export const routeResolutionApiRef = createApiRef<RouteResolutionApi>().with({
|
||||
id: 'core.route-resolution',
|
||||
pluginId: 'app',
|
||||
});
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ApiRef, createApiRef } from '../system';
|
||||
import { createApiRef } from '../system';
|
||||
import { JsonValue, Observable } from '@backstage/types';
|
||||
|
||||
/**
|
||||
@@ -105,6 +105,7 @@ export interface StorageApi {
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const storageApiRef: ApiRef<StorageApi> = createApiRef({
|
||||
export const storageApiRef = createApiRef<StorageApi>().with({
|
||||
id: 'core.storage',
|
||||
pluginId: 'app',
|
||||
});
|
||||
|
||||
@@ -36,6 +36,8 @@ export interface SwappableComponentsApi {
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const swappableComponentsApiRef = createApiRef<SwappableComponentsApi>({
|
||||
id: 'core.swappable-components',
|
||||
});
|
||||
export const swappableComponentsApiRef =
|
||||
createApiRef<SwappableComponentsApi>().with({
|
||||
id: 'core.swappable-components',
|
||||
pluginId: 'app',
|
||||
});
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ApiRef, createApiRef } from '../system';
|
||||
import { createApiRef } from '../system';
|
||||
import { Expand, ExpandRecursive, Observable } from '@backstage/types';
|
||||
import { TranslationRef } from '../../translation';
|
||||
import { JSX } from 'react';
|
||||
@@ -358,6 +358,7 @@ export type TranslationApi = {
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export const translationApiRef: ApiRef<TranslationApi> = createApiRef({
|
||||
export const translationApiRef = createApiRef<TranslationApi>().with({
|
||||
id: 'core.translation',
|
||||
pluginId: 'app',
|
||||
});
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
/* We want to maintain the same information as an enum, so we disable the redeclaration warning */
|
||||
/* eslint-disable @typescript-eslint/no-redeclare */
|
||||
|
||||
import { ApiRef, createApiRef } from '../system';
|
||||
import { createApiRef } from '../system';
|
||||
import { IconComponent, IconElement } from '../../icons/types';
|
||||
import { Observable } from '@backstage/types';
|
||||
|
||||
@@ -28,7 +28,10 @@ import { Observable } from '@backstage/types';
|
||||
* For example, a Google OAuth provider that supports OAuth 2 and OpenID Connect,
|
||||
* would be declared as follows:
|
||||
*
|
||||
* const googleAuthApiRef = createApiRef<OAuthApi & OpenIDConnectApi>({ ... })
|
||||
* const googleAuthApiRef = createApiRef<OAuthApi & OpenIDConnectApi>().with({
|
||||
* id: 'core.auth.google',
|
||||
* pluginId: 'app',
|
||||
* })
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -333,14 +336,15 @@ export type SessionApi = {
|
||||
* Note that the ID token payload is only guaranteed to contain the user's numerical Google ID,
|
||||
* email and expiration information. Do not rely on any other fields, as they might not be present.
|
||||
*/
|
||||
export const googleAuthApiRef: ApiRef<
|
||||
export const googleAuthApiRef = createApiRef<
|
||||
OAuthApi &
|
||||
OpenIdConnectApi &
|
||||
ProfileInfoApi &
|
||||
BackstageIdentityApi &
|
||||
SessionApi
|
||||
> = createApiRef({
|
||||
>().with({
|
||||
id: 'core.auth.google',
|
||||
pluginId: 'app',
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -352,10 +356,11 @@ export const googleAuthApiRef: ApiRef<
|
||||
* See {@link https://developer.github.com/apps/building-oauth-apps/understanding-scopes-for-oauth-apps/}
|
||||
* for a full list of supported scopes.
|
||||
*/
|
||||
export const githubAuthApiRef: ApiRef<
|
||||
export const githubAuthApiRef = createApiRef<
|
||||
OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
|
||||
> = createApiRef({
|
||||
>().with({
|
||||
id: 'core.auth.github',
|
||||
pluginId: 'app',
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -367,14 +372,15 @@ export const githubAuthApiRef: ApiRef<
|
||||
* See {@link https://developer.okta.com/docs/guides/implement-oauth-for-okta/scopes/}
|
||||
* for a full list of supported scopes.
|
||||
*/
|
||||
export const oktaAuthApiRef: ApiRef<
|
||||
export const oktaAuthApiRef = createApiRef<
|
||||
OAuthApi &
|
||||
OpenIdConnectApi &
|
||||
ProfileInfoApi &
|
||||
BackstageIdentityApi &
|
||||
SessionApi
|
||||
> = createApiRef({
|
||||
>().with({
|
||||
id: 'core.auth.okta',
|
||||
pluginId: 'app',
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -386,14 +392,15 @@ export const oktaAuthApiRef: ApiRef<
|
||||
* See {@link https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html#limiting-scopes-of-a-personal-access-token}
|
||||
* for a full list of supported scopes.
|
||||
*/
|
||||
export const gitlabAuthApiRef: ApiRef<
|
||||
export const gitlabAuthApiRef = createApiRef<
|
||||
OAuthApi &
|
||||
OpenIdConnectApi &
|
||||
ProfileInfoApi &
|
||||
BackstageIdentityApi &
|
||||
SessionApi
|
||||
> = createApiRef({
|
||||
>().with({
|
||||
id: 'core.auth.gitlab',
|
||||
pluginId: 'app',
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -406,14 +413,15 @@ export const gitlabAuthApiRef: ApiRef<
|
||||
* - {@link https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent}
|
||||
* - {@link https://docs.microsoft.com/en-us/graph/permissions-reference}
|
||||
*/
|
||||
export const microsoftAuthApiRef: ApiRef<
|
||||
export const microsoftAuthApiRef = createApiRef<
|
||||
OAuthApi &
|
||||
OpenIdConnectApi &
|
||||
ProfileInfoApi &
|
||||
BackstageIdentityApi &
|
||||
SessionApi
|
||||
> = createApiRef({
|
||||
>().with({
|
||||
id: 'core.auth.microsoft',
|
||||
pluginId: 'app',
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -421,14 +429,15 @@ export const microsoftAuthApiRef: ApiRef<
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const oneloginAuthApiRef: ApiRef<
|
||||
export const oneloginAuthApiRef = createApiRef<
|
||||
OAuthApi &
|
||||
OpenIdConnectApi &
|
||||
ProfileInfoApi &
|
||||
BackstageIdentityApi &
|
||||
SessionApi
|
||||
> = createApiRef({
|
||||
>().with({
|
||||
id: 'core.auth.onelogin',
|
||||
pluginId: 'app',
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -440,10 +449,11 @@ export const oneloginAuthApiRef: ApiRef<
|
||||
* See {@link https://support.atlassian.com/bitbucket-cloud/docs/use-oauth-on-bitbucket-cloud/}
|
||||
* for a full list of supported scopes.
|
||||
*/
|
||||
export const bitbucketAuthApiRef: ApiRef<
|
||||
export const bitbucketAuthApiRef = createApiRef<
|
||||
OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
|
||||
> = createApiRef({
|
||||
>().with({
|
||||
id: 'core.auth.bitbucket',
|
||||
pluginId: 'app',
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -455,10 +465,11 @@ export const bitbucketAuthApiRef: ApiRef<
|
||||
* See {@link https://confluence.atlassian.com/bitbucketserver/bitbucket-oauth-2-0-provider-api-1108483661.html#BitbucketOAuth2.0providerAPI-scopes}
|
||||
* for a full list of supported scopes.
|
||||
*/
|
||||
export const bitbucketServerAuthApiRef: ApiRef<
|
||||
export const bitbucketServerAuthApiRef = createApiRef<
|
||||
OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
|
||||
> = createApiRef({
|
||||
>().with({
|
||||
id: 'core.auth.bitbucket-server',
|
||||
pluginId: 'app',
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -470,10 +481,11 @@ export const bitbucketServerAuthApiRef: ApiRef<
|
||||
* See {@link https://developer.atlassian.com/cloud/jira/platform/scopes-for-connect-and-oauth-2-3LO-apps/}
|
||||
* for a full list of supported scopes.
|
||||
*/
|
||||
export const atlassianAuthApiRef: ApiRef<
|
||||
export const atlassianAuthApiRef = createApiRef<
|
||||
OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
|
||||
> = createApiRef({
|
||||
>().with({
|
||||
id: 'core.auth.atlassian',
|
||||
pluginId: 'app',
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -485,14 +497,15 @@ export const atlassianAuthApiRef: ApiRef<
|
||||
* For more info about VMware Cloud identity and access management:
|
||||
* - {@link https://docs.vmware.com/en/VMware-Cloud-services/services/Using-VMware-Cloud-Services/GUID-53D39337-D93A-4B84-BD18-DDF43C21479A.html}
|
||||
*/
|
||||
export const vmwareCloudAuthApiRef: ApiRef<
|
||||
export const vmwareCloudAuthApiRef = createApiRef<
|
||||
OAuthApi &
|
||||
OpenIdConnectApi &
|
||||
ProfileInfoApi &
|
||||
BackstageIdentityApi &
|
||||
SessionApi
|
||||
> = createApiRef({
|
||||
>().with({
|
||||
id: 'core.auth.vmware-cloud',
|
||||
pluginId: 'app',
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -506,8 +519,9 @@ export const vmwareCloudAuthApiRef: ApiRef<
|
||||
* {@link https://docs.redhat.com/en/documentation/openshift_container_platform/latest/html-single/authentication_and_authorization/index#tokens-scoping-about_configuring-internal-oauth}
|
||||
* for available scopes.
|
||||
*/
|
||||
export const openshiftAuthApiRef: ApiRef<
|
||||
export const openshiftAuthApiRef = createApiRef<
|
||||
OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
|
||||
> = createApiRef({
|
||||
>().with({
|
||||
id: 'core.auth.openshift',
|
||||
pluginId: 'app',
|
||||
});
|
||||
|
||||
@@ -15,13 +15,54 @@
|
||||
*/
|
||||
|
||||
import { createApiRef } from './ApiRef';
|
||||
import type { ApiRef as ApiRefType } from './types';
|
||||
|
||||
describe('ApiRef', () => {
|
||||
it('should be created', () => {
|
||||
it('should be created with config', () => {
|
||||
const ref = createApiRef({ id: 'abc' });
|
||||
expect(ref.$$type).toBe('@backstage/ApiRef');
|
||||
expect(ref.id).toBe('abc');
|
||||
expect(String(ref)).toBe('apiRef{abc}');
|
||||
expect(() => ref.T).toThrow('tried to read ApiRef.T of apiRef{abc}');
|
||||
expect(ref.T).toBeNull();
|
||||
});
|
||||
|
||||
it('should not accept pluginId with deprecated config form', () => {
|
||||
expect(createApiRef<string>({ id: 'abc' }).id).toBe('abc');
|
||||
|
||||
// @ts-expect-error pluginId is only supported through .with(...)
|
||||
createApiRef<string>({ id: 'abc', pluginId: 'test' });
|
||||
});
|
||||
|
||||
it('should keep the deprecated config form id wide', () => {
|
||||
const ref = createApiRef<string>({ id: 'abc' });
|
||||
const wideRef: ApiRefType<string> = ref;
|
||||
expect(wideRef.id).toBe('abc');
|
||||
|
||||
// @ts-expect-error deprecated config form should not infer literal ids
|
||||
const literalRef: ApiRefType<string, 'abc'> = ref;
|
||||
expect(literalRef.id).toBe('abc');
|
||||
});
|
||||
|
||||
it('should be created with builder pattern', () => {
|
||||
const ref = createApiRef<string>().with({ id: 'abc', pluginId: 'test' });
|
||||
expect(ref.$$type).toBe('@backstage/ApiRef');
|
||||
expect(ref.id).toBe('abc');
|
||||
expect(String(ref)).toBe('apiRef{abc}');
|
||||
expect(ref.T).toBeNull();
|
||||
expect((ref as { pluginId?: string }).pluginId).toBe('test');
|
||||
|
||||
// @ts-expect-error pluginId is internal runtime metadata
|
||||
expect(ref.pluginId).toBe('test');
|
||||
});
|
||||
|
||||
it('should infer literal ids with builder pattern', () => {
|
||||
const ref = createApiRef<string>().with({ id: 'abc', pluginId: 'test' });
|
||||
const literalRef: ApiRefType<string, 'abc'> = ref;
|
||||
expect(literalRef.id).toBe('abc');
|
||||
|
||||
// @ts-expect-error builder pattern should preserve literal ids
|
||||
const wrongLiteralRef: ApiRefType<string, 'def'> = ref;
|
||||
expect(wrongLiteralRef.id).toBe('abc');
|
||||
});
|
||||
|
||||
it('should reject invalid ids', () => {
|
||||
@@ -47,4 +88,10 @@ describe('ApiRef', () => {
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it('should reject invalid ids with builder pattern', () => {
|
||||
expect(() => createApiRef().with({ id: '123' })).toThrow(
|
||||
`API id must only contain period separated lowercase alphanum tokens with dashes, got '123'`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { OpaqueApiRef } from '@internal/frontend';
|
||||
import type { ApiRef } from './types';
|
||||
|
||||
/**
|
||||
@@ -25,48 +26,110 @@ export type ApiRefConfig = {
|
||||
id: string;
|
||||
};
|
||||
|
||||
class ApiRefImpl<T> implements ApiRef<T> {
|
||||
constructor(private readonly config: ApiRefConfig) {
|
||||
const valid = config.id
|
||||
.split('.')
|
||||
.flatMap(part => part.split('-'))
|
||||
.every(part => part.match(/^[a-z][a-z0-9]*$/));
|
||||
if (!valid) {
|
||||
throw new Error(
|
||||
`API id must only contain period separated lowercase alphanum tokens with dashes, got '${config.id}'`,
|
||||
);
|
||||
}
|
||||
}
|
||||
type ApiRefBuilderConfig<TId extends string> = {
|
||||
id: TId;
|
||||
pluginId?: string;
|
||||
};
|
||||
|
||||
get id(): string {
|
||||
return this.config.id;
|
||||
}
|
||||
|
||||
// Utility for getting type of an api, using `typeof apiRef.T`
|
||||
get T(): T {
|
||||
throw new Error(`tried to read ApiRef.T of ${this}`);
|
||||
}
|
||||
|
||||
toString() {
|
||||
return `apiRef{${this.config.id}}`;
|
||||
function validateId(id: string): void {
|
||||
const valid = id
|
||||
.split('.')
|
||||
.flatMap(part => part.split('-'))
|
||||
.every(part => part.match(/^[a-z][a-z0-9]*$/));
|
||||
if (!valid) {
|
||||
throw new Error(
|
||||
`API id must only contain period separated lowercase alphanum tokens with dashes, got '${id}'`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function makeApiRef<T, TId extends string>(
|
||||
config: ApiRefBuilderConfig<TId>,
|
||||
): ApiRef<T, TId> & { readonly $$type: '@backstage/ApiRef' } {
|
||||
return OpaqueApiRef.createInstance('v1', {
|
||||
id: config.id,
|
||||
...(config.pluginId ? { pluginId: config.pluginId } : {}),
|
||||
T: null as unknown as T,
|
||||
toString() {
|
||||
return `apiRef{${config.id}}`;
|
||||
},
|
||||
}) as ApiRef<T, TId> & { readonly $$type: '@backstage/ApiRef' };
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a reference to an API. The provided `id` is a stable identifier for
|
||||
* the API implementation.
|
||||
* Creates a reference to an API.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* The frontend system infers the owning plugin for an API from the `id`. The
|
||||
* The `id` is a stable identifier for the API implementation. The frontend
|
||||
* system infers the owning plugin for an API from the `id`. When using the
|
||||
* builder form, you can instead provide a `pluginId` explicitly. The
|
||||
* recommended pattern is `plugin.<plugin-id>.*` (for example,
|
||||
* `plugin.catalog.entity-presentation`). This ensures that other plugins can't
|
||||
* mistakenly override your API implementation.
|
||||
*
|
||||
* @param config - The descriptor of the API to reference.
|
||||
* @returns An API reference.
|
||||
* The recommended way to create an API reference is:
|
||||
*
|
||||
* ```ts
|
||||
* const myApiRef = createApiRef<MyApi>().with({
|
||||
* id: 'my-api',
|
||||
* pluginId: 'my-plugin',
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* The legacy way to create an API reference is:
|
||||
*
|
||||
* ```ts
|
||||
* const myApiRef = createApiRef<MyApi>({ id: 'plugin.my.api' });
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function createApiRef<T>(config: ApiRefConfig): ApiRef<T> {
|
||||
return new ApiRefImpl<T>(config);
|
||||
/**
|
||||
* Creates a reference to an API.
|
||||
*
|
||||
* @deprecated Use `createApiRef<T>().with(...)` instead.
|
||||
* @public
|
||||
*/
|
||||
export function createApiRef<T>(
|
||||
config: ApiRefConfig,
|
||||
): ApiRef<T> & { readonly $$type: '@backstage/ApiRef' };
|
||||
/**
|
||||
* Creates a reference to an API.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* Returns a builder with a `.with()` method for providing the API reference
|
||||
* configuration.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function createApiRef<T>(): {
|
||||
with<const TId extends string>(
|
||||
config: ApiRefConfig & { id: TId; pluginId?: string },
|
||||
): ApiRef<T, TId> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
};
|
||||
export function createApiRef<T>(config?: ApiRefConfig):
|
||||
| (ApiRef<T> & { readonly $$type: '@backstage/ApiRef' })
|
||||
| {
|
||||
with<const TId extends string>(
|
||||
config: ApiRefConfig & { id: TId; pluginId?: string },
|
||||
): ApiRef<T, TId> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
} {
|
||||
if (config) {
|
||||
validateId(config.id);
|
||||
return makeApiRef<T, string>(config);
|
||||
}
|
||||
return {
|
||||
with<const TId extends string>(
|
||||
withConfig: ApiRefConfig & { id: TId; pluginId?: string },
|
||||
): ApiRef<T, TId> & { readonly $$type: '@backstage/ApiRef' } {
|
||||
validateId(withConfig.id);
|
||||
return makeApiRef<T, TId>(withConfig);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -19,9 +19,10 @@
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type ApiRef<T> = {
|
||||
id: string;
|
||||
T: T;
|
||||
export type ApiRef<T, TId extends string = string> = {
|
||||
readonly $$type?: '@backstage/ApiRef';
|
||||
readonly id: TId;
|
||||
readonly T: T;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -38,7 +38,9 @@ describe('useApiHolder', () => {
|
||||
const renderedHook = renderHook(() => useApiHolder());
|
||||
|
||||
const holder = renderedHook.result.current;
|
||||
expect(holder.get(createApiRef<string>({ id: 'x' }))).toBeUndefined();
|
||||
expect(
|
||||
holder.get(createApiRef<string>().with({ id: 'x' })),
|
||||
).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -53,7 +55,7 @@ describe('useApi', () => {
|
||||
const get = jest.fn(() => 'my-api-impl');
|
||||
context.set({ 1: { get } });
|
||||
|
||||
const apiRef = createApiRef<string>({ id: 'x' });
|
||||
const apiRef = createApiRef<string>().with({ id: 'x' });
|
||||
const renderedHook = renderHook(() => useApi(apiRef));
|
||||
|
||||
const value = renderedHook.result.current;
|
||||
|
||||
@@ -20,7 +20,7 @@ import { createApiRef } from '../apis/system';
|
||||
|
||||
describe('ApiBlueprint', () => {
|
||||
it('should create an extension with sensible defaults', () => {
|
||||
const api = createApiRef<{ foo: string }>({ id: 'test' });
|
||||
const api = createApiRef<{ foo: string }>().with({ id: 'test' });
|
||||
|
||||
const extension = ApiBlueprint.make({
|
||||
params: defineParams =>
|
||||
@@ -57,8 +57,8 @@ describe('ApiBlueprint', () => {
|
||||
});
|
||||
|
||||
it('should properly type the API factory', () => {
|
||||
const fooApi = createApiRef<{ foo: string }>({ id: 'foo' });
|
||||
const barApi = createApiRef<{ bar: string }>({ id: 'bar' });
|
||||
const fooApi = createApiRef<{ foo: string }>().with({ id: 'foo' });
|
||||
const barApi = createApiRef<{ bar: string }>().with({ id: 'bar' });
|
||||
|
||||
expect('test').not.toBe('failing without assertions');
|
||||
|
||||
@@ -152,7 +152,7 @@ describe('ApiBlueprint', () => {
|
||||
});
|
||||
|
||||
it('should create an extension with custom factory', () => {
|
||||
const api = createApiRef<{ foo: string }>({ id: 'test' });
|
||||
const api = createApiRef<{ foo: string }>().with({ id: 'test' });
|
||||
const factory = jest.fn(() => ({ foo: 'bar' }));
|
||||
|
||||
const extension = ApiBlueprint.makeWithOverrides({
|
||||
|
||||
@@ -86,7 +86,7 @@ export const IconBundleBlueprint: ExtensionBlueprint<{
|
||||
};
|
||||
output: ExtensionDataRef<
|
||||
{
|
||||
[x: string]: IconComponent | IconElement;
|
||||
[x: string]: IconElement | IconComponent;
|
||||
},
|
||||
'core.icons',
|
||||
{}
|
||||
@@ -97,7 +97,7 @@ export const IconBundleBlueprint: ExtensionBlueprint<{
|
||||
dataRefs: {
|
||||
icons: ConfigurableExtensionDataRef<
|
||||
{
|
||||
[x: string]: IconComponent | IconElement;
|
||||
[x: string]: IconElement | IconComponent;
|
||||
},
|
||||
'core.icons',
|
||||
{}
|
||||
|
||||
@@ -478,7 +478,7 @@ const appPlugin: OverridableFrontendPlugin<
|
||||
icons: ExtensionInput<
|
||||
ConfigurableExtensionDataRef<
|
||||
{
|
||||
[x: string]: IconComponent | IconElement;
|
||||
[x: string]: IconElement | IconComponent;
|
||||
},
|
||||
'core.icons',
|
||||
{}
|
||||
|
||||
@@ -200,7 +200,9 @@ export type FormFieldExtensionData<
|
||||
};
|
||||
|
||||
// @alpha (undocumented)
|
||||
export const formFieldsApiRef: ApiRef<ScaffolderFormFieldsApi>;
|
||||
export const formFieldsApiRef: ApiRef<ScaffolderFormFieldsApi> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
|
||||
// @alpha (undocumented)
|
||||
export type FormValidation = {
|
||||
|
||||
@@ -208,7 +208,9 @@ export type ReviewStepProps = {
|
||||
export type ScaffolderApi = ScaffolderApi_2;
|
||||
|
||||
// @public (undocumented)
|
||||
export const scaffolderApiRef: ApiRef<ScaffolderApi_2>;
|
||||
export const scaffolderApiRef: ApiRef<ScaffolderApi_2> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
|
||||
// @public @deprecated (undocumented)
|
||||
export type ScaffolderDryRunOptions = ScaffolderDryRunOptions_2;
|
||||
|
||||
@@ -479,7 +479,9 @@ export const formDecoratorsApi: OverridableExtensionDefinition<{
|
||||
}>;
|
||||
|
||||
// @alpha (undocumented)
|
||||
export const formDecoratorsApiRef: ApiRef<ScaffolderFormDecoratorsApi>;
|
||||
export const formDecoratorsApiRef: ApiRef<ScaffolderFormDecoratorsApi> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
|
||||
export { formFieldsApiRef };
|
||||
|
||||
|
||||
@@ -528,7 +528,9 @@ export type RouterProps = {
|
||||
export type ScaffolderApi = ScaffolderApi_2;
|
||||
|
||||
// @public @deprecated (undocumented)
|
||||
export const scaffolderApiRef: ApiRef<ScaffolderApi_2>;
|
||||
export const scaffolderApiRef: ApiRef<ScaffolderApi_2> & {
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
|
||||
// @public @deprecated
|
||||
export class ScaffolderClient extends ScaffolderClient_2 {}
|
||||
|
||||
Reference in New Issue
Block a user