import { Compatibility } from "./compat" ; import { titleCase } from "./data" ; import { Log } from "./log" ; import { ScriptBlueprint } from "./runner" ; import { StorageFactory } from "./storage" ; import { Awaitable } from "./sync" ; import { WranglerConfig } from "./wrangler" ; export type Context = { [ key ] : any ; } ; export type AdditionalModules = { [ key ] : Context ; } ; export enum OptionType { NONE , BOOLEAN , NUMBER , STRING , STRING_POSITIONAL , BOOLEAN_STRING , BOOLEAN_NUMBER , ARRAY , OBJECT } export interface OptionMetadataType < Type extends OptionType , Value > { type : Type ; typeFormat ? : Type extends OptionType . OBJECT ? string : undefined ; name ? : string ; alias ? : string ; description ? : string ; negatable ? : Type extends OptionType . BOOLEAN | OptionType . BOOLEAN_STRING | OptionType . BOOLEAN_NUMBER ? boolean : undefined ; logName ? : string ; logValue ? : ( value : Value ) => string | undefined ; fromEntries ? : Type extends OptionType . OBJECT ? ( entries : [ key : string , value : string ] [ ] ) => Value : undefined ; fromWrangler ? : ( config : WranglerConfig , configDir : string ) => Value | undefined ; } export type OptionMetadata = OptionMetadataType < OptionType . NONE , any > | OptionMetadataType < OptionType . BOOLEAN , boolean > | OptionMetadataType < OptionType . NUMBER , number > | OptionMetadataType < OptionType . STRING , string > | OptionMetadataType < OptionType . STRING_POSITIONAL , string > | OptionMetadataType < OptionType . BOOLEAN_STRING , boolean | string > | OptionMetadataType < OptionType . BOOLEAN_NUMBER , boolean | number > | OptionMetadataType < OptionType . ARRAY , string [ ] > | OptionMetadataType < OptionType . OBJECT , any > ; export function Option ( metadata ) { return function ( prototype , key ) { ( prototype . opts ??= new Map < string | symbol , OptionMetadata > ( ) ) . set ( key , metadata ) ; } ; } export interface BeforeSetupResult { watch ? : string [ ] ; } export interface SetupResult extends BeforeSetupResult { globals ? : Context ; bindings ? : Context ; script ? : ScriptBlueprint ; requiresModuleExports ? : boolean ; additionalModules ? : AdditionalModules ; } export interface Mount < Request = any , Response = any > { moduleExports ? : Context ; dispatchFetch ? : ( request : Request ) => Promise < Response > ; } export interface PluginContext { log : Log ; compat : Compatibility ; rootPath : string ; globalAsyncIO ? : boolean ; } export abstract class Plugin < Options extends Context = never > { readonly #phantom ! : Options ; opts ? : Map < string | symbol , OptionMetadata > ; protected constructor ( protected readonly ctx ) { this . opts = new . target . prototype . opts ; } protected assignOptions ( options ? ) { if ( options === undefined || this . opts === undefined ) return ; for ( const key of this . opts . keys ( ) ) { ( this as any ) [ key ] = options [ key ] ; } } beforeSetup ? ( ) ; setup ? ( storageFactory ) ; beforeReload ? ( ) ; reload ? ( bindings , moduleExports , mounts ) ; dispose ? ( ) ; } export type PluginSignature = { new ( ctx , options ? ) : Plugin < Context > ; prototype : { opts ? : Map < string | symbol , OptionMetadata > ; } ; } ; export type PluginSignatures = { [ pluginName ] : PluginSignature ; } ; export type PluginEntries < Plugins extends PluginSignatures > = [ name : keyof Plugins , plugin : ValueOf < Plugins > ] [ ] ; export type ExtractOptions < Instance > = Instance extends Plugin < infer Options > ? Options : never ; export type PluginOptions < Plugins extends PluginSignatures > = { [ key in keyof Plugins ] : ExtractOptions < InstanceType < Plugins [ key ] > > ; } ; export type ValueOf < T > = T [ keyof T ] ; export type PluginOptionsUnion < Plugins extends PluginSignatures > = ValueOf < PluginOptions < Plugins > > ; export type UnionToIntersection < U > = ( U extends any ? ( k : U ) => void : never ) extends ( k : infer I ) => void ? I : never ; export type Options < Plugins extends PluginSignatures > = UnionToIntersection < PluginOptionsUnion < Plugins > > ; export function logOptions < Plugins extends PluginSignatures > ( plugins , log , options ) { log . debug ( "Options:" ) ; for ( const [ name , plugin ] of plugins ) { const pluginOptions = options [ name ] ; for ( const [ key , meta ] of plugin . prototype . opts ?. entries ( ) ?? [ ] ) { const value = pluginOptions [ key ] ; if ( value === undefined || meta . type === OptionType . NONE ) continue ; const keyName = meta ?. logName ?? titleCase ( typeof key === "symbol" ? "<symbol>" : key ) ; let str ; if ( meta . logValue ) { str = ( meta . logValue as any ) ( value ) ; if ( str === undefined ) continue ; } else if ( meta . type === OptionType . OBJECT ) { str = Object . keys ( value ) . join ( ", " ) ; } else if ( meta . type === OptionType . ARRAY ) { str = value . join ( ", " ) ; } else { str = value . toString ( ) ; } log . debug ( `- ${ keyName }: ${ str }` ) ; } } }