() => {
    localStorage.setItem("wbw-wm", "1");

    render(<HomePageWelcome />);

    mockAllIsIntersecting(true);

    expect(screen.queryByText(title)).not.toBeInTheDocument();
    expect(screen.queryByText(bodyRegexp)).not.toBeInTheDocument();
  }


() => {
    render(<HomePageWelcome />);

    mockAllIsIntersecting(true);

    userEvent.click(screen.getByText(/sebarkan sekarang/i));

    const safeUrl = encodeURIComponent(siteConfig.site_url);
    const safeText = encodeURIComponent(siteConfig.site_description);
    expect(window.open).toHaveBeenCalledWith(
      `https://twitter.com/intent/tweet?text=${safeText}+%0A+${safeUrl}`,
    );
  }


async () => {
    render(<HomePageWelcome />);

    mockAllIsIntersecting(true);

    Object.defineProperty(global.navigator, "share", {
      writable: true,
      value: jest.fn().mockImplementation(
        (shareObj) =>
          new Promise((resolve) => {
            expect(shareObj).toEqual(shareObject);
            resolve(null);
          }),
      ),
    });

    userEvent.click(screen.getByText(/sebarkan sekarang/i));

    await waitForElementToBeRemoved(() => screen.queryByText(title));

    expect(window.open).not.toHaveBeenCalled();
  }


(shareObj) =>
          new Promise((resolve) => {
            expect(shareObj).toEqual(shareObject);
            resolve(null);
          })


(resolve) => {
            expect(shareObj).toEqual(shareObject);
            resolve(null);
          }


async () => {
    const errorMessage = "Native sharing is unavailable at the moment";

    render(<HomePageWelcome />);

    mockAllIsIntersecting(true);

    Object.defineProperty(global.navigator, "share", {
      writable: true,
      value: jest.fn().mockImplementation(
        (shareObj) =>
          new Promise((_, reject) => {
            expect(shareObj).toEqual(shareObject);
            reject(errorMessage);
          }),
      ),
    });

    jest.spyOn(console, "error").mockImplementation(() => {});

    userEvent.click(screen.getByText(/sebarkan sekarang/i));

    await waitFor(() =>
      expect(console.error).toHaveBeenCalledWith(
        "Error sharing",
        errorMessage,
        shareObject,
      ),
    );
    expect(window.open).not.toHaveBeenCalled();
  }


(shareObj) =>
          new Promise((_, reject) => {
            expect(shareObj).toEqual(shareObject);
            reject(errorMessage);
          })


(_, reject) => {
            expect(shareObj).toEqual(shareObject);
            reject(errorMessage);
          }


() =>
      expect(console.error).toHaveBeenCalledWith(
        "Error sharing",
        errorMessage,
        shareObject,
      )


@ObjectType()
@Entity()
export class FollowModel {

  @PrimaryColumn()
  @Field({ nullable: false })
  followerId?: string;

  @PrimaryColumn()
  @Field({ nullable: false })
  followeeId?: string;

  @CreateDateColumn()
  @Field({ nullable: false })
  createdAt?: Date;

//   @Field(type => [Post])
//   posts: Post[];
}


export function isControl(control: Object): boolean {
  return control instanceof AbstractControl;
}


function _find(control: AbstractControl, path: Array<string|number>| string) {
  if (isBlank(path)) return null;

  if (!(path instanceof Array)) {
    path = (<string>path).split('/');
  }
  if (path instanceof Array && ListWrapper.isEmpty(path)) return null;

  return (<Array<string|number>>path).reduce((v, name) => {
    if (v instanceof FormGroup) {
      return isPresent(v.controls[name]) ? v.controls[name] : null;
    } else if (v instanceof FormArray) {
      var index = <number>name;
      return isPresent(v.at(index)) ? v.at(index) : null;
    } else {
      return null;
    }
  }, control);
}


function toObservable(r: any): Observable<any> {
  return PromiseWrapper.isPromise(r) ? ObservableWrapper.fromPromise(r) : r;
}


function coerceToValidator(validator: ValidatorFn | ValidatorFn[]): ValidatorFn {
  return Array.isArray(validator) ? composeValidators(validator) : validator;
}


function coerceToAsyncValidator(asyncValidator: AsyncValidatorFn | AsyncValidatorFn[]):
    AsyncValidatorFn {
  return Array.isArray(asyncValidator) ? composeAsyncValidators(asyncValidator) : asyncValidator;
}


(v, name) => {
    if (v instanceof FormGroup) {
      return isPresent(v.controls[name]) ? v.controls[name] : null;
    } else if (v instanceof FormArray) {
      var index = <number>name;
      return isPresent(v.at(index)) ? v.at(index) : null;
    } else {
      return null;
    }
  }


(res: {[key: string]: any}) => this.setErrors(res, {emitEvent: emitEvent})


(changeFn) => changeFn(this._value)


(control: AbstractControl, name: string) => { control.setParent(this); }


(control: AbstractControl, name: string) => {
      res = res || (this.contains(name) && control.status == status);
    }


(acc: {[k: string]: AbstractControl}, control: AbstractControl, name: string) => {
          acc[name] = control.value;
          return acc;
        }


(control: AbstractControl, name: string) => {
      if (this._included(name)) {
        res = fn(res, control, name);
      }
    }


(control) => control.value


c => c.status == status


(control) => { control.setParent(this); }


/**
 * @experimental
 */
export abstract class AbstractControl {
  /** @internal */
  _value: any;

  private _valueChanges: EventEmitter<any>;
  private _statusChanges: EventEmitter<any>;
  private _status: string;
  private _errors: {[key: string]: any};
  private _pristine: boolean = true;
  private _touched: boolean = false;
  private _parent: FormGroup|FormArray;
  private _asyncValidationSubscription: any;

  constructor(public validator: ValidatorFn, public asyncValidator: AsyncValidatorFn) {}

  get value(): any { return this._value; }

  get status(): string { return this._status; }

  get valid(): boolean { return this._status === VALID; }

  /**
   * Returns the errors of this control.
   */
  get errors(): {[key: string]: any} { return this._errors; }

  get pristine(): boolean { return this._pristine; }

  get dirty(): boolean { return !this.pristine; }

  get touched(): boolean { return this._touched; }

  get untouched(): boolean { return !this._touched; }

  get valueChanges(): Observable<any> { return this._valueChanges; }

  get statusChanges(): Observable<any> { return this._statusChanges; }

  get pending(): boolean { return this._status == PENDING; }

  markAsTouched(): void { this._touched = true; }

  markAsDirty({onlySelf}: {onlySelf?: boolean} = {}): void {
    onlySelf = normalizeBool(onlySelf);
    this._pristine = false;

    if (isPresent(this._parent) && !onlySelf) {
      this._parent.markAsDirty({onlySelf: onlySelf});
    }
  }

  markAsPending({onlySelf}: {onlySelf?: boolean} = {}): void {
    onlySelf = normalizeBool(onlySelf);
    this._status = PENDING;

    if (isPresent(this._parent) && !onlySelf) {
      this._parent.markAsPending({onlySelf: onlySelf});
    }
  }

  setParent(parent: FormGroup|FormArray): void { this._parent = parent; }

  updateValueAndValidity({onlySelf, emitEvent}: {onlySelf?: boolean, emitEvent?: boolean} = {}):
      void {
    onlySelf = normalizeBool(onlySelf);
    emitEvent = isPresent(emitEvent) ? emitEvent : true;

    this._updateValue();

    this._errors = this._runValidator();
    this._status = this._calculateStatus();

    if (this._status == VALID || this._status == PENDING) {
      this._runAsyncValidator(emitEvent);
    }

    if (emitEvent) {
      ObservableWrapper.callEmit(this._valueChanges, this._value);
      ObservableWrapper.callEmit(this._statusChanges, this._status);
    }

    if (isPresent(this._parent) && !onlySelf) {
      this._parent.updateValueAndValidity({onlySelf: onlySelf, emitEvent: emitEvent});
    }
  }

  private _runValidator(): {[key: string]: any} {
    return isPresent(this.validator) ? this.validator(this) : null;
  }

  private _runAsyncValidator(emitEvent: boolean): void {
    if (isPresent(this.asyncValidator)) {
      this._status = PENDING;
      this._cancelExistingSubscription();
      var obs = toObservable(this.asyncValidator(this));
      this._asyncValidationSubscription = ObservableWrapper.subscribe(
          obs, (res: {[key: string]: any}) => this.setErrors(res, {emitEvent: emitEvent}));
    }
  }

  private _cancelExistingSubscription(): void {
    if (isPresent(this._asyncValidationSubscription)) {
      ObservableWrapper.dispose(this._asyncValidationSubscription);
    }
  }

  /**
   * Sets errors on a form control.
   *
   * This is used when validations are run not automatically, but manually by the user.
   *
   * Calling `setErrors` will also update the validity of the parent control.
   *
   * ## Usage
   *
   * ```
   * var login = new FormControl("someLogin");
   * login.setErrors({
   *   "notUnique": true
   * });
   *
   * expect(login.valid).toEqual(false);
   * expect(login.errors).toEqual({"notUnique": true});
   *
   * login.updateValue("someOtherLogin");
   *
   * expect(login.valid).toEqual(true);
   * ```
   */
  setErrors(errors: {[key: string]: any}, {emitEvent}: {emitEvent?: boolean} = {}): void {
    emitEvent = isPresent(emitEvent) ? emitEvent : true;

    this._errors = errors;
    this._status = this._calculateStatus();

    if (emitEvent) {
      ObservableWrapper.callEmit(this._statusChanges, this._status);
    }

    if (isPresent(this._parent)) {
      this._parent._updateControlsErrors();
    }
  }

  find(path: Array<string|number>|string): AbstractControl { return _find(this, path); }

  getError(errorCode: string, path: string[] = null): any {
    var control = isPresent(path) && !ListWrapper.isEmpty(path) ? this.find(path) : this;
    if (isPresent(control) && isPresent(control._errors)) {
      return StringMapWrapper.get(control._errors, errorCode);
    } else {
      return null;
    }
  }

  hasError(errorCode: string, path: string[] = null): boolean {
    return isPresent(this.getError(errorCode, path));
  }

  get root(): AbstractControl {
    let x: AbstractControl = this;

    while (isPresent(x._parent)) {
      x = x._parent;
    }

    return x;
  }

  /** @internal */
  _updateControlsErrors(): void {
    this._status = this._calculateStatus();

    if (isPresent(this._parent)) {
      this._parent._updateControlsErrors();
    }
  }

  /** @internal */
  _initObservables() {
    this._valueChanges = new EventEmitter();
    this._statusChanges = new EventEmitter();
  }

  private _calculateStatus(): string {
    if (isPresent(this._errors)) return INVALID;
    if (this._anyControlsHaveStatus(PENDING)) return PENDING;
    if (this._anyControlsHaveStatus(INVALID)) return INVALID;
    return VALID;
  }

  /** @internal */
  abstract _updateValue(): void;

  /** @internal */
  abstract _anyControlsHaveStatus(status: string): boolean;
}


/**
 * Defines a part of a form that cannot be divided into other controls. `FormControl`s have values
 * and
 * validation state, which is determined by an optional validation function.
 *
 * `FormControl` is one of the three fundamental building blocks used to define forms in Angular,
 * along
 * with {@link FormGroup} and {@link FormArray}.
 *
 * ## Usage
 *
 * By default, a `FormControl` is created for every `<input>` or other form component.
 * With {@link FormControlDirective} or {@link FormGroupDirective} an existing {@link FormControl}
 * can be bound to a DOM element instead. This `FormControl` can be configured with a custom
 * validation function.
 *
 * @experimental
 */
export class FormControl extends AbstractControl {
  /** @internal */
  _onChange: Function[] = [];

  constructor(
      value: any = null, validator: ValidatorFn|ValidatorFn[] = null,
      asyncValidator: AsyncValidatorFn|AsyncValidatorFn[] = null) {
    super(coerceToValidator(validator), coerceToAsyncValidator(asyncValidator));
    this._value = value;
    this.updateValueAndValidity({onlySelf: true, emitEvent: false});
    this._initObservables();
  }

  /**
   * Set the value of the form control to `value`.
   *
   * If `onlySelf` is `true`, this change will only affect the validation of this `FormControl`
   * and not its parent component. If `emitEvent` is `true`, this change will cause a
   * `valueChanges` event on the `FormControl` to be emitted. Both of these options default to
   * `false`.
   *
   * If `emitModelToViewChange` is `true`, the view will be notified about the new value
   * via an `onChange` event. This is the default behavior if `emitModelToViewChange` is not
   * specified.
   */
  updateValue(value: any, {onlySelf, emitEvent, emitModelToViewChange}: {
    onlySelf?: boolean,
    emitEvent?: boolean,
    emitModelToViewChange?: boolean
  } = {}): void {
    emitModelToViewChange = isPresent(emitModelToViewChange) ? emitModelToViewChange : true;
    this._value = value;
    if (this._onChange.length && emitModelToViewChange) {
      this._onChange.forEach((changeFn) => changeFn(this._value));
    }
    this.updateValueAndValidity({onlySelf: onlySelf, emitEvent: emitEvent});
  }

  /**
   * @internal
   */
  _updateValue() {}

  /**
   * @internal
   */
  _anyControlsHaveStatus(status: string): boolean { return false; }

  /**
   * Register a listener for change events.
   */
  registerOnChange(fn: Function): void { this._onChange.push(fn); }
}


/**
 * Defines a part of a form, of fixed length, that can contain other controls.
 *
 * A `FormGroup` aggregates the values of each {@link FormControl} in the group.
 * The status of a `FormGroup` depends on the status of its children.
 * If one of the controls in a group is invalid, the entire group is invalid.
 * Similarly, if a control changes its value, the entire group changes as well.
 *
 * `FormGroup` is one of the three fundamental building blocks used to define forms in Angular,
 * along with {@link FormControl} and {@link FormArray}. {@link FormArray} can also contain other
 * controls, but is of variable length.
 *
 * ### Example ([live demo](http://plnkr.co/edit/23DESOpbNnBpBHZt1BR4?p=preview))
 *
 * @experimental
 */
export class FormGroup extends AbstractControl {
  private _optionals: {[key: string]: boolean};

  constructor(
      public controls: {[key: string]: AbstractControl}, optionals: {[key: string]: boolean} = null,
      validator: ValidatorFn = null, asyncValidator: AsyncValidatorFn = null) {
    super(validator, asyncValidator);
    this._optionals = isPresent(optionals) ? optionals : {};
    this._initObservables();
    this._setParentForControls();
    this.updateValueAndValidity({onlySelf: true, emitEvent: false});
  }

  /**
   * Register a control with the group's list of controls.
   */
  registerControl(name: string, control: AbstractControl): AbstractControl {
    if (this.controls[name]) return this.controls[name];
    this.controls[name] = control;
    control.setParent(this);
    return control;
  }

  /**
   * Add a control to this group.
   */
  addControl(name: string, control: AbstractControl): void {
    this.registerControl(name, control);
    this.updateValueAndValidity();
  }

  /**
   * Remove a control from this group.
   */
  removeControl(name: string): void {
    StringMapWrapper.delete(this.controls, name);
    this.updateValueAndValidity();
  }

  /**
   * Mark the named control as non-optional.
   */
  include(controlName: string): void {
    StringMapWrapper.set(this._optionals, controlName, true);
    this.updateValueAndValidity();
  }

  /**
   * Mark the named control as optional.
   */
  exclude(controlName: string): void {
    StringMapWrapper.set(this._optionals, controlName, false);
    this.updateValueAndValidity();
  }

  /**
   * Check whether there is a control with the given name in the group.
   */
  contains(controlName: string): boolean {
    var c = StringMapWrapper.contains(this.controls, controlName);
    return c && this._included(controlName);
  }

  /** @internal */
  _setParentForControls() {
    StringMapWrapper.forEach(
        this.controls, (control: AbstractControl, name: string) => { control.setParent(this); });
  }

  /** @internal */
  _updateValue() { this._value = this._reduceValue(); }

  /** @internal */
  _anyControlsHaveStatus(status: string): boolean {
    var res = false;
    StringMapWrapper.forEach(this.controls, (control: AbstractControl, name: string) => {
      res = res || (this.contains(name) && control.status == status);
    });
    return res;
  }

  /** @internal */
  _reduceValue() {
    return this._reduceChildren(
        {}, (acc: {[k: string]: AbstractControl}, control: AbstractControl, name: string) => {
          acc[name] = control.value;
          return acc;
        });
  }

  /** @internal */
  _reduceChildren(initValue: any, fn: Function) {
    var res = initValue;
    StringMapWrapper.forEach(this.controls, (control: AbstractControl, name: string) => {
      if (this._included(name)) {
        res = fn(res, control, name);
      }
    });
    return res;
  }

  /** @internal */
  _included(controlName: string): boolean {
    var isOptional = StringMapWrapper.contains(this._optionals, controlName);
    return !isOptional || StringMapWrapper.get(this._optionals, controlName);
  }
}


/**
 * Defines a part of a form, of variable length, that can contain other controls.
 *
 * A `FormArray` aggregates the values of each {@link FormControl} in the group.
 * The status of a `FormArray` depends on the status of its children.
 * If one of the controls in a group is invalid, the entire array is invalid.
 * Similarly, if a control changes its value, the entire array changes as well.
 *
 * `FormArray` is one of the three fundamental building blocks used to define forms in Angular,
 * along with {@link FormControl} and {@link FormGroup}. {@link FormGroup} can also contain
 * other controls, but is of fixed length.
 *
 * ## Adding or removing controls
 *
 * To change the controls in the array, use the `push`, `insert`, or `removeAt` methods
 * in `FormArray` itself. These methods ensure the controls are properly tracked in the
 * form's hierarchy. Do not modify the array of `AbstractControl`s used to instantiate
 * the `FormArray` directly, as that will result in strange and unexpected behavior such
 * as broken change detection.
 *
 * ### Example ([live demo](http://plnkr.co/edit/23DESOpbNnBpBHZt1BR4?p=preview))
 *
 * @experimental
 */
export class FormArray extends AbstractControl {
  constructor(
      public controls: AbstractControl[], validator: ValidatorFn = null,
      asyncValidator: AsyncValidatorFn = null) {
    super(validator, asyncValidator);
    this._initObservables();
    this._setParentForControls();
    this.updateValueAndValidity({onlySelf: true, emitEvent: false});
  }

  /**
   * Get the {@link AbstractControl} at the given `index` in the array.
   */
  at(index: number): AbstractControl { return this.controls[index]; }

  /**
   * Insert a new {@link AbstractControl} at the end of the array.
   */
  push(control: AbstractControl): void {
    this.controls.push(control);
    control.setParent(this);
    this.updateValueAndValidity();
  }

  /**
   * Insert a new {@link AbstractControl} at the given `index` in the array.
   */
  insert(index: number, control: AbstractControl): void {
    ListWrapper.insert(this.controls, index, control);
    control.setParent(this);
    this.updateValueAndValidity();
  }

  /**
   * Remove the control at the given `index` in the array.
   */
  removeAt(index: number): void {
    ListWrapper.removeAt(this.controls, index);
    this.updateValueAndValidity();
  }

  /**
   * Length of the control array.
   */
  get length(): number { return this.controls.length; }

  /** @internal */
  _updateValue(): void { this._value = this.controls.map((control) => control.value); }

  /** @internal */
  _anyControlsHaveStatus(status: string): boolean {
    return this.controls.some(c => c.status == status);
  }

  /** @internal */
  _setParentForControls(): void {
    this.controls.forEach((control) => { control.setParent(this); });
  }
}


markAsTouched(): void { this._touched = true; }


markAsDirty({onlySelf}: {onlySelf?: boolean} = {}): void {
    onlySelf = normalizeBool(onlySelf);
    this._pristine = false;

    if (isPresent(this._parent) && !onlySelf) {
      this._parent.markAsDirty({onlySelf: onlySelf});
    }
  }


markAsPending({onlySelf}: {onlySelf?: boolean} = {}): void {
    onlySelf = normalizeBool(onlySelf);
    this._status = PENDING;

    if (isPresent(this._parent) && !onlySelf) {
      this._parent.markAsPending({onlySelf: onlySelf});
    }
  }


setParent(parent: FormGroup|FormArray): void { this._parent = parent; }


updateValueAndValidity({onlySelf, emitEvent}: {onlySelf?: boolean, emitEvent?: boolean} = {}):
      void {
    onlySelf = normalizeBool(onlySelf);
    emitEvent = isPresent(emitEvent) ? emitEvent : true;

    this._updateValue();

    this._errors = this._runValidator();
    this._status = this._calculateStatus();

    if (this._status == VALID || this._status == PENDING) {
      this._runAsyncValidator(emitEvent);
    }

    if (emitEvent) {
      ObservableWrapper.callEmit(this._valueChanges, this._value);
      ObservableWrapper.callEmit(this._statusChanges, this._status);
    }

    if (isPresent(this._parent) && !onlySelf) {
      this._parent.updateValueAndValidity({onlySelf: onlySelf, emitEvent: emitEvent});
    }
  }


private _runValidator(): {[key: string]: any} {
    return isPresent(this.validator) ? this.validator(this) : null;
  }


private _runAsyncValidator(emitEvent: boolean): void {
    if (isPresent(this.asyncValidator)) {
      this._status = PENDING;
      this._cancelExistingSubscription();
      var obs = toObservable(this.asyncValidator(this));
      this._asyncValidationSubscription = ObservableWrapper.subscribe(
          obs, (res: {[key: string]: any}) => this.setErrors(res, {emitEvent: emitEvent}));
    }
  }


private _cancelExistingSubscription(): void {
    if (isPresent(this._asyncValidationSubscription)) {
      ObservableWrapper.dispose(this._asyncValidationSubscription);
    }
  }


/**
   * Sets errors on a form control.
   *
   * This is used when validations are run not automatically, but manually by the user.
   *
   * Calling `setErrors` will also update the validity of the parent control.
   *
   * ## Usage
   *
   * ```
   * var login = new FormControl("someLogin");
   * login.setErrors({
   *   "notUnique": true
   * });
   *
   * expect(login.valid).toEqual(false);
   * expect(login.errors).toEqual({"notUnique": true});
   *
   * login.updateValue("someOtherLogin");
   *
   * expect(login.valid).toEqual(true);
   * ```
   */
setErrors(errors: {[key: string]: any}, {emitEvent}: {emitEvent?: boolean} = {}): void {
    emitEvent = isPresent(emitEvent) ? emitEvent : true;

    this._errors = errors;
    this._status = this._calculateStatus();

    if (emitEvent) {
      ObservableWrapper.callEmit(this._statusChanges, this._status);
    }

    if (isPresent(this._parent)) {
      this._parent._updateControlsErrors();
    }
  }


find(path: Array<string|number>|string): AbstractControl { return _find(this, path); }


getError(errorCode: string, path: string[] = null): any {
    var control = isPresent(path) && !ListWrapper.isEmpty(path) ? this.find(path) : this;
    if (isPresent(control) && isPresent(control._errors)) {
      return StringMapWrapper.get(control._errors, errorCode);
    } else {
      return null;
    }
  }


hasError(errorCode: string, path: string[] = null): boolean {
    return isPresent(this.getError(errorCode, path));
  }


/** @internal */
_updateControlsErrors(): void {
    this._status = this._calculateStatus();

    if (isPresent(this._parent)) {
      this._parent._updateControlsErrors();
    }
  }


/** @internal */
_initObservables() {
    this._valueChanges = new EventEmitter();
    this._statusChanges = new EventEmitter();
  }


private _calculateStatus(): string {
    if (isPresent(this._errors)) return INVALID;
    if (this._anyControlsHaveStatus(PENDING)) return PENDING;
    if (this._anyControlsHaveStatus(INVALID)) return INVALID;
    return VALID;
  }


/** @internal */
abstract _updateValue(): void;


/** @internal */
abstract _anyControlsHaveStatus(status: string): boolean;


/**
   * Set the value of the form control to `value`.
   *
   * If `onlySelf` is `true`, this change will only affect the validation of this `FormControl`
   * and not its parent component. If `emitEvent` is `true`, this change will cause a
   * `valueChanges` event on the `FormControl` to be emitted. Both of these options default to
   * `false`.
   *
   * If `emitModelToViewChange` is `true`, the view will be notified about the new value
   * via an `onChange` event. This is the default behavior if `emitModelToViewChange` is not
   * specified.
   */
updateValue(value: any, {onlySelf, emitEvent, emitModelToViewChange}: {
    onlySelf?: boolean,
    emitEvent?: boolean,
    emitModelToViewChange?: boolean
  } = {}): void {
    emitModelToViewChange = isPresent(emitModelToViewChange) ? emitModelToViewChange : true;
    this._value = value;
    if (this._onChange.length && emitModelToViewChange) {
      this._onChange.forEach((changeFn) => changeFn(this._value));
    }
    this.updateValueAndValidity({onlySelf: onlySelf, emitEvent: emitEvent});
  }


/**
   * @internal
   */
_updateValue() {}


/**
   * @internal
   */
_anyControlsHaveStatus(status: string): boolean { return false; }


/**
   * Register a listener for change events.
   */
registerOnChange(fn: Function): void { this._onChange.push(fn); }


/**
   * Register a control with the group's list of controls.
   */
registerControl(name: string, control: AbstractControl): AbstractControl {
    if (this.controls[name]) return this.controls[name];
    this.controls[name] = control;
    control.setParent(this);
    return control;
  }


/**
   * Add a control to this group.
   */
addControl(name: string, control: AbstractControl): void {
    this.registerControl(name, control);
    this.updateValueAndValidity();
  }


/**
   * Remove a control from this group.
   */
removeControl(name: string): void {
    StringMapWrapper.delete(this.controls, name);
    this.updateValueAndValidity();
  }


/**
   * Mark the named control as non-optional.
   */
include(controlName: string): void {
    StringMapWrapper.set(this._optionals, controlName, true);
    this.updateValueAndValidity();
  }


/**
   * Mark the named control as optional.
   */
exclude(controlName: string): void {
    StringMapWrapper.set(this._optionals, controlName, false);
    this.updateValueAndValidity();
  }


/**
   * Check whether there is a control with the given name in the group.
   */
contains(controlName: string): boolean {
    var c = StringMapWrapper.contains(this.controls, controlName);
    return c && this._included(controlName);
  }


/** @internal */
_setParentForControls() {
    StringMapWrapper.forEach(
        this.controls, (control: AbstractControl, name: string) => { control.setParent(this); });
  }


/** @internal */
_updateValue() { this._value = this._reduceValue(); }


/** @internal */
_anyControlsHaveStatus(status: string): boolean {
    var res = false;
    StringMapWrapper.forEach(this.controls, (control: AbstractControl, name: string) => {
      res = res || (this.contains(name) && control.status == status);
    });
    return res;
  }


/** @internal */
_reduceValue() {
    return this._reduceChildren(
        {}, (acc: {[k: string]: AbstractControl}, control: AbstractControl, name: string) => {
          acc[name] = control.value;
          return acc;
        });
  }


/** @internal */
_reduceChildren(initValue: any, fn: Function) {
    var res = initValue;
    StringMapWrapper.forEach(this.controls, (control: AbstractControl, name: string) => {
      if (this._included(name)) {
        res = fn(res, control, name);
      }
    });
    return res;
  }


/** @internal */
_included(controlName: string): boolean {
    var isOptional = StringMapWrapper.contains(this._optionals, controlName);
    return !isOptional || StringMapWrapper.get(this._optionals, controlName);
  }


/**
   * Get the {@link AbstractControl} at the given `index` in the array.
   */
at(index: number): AbstractControl { return this.controls[index]; }


/**
   * Insert a new {@link AbstractControl} at the end of the array.
   */
push(control: AbstractControl): void {
    this.controls.push(control);
    control.setParent(this);
    this.updateValueAndValidity();
  }


/**
   * Insert a new {@link AbstractControl} at the given `index` in the array.
   */
insert(index: number, control: AbstractControl): void {
    ListWrapper.insert(this.controls, index, control);
    control.setParent(this);
    this.updateValueAndValidity();
  }


/**
   * Remove the control at the given `index` in the array.
   */
removeAt(index: number): void {
    ListWrapper.removeAt(this.controls, index);
    this.updateValueAndValidity();
  }


/** @internal */
_updateValue(): void { this._value = this.controls.map((control) => control.value); }


/** @internal */
_anyControlsHaveStatus(status: string): boolean {
    return this.controls.some(c => c.status == status);
  }


/** @internal */
_setParentForControls(): void {
    this.controls.forEach((control) => { control.setParent(this); });
  }


export function noContent<Status extends number>(
  status: Status
): server.Response<Status, typeof noContentContentType, null> {
  return {
    status,
    value: {
      contentType: noContentContentType,
      value: null
    }
  };
}


export function json<Status extends number, Value>(
  status: Status,
  value: Value
): server.Response<Status, 'application/json', Value> {
  return {
    status,
    value: {
      contentType: 'application/json',
      value
    }
  };
}


export function text<Status extends number, Value>(
  status: Status,
  value: Value
): server.Response<Status, 'text/plain', Value> {
  return {
    status,
    value: {
      contentType: 'text/plain',
      value
    }
  };
}


export function set<Cls>(
  to: Cls,
  set: Cls extends valueClass.ValueClass<infer Shape, any> ? Partial<Shape> : never
): make.Make<Cls> {
  return (to as any).constructor.make({ ...to, ...set });
}


export function map<A extends ValueType, T extends ValueType>(
  value: A,
  predicate: (a: any) => a is T,
  fn: (p: T, traversalPath: string[]) => T
): A {
  return mapInternal(value, predicate, fn, []);
}


function mapInternal<A extends ValueType, T extends ValueType>(
  value: A,
  predicate: (a: any) => a is T,
  fn: (p: T, traversalPath: string[]) => T,
  traversalPath: string[]
): A {
  if (predicate(value)) {
    value = fn(value, traversalPath) as any;
  }
  if (Array.isArray(value)) {
    const arr: any = value.map((item, index) =>
      mapInternal(item, predicate, fn, traversalPath.concat(String(index)))
    );
    return selectArray(value, arr) as any;
  }
  if (value && typeof value === 'object') {
    const record: any = {};
    Object.keys(value).map(key => {
      record[key] = mapInternal((value as any)[key], predicate, fn, traversalPath.concat(key));
    });
    return selectRecord(value, record);
  }
  return value;
}


export async function pmap<A extends ValueType, T extends ValueType>(
  value: A,
  predicate: (a: any) => a is T,
  map: (p: T, traversalPath: string[]) => Promise<T>
): Promise<A> {
  return pmapInternal(value, predicate, map, []);
}


function isPromise(p: any): p is Promise<any> {
  return p && typeof p.then === 'function';
}


function pmapInternal<A extends ValueType, T extends ValueType>(
  value: A,
  predicate: (a: any) => a is T,
  map: (p: T, traversalPath: string[]) => Promise<T>,
  traversalPath: string[]
): Promise<A> | A {
  if (predicate(value)) {
    value = map(value, traversalPath) as any;
  }
  if (isPromise(value)) {
    return value.then(n => pmapComposite(n, predicate, map, traversalPath));
  }
  return pmapComposite(value, predicate, map, traversalPath);
}


function selectArray<T>(original: T[], newArray: T[]): T[] {
  for (let i = 0; i < original.length; i++) {
    if (original[i] !== newArray[i]) {
      return newArray;
    }
  }
  return original.length !== newArray.length ? newArray : original;
}


function selectRecord<T extends { [key: string]: unknown }>(original: T, newRecord: T) {
  const changed = Object.keys(original).some(key => {
    return original[key] !== newRecord[key];
  });
  if (!changed) {
    return original;
  }
  if (original instanceof valueClass.ValueClass) {
    return set<valueClass.ValueClass<any, any>>(original, newRecord).success();
  }
  return newRecord;
}


function pmapArray<A, T>(
  value: A[],
  predicate: (v: any) => v is T,
  map: (v: T, traversalPath: string[]) => Promise<T>,
  traversalPath: string[]
): Promise<A[]> | A[] {
  const mapped = value.map((n, i) =>
    pmapInternal<A, T>(n, predicate, map, traversalPath.concat(String(i)))
  );
  if (mapped.some(isPromise)) {
    return Promise.all(mapped).then(newValues => {
      return selectArray(value, newValues);
    });
  }
  return selectArray<A>(value, mapped as any);
}


function pmapObject<A, T>(
  value: A,
  predicate: (v: any) => v is T,
  map: (v: T, traversalPath: string[]) => Promise<T>,
  traversalPath: string[]
): Promise<A> | A {
  const record: any = {};
  const promises: Array<Promise<unknown>> = [];
  Object.keys(value).forEach(key => {
    const v = pmapInternal((value as any)[key], predicate, map, traversalPath.concat(key));
    if (isPromise(v)) {
      promises.push(
        v.then(result => {
          record[key] = result;
        })
      );
    } else {
      record[key] = v;
    }
  });

  if (promises.length) {
    return Promise.all(promises).then(() => selectRecord(value, record));
  }
  return selectRecord(value, record);
}


function pmapComposite<A, T>(
  value: A,
  predicate: (v: any) => v is T,
  map: (v: T, traversalPath: string[]) => Promise<T>,
  traversalPath: string[]
): Promise<A> | A {
  if (Array.isArray(value)) {
    return pmapArray(value, predicate, map, traversalPath) as any;
  }
  if (value && typeof value === 'object') {
    return pmapObject(value, predicate, map, traversalPath);
  }
  return value;
}


(item, index) =>
      mapInternal(item, predicate, fn, traversalPath.concat(String(index)))


key => {
      record[key] = mapInternal((value as any)[key], predicate, fn, traversalPath.concat(key));
    }


n => pmapComposite(n, predicate, map, traversalPath)


key => {
    return original[key] !== newRecord[key];
  }


(n, i) =>
    pmapInternal<A, T>(n, predicate, map, traversalPath.concat(String(i)))


newValues => {
      return selectArray(value, newValues);
    }


key => {
    const v = pmapInternal((value as any)[key], predicate, map, traversalPath.concat(key));
    if (isPromise(v)) {
      promises.push(
        v.then(result => {
          record[key] = result;
        })
      );
    } else {
      record[key] = v;
    }
  }


result => {
          record[key] = result;
        }


() => selectRecord(value, record)


type ValueType =
  | valueClass.ValueClass<any, any>
  | { [key: string]: any }
  | readonly any[]
  | string
  | boolean
  | number;


(record: any, index: number) => {
            let srfmajortext = record.srfmajortext;
            if (index < 5) {
                if (!Object.is(dataInfo, '')) {
                    dataInfo += '、';
                }
                dataInfo += srfmajortext;
            } else {
                return false;
            }

        }


() => {
                this.removeData(null);
            }


(record) => {
                let key = record.srfkey;
                if (!Object.is(keys, '')) {
                    keys += ';';
                }
                keys += key;
            }


/**
 * 视图引擎基础
 *
 * @export
 * @class DataViewEngine
 * @extends {MDViewEngine}
 */
export default class DataViewEngine extends MDViewEngine {

    /**
     * 表格部件
     *
     * @type {*}
     * @memberof DataViewEngine
     */
    protected dataView: any;

    /**
     * Creates an instance of DataViewEngine.
     * @memberof DataViewEngine
     */
    constructor() {
        super();
    }

    /**
     * 引擎初始化
     *
     * @param {*} [options={}]
     * @memberof DataViewEngine
     */
    public init(options: any = {}): void {
        this.dataView = options.dataview;
        super.init(options);
    }

    /**
     * 部件事件
     *
     * @param {string} ctrlName
     * @param {string} eventName
     * @param {*} args
     * @memberof DataViewEngine
     */
    public onCtrlEvent(ctrlName: string, eventName: string, args: any): void {
        if (Object.is(ctrlName, 'dataview')) {
            this.MDCtrlEvent(eventName, args);
        }
        super.onCtrlEvent(ctrlName, eventName, args);
    }

    /**
     * 获取多数据部件
     *
     * @returns {*}
     * @memberof DataViewEngine
     */
    public getMDCtrl(): any {
        return this.dataView;
    }

    /**
     * 删除操作
     *
     * @returns {void}
     * @memberof DataViewEngine
     */
    public doRemove(): void {

        let selectedData = this.getMDCtrl() && this.getMDCtrl().getSelection();
        if (!selectedData || selectedData == null || selectedData.length === 0) {
            return;
        }

        let dataInfo = '';

        selectedData.forEach((record: any, index: number) => {
            let srfmajortext = record.srfmajortext;
            if (index < 5) {
                if (!Object.is(dataInfo, '')) {
                    dataInfo += '、';
                }
                dataInfo += srfmajortext;
            } else {
                return false;
            }

        });

        if (selectedData.length < 5) {
            dataInfo = dataInfo + '共' + selectedData.length + '条数据';
        } else {
            dataInfo = dataInfo + '...' + '共' + selectedData.length + '条数据';
        }

        dataInfo = dataInfo.replace(/[null]/g, '').replace(/[undefined]/g, '').replace(/[ ]/g, '');

        // 询问框
        this.view.$Modal.confirm({
            title:'警告',
            content: '确认要删除 ' + dataInfo + '，删除操作将不可恢复？',
            onOk:() => {
                this.removeData(null);
            },
            onCancel: () => {

            }
        });
    }

    /**
     * 删除
     *
     * @param {*} [arg={}]
     * @returns {void}
     * @memberof DataViewEngine
     */
    public removeData(arg: any = {}): void {
        if (!arg) {
            arg = {};
        }

        // if (this.getParentMode()) {
        //     Object.assign(arg, this.getParentMode());
        // }

        // if (this.getParentData()) {
        //     Object.assign(arg, this.getParentData());
        // }

        if (!arg.srfkeys) {
            // 获取要删除的数据集合
            const selectedData: Array<any> = this.getMDCtrl() && this.getMDCtrl().getSelection();
            if (!selectedData || selectedData == null || selectedData.length === 0) {
                return;
            }

            let keys = '';
            selectedData.forEach((record) => {
                let key = record.srfkey;
                if (!Object.is(keys, '')) {
                    keys += ';';
                }
                keys += key;
            });
            arg.srfkeys = keys;
        }

        const grid: any = this.getMDCtrl();
        if (grid) {
            grid.remove(arg);
        }
    }

}


/**
     * 引擎初始化
     *
     * @param {*} [options={}]
     * @memberof DataViewEngine
     */
public init(options: any = {}): void {
        this.dataView = options.dataview;
        super.init(options);
    }


/**
     * 部件事件
     *
     * @param {string} ctrlName
     * @param {string} eventName
     * @param {*} args
     * @memberof DataViewEngine
     */
public onCtrlEvent(ctrlName: string, eventName: string, args: any): void {
        if (Object.is(ctrlName, 'dataview')) {
            this.MDCtrlEvent(eventName, args);
        }
        super.onCtrlEvent(ctrlName, eventName, args);
    }


/**
     * 获取多数据部件
     *
     * @returns {*}
     * @memberof DataViewEngine
     */
public getMDCtrl(): any {
        return this.dataView;
    }


/**
     * 删除操作
     *
     * @returns {void}
     * @memberof DataViewEngine
     */
public doRemove(): void {

        let selectedData = this.getMDCtrl() && this.getMDCtrl().getSelection();
        if (!selectedData || selectedData == null || selectedData.length === 0) {
            return;
        }

        let dataInfo = '';

        selectedData.forEach((record: any, index: number) => {
            let srfmajortext = record.srfmajortext;
            if (index < 5) {
                if (!Object.is(dataInfo, '')) {
                    dataInfo += '、';
                }
                dataInfo += srfmajortext;
            } else {
                return false;
            }

        });

        if (selectedData.length < 5) {
            dataInfo = dataInfo + '共' + selectedData.length + '条数据';
        } else {
            dataInfo = dataInfo + '...' + '共' + selectedData.length + '条数据';
        }

        dataInfo = dataInfo.replace(/[null]/g, '').replace(/[undefined]/g, '').replace(/[ ]/g, '');

        // 询问框
        this.view.$Modal.confirm({
            title:'警告',
            content: '确认要删除 ' + dataInfo + '，删除操作将不可恢复？',
            onOk:() => {
                this.removeData(null);
            },
            onCancel: () => {

            }
        });
    }


/**
     * 删除
     *
     * @param {*} [arg={}]
     * @returns {void}
     * @memberof DataViewEngine
     */
public removeData(arg: any = {}): void {
        if (!arg) {
            arg = {};
        }

        // if (this.getParentMode()) {
        //     Object.assign(arg, this.getParentMode());
        // }

        // if (this.getParentData()) {
        //     Object.assign(arg, this.getParentData());
        // }

        if (!arg.srfkeys) {
            // 获取要删除的数据集合
            const selectedData: Array<any> = this.getMDCtrl() && this.getMDCtrl().getSelection();
            if (!selectedData || selectedData == null || selectedData.length === 0) {
                return;
            }

            let keys = '';
            selectedData.forEach((record) => {
                let key = record.srfkey;
                if (!Object.is(keys, '')) {
                    keys += ';';
                }
                keys += key;
            });
            arg.srfkeys = keys;
        }

        const grid: any = this.getMDCtrl();
        if (grid) {
            grid.remove(arg);
        }
    }


export class StatsDatabase extends Dexie {
  public launches!: Dexie.Table<ILaunchStats, number>
  public dailyMeasures!: Dexie.Table<IDailyMeasures, number>

  public constructor(name: string) {
    super(name)

    this.version(1).stores({
      launches: '++',
    })

    this.version(DatabaseVersion).stores({
      dailyMeasures: '++id',
    })
  }
}


/** The timing stats for app launch. */
export interface ILaunchStats {
  /**
   * The time (in milliseconds) it takes from when our main process code is
   * first loaded until the app `ready` event is emitted.
   */
  readonly mainReadyTime: number

  /**
   * The time (in milliseconds) it takes from when loading begins to loading
   * end.
   */
  readonly loadTime: number

  /**
   * The time (in milliseconds) it takes from when our renderer process code is
   * first loaded until the renderer `ready` event is emitted.
   */
  readonly rendererReadyTime: number
}


/** The daily measures captured for stats. */
export interface IDailyMeasures {
  /** The ID in the database. */
  readonly id?: number

  /** The number of commits. */
  readonly commits: number

  /** The number of times the user has opened a shell from the app. */
  readonly openShellCount: number

  /** The number of partial commits. */
  readonly partialCommits: number

  /** The number of commits created with one or more co-authors. */
  readonly coAuthoredCommits: number
}


(a: ReleaseType, b: ReleaseType) =>
  RELEASE_PRECEDENCE.indexOf(a) > RELEASE_PRECEDENCE.indexOf(b)


(text: string) =>
  text
    .split('\n')
    .slice(0, -1)
    .join('\n')
    .trim()


({
  releaseNotes,
  message,
  versionBump,
  newVersion,
  repo,
  url
}: MakeTweetArgs) => {
  const build = (notes: string) =>
    message
      .replace('%release', versionBump)
      .replace('%package', repo)
      .replace('%version', newVersion)
      .replace('%notes', notes)
      .replace('%link', url);

  let truncatedNotes = releaseNotes
    .split('#### Authors')[0]
    .replace(/#### /gm, '')
    .replace(/\(?\[\S+\]\(\S+\)/gm, '')
    .trim();
  let tweet = build(truncatedNotes);

  while (!tweetValidation.parseTweet(tweet).valid) {
    truncatedNotes = removeLastLine(truncatedNotes);
    tweet = build(dedent`
      ${truncatedNotes}
      …
    `);
  }

  return tweet;
}


(notes: string) =>
    message
      .replace('%release', versionBump)
      .replace('%package', repo)
      .replace('%version', newVersion)
      .replace('%notes', notes)
      .replace('%link', url)


async ({ newVersion, lastRelease, response, releaseNotes }) => {
        if (!newVersion || !response || !auto.git) {
          return;
        }

        const versionBump = diff(newVersion, lastRelease) || 'patch';

        if (isGreaterThan(this.options.threshold as ReleaseType, versionBump)) {
          return;
        }

        await this.tweet(
          makeTweet({
            releaseNotes,
            message: this.options.message,
            versionBump,
            newVersion,
            repo: auto.git.options.repo,
            url: response.data.html_url
          })
        );
      }


export default class TwitterPlugin implements IPlugin {
  name = 'Twitter';

  readonly options: ITwitterPluginOptions;
  private readonly tweet: (message: string) => Promise<void>;

  constructor(options: Partial<ITwitterPluginOptions> = {}) {
    this.options = { ...defaults, ...options };

    if (
      !process.env.TWITTER_ACCESS_TOKEN ||
      !process.env.TWITTER_ACCESS_TOKEN_SECRET ||
      !process.env.TWITTER_CONSUMER_KEY ||
      !process.env.TWITTER_CONSUMER_KEY_SECRET
    ) {
      throw new Error(
        'Need all of the following secrets available on the environment: TWITTER_ACCESS_TOKEN, TWITTER_ACCESS_TOKEN_SECRET, TWITTER_CONSUMER_KEY, TWITTER_CONSUMER_KEY_SECRET'
      );
    }

    this.tweet = promisify(
      twitter({
        accessToken: process.env.TWITTER_ACCESS_TOKEN,
        accessTokenSecret: process.env.TWITTER_ACCESS_TOKEN_SECRET,
        consumerKey: process.env.TWITTER_CONSUMER_KEY,
        consumerSecret: process.env.TWITTER_CONSUMER_KEY_SECRET
      })
    );
  }

  apply(auto: Auto) {
    auto.hooks.afterRelease.tapPromise(
      this.name,
      async ({ newVersion, lastRelease, response, releaseNotes }) => {
        if (!newVersion || !response || !auto.git) {
          return;
        }

        const versionBump = diff(newVersion, lastRelease) || 'patch';

        if (isGreaterThan(this.options.threshold as ReleaseType, versionBump)) {
          return;
        }

        await this.tweet(
          makeTweet({
            releaseNotes,
            message: this.options.message,
            versionBump,
            newVersion,
            repo: auto.git.options.repo,
            url: response.data.html_url
          })
        );
      }
    );
  }
}


interface ITwitterPluginOptions {
  message: string;
  threshold: SEMVER;
}


interface MakeTweetArgs {
  releaseNotes: string;
  message: string;
  versionBump: ReleaseType;
  newVersion: string;
  repo: string;
  url: string;
}


apply(auto: Auto) {
    auto.hooks.afterRelease.tapPromise(
      this.name,
      async ({ newVersion, lastRelease, response, releaseNotes }) => {
        if (!newVersion || !response || !auto.git) {
          return;
        }

        const versionBump = diff(newVersion, lastRelease) || 'patch';

        if (isGreaterThan(this.options.threshold as ReleaseType, versionBump)) {
          return;
        }

        await this.tweet(
          makeTweet({
            releaseNotes,
            message: this.options.message,
            versionBump,
            newVersion,
            repo: auto.git.options.repo,
            url: response.data.html_url
          })
        );
      }
    );
  }


function throwInvalidInvocation() {
  throw new Error(
    `Specify the project name and the target (e.g., nx run proj:build)`
  );
}


function parseRunOpts(
  cwd: string,
  args: string[],
  defaultProjectName: string | null
): RunOptions {
  const runOptions = convertToCamelCase(
    yargsParser(args, {
      boolean: ['help', 'prod'],
      string: ['configuration', 'project'],
      alias: {
        c: 'configuration',
      },
    })
  );
  const help = runOptions.help as boolean;
  if (!runOptions._ || !runOptions._[0]) {
    throwInvalidInvocation();
  }
  // eslint-disable-next-line prefer-const
  let [project, target, configuration]: [string, string, string] =
    runOptions._[0].split(':');
  if (!project && defaultProjectName) {
    logger.debug(
      `No project name specified. Using default project : ${chalk.bold(
        defaultProjectName
      )}`
    );
    project = defaultProjectName;
  }
  if (runOptions.configuration) {
    configuration = runOptions.configuration as string;
  }
  if (runOptions.prod) {
    configuration = 'production';
  }
  if (runOptions.project) {
    project = runOptions.project as string;
  }
  if (!project || !target) {
    throwInvalidInvocation();
  }
  const res = { project, target, configuration, help, runOptions };
  delete runOptions['help'];
  delete runOptions['_'];
  delete runOptions['c'];
  delete runOptions['configuration'];
  delete runOptions['prod'];
  delete runOptions['project'];

  return res;
}


export function printRunHelp(
  opts: { project: string; target: string },
  schema: Schema
) {
  printHelp(`nx run ${opts.project}:${opts.target}`, schema);
}


export function validateProject(
  workspace: WorkspaceJsonConfiguration,
  projectName: string
) {
  const project = workspace.projects[projectName];
  if (!project) {
    throw new Error(`Could not find project "${projectName}"`);
  }
}


function isPromise<T extends { success: boolean }>(
  v: Promise<T> | AsyncIterableIterator<T>
): v is Promise<T> {
  return typeof (v as any)?.then === 'function';
}


function isAsyncIterator<T extends { success: boolean }>(
  v: Promise<{ success: boolean }> | AsyncIterableIterator<T>
): v is AsyncIterableIterator<T> {
  return typeof (v as any)?.[Symbol.asyncIterator] === 'function';
}


async function* promiseToIterator<T extends { success: boolean }>(
  v: Promise<T>
): AsyncIterableIterator<T> {
  yield await v;
}


async function iteratorToProcessStatusCode(
  i: AsyncIterableIterator<{ success: boolean }>
): Promise<number> {
  let success: boolean;

  // This is a workaround to fix an issue that only happens with
  // the @angular-devkit/build-angular:browser builder. Starting
  // on version 12.0.1, a SASS compilation implementation was
  // introduced making use of workers and it's unref()-ing the worker
  // too early, causing the process to exit early in environments
  // like CI or when running Docker builds.
  const keepProcessAliveInterval = setInterval(() => {}, 1000);
  try {
    let prev: IteratorResult<{ success: boolean }>;
    let current: IteratorResult<{ success: boolean }>;
    do {
      prev = current;
      current = await i.next();
    } while (!current.done);

    success =
      current.value !== undefined || !prev
        ? current.value.success
        : prev.value.success;

    return success ? 0 : 1;
  } finally {
    clearInterval(keepProcessAliveInterval);
  }
}


function createImplicitTargetConfig(
  proj: ProjectConfiguration,
  targetName: string
): TargetConfiguration {
  return {
    executor: '@nrwl/workspace:run-script',
    options: {
      script: targetName,
    },
  };
}


async function runExecutorInternal<T extends { success: boolean }>(
  {
    project,
    target,
    configuration,
  }: {
    project: string;
    target: string;
    configuration?: string;
  },
  options: { [k: string]: any },
  root: string,
  cwd: string,
  workspace: WorkspaceJsonConfiguration,
  isVerbose: boolean,
  printHelp: boolean
): Promise<AsyncIterableIterator<T>> {
  validateProject(workspace, project);

  const ws = new Workspaces(root);
  const proj = workspace.projects[project];
  const targetConfig = proj.targets
    ? proj.targets[target]
    : createImplicitTargetConfig(proj, target);

  if (!targetConfig) {
    throw new Error(
      `NX Cannot find target '${target}' for project '${project}'`
    );
  }

  configuration = configuration ?? targetConfig.defaultConfiguration;

  const [nodeModule, executor] = targetConfig.executor.split(':');
  const { schema, implementationFactory } = ws.readExecutor(
    nodeModule,
    executor
  );

  if (printHelp) {
    printRunHelp({ project, target }, schema);
    process.exit(0);
  }

  const combinedOptions = combineOptionsForExecutor(
    options,
    configuration,
    targetConfig,
    schema,
    project,
    ws.relativeCwd(cwd),
    isVerbose
  );

  if (ws.isNxExecutor(nodeModule, executor)) {
    const implementation = implementationFactory() as Executor<any>;
    const r = implementation(combinedOptions, {
      root,
      target: targetConfig,
      workspace,
      projectName: project,
      targetName: target,
      configurationName: configuration,
      cwd,
      isVerbose,
    }) as Promise<T> | AsyncIterableIterator<T>;
    if (isPromise<T>(r)) {
      return promiseToIterator<T>(r);
    } else if (isAsyncIterator<T>(r)) {
      return r;
    } else {
      throw new TypeError(
        `NX Executor "${targetConfig.executor}" should return either a Promise or an AsyncIterator`
      );
    }
  } else {
    require('../compat/compat');
    const observable = await (
      await import('./ngcli-adapter')
    ).scheduleTarget(
      root,
      {
        project,
        target,
        configuration,
        runOptions: combinedOptions,
        executor: targetConfig.executor,
      },
      isVerbose
    );
    const { eachValueFrom } = require('rxjs-for-await');
    return eachValueFrom(observable as any);
  }
}


/**
 * Loads and invokes executor.
 *
 * This is analogous to invoking executor from the terminal, with the exception
 * that the params aren't parsed from the string, but instead provided parsed already.
 *
 * Apart from that, it works the same way:
 *
 * - it will load the workspace configuration
 * - it will resolve the target
 * - it will load the executor and the schema
 * - it will load the options for the appropriate configuration
 * - it will run the validations and will set the default
 * - and, of course, it will invoke the executor
 *
 * Example:
 *
 * ```typescript
 * for await (const s of await runExecutor({project: 'myproj', target: 'serve'}, {watch: true}, context)) {
 *   // s.success
 * }
 * ```
 *
 * Note that the return value is a promise of an iterator, so you need to await before iterating over it.
 */
export async function runExecutor<T extends { success: boolean }>(
  targetDescription: {
    project: string;
    target: string;
    configuration?: string;
  },
  options: { [k: string]: any },
  context: ExecutorContext
): Promise<AsyncIterableIterator<T>> {
  return await runExecutorInternal<T>(
    targetDescription,
    options,
    context.root,
    context.cwd,
    context.workspace,
    context.isVerbose,
    false
  );
}


export async function run(
  cwd: string,
  root: string,
  args: string[],
  isVerbose: boolean
) {
  const ws = new Workspaces(root);

  return handleErrors(isVerbose, async () => {
    const workspace = ws.readWorkspaceConfiguration();
    const defaultProjectName = ws.calculateDefaultProjectName(cwd, workspace);
    const opts = parseRunOpts(cwd, args, defaultProjectName);
    return iteratorToProcessStatusCode(
      await runExecutorInternal(
        opts,
        opts.runOptions,
        root,
        cwd,
        workspace,
        isVerbose,
        opts.help
      )
    );
  });
}


async () => {
    const workspace = ws.readWorkspaceConfiguration();
    const defaultProjectName = ws.calculateDefaultProjectName(cwd, workspace);
    const opts = parseRunOpts(cwd, args, defaultProjectName);
    return iteratorToProcessStatusCode(
      await runExecutorInternal(
        opts,
        opts.runOptions,
        root,
        cwd,
        workspace,
        isVerbose,
        opts.help
      )
    );
  }


export interface Target {
  project: string;
  target: string;
  configuration?: string;
}


export interface RunOptions extends Target {
  help: boolean;
  runOptions: Options;
}


function mapStateToProps(state: AppState) {
    return {
        currentFilter: state.currentFilter
    }
}


function mapDispatchToProps(dispatch: any)  {
    return {
        updateFilters: (  newFilters: Filters ) => dispatch(updateFilters(newFilters))
    }
}


(event: React.ChangeEvent<HTMLInputElement>) =>{
        let changingLabel = event.target.value;
        // typescript sucks and doesn't let me do the line below without the ts-ignore....
        // @ts-ignore
        let pastValue = this.state[changingLabel]

        // updating state to toggle boolean value
        this.setState({
            [changingLabel]: !pastValue
        } as any);
    }


answer => {
                        // @ts-ignore
                        let booleanChecked = this.state[answer.value];
                        return(

                                {/* @ts-ignore */}
                                <input className="answer" type="checkbox" onChange={this.handleChange} checked={booleanChecked}
                                id={answer.value} value={answer.value}></input>
                                {answer.label}


(  newFilters: Filters ) => dispatch(updateFilters(newFilters))


class LanguageQ extends React.Component<props, state> {
    constructor(props:any) {
        super(props);
        // setting state to what is dictated in redux (aka storing prev values here)
        this.state = this.props.currentFilter.language.value;
    }

    // add states to redux here when component is removed from screen
    // aka when user is done inputing
    componentWillUnmount() {
        let changes = this.props.currentFilter;
        changes.language.value = this.state;
        updateFilters(changes);
    }

    // adding redux here to change filters properties
    private handleChange = (event: React.ChangeEvent<HTMLInputElement>) =>{
        let changingLabel = event.target.value;
        // typescript sucks and doesn't let me do the line below without the ts-ignore....
        // @ts-ignore
        let pastValue = this.state[changingLabel]

        // updating state to toggle boolean value
        this.setState({
            [changingLabel]: !pastValue
        } as any);
    }

    render() {
        return(

For technical assistance, what language(s) would you prefer to use?

Select all options that apply to you.

                  {/*  */}

                    {this.q1Options.map(answer => {
                        // @ts-ignore
                        let booleanChecked = this.state[answer.value];
                        return(

                                {/* @ts-ignore */}
                                <input className="answer" type="checkbox" onChange={this.handleChange} checked={booleanChecked}
                                id={answer.value} value={answer.value}></input>
                                {answer.label}

                        )
                    })}


interface state {
    Spanish: boolean,
    Mandarin: boolean,
    Vietnamese: boolean,
    Russian: boolean,
    Swahili: boolean,
    French: boolean,
    ASL: boolean,
    Laotian: boolean,
    Thai: boolean,
    English: boolean
}


interface props {
    currentFilter: Filters,
    updateFilters: (newFilters: Filters) => void,
}


// add states to redux here when component is removed from screen
// aka when user is done inputing
componentWillUnmount() {
        let changes = this.props.currentFilter;
        changes.language.value = this.state;
        updateFilters(changes);
    }


render() {
        return(

For technical assistance, what language(s) would you prefer to use?

Select all options that apply to you.

                  {/*  */}

                    {this.q1Options.map(answer => {
                        // @ts-ignore
                        let booleanChecked = this.state[answer.value];
                        return(

                                {/* @ts-ignore */}
                                <input className="answer" type="checkbox" onChange={this.handleChange} checked={booleanChecked}
                                id={answer.value} value={answer.value}></input>
                                {answer.label}

                        )
                    })}


(cookieKey: string) => {
	let cookieValue: string = "";
	if(document.cookie){
			let array = document.cookie.split((escape(cookieKey)+'='));
			if(array.length >= 2){
					let arraySub = array[1].split(';');
					cookieValue = unescape(arraySub[0]);
			}
	}

	return cookieValue;
}


(
	cookieKey: string,
	cookieValue: string,
	cookieExpired: number,
	isSecure: boolean,
	cookiePath: string = '/'
) => {
	const date = new Date();
	date.setTime(date.getTime() + cookieExpired * 60 * 60 * 1000);

	let cookie = `${escape(cookieKey)}=${escape(cookieValue)}`;
	cookie+=(cookieExpired ? '; EXPIRES='+date.toUTCString() : '');
	cookie+=(cookiePath ? '; PATH='+cookiePath : '');
	cookie+=(isSecure ? '; SECURE' : '');

	document.cookie = cookie;
}


export function RawCodeBlock({
  code,
  language,
  className: extraClassName,
  disablePrefixes,
  enableLineRef = false,
}: CodeBlockProps & {
  className?: string;
  enableLineRef?: boolean;
}): React.ReactElement {
  const [hashValue, setHashValue] = useState("");
  const codeDivClassNames =
    "text-gray-300 token-line text-right select-none text-xs";
  const onClick = (e: React.MouseEvent) => {
    if (e.shiftKey) {
      e.preventDefault();
      const { hash } = location;
      const target = (e.target as HTMLAnchorElement).hash;
      location.hash = hash
        ? hash.replace(/(-.+)?$/, target.replace("#", "-"))
        : target;
    }
  };

  if (enableLineRef) {
    useEffect(() => {
      const onHashChange = () => {
        setHashValue(location.hash);
        const id = location.hash.substring(1);
        document.getElementById(id)?.scrollIntoView();
      };
      window.addEventListener("hashchange", onHashChange);
      onHashChange();
      return () => {
        window.removeEventListener("hashchange", onHashChange);
      };
    }, []);

    useLayoutEffect(() => {
      const parts = hashValue.split("-");
      if (parts.length > 1) {
        const hash = parts
          .map((e) => /([\d]+)/.exec(e)![0])
          .map((e) => parseInt(e, 10))
          .sort((a, b) => a - b)
          .map((e) => `L${e}`);
        if (hash.length) {
          const idEl = document.getElementById(hash[0]);
          if (idEl) {
            idEl.scrollIntoView({ block: "center", behavior: "smooth" });
            return;
          }
        }
      }
    });
  }

  return (
    <Highlight
      Prism={Prism}
      theme={light}
      code={code}
      // @ts-expect-error because typings are bad
      language={
        language === "shell" ? "bash" : language === "text" ? "diff" : language
      }


function CodeBlock({
  code,
  language,
  disablePrefixes,
}: CodeBlockProps): React.ReactElement {
  return (
    <RawCodeBlock
      code={code}
      language={language}
      disablePrefixes={disablePrefixes}
      className="p-4"
    />
  );
}


(e: React.MouseEvent) => {
    if (e.shiftKey) {
      e.preventDefault();
      const { hash } = location;
      const target = (e.target as HTMLAnchorElement).hash;
      location.hash = hash
        ? hash.replace(/(-.+)?$/, target.replace("#", "-"))
        : target;
    }
  }


() => {
      const onHashChange = () => {
        setHashValue(location.hash);
        const id = location.hash.substring(1);
        document.getElementById(id)?.scrollIntoView();
      };
      window.addEventListener("hashchange", onHashChange);
      onHashChange();
      return () => {
        window.removeEventListener("hashchange", onHashChange);
      };
    }


() => {
        setHashValue(location.hash);
        const id = location.hash.substring(1);
        document.getElementById(id)?.scrollIntoView();
      }


() => {
        window.removeEventListener("hashchange", onHashChange);
      }


() => {
      const parts = hashValue.split("-");
      if (parts.length > 1) {
        const hash = parts
          .map((e) => /([\d]+)/.exec(e)![0])
          .map((e) => parseInt(e, 10))
          .sort((a, b) => a - b)
          .map((e) => `L${e}`);
        if (hash.length) {
          const idEl = document.getElementById(hash[0]);
          if (idEl) {
            idEl.scrollIntoView({ block: "center", behavior: "smooth" });
            return;
          }
        }
      }
    }


(e) => /([\d]+)/.exec(e)![0]


(e) => parseInt(e, 10)


(a, b) => a - b


(e) => `L${e}`


(line, i) =>
                line[0]?.empty && i === tokens.length - 1 ? null : (
                  <div key={i + "l"


(line, i) => {
              const lineProps = getLineProps({ line, key: i });
              lineProps.className += " text-xs";
              if (
                enableLineRef &&
                hashValue &&
                ((arr, index) =>
                  Math.min(...arr) <= index && index <= Math.max(...arr))(
                  hashValue
                    .split("-")
                    .map((e) => /([\d]+)/.exec(e)![1])
                    .map((n) => parseInt(n, 10)),
                  i + 1
                )
              ) {
                lineProps.className = `${lineProps.className} highlight-line`;
              }
              return line[0]?.empty && i === tokens.length - 1 ? null : (
                <div key={i} {


(arr, index) =>
                  Math.min(...arr) <= index && index <= Math.max(...arr)


(e) => /([\d]+)/.exec(e)![1]


(n) => parseInt(n, 10)


(token, key) => (
                    <span key={key}


export interface CodeBlockProps {
  code: string;
  disablePrefixes?: boolean;
  language:
    | "javascript"
    | "typescript"
    | "jsx"
    | "tsx"
    | "json"
    | "yaml"
    | "markdown"
    | "bash"
    | "shell"
    | "text"
    | "rust"
    | "python"
    | "toml"
    | "wasm"
    | "makefile"
    | "dockerfile";
}


enableLineRef ? (
                      <a
                        id={`L${i + 1}`


export function initializeIcons(baseUrl: string = ''): void {
  registerIcons({
    style: {
      MozOsxFontSmoothing: 'grayscale',
      WebkitFontSmoothing: 'antialiased',
      fontStyle: 'normal',
      fontWeight: 'normal',
      speak: 'none'
    },
    fontFace: {
      fontFamily: `"FabricMDL2Icons-7"`,
      src: `url('${baseUrl}fabric-icons-7-1bae3315.woff') format('woff')`,
    },
    icons: {
      'PageCheckedOut': '\uF02C',
      'SaveAndClose': '\uF038',
      'Script': '\uF03A',
      'Archive': '\uF03F',
      'ActivityFeed': '\uF056',
      'EventDate': '\uF059',
      'CaretRight': '\uF06B',
      'SetAction': '\uF071',
      'CaretSolidLeft': '\uF08D',
      'CaretSolidDown': '\uF08E',
      'CaretSolidRight': '\uF08F',
      'CaretSolidUp': '\uF090',
      'PowerAppsLogo': '\uF091',
      'PowerApps2Logo': '\uF092',
      'SearchIssue': '\uF09A',
      'SearchIssueMirrored': '\uF09B',
      'FabricAssetLibrary': '\uF09C',
      'FabricDataConnectionLibrary': '\uF09D',
      'FabricDocLibrary': '\uF09E',
      'FabricFormLibrary': '\uF09F',
      'FabricFormLibraryMirrored': '\uF0A0',
      'FabricReportLibrary': '\uF0A1',
      'FabricReportLibraryMirrored': '\uF0A2',
      'FabricPublicFolder': '\uF0A3',
      'FabricFolderSearch': '\uF0A4',
      'FabricMovetoFolder': '\uF0A5',
      'FabricUnsyncFolder': '\uF0A6',
      'FabricSyncFolder': '\uF0A7',
      'FabricOpenFolderHorizontal': '\uF0A8',
      'FabricFolder': '\uF0A9',
      'FabricFolderFill': '\uF0AA',
      'FabricNewFolder': '\uF0AB',
      'FabricPictureLibrary': '\uF0AC',
      'AddFavorite': '\uF0C8',
      'AddFavoriteFill': '\uF0C9',
      'BufferTimeBefore': '\uF0CF',
      'BufferTimeAfter': '\uF0D0',
      'BufferTimeBoth': '\uF0D1',
      'CannedChat': '\uF0F2',
      'SkypeForBusinessLogo': '\uF0FC',
      'PageCheckedin': '\uF104',
      'CaretBottomLeftSolid8': '\uF121',
      'CaretBottomRightSolid8': '\uF122',
      'FolderHorizontal': '\uF12B',
      'MicrosoftStaffhubLogo': '\uF130',
      'GiftboxOpen': '\uF133',
      'StatusCircleOuter': '\uF136',
      'StatusCircleInner': '\uF137',
      'ExploreContentSingle': '\uF164',
      'CollapseContent': '\uF165',
      'CollapseContentSingle': '\uF166',
      'InfoSolid': '\uF167',
      'ProgressRingDots': '\uF16A',
      'CaloriesAdd': '\uF172',
      'BranchFork': '\uF173',
      'HardDriveGroup': '\uF18F',
      'BucketColor': '\uF1B6',
      'BucketColorFill': '\uF1B7',
      'Taskboard': '\uF1C2',
      'SingleColumn': '\uF1D3',
      'DoubleColumn': '\uF1D4',
      'TripleColumn': '\uF1D5',
      'ColumnLeftTwoThirds': '\uF1D6',
      'ColumnRightTwoThirds': '\uF1D7',
      'AccessLogoFill': '\uF1DB',
      'AnalyticsLogo': '\uF1DE',
      'AnalyticsQuery': '\uF1DF',
      'NewAnalyticsQuery': '\uF1E0',
      'AnalyticsReport': '\uF1E1',
      'WordLogo': '\uF1E3',
      'WordLogoFill': '\uF1E4',
      'ExcelLogo': '\uF1E5',
      'ExcelLogoFill': '\uF1E6',
      'OneNoteLogo': '\uF1E7',
      'OneNoteLogoFill': '\uF1E8',
      'OutlookLogo': '\uF1E9',
      'OutlookLogoFill': '\uF1EA',
      'PowerPointLogo': '\uF1EB',
      'PowerPointLogoFill': '\uF1EC',
      'PublisherLogo': '\uF1ED',
      'PublisherLogoFill': '\uF1EE',
      'ScheduleEventAction': '\uF1EF',
      'FlameSolid': '\uF1F3',
      'ServerProcesses': '\uF1FE',
      'Server': '\uF201',
      'SaveAll': '\uF203',
      'LinkedInLogo': '\uF20A',
      'SidePanelMirrored': '\uF221',
      'ProtectRestrict': '\uF22A',
      'GridViewSmall': '\uF232',
      'GridViewMedium': '\uF233',
      'GridViewLarge': '\uF234',
      'Step': '\uF241',
      'StepInsert': '\uF242',
      'StepShared': '\uF243',
      'StepSharedAdd': '\uF244',
      'StepSharedInsert': '\uF245',
      'ViewDashboard': '\uF246',
      'ViewList': '\uF247',
      'ViewListGroup': '\uF248'
    }
  });
}


async function createDetachedContainerAndGetRootDataStore() {
        const container = await loader.createDetachedContainer(codeDetails);
        // Get the root dataStore from the detached container.
        const response = await container.request({ url: "/" });
        const defaultDataStore = response.value as TestFluidObject;
        return {
            container,
            defaultDataStore,
        };
    }


function createTestLoader(): Loader {
        const factory: TestFluidObjectFactory = new TestFluidObjectFactory([
            [sharedStringId, SharedString.getFactory()],
            [sharedMapId, SharedMap.getFactory()],
            [crcId, ConsensusRegisterCollection.getFactory()],
            [sharedDirectoryId, SharedDirectory.getFactory()],
            [sharedCellId, SharedCell.getFactory()],
            [sharedInkId, Ink.getFactory()],
            [sharedMatrixId, SharedMatrix.getFactory()],
            [cocId, ConsensusQueue.getFactory()],
            [sparseMatrixId, SparseMatrix.getFactory()],
            [sharedCounterId, SharedCounter.getFactory()],
        ]);
        const codeLoader = new LocalCodeLoader([[codeDetails, factory]]);
        const documentServiceFactory = driver.createDocumentServiceFactory();
        return new Loader({
            urlResolver: driver.createUrlResolver(),
            documentServiceFactory,
            codeLoader,
        });
    }


()=>{
        driver = getFluidTestDriver();
    }


async (
        containerRuntime: IContainerRuntimeBase,
    ) => {
        const peerDataStore = await requestFluidObject<ITestFluidObject>(
            await containerRuntime.createDataStore(["default"]),
            "/");
        return {
            peerDataStore,
            peerDataStoreRuntimeChannel: peerDataStore.channel,
        };
    }


async () => {
        const documentId = createDocumentId();
        request = driver.createCreateNewRequest(documentId);
        loader = createTestLoader();
        opProcessingController = new OpProcessingController();
    }


async () => {
        const { container } =
            await createDetachedContainerAndGetRootDataStore();
        const snapshotTree = JSON.parse(container.serialize());

        assert.ok(snapshotTree.trees[".protocol"], "protocol tree not present");
        assert.ok(snapshotTree.trees.default, "default dataStore tree not present");
        assert.ok(snapshotTree.trees._scheduler, "scheduler tree not present");
        assert.strictEqual(Object.keys(snapshotTree.trees[".protocol"].blobs).length, 8,
            "4 protocol blobs should be there(8 mappings)");

        // Check for protocol attributes
        const protocolAttributesBlobId = snapshotTree.trees[".protocol"].blobs.attributes;
        const protocolAttributes: IDocumentAttributes =
            JSON.parse(fromBase64ToUtf8(snapshotTree.trees[".protocol"].blobs[protocolAttributesBlobId]));
        assert.strictEqual(protocolAttributes.sequenceNumber, detachedContainerRefSeqNumber, "initial aeq #");
        assert(
            protocolAttributes.minimumSequenceNumber <= protocolAttributes.sequenceNumber,
            "Min Seq # <= seq #");

        // Check for default dataStore
        const defaultDataStoreBlobId = snapshotTree.trees.default.blobs[".component"];
        const dataStoreAttributes =
            JSON.parse(fromBase64ToUtf8(snapshotTree.trees.default.blobs[defaultDataStoreBlobId]));
        assert.strictEqual(dataStoreAttributes.pkg, JSON.stringify(["default"]), "Package name should be default");
    }


async () => {
        const { container, defaultDataStore } =
            await createDetachedContainerAndGetRootDataStore();
        const snapshotTree1 = JSON.parse(container.serialize());
        // Create a channel
        const channel = defaultDataStore.runtime.createChannel("test1",
            "https://graph.microsoft.com/types/map") as SharedMap;
        channel.bindToContext();
        const snapshotTree2 = JSON.parse(container.serialize());

        assert.strictEqual(JSON.stringify(Object.keys(snapshotTree1.trees)),
            JSON.stringify(Object.keys(snapshotTree2.trees)),
            "3 trees should be there(protocol, default dataStore, scheduler");

        // Check for protocol attributes
        const protocolAttributesBlobId1 = snapshotTree1.trees[".protocol"].blobs.attributes;
        const protocolAttributesBlobId2 = snapshotTree2.trees[".protocol"].blobs.attributes;
        const protocolAttributes1: IDocumentAttributes =
            JSON.parse(fromBase64ToUtf8(snapshotTree1.trees[".protocol"].blobs[protocolAttributesBlobId1]));
        const protocolAttributes2: IDocumentAttributes =
            JSON.parse(fromBase64ToUtf8(snapshotTree2.trees[".protocol"].blobs[protocolAttributesBlobId2]));
        assert.strictEqual(JSON.stringify(protocolAttributes1), JSON.stringify(protocolAttributes2),
            "Protocol attributes should be same as no change happened");

        // Check for newly create channel
        assert.strictEqual(snapshotTree1.trees.default.trees.test1, undefined,
            "Test channel 1 should not be present in snapshot 1");
        assert(snapshotTree2.trees.default.trees.test1,
            "Test channel 1 should be present in snapshot 2");
    }


async () => {
        const { container, defaultDataStore } =
            await createDetachedContainerAndGetRootDataStore();

        // Create another dataStore
        const peerDataStore = await createPeerDataStore(defaultDataStore.context.containerRuntime);
        const dataStore2 = peerDataStore.peerDataStore as TestFluidObject;

        // Create a channel
        const rootOfDataStore1 = await defaultDataStore.getSharedObject<SharedMap>(sharedMapId);
        rootOfDataStore1.set("dataStore2", dataStore2.handle);

        const snapshotTree = JSON.parse(container.serialize());

        assert.ok(snapshotTree.trees[".protocol"], "protocol tree not present");
        assert.ok(snapshotTree.trees.default, "default dataStore tree not present");
        assert.ok(snapshotTree.trees._scheduler, "scheduler tree not present");
        assert.ok(
            // eslint-disable-next-line unicorn/no-unsafe-regex
            Object.keys(snapshotTree.trees).some((key) => /^(?:\w+-){4}\w+$/.test(key)),
            "peer data store tree not present",
        );

        assert(snapshotTree.trees[dataStore2.runtime.id], "Handle Bounded dataStore should be in summary");
    }


(key) => /^(?:\w+-){4}\w+$/.test(key)


async () => {
        const { container } =
            await createDetachedContainerAndGetRootDataStore();

        const snapshotTree = container.serialize();

        const container2 = await loader.rehydrateDetachedContainerFromSnapshot(snapshotTree);

        // Check for scheduler
        const schedulerResponse = await container2.request({ url: "_scheduler" });
        assert.strictEqual(schedulerResponse.status, 200, "Scheduler Component should exist!!");
        const schedulerDataStore = schedulerResponse.value as TestFluidObject;
        assert.strictEqual(schedulerDataStore.runtime.id, "_scheduler", "Id should be of scheduler");

        // Check for default data store
        const response = await container2.request({ url: "/" });
        assert.strictEqual(response.status, 200, "Component should exist!!");
        const defaultDataStore = response.value as TestFluidObject;
        assert.strictEqual(defaultDataStore.runtime.id, "default", "Id should be default");

        // Check for dds
        const sharedMap = await defaultDataStore.getSharedObject<SharedMap>(sharedMapId);
        const sharedDir = await defaultDataStore.getSharedObject<SharedDirectory>(sharedDirectoryId);
        const sharedString = await defaultDataStore.getSharedObject<SharedString>(sharedStringId);
        const sharedCell = await defaultDataStore.getSharedObject<SharedCell>(sharedCellId);
        const sharedCounter = await defaultDataStore.getSharedObject<SharedCounter>(sharedCounterId);
        const crc = await defaultDataStore.getSharedObject<ConsensusRegisterCollection<string>>(crcId);
        const coc = await defaultDataStore.getSharedObject<ConsensusOrderedCollection>(cocId);
        const ink = await defaultDataStore.getSharedObject<Ink>(sharedInkId);
        const sharedMatrix = await defaultDataStore.getSharedObject<SharedMatrix>(sharedMatrixId);
        const sparseMatrix = await defaultDataStore.getSharedObject<SparseMatrix>(sparseMatrixId);
        assert.strictEqual(sharedMap.id, sharedMapId, "Shared map should exist!!");
        assert.strictEqual(sharedDir.id, sharedDirectoryId, "Shared directory should exist!!");
        assert.strictEqual(sharedString.id, sharedStringId, "Shared string should exist!!");
        assert.strictEqual(sharedCell.id, sharedCellId, "Shared cell should exist!!");
        assert.strictEqual(sharedCounter.id, sharedCounterId, "Shared counter should exist!!");
        assert.strictEqual(crc.id, crcId, "CRC should exist!!");
        assert.strictEqual(coc.id, cocId, "COC should exist!!");
        assert.strictEqual(ink.id, sharedInkId, "Shared ink should exist!!");
        assert.strictEqual(sharedMatrix.id, sharedMatrixId, "Shared matrix should exist!!");
        assert.strictEqual(sparseMatrix.id, sparseMatrixId, "Sparse matrix should exist!!");
    }


async () => {
        const { container } =
            await createDetachedContainerAndGetRootDataStore();

        const snapshotTree = container.serialize();

        const container2 = await loader.rehydrateDetachedContainerFromSnapshot(snapshotTree);
        await container2.attach(request);

        // Check for scheduler
        const schedulerResponse = await container2.request({ url: "_scheduler" });
        assert.strictEqual(schedulerResponse.status, 200, "Scheduler Component should exist!!");
        const schedulerDataStore = schedulerResponse.value as TestFluidObject;
        assert.strictEqual(schedulerDataStore.runtime.id, "_scheduler", "Id should be of scheduler");

        // Check for default data store
        const response = await container2.request({ url: "/" });
        assert.strictEqual(response.status, 200, "Component should exist!!");
        const defaultDataStore = response.value as TestFluidObject;
        assert.strictEqual(defaultDataStore.runtime.id, "default", "Id should be default");

        // Check for dds
        const sharedMap = await defaultDataStore.getSharedObject<SharedMap>(sharedMapId);
        const sharedDir = await defaultDataStore.getSharedObject<SharedDirectory>(sharedDirectoryId);
        const sharedString = await defaultDataStore.getSharedObject<SharedString>(sharedStringId);
        const sharedCell = await defaultDataStore.getSharedObject<SharedCell>(sharedCellId);
        const sharedCounter = await defaultDataStore.getSharedObject<SharedCounter>(sharedCounterId);
        const crc = await defaultDataStore.getSharedObject<ConsensusRegisterCollection<string>>(crcId);
        const coc = await defaultDataStore.getSharedObject<ConsensusOrderedCollection>(cocId);
        const ink = await defaultDataStore.getSharedObject<Ink>(sharedInkId);
        const sharedMatrix = await defaultDataStore.getSharedObject<SharedMatrix>(sharedMatrixId);
        const sparseMatrix = await defaultDataStore.getSharedObject<SparseMatrix>(sparseMatrixId);
        assert.strictEqual(sharedMap.id, sharedMapId, "Shared map should exist!!");
        assert.strictEqual(sharedDir.id, sharedDirectoryId, "Shared directory should exist!!");
        assert.strictEqual(sharedString.id, sharedStringId, "Shared string should exist!!");
        assert.strictEqual(sharedCell.id, sharedCellId, "Shared cell should exist!!");
        assert.strictEqual(sharedCounter.id, sharedCounterId, "Shared counter should exist!!");
        assert.strictEqual(crc.id, crcId, "CRC should exist!!");
        assert.strictEqual(coc.id, cocId, "COC should exist!!");
        assert.strictEqual(ink.id, sharedInkId, "Shared ink should exist!!");
        assert.strictEqual(sharedMatrix.id, sharedMatrixId, "Shared matrix should exist!!");
        assert.strictEqual(sparseMatrix.id, sparseMatrixId, "Sparse matrix should exist!!");
    }


async () => {
        const { container } =
            await createDetachedContainerAndGetRootDataStore();

        const responseBefore = await container.request({ url: "/" });
        const defaultDataStoreBefore = responseBefore.value as TestFluidObject;
        const sharedStringBefore = await defaultDataStoreBefore.getSharedObject<SharedString>(sharedStringId);
        sharedStringBefore.insertText(0, "Hello");

        const snapshotTree = container.serialize();

        const container2 = await loader.rehydrateDetachedContainerFromSnapshot(snapshotTree);

        const responseAfter = await container2.request({ url: "/" });
        const defaultComponentAfter = responseAfter.value as TestFluidObject;
        const sharedStringAfter = await defaultComponentAfter.getSharedObject<SharedString>(sharedStringId);
        assert.strictEqual(
            JSON.stringify(sharedStringAfter.summarize()),
            JSON.stringify(sharedStringBefore.summarize()),
            "Summaries of shared string should match and contents should be same!!");
    }


async () => {
        const { container } =
            await createDetachedContainerAndGetRootDataStore();
        let str = "AA";
        const response1 = await container.request({ url: "/" });
        const defaultComponent1 = response1.value as TestFluidObject;
        const sharedString1 = await defaultComponent1.getSharedObject<SharedString>(sharedStringId);
        sharedString1.insertText(0, str);
        const snapshotTree = container.serialize();

        const container2 = await loader.rehydrateDetachedContainerFromSnapshot(snapshotTree);
        const responseBefore = await container2.request({ url: "/" });
        const defaultDataStoreBefore = responseBefore.value as TestFluidObject;
        const sharedStringBefore = await defaultDataStoreBefore.getSharedObject<SharedString>(sharedStringId);
        const sharedMapBefore = await defaultDataStoreBefore.getSharedObject<SharedMap>(sharedMapId);
        str += "BB";
        sharedStringBefore.insertText(0, str);
        sharedMapBefore.set("0", str);

        await container2.attach(request);
        const responseAfter = await container2.request({ url: "/" });
        const defaultComponentAfter = responseAfter.value as TestFluidObject;
        const sharedStringAfter = await defaultComponentAfter.getSharedObject<SharedString>(sharedStringId);
        const sharedMapAfter = await defaultComponentAfter.getSharedObject<SharedMap>(sharedMapId);
        assert.strictEqual(
            JSON.stringify(sharedStringAfter.summarize()),
            JSON.stringify(sharedStringBefore.summarize()),
            "Summaries of shared string should match and contents should be same!!");
        assert.strictEqual(
            JSON.stringify(sharedMapAfter.summarize()),
            JSON.stringify(sharedMapBefore.summarize()),
            "Summaries of shared map should match and contents should be same!!");
    }


async () => {
        const { container, defaultDataStore } =
            await createDetachedContainerAndGetRootDataStore();

        // Create another dataStore
        const peerDataStore = await createPeerDataStore(defaultDataStore.context.containerRuntime);
        const dataStore2 = peerDataStore.peerDataStore as TestFluidObject;
        peerDataStore.peerDataStoreRuntimeChannel.bindToContext();
        const sharedMap1 = await dataStore2.getSharedObject<SharedMap>(sharedMapId);
        sharedMap1.set("0", "A");
        const snapshotTree = container.serialize();

        const rehydratedContainer = await loader.rehydrateDetachedContainerFromSnapshot(snapshotTree);
        opProcessingController.addDeltaManagers(rehydratedContainer.deltaManager);
        await rehydratedContainer.attach(request);

        // Now load the container from another loader.
        const urlResolver2 = driver.createUrlResolver();
        const loader2 = createTestLoader();
        assert(rehydratedContainer.resolvedUrl);
        const requestUrl2 = await urlResolver2.getAbsoluteUrl(rehydratedContainer.resolvedUrl, "");
        const container2 = await loader2.resolve({ url: requestUrl2 });
        opProcessingController.addDeltaManagers(rehydratedContainer.deltaManager);

        // Get the sharedString1 from dataStore2 in rehydrated container.
        const responseBefore = await rehydratedContainer.request({ url: `/${dataStore2.context.id}` });
        const dataStore2FromRC = responseBefore.value as TestFluidObject;
        const sharedMapFromRC = await dataStore2FromRC.getSharedObject<SharedMap>(sharedMapId);
        sharedMapFromRC.set("1", "B");

        const responseAfter = await container2.request({ url: `/${dataStore2.context.id}` });
        const dataStore3 = responseAfter.value as TestFluidObject;
        const sharedMap3 = await dataStore3.getSharedObject<SharedMap>(sharedMapId);

        await opProcessingController.process();
        assert.strictEqual(sharedMap3.get("1"), "B", "Contents should be as required");
        assert.strictEqual(
            JSON.stringify(sharedMap3.summarize()),
            JSON.stringify(sharedMapFromRC.summarize()),
            "Summaries of shared string should match and contents should be same!!");
    }


async () => {
        const { container, defaultDataStore } =
            await createDetachedContainerAndGetRootDataStore();

        // Create another dataStore
        const peerDataStore = await createPeerDataStore(defaultDataStore.context.containerRuntime);
        const dataStore2 = peerDataStore.peerDataStore as TestFluidObject;
        peerDataStore.peerDataStoreRuntimeChannel.bindToContext();
        const sharedMap1 = await dataStore2.getSharedObject<SharedMap>(sharedMapId);
        sharedMap1.set("0", "A");
        const snapshotTree = container.serialize();

        const rehydratedContainer = await loader.rehydrateDetachedContainerFromSnapshot(snapshotTree);
        opProcessingController.addDeltaManagers(rehydratedContainer.deltaManager);
        await rehydratedContainer.attach(request);

        // Now load the container from another loader.
        const urlResolver2 = driver.createUrlResolver();
        const loader2 = createTestLoader();
        assert(rehydratedContainer.resolvedUrl);
        const requestUrl2 = await urlResolver2.getAbsoluteUrl(rehydratedContainer.resolvedUrl, "");
        const container2 = await loader2.resolve({ url: requestUrl2 });
        opProcessingController.addDeltaManagers(container2.deltaManager);

        // Get the sharedString1 from dataStore2 in container2.
        const responseBefore = await container2.request({ url: `/${dataStore2.context.id}` });
        const dataStore3 = responseBefore.value as TestFluidObject;
        const sharedMap3 = await dataStore3.getSharedObject<SharedMap>(sharedMapId);
        sharedMap3.set("1", "B");

        // Get the sharedString1 from dataStore2 in rehydrated container.
        const responseAfter = await rehydratedContainer.request({ url: `/${dataStore2.context.id}` });
        const dataStore2FromRC = responseAfter.value as TestFluidObject;
        const sharedMapFromRC = await dataStore2FromRC.getSharedObject<SharedMap>(sharedMapId);

        await opProcessingController.process();
        assert.strictEqual(sharedMapFromRC.get("1"), "B", "Changes should be reflected in other map");
        assert.strictEqual(
            JSON.stringify(sharedMap3.summarize()),
            JSON.stringify(sharedMapFromRC.summarize()),
            "Summaries of shared string should match and contents should be same!!");
    }


async () => {
        const { container, defaultDataStore } =
            await createDetachedContainerAndGetRootDataStore();

        // Create another dataStore
        const peerDataStore = await createPeerDataStore(defaultDataStore.context.containerRuntime);
        const dataStore2 = peerDataStore.peerDataStore as TestFluidObject;

        const rootOfDataStore1 = await defaultDataStore.getSharedObject<SharedMap>(sharedMapId);
        rootOfDataStore1.set("dataStore2", dataStore2.handle);

        const snapshotTree = container.serialize();
        const rehydratedContainer = await loader.rehydrateDetachedContainerFromSnapshot(snapshotTree);

        const response = await rehydratedContainer.request({ url: `/${dataStore2.context.id}` });
        const dataStore2FromRC = response.value as TestFluidObject;
        assert(dataStore2FromRC, "DataStore2 should have been serialized properly");
        assert.strictEqual(dataStore2FromRC.runtime.id, dataStore2.runtime.id, "DataStore2 id should match");
    }


async () => {
        const { container, defaultDataStore } =
            await createDetachedContainerAndGetRootDataStore();

        // Create another not bounded dds
        const ddsId = "notbounddds";
        const dds2 = defaultDataStore.runtime.createChannel(ddsId, SharedString.getFactory().type);

        const rootOfDataStore1 = await defaultDataStore.getSharedObject<SharedMap>(sharedMapId);
        rootOfDataStore1.set("dd2", dds2.handle);

        const snapshotTree = container.serialize();
        const rehydratedContainer = await loader.rehydrateDetachedContainerFromSnapshot(snapshotTree);

        const response = await rehydratedContainer.request({ url: `/${defaultDataStore.runtime.id}/${ddsId}` });
        const ddd2FromRC = response.value as SharedString;
        assert(ddd2FromRC, "ddd2 should have been serialized properly");
        assert.strictEqual(ddd2FromRC.id, ddsId, "DDS id should match");
        assert.strictEqual(ddd2FromRC.id, dds2.id, "Both dds id should match");
    }


async () => {
        const { container, defaultDataStore } =
            await createDetachedContainerAndGetRootDataStore();

        // Create another not bounded dataStore
        const peerDataStore = await createPeerDataStore(defaultDataStore.context.containerRuntime);
        const dataStore2 = peerDataStore.peerDataStore as TestFluidObject;

        // Create another not bounded dds
        const ddsId = "notbounddds";
        const dds2 = defaultDataStore.runtime.createChannel(ddsId, SharedMap.getFactory().type) as SharedMap;
        dds2.set("dataStore2", dataStore2.handle);

        const rootOfDataStore1 = await defaultDataStore.getSharedObject<SharedMap>(sharedMapId);
        rootOfDataStore1.set("dd2", dds2.handle);

        const snapshotTree = container.serialize();
        const rehydratedContainer = await loader.rehydrateDetachedContainerFromSnapshot(snapshotTree);

        const responseForDDS =
            await rehydratedContainer.request({ url: `/${defaultDataStore.runtime.id}/${ddsId}` });
        const ddd2FromRC = responseForDDS.value as SharedString;
        assert(ddd2FromRC, "ddd2 should have been serialized properly");
        assert.strictEqual(ddd2FromRC.id, ddsId, "DDS id should match");
        assert.strictEqual(ddd2FromRC.id, dds2.id, "Both dds id should match");

        const responseForDataStore = await rehydratedContainer.request({ url: `/${dataStore2.context.id}` });
        const dataStore2FromRC = responseForDataStore.value as TestFluidObject;
        assert(dataStore2FromRC, "DataStore2 should have been serialized properly");
        assert.strictEqual(dataStore2FromRC.runtime.id, dataStore2.runtime.id, "DataStore2 id should match");
    }


async () => {
        const { container, defaultDataStore } =
            await createDetachedContainerAndGetRootDataStore();

        // Create another not bounded dataStore
        const peerDataStore = await createPeerDataStore(defaultDataStore.context.containerRuntime);
        const dataStore2 = peerDataStore.peerDataStore as TestFluidObject;

        // Create another not bounded dds
        const ddsId = "notbounddds";
        const dds2 = dataStore2.runtime.createChannel(ddsId, SharedMap.getFactory().type) as SharedMap;
        const rootOfDataStore2 = await dataStore2.getSharedObject<SharedMap>(sharedMapId);
        rootOfDataStore2.set("dds2", dds2.handle);

        const rootOfDataStore1 = await defaultDataStore.getSharedObject<SharedMap>(sharedMapId);
        rootOfDataStore1.set("dataStore2", dataStore2.handle);

        const snapshotTree = container.serialize();
        const rehydratedContainer = await loader.rehydrateDetachedContainerFromSnapshot(snapshotTree);

        const responseForDDS = await rehydratedContainer.request({ url: `/${dataStore2.runtime.id}/${ddsId}` });
        const ddd2FromRC = responseForDDS.value as SharedString;
        assert(ddd2FromRC, "ddd2 should have been serialized properly");
        assert.strictEqual(ddd2FromRC.id, ddsId, "DDS id should match");
        assert.strictEqual(ddd2FromRC.id, dds2.id, "Both dds id should match");

        const responseForDataStore = await rehydratedContainer.request({ url: `/${dataStore2.context.id}` });
        const dataStore2FromRC = responseForDataStore.value as TestFluidObject;
        assert(dataStore2FromRC, "DataStore2 should have been serialized properly");
        assert.strictEqual(dataStore2FromRC.runtime.id, dataStore2.runtime.id, "DataStore2 id should match");
    }


async () => {
        const { container, defaultDataStore } =
            await createDetachedContainerAndGetRootDataStore();

        // Create another not bounded dataStore
        await createPeerDataStore(defaultDataStore.context.containerRuntime);

        const snapshotTree = JSON.parse(container.serialize());

        assert.ok(snapshotTree.trees[".protocol"], "protocol tree not present");
        assert.ok(snapshotTree.trees.default, "default dataStore tree not present");
        assert.ok(snapshotTree.trees._scheduler, "scheduler tree not present");
        assert.ok(
            // eslint-disable-next-line unicorn/no-unsafe-regex
            !Object.keys(snapshotTree.trees).some((key) => /^(?:\w+-){4}\w+$/.test(key)),
            "unbounded/unreferenced data store tree present",
        );
    }


export function reorderArray<T>(items: T[], oldIndex: number, newIndex: number): T[] {
  const newItems = items.concat();

  if (newIndex > oldIndex) newIndex--;

  newItems.splice(newIndex, 0, newItems.splice(oldIndex, 1)[0]);

  return newItems;
}


async datasource => {
        const historyResp = await axios(`/druid/coordinator/v1/rules/${datasource}/history`);
        return historyResp.data;
      }


({ result }) => {
        this.setState({
          historyRecords: result,
        });
      }


(comment: string) => {
    const { datasource, onSave } = this.props;
    const { currentRules } = this.state;

    onSave(datasource, currentRules, comment);
  }


(newRule: any, index: number) => {
    const { currentRules } = this.state;

    const newRules = (currentRules || []).map((r, i) => {
      if (i === index) return newRule;
      return r;
    });

    this.setState({
      currentRules: newRules,
    });
  }


(r, i) => {
      if (i === index) return newRule;
      return r;
    }


(index: number) => {
    const { currentRules } = this.state;

    const newRules = (currentRules || []).filter((_r, i) => i !== index);

    this.setState({
      currentRules: newRules,
    });
  }


(_r, i) => i !== index


(rule: any, index: number) => {
    const { tiers } = this.props;
    const { currentRules } = this.state;

    return (
      <RuleEditor
        rule={rule}
        tiers={tiers}
        key={index}
        onChange={r => this.changeRule(r, index)}
        onDelete={() => this.onDeleteRule(index)}


() => this.moveRule(index, -1)


() => this.moveRule(index, 2)


() => {
    const { rules } = this.props;

    this.setState({
      currentRules: rules.concat(),
    });
  }


() => {
    const { tiers } = this.props;
    const { currentRules } = this.state;

    const newRules = (currentRules || []).concat({
      type: 'loadForever',
      tieredReplicants: { [tiers[0]]: 2 },
    });

    this.setState({
      currentRules: newRules,
    });
  }