frontend-plugin-api: refactor extension "at" option to "attachTo"

Co-authored-by: Camila Belo <camilaibs@gmail.com>
Co-authored-by: Fredrik Adelöw <freben@gmail.com>
Co-authored-by: Vincenzo Scamporlino <vincenzos@spotify.com>
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2023-10-06 15:25:10 +02:00
parent dc3f392940
commit 06432f900c
27 changed files with 170 additions and 106 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/frontend-plugin-api': minor
---
Extension attachment point is now configured via `attachTo: { id, input }` instead of `at: 'id/input'`.
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/plugin-search-react': patch
'@backstage/plugin-graphiql': patch
---
Updated `/alpha` exports to use new `attachTo` option.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/frontend-app-api': patch
---
Updates for `at` -> `attachTo` refactor.
@@ -22,7 +22,7 @@ import {
export const Core = createExtension({
id: 'core',
at: 'root',
attachTo: { id: 'root', input: 'default' },
inputs: {
apis: createExtensionInput({
api: coreExtensionData.apiFactory,
@@ -24,7 +24,7 @@ import { SidebarPage } from '@backstage/core-components';
export const CoreLayout = createExtension({
id: 'core.layout',
at: 'root',
attachTo: { id: 'root', input: 'default' },
inputs: {
nav: createExtensionInput(
{
@@ -73,7 +73,7 @@ const SidebarNavItem = (props: NavTarget) => {
export const CoreNav = createExtension({
id: 'core.nav',
at: 'core.layout/nav',
attachTo: { id: 'core.layout', input: 'nav' },
inputs: {
items: createExtensionInput({
target: coreExtensionData.navTarget,
@@ -24,7 +24,7 @@ import { useRoutes } from 'react-router-dom';
export const CoreRoutes = createExtension({
id: 'core.routes',
at: 'core.layout/content',
attachTo: { id: 'core.layout', input: 'content' },
inputs: {
routes: createExtensionInput({
path: coreExtensionData.routePath,
@@ -40,13 +40,15 @@ const refOrder = [ref1, ref2, ref3, ref4, ref5];
function createTestExtension(options: {
id: string;
at?: string;
parent?: string;
path?: string;
routeRef?: RouteRef;
}) {
return createExtension({
id: options.id,
at: options.at ?? 'core.routes/children',
attachTo: options.parent
? { id: options.parent, input: 'children' }
: { id: 'core.routes', input: 'children' },
output: {
element: coreExtensionData.reactElement,
path: coreExtensionData.routePath.optional(),
@@ -126,13 +128,13 @@ describe('discovery', () => {
}),
createTestExtension({
id: 'page2',
at: 'page1/children',
parent: 'page1',
path: 'bar/:id',
routeRef: ref2,
}),
createTestExtension({
id: 'page3',
at: 'page2/children',
parent: 'page2',
path: 'baz',
routeRef: ref3,
}),
@@ -143,7 +145,7 @@ describe('discovery', () => {
}),
createTestExtension({
id: 'page5',
at: 'page1/children',
parent: 'page1',
path: 'blop',
routeRef: ref5,
}),
@@ -194,7 +196,7 @@ describe('discovery', () => {
}),
createTestExtension({
id: 'page2',
at: 'page1/children',
parent: 'page1',
path: 'bar/:id',
routeRef: ref2,
}),
@@ -205,13 +207,13 @@ describe('discovery', () => {
}),
createTestExtension({
id: 'page4',
at: 'page3/children',
parent: 'page3',
path: 'divsoup',
routeRef: ref4,
}),
createTestExtension({
id: 'page5',
at: 'page3/children',
parent: 'page3',
path: 'blop',
routeRef: ref5,
}),
@@ -242,7 +244,7 @@ describe('discovery', () => {
}),
createTestExtension({
id: 'page2',
at: 'page1/children',
parent: 'page1',
path: '/bar/:id',
routeRef: ref2,
}),
@@ -253,13 +255,13 @@ describe('discovery', () => {
}),
createTestExtension({
id: 'page4',
at: 'page3/children',
parent: 'page3',
path: '/divsoup',
routeRef: ref4,
}),
createTestExtension({
id: 'page5',
at: 'page3/children',
parent: 'page3',
path: '/blop',
routeRef: ref5,
}),
@@ -289,16 +291,16 @@ describe('discovery', () => {
}),
createTestExtension({
id: 'page1',
at: 'foo/children',
parent: 'foo',
routeRef: ref1,
}),
createTestExtension({
id: 'fooChild',
at: 'foo/children',
parent: 'foo',
}),
createTestExtension({
id: 'page2',
at: 'fooChild/children',
parent: 'fooChild',
routeRef: ref2,
}),
createTestExtension({
@@ -311,17 +313,17 @@ describe('discovery', () => {
}),
createTestExtension({
id: 'page3Child',
at: 'page3/children',
parent: 'page3',
path: '',
}),
createTestExtension({
id: 'page4',
at: 'page3Child/children',
parent: 'page3Child',
routeRef: ref4,
}),
createTestExtension({
id: 'page5',
at: 'page4/children',
parent: 'page4',
routeRef: ref5,
}),
]);
@@ -361,29 +363,29 @@ describe('discovery', () => {
}),
createTestExtension({
id: 'page1Child',
at: 'page1/children',
parent: 'page1',
path: 'bar',
}),
createTestExtension({
id: 'page2',
at: 'page1Child/children',
parent: 'page1Child',
routeRef: ref2,
}),
createTestExtension({
id: 'page3',
at: 'page2/children',
parent: 'page2',
path: 'baz',
routeRef: ref3,
}),
createTestExtension({
id: 'page4',
at: 'page3/children',
parent: 'page3',
path: '/blop',
routeRef: ref4,
}),
createTestExtension({
id: 'page5',
at: 'page2/children',
parent: 'page2',
routeRef: ref5,
}),
]);
@@ -445,30 +447,30 @@ describe('discovery', () => {
}),
createTestExtension({
id: 'page1',
at: 'r/children',
parent: 'r',
path: 'x',
routeRef: ref1,
}),
createTestExtension({
id: 'y',
path: 'y',
at: 'r/children',
parent: 'r',
}),
createTestExtension({
id: 'page2',
at: 'y/children',
parent: 'y',
path: '1',
routeRef: ref2,
}),
createTestExtension({
id: 'page3',
at: 'page2/children',
parent: 'page2',
path: 'a',
routeRef: ref3,
}),
createTestExtension({
id: 'page4',
at: 'page2/children',
parent: 'page2',
path: 'b',
routeRef: ref4,
}),
@@ -32,9 +32,7 @@ describe('createInstances', () => {
app: {
extensions: [
{
root: {
at: '',
},
root: {},
},
],
},
@@ -58,7 +56,7 @@ describe('createInstances', () => {
extensions: [
createExtension({
id: 'root',
at: 'core.routes/route',
attachTo: { id: 'core.routes', input: 'route' },
inputs: {},
output: {},
factory() {},
@@ -202,8 +202,8 @@ export function createInstances(options: {
Map<string, ExtensionInstanceParameters[]>
>();
for (const instanceParams of extensionParams) {
const [extensionId, pointId = 'default'] = instanceParams.at.split('/');
const extensionId = instanceParams.attachTo.id;
const pointId = instanceParams.attachTo.input;
let pointMap = attachmentMap.get(extensionId);
if (!pointMap) {
pointMap = new Map();
@@ -28,7 +28,7 @@ const inputMirrorDataRef = createExtensionDataRef<unknown>('mirror');
const simpleExtension = createExtension({
id: 'core.test',
at: 'ignored',
attachTo: { id: 'ignored', input: 'ignored' },
output: {
test: testDataRef,
other: otherDataRef.optional(),
@@ -101,7 +101,7 @@ describe('createExtensionInstance', () => {
config: undefined,
extension: createExtension({
id: 'core.test',
at: 'ignored',
attachTo: { id: 'ignored', input: 'ignored' },
inputs: {
optionalSingletonPresent: createExtensionInput(
{
@@ -166,7 +166,7 @@ describe('createExtensionInstance', () => {
config: { other: 'not-a-number' },
extension: createExtension({
id: 'core.test',
at: 'ignored',
attachTo: { id: 'ignored', input: 'ignored' },
output: {},
factory() {
const error = new Error('NOPE');
@@ -188,7 +188,7 @@ describe('createExtensionInstance', () => {
config: undefined,
extension: createExtension({
id: 'core.test',
at: 'ignored',
attachTo: { id: 'ignored', input: 'ignored' },
output: {
test1: testDataRef,
test2: testDataRef,
@@ -211,7 +211,7 @@ describe('createExtensionInstance', () => {
config: undefined,
extension: createExtension({
id: 'core.test',
at: 'ignored',
attachTo: { id: 'ignored', input: 'ignored' },
output: {
test: testDataRef,
},
@@ -232,7 +232,7 @@ describe('createExtensionInstance', () => {
config: undefined,
extension: createExtension({
id: 'core.test',
at: 'ignored',
attachTo: { id: 'ignored', input: 'ignored' },
inputs: {
singleton: createExtensionInput(
{
@@ -273,7 +273,7 @@ describe('createExtensionInstance', () => {
config: undefined,
extension: createExtension({
id: 'core.test',
at: 'ignored',
attachTo: { id: 'ignored', input: 'ignored' },
inputs: {
singleton: createExtensionInput(
{
@@ -314,7 +314,7 @@ describe('createExtensionInstance', () => {
config: undefined,
extension: createExtension({
id: 'core.test',
at: 'ignored',
attachTo: { id: 'ignored', input: 'ignored' },
inputs: {
singleton: createExtensionInput(
{
@@ -350,7 +350,7 @@ describe('createExtensionInstance', () => {
config: undefined,
extension: createExtension({
id: 'core.test',
at: 'ignored',
attachTo: { id: 'ignored', input: 'ignored' },
inputs: {
singleton: createExtensionInput(
{
@@ -26,7 +26,7 @@ import {
function makeExt(id: string, status: 'disabled' | 'enabled' = 'enabled') {
return {
id,
at: 'root',
attachTo: { id: 'root', input: 'default' },
disabled: status === 'disabled',
} as Extension<unknown>;
}
@@ -52,8 +52,8 @@ describe('mergeExtensionParameters', () => {
parameters: [],
}),
).toEqual([
{ extension: a, at: 'root' },
{ extension: b, at: 'root' },
{ extension: a, attachTo: { id: 'root', input: 'default' } },
{ extension: b, attachTo: { id: 'root', input: 'default' } },
]);
});
@@ -68,13 +68,17 @@ describe('mergeExtensionParameters', () => {
parameters: [
{
id: 'b',
at: 'derp',
attachTo: { id: 'derp', input: 'default' },
},
],
}),
).toEqual([
{ extension: a, at: 'root', source: pluginA },
{ extension: b, at: 'derp' },
{
extension: a,
attachTo: { id: 'root', input: 'default' },
source: pluginA,
},
{ extension: b, attachTo: { id: 'derp', input: 'default' } },
]);
});
@@ -102,8 +106,18 @@ describe('mergeExtensionParameters', () => {
],
}),
).toEqual([
{ extension: a, at: 'root', source: plugin, config: { foo: { bar: 1 } } },
{ extension: b, at: 'root', source: plugin, config: { foo: { qux: 3 } } },
{
extension: a,
attachTo: { id: 'root', input: 'default' },
source: plugin,
config: { foo: { bar: 1 } },
},
{
extension: b,
attachTo: { id: 'root', input: 'default' },
source: plugin,
config: { foo: { qux: 3 } },
},
]);
});
@@ -126,8 +140,8 @@ describe('mergeExtensionParameters', () => {
],
}),
).toEqual([
{ extension: b, at: 'root' },
{ extension: a, at: 'root' },
{ extension: b, attachTo: { id: 'root', input: 'default' } },
{ extension: a, attachTo: { id: 'root', input: 'default' } },
]);
});
});
@@ -315,14 +329,18 @@ describe('expandShorthandExtensionParameters', () => {
expect(() =>
run({ 'core.router': { id: 'some.id' } }),
).toThrowErrorMatchingInlineSnapshot(
`"Invalid extension configuration at app.extensions[1][core.router].id, unknown parameter; expected one of 'at', 'disabled', 'config'"`,
`"Invalid extension configuration at app.extensions[1][core.router].id, unknown parameter; expected one of 'attachTo', 'disabled', 'config'"`,
);
});
it('supports object at', () => {
expect(run({ 'core.router': { at: 'other.root/inputs' } })).toEqual({
it('supports object attachTo', () => {
expect(
run({
'core.router': { attachTo: { id: 'other.root', input: 'inputs' } },
}),
).toEqual({
id: 'core.router',
at: 'other.root/inputs',
attachTo: { id: 'other.root', input: 'inputs' },
});
expect(() =>
run({
@@ -331,7 +349,7 @@ describe('expandShorthandExtensionParameters', () => {
},
}),
).toThrowErrorMatchingInlineSnapshot(
`"Invalid extension configuration at app.extensions[1][core.router].id, unknown parameter; expected one of 'at', 'disabled', 'config'"`,
`"Invalid extension configuration at app.extensions[1][core.router].id, unknown parameter; expected one of 'attachTo', 'disabled', 'config'"`,
);
});
@@ -369,7 +387,7 @@ describe('expandShorthandExtensionParameters', () => {
expect(() =>
run({ 'core.router': { foo: { settings: true } } }),
).toThrowErrorMatchingInlineSnapshot(
`"Invalid extension configuration at app.extensions[1][core.router].foo, unknown parameter; expected one of 'at', 'disabled', 'config'"`,
`"Invalid extension configuration at app.extensions[1][core.router].foo, unknown parameter; expected one of 'attachTo', 'disabled', 'config'"`,
);
});
});
@@ -20,12 +20,12 @@ import { JsonValue } from '@backstage/types';
export interface ExtensionParameters {
id: string;
at?: string;
attachTo?: { id: string; input: string };
disabled?: boolean;
config?: unknown;
}
const knownExtensionParameters = ['at', 'disabled', 'config'];
const knownExtensionParameters = ['attachTo', 'disabled', 'config'];
// Since we'll never merge arrays in config the config reader context
// isn't too much of a help. Fall back to manual config reading logic
@@ -143,15 +143,33 @@ export function expandShorthandExtensionParameters(
throw new Error(errorMsg('value must be a boolean or object', id));
}
const at = value.at;
const attachTo = value.attachTo as { id: string; input: string } | undefined;
const disabled = value.disabled;
const config = value.config;
if (at !== undefined && typeof at !== 'string') {
throw new Error(errorMsg('must be a string', id, 'at'));
} else if (disabled !== undefined && typeof disabled !== 'boolean') {
if (attachTo !== undefined) {
if (
attachTo === null ||
typeof attachTo !== 'object' ||
Array.isArray(attachTo)
) {
throw new Error(errorMsg('must be an object', id, 'attachTo'));
}
if (typeof attachTo.id !== 'string' || attachTo.id === '') {
throw new Error(
errorMsg('must be a non-empty string', id, 'attachTo.id'),
);
}
if (typeof attachTo.input !== 'string' || attachTo.input === '') {
throw new Error(
errorMsg('must be a non-empty string', id, 'attachTo.input'),
);
}
}
if (disabled !== undefined && typeof disabled !== 'boolean') {
throw new Error(errorMsg('must be a boolean', id, 'disabled'));
} else if (
}
if (
config !== undefined &&
(typeof config !== 'object' || config === null || Array.isArray(config))
) {
@@ -175,7 +193,7 @@ export function expandShorthandExtensionParameters(
return {
id,
at,
attachTo,
disabled,
config,
};
@@ -184,7 +202,7 @@ export function expandShorthandExtensionParameters(
export interface ExtensionInstanceParameters {
extension: Extension<unknown>;
source?: BackstagePlugin;
at: string;
attachTo: { id: string; input: string };
config?: unknown;
}
@@ -217,7 +235,7 @@ export function mergeExtensionParameters(options: {
extension,
params: {
source,
at: extension.at,
attachTo: extension.attachTo,
disabled: extension.disabled,
config: undefined as unknown,
},
@@ -226,7 +244,7 @@ export function mergeExtensionParameters(options: {
extension,
params: {
source: undefined,
at: extension.at,
attachTo: extension.attachTo,
disabled: extension.disabled,
config: undefined as unknown,
},
@@ -283,8 +301,8 @@ export function mergeExtensionParameters(options: {
);
if (existingIndex !== -1) {
const existing = overrides[existingIndex];
if (overrideParam.at) {
existing.params.at = overrideParam.at;
if (overrideParam.attachTo) {
existing.params.attachTo = overrideParam.attachTo;
}
if (overrideParam.config) {
// TODO: merge config?
@@ -309,7 +327,7 @@ export function mergeExtensionParameters(options: {
.filter(override => !override.params.disabled)
.map(param => ({
extension: param.extension,
at: param.params.at,
attachTo: param.params.attachTo,
source: param.params.source,
config: param.params.config,
}));
+12 -3
View File
@@ -136,7 +136,10 @@ export interface CreateExtensionOptions<
TConfig,
> {
// (undocumented)
at: string;
attachTo: {
id: string;
input: string;
};
// (undocumented)
configSchema?: PortableSchema<TConfig>;
// (undocumented)
@@ -182,7 +185,10 @@ export function createPageExtension<
}
) & {
id: string;
at?: string;
attachTo?: {
id: string;
input: string;
};
disabled?: boolean;
inputs?: TInputs;
routeRef?: RouteRef;
@@ -209,7 +215,10 @@ export interface Extension<TConfig> {
// (undocumented)
$$type: '@backstage/Extension';
// (undocumented)
at: string;
attachTo: {
id: string;
input: string;
};
// (undocumented)
configSchema?: PortableSchema<TConfig>;
// (undocumented)
@@ -33,7 +33,7 @@ describe('createApiExtension', () => {
expect(extension).toEqual({
$$type: '@backstage/Extension',
id: 'apis.test',
at: 'core/apis',
attachTo: { id: 'core', input: 'apis' },
disabled: false,
configSchema: undefined,
inputs: {},
@@ -67,7 +67,7 @@ describe('createApiExtension', () => {
expect(extension).toEqual({
$$type: '@backstage/Extension',
id: 'apis.test',
at: 'core/apis',
attachTo: { id: 'core', input: 'apis' },
disabled: false,
configSchema: undefined,
inputs: {},
@@ -51,7 +51,7 @@ export function createApiExtension<
return createExtension({
id: `apis.${apiRef.id}`,
at: 'core/apis',
attachTo: { id: 'core', input: 'apis' },
inputs: extensionInputs,
configSchema,
output: {
@@ -31,7 +31,7 @@ export function createNavItemExtension(options: {
const { id, routeRef, title, icon } = options;
return createExtension({
id,
at: 'core.nav/items',
attachTo: { id: 'core.nav', input: 'items' },
configSchema: createSchemaFromZod(z =>
z.object({
title: z.string().default(title),
@@ -35,7 +35,7 @@ describe('createPageExtension', () => {
).toEqual({
$$type: '@backstage/Extension',
id: 'test',
at: 'core.routes/routes',
attachTo: { id: 'core.routes', input: 'routes' },
configSchema: expect.anything(),
disabled: false,
inputs: {},
@@ -50,7 +50,7 @@ describe('createPageExtension', () => {
expect(
createPageExtension({
id: 'test',
at: 'other/place',
attachTo: { id: 'other', input: 'place' },
disabled: true,
configSchema,
inputs: {
@@ -63,7 +63,7 @@ describe('createPageExtension', () => {
).toEqual({
$$type: '@backstage/Extension',
id: 'test',
at: 'other/place',
attachTo: { id: 'other', input: 'place' },
configSchema: expect.anything(),
disabled: true,
inputs: {
@@ -88,7 +88,7 @@ describe('createPageExtension', () => {
).toEqual({
$$type: '@backstage/Extension',
id: 'test',
at: 'core.routes/routes',
attachTo: { id: 'core.routes', input: 'routes' },
configSchema: expect.anything(),
disabled: false,
inputs: {},
@@ -44,7 +44,7 @@ export function createPageExtension<
}
) & {
id: string;
at?: string;
attachTo?: { id: string; input: string };
disabled?: boolean;
inputs?: TInputs;
routeRef?: RouteRef;
@@ -63,7 +63,7 @@ export function createPageExtension<
return createExtension({
id: options.id,
at: options.at ?? 'core.routes/routes',
attachTo: options.attachTo ?? { id: 'core.routes', input: 'routes' },
disabled: options.disabled,
output: {
element: coreExtensionData.reactElement,
@@ -21,7 +21,7 @@ import { AppTheme } from '@backstage/core-plugin-api';
export function createThemeExtension(theme: AppTheme) {
return createExtension({
id: `themes.${theme.id}`,
at: 'core/themes',
attachTo: { id: 'core', input: 'themes' },
output: {
theme: coreExtensionData.theme,
},
@@ -26,7 +26,7 @@ describe('createExtension', () => {
it('should create an extension with a simple output', () => {
const extension = createExtension({
id: 'test',
at: 'root',
attachTo: { id: 'root', input: 'default' },
output: {
foo: stringData,
},
@@ -56,7 +56,7 @@ describe('createExtension', () => {
it('should create an extension with a some optional output', () => {
const extension = createExtension({
id: 'test',
at: 'root',
attachTo: { id: 'root', input: 'default' },
output: {
foo: stringData,
bar: stringData.optional(),
@@ -94,7 +94,7 @@ describe('createExtension', () => {
it('should create an extension with input', () => {
const extension = createExtension({
id: 'test',
at: 'root',
attachTo: { id: 'root', input: 'default' },
inputs: {
mixed: createExtensionInput({
required: stringData,
@@ -80,7 +80,7 @@ export interface CreateExtensionOptions<
TConfig,
> {
id: string;
at: string;
attachTo: { id: string; input: string };
disabled?: boolean;
inputs?: TInputs;
output: TOutput;
@@ -97,7 +97,7 @@ export interface CreateExtensionOptions<
export interface Extension<TConfig> {
$$type: '@backstage/Extension';
id: string;
at: string;
attachTo: { id: string; input: string };
disabled: boolean;
inputs: AnyExtensionInputMap;
output: AnyExtensionDataMap;
@@ -30,7 +30,7 @@ const nameExtensionDataRef = createExtensionDataRef<string>('name');
const TechRadarPage = createExtension({
id: 'plugin.techradar.page',
at: 'test.output/names',
attachTo: { id: 'test.output', input: 'names' },
output: {
name: nameExtensionDataRef,
},
@@ -41,7 +41,7 @@ const TechRadarPage = createExtension({
const CatalogPage = createExtension({
id: 'plugin.catalog.page',
at: 'test.output/names',
attachTo: { id: 'test.output', input: 'names' },
output: {
name: nameExtensionDataRef,
},
@@ -55,7 +55,7 @@ const CatalogPage = createExtension({
const TechDocsAddon = createExtension({
id: 'plugin.techdocs.addon.example',
at: 'plugin.techdocs.page/addons',
attachTo: { id: 'plugin.techdocs.page', input: 'addons' },
output: {
name: nameExtensionDataRef,
},
@@ -69,7 +69,7 @@ const TechDocsAddon = createExtension({
const TechDocsPage = createExtension({
id: 'plugin.techdocs.page',
at: 'test.output/names',
attachTo: { id: 'test.output', input: 'names' },
inputs: {
addons: createExtensionInput({
name: nameExtensionDataRef,
@@ -85,7 +85,7 @@ const TechDocsPage = createExtension({
const outputExtension = createExtension({
id: 'test.output',
at: 'root',
attachTo: { id: 'root', input: 'default' },
inputs: {
names: createExtensionInput({
name: nameExtensionDataRef,
+1 -1
View File
@@ -86,7 +86,7 @@ export function createEndpointExtension<TConfig extends {}>(options: {
}) {
return createExtension({
id: `apis.plugin.graphiql.browse.${options.id}`,
at: 'apis.plugin.graphiql.browse/endpoints',
attachTo: { id: 'apis.plugin.graphiql.browse', input: 'endpoints' },
configSchema: options.configSchema,
disabled: options.disabled ?? false,
output: {
+4 -1
View File
@@ -48,7 +48,10 @@ export type SearchResultItemExtensionOptions<
},
> = {
id: string;
at?: string;
attachTo?: {
id: string;
input: string;
};
configSchema?: PortableSchema<TConfig>;
component: (options: {
config: TConfig;
+2 -2
View File
@@ -58,7 +58,7 @@ describe('createSearchResultListItemExtension', () => {
const TechDocsSearchResultItemExtension =
createSearchResultListItemExtension({
id: 'techdocs',
at: 'plugin.search.page/items',
attachTo: { id: 'plugin.search.page', input: 'items' },
configSchema: createSchemaFromZod(z =>
z.object({
noTrack: z.boolean().default(true),
@@ -79,7 +79,7 @@ describe('createSearchResultListItemExtension', () => {
const ExploreSearchResultItemExtension =
createSearchResultListItemExtension({
id: 'explore',
at: 'plugin.search.page/items',
attachTo: { id: 'plugin.search.page', input: 'items' },
predicate: result => result.type === 'explore',
component: async () => ExploreSearchResultItemComponent,
});
+2 -2
View File
@@ -65,7 +65,7 @@ export type SearchResultItemExtensionOptions<
/**
* The extension attachment point (e.g., search modal or page).
*/
at?: string;
attachTo?: { id: string; input: string };
/**
* Optional extension config schema.
*/
@@ -97,7 +97,7 @@ export function createSearchResultListItemExtension<
) as PortableSchema<TConfig>);
return createExtension({
id: `plugin.search.result.item.${options.id}`,
at: options.at ?? 'plugin.search.page/items',
attachTo: options.attachTo ?? { id: 'plugin.search.page', input: 'items' },
configSchema,
output: {
item: searchResultItemExtensionData,