frontend-plugin-api: clean up internal createExtension{,Input,Blueprint} types

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2024-08-21 22:40:00 +02:00
parent a428f5512b
commit c5d2530000
3 changed files with 99 additions and 359 deletions
@@ -365,50 +365,6 @@ export function createExtension<
namespace: string | undefined extends TNamespace ? undefined : TNamespace;
name: string | undefined extends TName ? undefined : TName;
}
>;
export function createExtension<
const TKind extends string | undefined,
const TNamespace extends string | undefined,
const TName extends string | undefined,
UOutput extends AnyExtensionDataRef,
TInputs extends {
[inputName in string]: ExtensionInput<
AnyExtensionDataRef,
{ optional: boolean; singleton: boolean }
>;
},
TConfigSchema extends { [key: string]: (zImpl: typeof z) => z.ZodType },
UFactoryOutput extends ExtensionDataValue<any, any>,
>(
options: CreateExtensionOptions<
TKind,
TNamespace,
TName,
UOutput,
TInputs,
TConfigSchema,
UFactoryOutput
>,
): ExtensionDefinition<
string extends keyof TConfigSchema
? {}
: {
[key in keyof TConfigSchema]: z.infer<ReturnType<TConfigSchema[key]>>;
},
string extends keyof TConfigSchema
? {}
: z.input<
z.ZodObject<{
[key in keyof TConfigSchema]: ReturnType<TConfigSchema[key]>;
}>
>,
UOutput,
TInputs,
{
kind: TKind;
namespace: TNamespace;
name: TName;
}
> {
const schemaDeclaration = options.config?.schema;
const configSchema =
@@ -447,76 +403,7 @@ export function createExtension<
parts.push(`attachTo=${options.attachTo.id}@${options.attachTo.input}`);
return `ExtensionDefinition{${parts.join(',')}}`;
},
override: <
TExtensionConfigSchema extends {
[key in string]: (zImpl: typeof z) => z.ZodType;
},
UOverrideFactoryOutput extends ExtensionDataValue<any, any>,
UNewOutput extends AnyExtensionDataRef,
TExtraInputs extends {
[inputName in string]: ExtensionInput<
AnyExtensionDataRef,
{ optional: boolean; singleton: boolean }
>;
},
>(overrideOptions: {
attachTo?: { id: string; input: string };
disabled?: boolean;
inputs?: TExtraInputs;
output?: Array<UNewOutput>;
config?: {
schema: TExtensionConfigSchema;
};
factory(
originalFactory: (context?: {
config?: {
[key in keyof TConfigSchema]: z.infer<
ReturnType<TConfigSchema[key]>
>;
};
inputs?: ResolveInputValueOverrides<TInputs>;
}) => ExtensionDataContainer<UOutput>,
context: {
node: AppNode;
config: {
[key in keyof TExtensionConfigSchema]: z.infer<
ReturnType<TExtensionConfigSchema[key]>
>;
} & {
[key in keyof TConfigSchema]: z.infer<
ReturnType<TConfigSchema[key]>
>;
};
inputs: Expand<ResolvedExtensionInputs<TInputs & TExtraInputs>>;
},
): Iterable<UOverrideFactoryOutput>;
}): ExtensionDefinition<
{
[key in keyof TExtensionConfigSchema]: z.infer<
ReturnType<TExtensionConfigSchema[key]>
>;
} & {
[key in keyof TConfigSchema]: z.infer<ReturnType<TConfigSchema[key]>>;
},
z.input<
z.ZodObject<
{
[key in keyof TExtensionConfigSchema]: ReturnType<
TExtensionConfigSchema[key]
>;
} & {
[key in keyof TConfigSchema]: ReturnType<TConfigSchema[key]>;
}
>
>,
AnyExtensionDataRef extends UNewOutput ? UOutput : UNewOutput,
TInputs & TExtraInputs,
{
kind: TKind;
namespace: TNamespace;
name: TName;
}
> => {
override(overrideOptions) {
if (!Array.isArray(options.output)) {
throw new Error(
'Cannot override an extension that is not declared using the new format with outputs as an array',
@@ -531,12 +418,6 @@ export function createExtension<
TConfigSchema,
UFactoryOutput
>;
const overrideNewConfigSchema = overrideOptions.config?.schema;
const schema = {
...newOptions.config?.schema,
...overrideNewConfigSchema,
} as TConfigSchema & TExtensionConfigSchema;
return createExtension({
kind: newOptions.kind,
@@ -545,75 +426,74 @@ export function createExtension<
attachTo: overrideOptions.attachTo ?? newOptions.attachTo,
disabled: overrideOptions.disabled ?? newOptions.disabled,
inputs: { ...overrideOptions.inputs, ...newOptions.inputs },
output: overrideOptions.output ?? newOptions.output,
config: Object.keys(schema).length === 0 ? undefined : { schema },
output: (overrideOptions.output ??
newOptions.output) as AnyExtensionDataRef[],
config:
newOptions.config || overrideOptions.config
? {
schema: {
...newOptions.config?.schema,
...overrideOptions.config?.schema,
},
}
: undefined,
factory: ({ node, config, inputs }) => {
if (!overrideOptions.factory) {
return newOptions.factory({
node,
config,
inputs: inputs as unknown as Expand<
ResolvedExtensionInputs<TInputs>
>,
config: config as any,
inputs: inputs as any,
});
}
const parentResult = overrideOptions.factory(
(innerContext?: {
config?: {
[key in keyof TConfigSchema]: z.infer<
ReturnType<TConfigSchema[key]>
>;
};
inputs?: ResolveInputValueOverrides<TInputs>;
}): ExtensionDataContainer<UOutput> => {
(innerContext): ExtensionDataContainer<UOutput> => {
return createExtensionDataContainer<UOutput>(
newOptions.factory({
node,
config: innerContext?.config ?? config,
config: (innerContext?.config ?? config) as any,
inputs: resolveInputOverrides(
newOptions.inputs,
inputs,
innerContext?.inputs,
) as any, // TODO: Might be able to improve this once legacy inputs are gone
) as any,
}) as Iterable<any>,
newOptions.output,
);
},
{
node,
config,
inputs,
config: config as any,
inputs: inputs as any,
},
);
const deduplicatedResult = new Map<string, UOverrideFactoryOutput>();
const deduplicatedResult = new Map<
string,
ExtensionDataValue<any, any>
>();
for (const item of parentResult) {
deduplicatedResult.set(item.id, item);
}
return deduplicatedResult.values() as Iterable<UOverrideFactoryOutput>;
return deduplicatedResult.values();
},
} as CreateExtensionOptions<TKind, TNamespace, TName, AnyExtensionDataRef extends UNewOutput ? UOutput : UNewOutput, TInputs & TExtraInputs, TConfigSchema & TExtensionConfigSchema, UOverrideFactoryOutput>);
}) as ExtensionDefinition<any>;
},
} as InternalExtensionDefinition<
string extends keyof TConfigSchema
? {}
: {
[key in keyof TConfigSchema]: z.infer<ReturnType<TConfigSchema[key]>>;
},
string extends keyof TConfigSchema
? {}
: z.input<
z.ZodObject<{
[key in keyof TConfigSchema]: ReturnType<TConfigSchema[key]>;
}>
>,
{
[key in keyof TConfigSchema]: z.infer<ReturnType<TConfigSchema[key]>>;
},
z.input<
z.ZodObject<{
[key in keyof TConfigSchema]: ReturnType<TConfigSchema[key]>;
}>
>,
UOutput,
TInputs,
{
kind: TKind;
namespace: TNamespace;
name: TName;
kind: string | undefined extends TKind ? undefined : TKind;
namespace: string | undefined extends TNamespace ? undefined : TNamespace;
name: string | undefined extends TName ? undefined : TName;
}
>;
}
@@ -17,7 +17,6 @@
import { AppNode } from '../apis';
import { Expand } from '../types';
import {
CreateExtensionOptions,
ExtensionDefinition,
ResolvedExtensionInputs,
VerifyExtensionFactoryOutput,
@@ -211,193 +210,6 @@ export interface ExtensionBlueprint<
>;
}
/**
* @internal
*/
class ExtensionBlueprintImpl<
TKind extends string,
TNamespace extends string | undefined,
TName extends string | undefined,
TParams,
UOutput extends AnyExtensionDataRef,
TInputs extends {
[inputName in string]: ExtensionInput<
AnyExtensionDataRef,
{ optional: boolean; singleton: boolean }
>;
},
TConfigSchema extends { [key in string]: (zImpl: typeof z) => z.ZodType },
TDataRefs extends { [name in string]: AnyExtensionDataRef },
> {
constructor(
private readonly options: CreateExtensionBlueprintOptions<
TKind,
TNamespace,
TName,
TParams,
UOutput,
TInputs,
TConfigSchema,
any,
TDataRefs
>,
) {
this.dataRefs = options.dataRefs!;
}
dataRefs: TDataRefs;
public makeWithOverrides<
TExtensionConfigSchema extends {
[key in string]: (zImpl: typeof z) => z.ZodType;
},
UFactoryOutput extends ExtensionDataValue<any, any>,
UNewOutput extends AnyExtensionDataRef,
TExtraInputs extends {
[inputName in string]: ExtensionInput<
AnyExtensionDataRef,
{ optional: boolean; singleton: boolean }
>;
},
TNewNamespace extends string | undefined = undefined,
TNewName extends string | undefined = undefined,
>(args: {
namespace?: TNewNamespace;
name?: TNewName;
attachTo?: { id: string; input: string };
disabled?: boolean;
inputs?: TExtraInputs;
output?: Array<UNewOutput>;
config?: {
schema: TExtensionConfigSchema;
};
factory(
originalFactory: (
params: TParams,
context?: {
config?: {
[key in keyof TConfigSchema]: z.infer<
ReturnType<TConfigSchema[key]>
>;
};
inputs?: ResolveInputValueOverrides<TInputs>;
},
) => ExtensionDataContainer<UOutput>,
context: {
node: AppNode;
config: {
[key in keyof TExtensionConfigSchema]: z.infer<
ReturnType<TExtensionConfigSchema[key]>
>;
} & {
[key in keyof TConfigSchema]: z.infer<ReturnType<TConfigSchema[key]>>;
};
inputs: Expand<ResolvedExtensionInputs<TInputs & TExtraInputs>>;
},
): Iterable<UFactoryOutput>;
}): ExtensionDefinition<
{
[key in keyof TExtensionConfigSchema]: z.infer<
ReturnType<TExtensionConfigSchema[key]>
>;
} & {
[key in keyof TConfigSchema]: z.infer<ReturnType<TConfigSchema[key]>>;
},
z.input<
z.ZodObject<
{
[key in keyof TExtensionConfigSchema]: ReturnType<
TExtensionConfigSchema[key]
>;
} & {
[key in keyof TConfigSchema]: ReturnType<TConfigSchema[key]>;
}
>
>
> {
const schema = {
...this.options.config?.schema,
...args.config?.schema,
} as TConfigSchema & TExtensionConfigSchema;
return createExtension({
kind: this.options.kind,
namespace: args.namespace ?? this.options.namespace,
name: args.name ?? this.options.name,
attachTo: args.attachTo ?? this.options.attachTo,
disabled: args.disabled ?? this.options.disabled,
inputs: { ...args.inputs, ...this.options.inputs },
output: args.output ?? this.options.output,
config: Object.keys(schema).length === 0 ? undefined : { schema },
factory: ({ node, config, inputs }) => {
return args.factory(
(
innerParams: TParams,
innerContext?: {
config?: {
[key in keyof TConfigSchema]: z.infer<
ReturnType<TConfigSchema[key]>
>;
};
inputs?: ResolveInputValueOverrides;
},
): ExtensionDataContainer<UOutput> => {
return createExtensionDataContainer<UOutput>(
this.options.factory(innerParams, {
node,
config: innerContext?.config ?? config,
inputs: resolveInputOverrides(
this.options.inputs,
inputs,
innerContext?.inputs,
) as any, // TODO: Might be able to improve this once legacy inputs are gone
}),
this.options.output,
);
},
{
node,
config,
inputs,
},
);
},
} as CreateExtensionOptions<TKind, string | undefined extends TNewNamespace ? TNamespace : TNewNamespace, string | undefined extends TNewName ? TName : TNewName, AnyExtensionDataRef extends UNewOutput ? UOutput : UNewOutput, TInputs & TExtraInputs, TConfigSchema & TExtensionConfigSchema, UFactoryOutput>);
}
public make<
TNewNamespace extends string | undefined = undefined,
TNewName extends string | undefined = undefined,
>(args: {
namespace?: TNewNamespace;
name?: TNewName;
attachTo?: { id: string; input: string };
disabled?: boolean;
params: TParams;
}): ExtensionDefinition<
{
[key in keyof TConfigSchema]: z.infer<ReturnType<TConfigSchema[key]>>;
},
z.input<
z.ZodObject<{
[key in keyof TConfigSchema]: ReturnType<TConfigSchema[key]>;
}>
>
> {
return createExtension({
kind: this.options.kind,
namespace: args.namespace ?? this.options.namespace,
name: args.name ?? this.options.name,
attachTo: args.attachTo ?? this.options.attachTo,
disabled: args.disabled ?? this.options.disabled,
inputs: this.options.inputs,
output: this.options.output,
config: this.options.config,
factory: ctx => this.options.factory(args.params, ctx),
} as CreateExtensionOptions<TKind, string | undefined extends TNewNamespace ? TNamespace : TNewNamespace, string | undefined extends TNewName ? TName : TNewName, UOutput, TInputs, TConfigSchema, any>);
}
}
/**
* A simpler replacement for wrapping up `createExtension` inside a kind or type. This allows for a cleaner API for creating
* types and instances of those types.
@@ -452,7 +264,68 @@ export function createExtensionBlueprint<
>,
TDataRefs
> {
return new ExtensionBlueprintImpl(options) as ExtensionBlueprint<
return {
dataRefs: options.dataRefs,
make(args) {
return createExtension({
kind: options.kind,
namespace: args.namespace ?? options.namespace,
name: args.name ?? options.name,
attachTo: args.attachTo ?? options.attachTo,
disabled: args.disabled ?? options.disabled,
inputs: options.inputs,
output: options.output as AnyExtensionDataRef[],
config: options.config,
factory: ctx =>
options.factory(args.params, ctx) as Iterable<
ExtensionDataValue<any, any>
>,
});
},
makeWithOverrides(args) {
return createExtension({
kind: options.kind,
namespace: args.namespace ?? options.namespace,
name: args.name ?? options.name,
attachTo: args.attachTo ?? options.attachTo,
disabled: args.disabled ?? options.disabled,
inputs: { ...args.inputs, ...options.inputs },
output: (args.output ?? options.output) as AnyExtensionDataRef[],
config:
options.config || args.config
? {
schema: {
...options.config?.schema,
...args.config?.schema,
},
}
: undefined,
factory: ({ node, config, inputs }) => {
return args.factory(
(innerParams, innerContext) => {
return createExtensionDataContainer<UOutput>(
options.factory(innerParams, {
node,
config: (innerContext?.config ?? config) as any,
inputs: resolveInputOverrides(
options.inputs,
inputs,
innerContext?.inputs,
) as any,
}) as Iterable<any>,
options.output,
);
},
{
node,
config: config as any,
inputs: inputs as any,
},
) as Iterable<ExtensionDataValue<any, any>>;
},
}) as ExtensionDefinition<any>;
},
} as ExtensionBlueprint<
{
kind: TKind;
namespace: TNamespace;
@@ -39,19 +39,6 @@ export function createExtensionInput<
singleton: TConfig['singleton'] extends true ? true : false;
optional: TConfig['optional'] extends true ? true : false;
}
>;
export function createExtensionInput<
TExtensionData extends ExtensionDataRef<unknown, string, { optional?: true }>,
TConfig extends { singleton?: boolean; optional?: boolean },
>(
extensionData: Array<TExtensionData>,
config?: TConfig,
): ExtensionInput<
TExtensionData,
{
singleton: TConfig['singleton'] extends true ? true : false;
optional: TConfig['optional'] extends true ? true : false;
}
> {
if (process.env.NODE_ENV !== 'production') {
if (Array.isArray(extensionData)) {
@@ -85,7 +72,7 @@ export function createExtensionInput<
: false,
},
} as ExtensionInput<
TExtensionData,
UExtensionData,
{
singleton: TConfig['singleton'] extends true ? true : false;
optional: TConfig['optional'] extends true ? true : false;