<commit_before>import { err, ERROR } from "../errors";
import { HIDDEN, defineChainableProperty, exceptionHandler } from "./util";
import { Binding } from "./Binding";
import { ManagedEvent, ManagedParentChangeEvent, ManagedCoreEvent } from "./ManagedEvent";
import { ManagedList } from "./ManagedList";
import { ManagedMap } from "./ManagedMap";
import { ManagedObject } from "./ManagedObject";
import { onPropertyChange, observe } from "./observe";
import { logUnhandledException } from "./UnhandledErrorEmitter";

/** Event that is emitted on a particular `Component` instance, with reference to the source component as `source` */
export class ComponentEvent<TComponent extends Component = Component> extends ManagedEvent {
  constructor(name: string, source: TComponent, inner?: ManagedEvent) {
    super(name);
    this.source = source;
    this.inner = inner;
  }

  /** Source component */
  source: TComponent;

  /** Encapsulated event, if the event was originally of a different type */
  inner?: ManagedEvent;
}

/** Event handler type, can be used to define the type of a preset event handler as a string or function */
export type ComponentEventHandler<TComponent = Component, TEvent = ComponentEvent> =
  | string
  | ((this: TComponent, e: TEvent) => void);

/**
 * Component-specific event that represents the result of a user action, with reference to the source component and optional additional context. Actions are intended to be handled by parent components, or propagated further up the component hierarchy by re-emitting them (default behavior for `Component.delegateEvent`).
 * @note This event type is used when handling events using _preset_ components, using e.g. `{ "onClick": "EventName" }`. The method `Component.emitAction()` can also be used to emit action events.
 */
export class ActionEvent<
  TComponent extends Component = Component,
  TContext extends ManagedObject = ManagedObject
> extends ComponentEvent<TComponent> {
  /** Action context (if different from `source` component) */
  context?: TContext;
}

/** Generic constructor type for Component classes */
export type ComponentConstructor<TComponent extends Component = Component> = new (
  ...args: never[]
) => TComponent;

export namespace ComponentConstructor {
  /** Inferred partial type of the argument to `Component.with` without bindings, for a specific component constructor */
  export type PresetType<
    TComponentCtor extends ComponentConstructor
  > = TComponentCtor extends { preset: (presets: infer TPreset) => void } ? TPreset : any;

  /** Inferred type of the argument to `Component.with` for a specific component constructor */
  export type PresetArgType<TComponentCtor extends ComponentConstructor> = {
    [P in keyof PresetType<TComponentCtor>]?: PresetType<TComponentCtor>[P] | Binding.Type;
  } & {
    [P: string]: any;
  };

  /** Inferred type of the rest arguments to `Component.with` for a specific component constructor */
  export type PresetRestType<
    TComponentCtor extends ComponentConstructor
  > = TComponentCtor extends {
    preset: (presets: PresetType<TComponentCtor>, ...rest: infer TRest) => void;
  }
    ? TRest
    : never;

  export type WithPresetType<
    TComponentCtor extends ComponentConstructor
  > = TComponentCtor & { preset(presets: PresetType<TComponentCtor>): Function };
}

/** @internal Event that is emitted on (parent) components when a child component is added. The parent component observer responds by setting the `parentObserver` property on the child observer. */
export class ComponentChildAddedEvent extends ManagedEvent {
  constructor(observer: Component.ComponentObserver) {
    super("ChildComponentAdded");
    this.source = observer.component;
    this.observer = observer;
  }

  /** Source (added) component */
  source: Component;

  /** The observer for the new child component */
  readonly observer: Component.ComponentObserver;
}

/** @internal Event that is emitted when the composite parent object reference has been set for a component */
export class CompositeParentChangeEvent extends ManagedEvent {
  constructor(component: Component, composite?: Component) {
    super("CompositeParentChange");
    this.source = component;
    this.composite = composite;
  }

  /** Source component (with new parent) */
  source: Component;

  /** The composite parent object reference */
  composite?: Component;
}

/**
 * Component base class. Represents a managed object (see `ManagedObject`) that can be instantiated using a 'preset' constructor, i.e. the result of a call to the static `with` method.
 * When components are constructed, they are initialized with preset values for given properties, as well as preset bindings for properties which should automatically observe properties on the nearest parent component that (indirectly) references this component using the `@managedBound` decorator.
 */
export class Component extends ManagedObject {
  /** Create a new component constructor, which automatically initializes new instances with given properties, bindings, event handlers, and other values. */
  static with<TComponentCtor extends ComponentConstructor>(
    this: ComponentConstructor.WithPresetType<TComponentCtor>,
    ...rest: ComponentConstructor.PresetRestType<TComponentCtor>
  ): TComponentCtor;
  static with<TComponentCtor extends ComponentConstructor>(
    this: ComponentConstructor.WithPresetType<TComponentCtor>,
    presets: new () => any,
    ...rest: ComponentConstructor.PresetRestType<TComponentCtor>
  ): "INVALID_PRESET_ARGUMENT";
  static with<TComponentCtor extends ComponentConstructor>(
    this: ComponentConstructor.WithPresetType<TComponentCtor>,
    presets: ComponentConstructor.PresetArgType<TComponentCtor>,
    ...rest: ComponentConstructor.PresetRestType<TComponentCtor>
  ): TComponentCtor;
  static with<TComponentCtor extends ComponentConstructor>(
    this: ComponentConstructor.WithPresetType<TComponentCtor>,
    ...presets: any[]
  ): TComponentCtor {
    // create a new class that extends the current class
    let Self: new (...args: any[]) => Component = this as any;
    let presetFunc: Function;
    class PresetComponent extends Self {
      static preset: typeof Component.preset;
      constructor(...args: any[]) {
        // call super first, then own preset function
        super(...args);
        presetFunc.call(this);
      }
      protected isPresetComponent() {
        return true;
      }
    }

    // call preset method and store result for use above
    let obj: any = presets[0];
    if (typeof obj !== "object" || Object.getPrototypeOf(obj) !== Object.prototype) {
      presetFunc = PresetComponent.preset(Object.create(null), ...presets);
    } else {
      presetFunc = PresetComponent.preset(...(presets as [{}]));
    }

    // return the extended class
    return PresetComponent as any;
  }

  /**
   * Add bindings, components, and event handlers from given presets to the current component constructor. This method is called by `Component.with` with the same arguments, and should not be called directly.
   * Component classes may override this method and return the result of `super.preset(...)`, to add further presets and bindings using static methods on this component class.
   * @returns A function (*must* be typed as `Function` even in derived classes) that is called by the constructor for each new instance, to apply remaining values from the preset object to the component object that is passed through `this`.
   */
  static preset(presets: object, ...rest: unknown[]): Function {
    // take and apply bindings and components to the constructor already
    let eventActions: { [eventName: string]: string } | undefined;
    let eventHandlers:
      | { [eventName: string]: (this: Component, e: any) => void }
      | undefined;
    for (let p in presets) {
      let v = (presets as any)[p];
      if (Binding.isBinding(v)) {
        // add binding to constructor and remove from presets
        this.presetBinding(p, v);<commit_msg>Fix: allow argument to Component.with to have no prototype (#99)<commit_after>import { err, ERROR } from "../errors";
import { HIDDEN, defineChainableProperty, exceptionHandler } from "./util";
import { Binding } from "./Binding";
import { ManagedEvent, ManagedParentChangeEvent, ManagedCoreEvent } from "./ManagedEvent";
import { ManagedList } from "./ManagedList";
import { ManagedMap } from "./ManagedMap";
import { ManagedObject } from "./ManagedObject";
import { onPropertyChange, observe } from "./observe";
import { logUnhandledException } from "./UnhandledErrorEmitter";

/** Event that is emitted on a particular `Component` instance, with reference to the source component as `source` */
export class ComponentEvent<TComponent extends Component = Component> extends ManagedEvent {
  constructor(name: string, source: TComponent, inner?: ManagedEvent) {
    super(name);
    this.source = source;
    this.inner = inner;
  }

  /** Source component */
  source: TComponent;

  /** Encapsulated event, if the event was originally of a different type */
  inner?: ManagedEvent;
}

/** Event handler type, can be used to define the type of a preset event handler as a string or function */
export type ComponentEventHandler<TComponent = Component, TEvent = ComponentEvent> =
  | string
  | ((this: TComponent, e: TEvent) => void);

/**
 * Component-specific event that represents the result of a user action, with reference to the source component and optional additional context. Actions are intended to be handled by parent components, or propagated further up the component hierarchy by re-emitting them (default behavior for `Component.delegateEvent`).
 * @note This event type is used when handling events using _preset_ components, using e.g. `{ "onClick": "EventName" }`. The method `Component.emitAction()` can also be used to emit action events.
 */
export class ActionEvent<
  TComponent extends Component = Component,
  TContext extends ManagedObject = ManagedObject
> extends ComponentEvent<TComponent> {
  /** Action context (if different from `source` component) */
  context?: TContext;
}

/** Generic constructor type for Component classes */
export type ComponentConstructor<TComponent extends Component = Component> = new (
  ...args: never[]
) => TComponent;

export namespace ComponentConstructor {
  /** Inferred partial type of the argument to `Component.with` without bindings, for a specific component constructor */
  export type PresetType<
    TComponentCtor extends ComponentConstructor
  > = TComponentCtor extends { preset: (presets: infer TPreset) => void } ? TPreset : any;

  /** Inferred type of the argument to `Component.with` for a specific component constructor */
  export type PresetArgType<TComponentCtor extends ComponentConstructor> = {
    [P in keyof PresetType<TComponentCtor>]?: PresetType<TComponentCtor>[P] | Binding.Type;
  } & {
    [P: string]: any;
  };

  /** Inferred type of the rest arguments to `Component.with` for a specific component constructor */
  export type PresetRestType<
    TComponentCtor extends ComponentConstructor
  > = TComponentCtor extends {
    preset: (presets: PresetType<TComponentCtor>, ...rest: infer TRest) => void;
  }
    ? TRest
    : never;

  export type WithPresetType<
    TComponentCtor extends ComponentConstructor
  > = TComponentCtor & { preset(presets: PresetType<TComponentCtor>): Function };
}

/** @internal Event that is emitted on (parent) components when a child component is added. The parent component observer responds by setting the `parentObserver` property on the child observer. */
export class ComponentChildAddedEvent extends ManagedEvent {
  constructor(observer: Component.ComponentObserver) {
    super("ChildComponentAdded");
    this.source = observer.component;
    this.observer = observer;
  }

  /** Source (added) component */
  source: Component;

  /** The observer for the new child component */
  readonly observer: Component.ComponentObserver;
}

/** @internal Event that is emitted when the composite parent object reference has been set for a component */
export class CompositeParentChangeEvent extends ManagedEvent {
  constructor(component: Component, composite?: Component) {
    super("CompositeParentChange");
    this.source = component;
    this.composite = composite;
  }

  /** Source component (with new parent) */
  source: Component;

  /** The composite parent object reference */
  composite?: Component;
}

/**
 * Component base class. Represents a managed object (see `ManagedObject`) that can be instantiated using a 'preset' constructor, i.e. the result of a call to the static `with` method.
 * When components are constructed, they are initialized with preset values for given properties, as well as preset bindings for properties which should automatically observe properties on the nearest parent component that (indirectly) references this component using the `@managedBound` decorator.
 */
export class Component extends ManagedObject {
  /** Create a new component constructor, which automatically initializes new instances with given properties, bindings, event handlers, and other values. */
  static with<TComponentCtor extends ComponentConstructor>(
    this: ComponentConstructor.WithPresetType<TComponentCtor>,
    ...rest: ComponentConstructor.PresetRestType<TComponentCtor>
  ): TComponentCtor;
  static with<TComponentCtor extends ComponentConstructor>(
    this: ComponentConstructor.WithPresetType<TComponentCtor>,
    presets: new () => any,
    ...rest: ComponentConstructor.PresetRestType<TComponentCtor>
  ): "INVALID_PRESET_ARGUMENT";
  static with<TComponentCtor extends ComponentConstructor>(
    this: ComponentConstructor.WithPresetType<TComponentCtor>,
    presets: ComponentConstructor.PresetArgType<TComponentCtor>,
    ...rest: ComponentConstructor.PresetRestType<TComponentCtor>
  ): TComponentCtor;
  static with<TComponentCtor extends ComponentConstructor>(
    this: ComponentConstructor.WithPresetType<TComponentCtor>,
    ...presets: any[]
  ): TComponentCtor {
    // create a new class that extends the current class
    let Self: new (...args: any[]) => Component = this as any;
    let presetFunc: Function;
    class PresetComponent extends Self {
      static preset: typeof Component.preset;
      constructor(...args: any[]) {
        // call super first, then own preset function
        super(...args);
        presetFunc.call(this);
      }
      protected isPresetComponent() {
        return true;
      }
    }

    // call preset method and store result for use above
    let obj: any = presets[0];
    let objPrototype = Object.getPrototypeOf(obj);
    if (typeof obj !== "object" || (objPrototype && objPrototype !== Object.prototype)) {
      presetFunc = PresetComponent.preset(Object.create(null), ...presets);
    } else {
      presetFunc = PresetComponent.preset(...(presets as [{}]));
    }

    // return the extended class
    return PresetComponent as any;
  }

  /**
   * Add bindings, components, and event handlers from given presets to the current component constructor. This method is called by `Component.with` with the same arguments, and should not be called directly.
   * Component classes may override this method and return the result of `super.preset(...)`, to add further presets and bindings using static methods on this component class.
   * @returns A function (*must* be typed as `Function` even in derived classes) that is called by the constructor for each new instance, to apply remaining values from the preset object to the component object that is passed through `this`.
   */
  static preset(presets: object, ...rest: unknown[]): Function {
    // take and apply bindings and components to the constructor already
    let eventActions: { [eventName: string]: string } | undefined;
    let eventHandlers:
      | { [eventName: string]: (this: Component, e: any) => void }
      | undefined;
    for (let p in presets) {
      let v = (presets as any)[p];
      if (Binding.isBinding(v)) {
        // add binding to constructor and remove from presets
        this.presetBinding(p, v);
{'fraction_non_alphanumeric': 0.09371902874132805, 'fraction_numerical': 0.0002477700693756194, 'mean_word_length': 4.169708613512648, 'pattern_counts': {'":': 2, '<': 53, '<?xml version=': 0, '>': 67, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4401592', 'n_tokens_mistral': 4135, 'n_tokens_neox': 4076, 'n_words': 1596}