/**
 * Creates and manages an AWS IoT Thing Type.
 *
 * ## Example Usage
 *
 * ```typescript
 * import * as pulumi from "@pulumi/pulumi";
 * import * as aws from "@pulumi/aws";
 *
 * const foo = new aws.iot.ThingType("foo", {});
 * ```
 */
export class ThingType extends pulumi.CustomResource {
    /**
     * Get an existing ThingType resource's state with the given name, ID, and optional extra
     * properties used to qualify the lookup.
     *
     * @param name The _unique_ name of the resulting resource.
     * @param id The _unique_ provider ID of the resource to lookup.
     * @param state Any extra arguments used during the lookup.
     */
    public static get(name: string, id: pulumi.Input<pulumi.ID>, state?: ThingTypeState, opts?: pulumi.CustomResourceOptions): ThingType {
        return new ThingType(name, <any>state, { ...opts, id: id });
    }

    /** @internal */
    public static readonly __pulumiType = 'aws:iot/thingType:ThingType';

    /**
     * Returns true if the given object is an instance of ThingType.  This is designed to work even
     * when multiple copies of the Pulumi SDK have been loaded into the same process.
     */
    public static isInstance(obj: any): obj is ThingType {
        if (obj === undefined || obj === null) {
            return false;
        }
        return obj['__pulumiType'] === ThingType.__pulumiType;
    }

    /**
     * The ARN of the created AWS IoT Thing Type.
     */
    public /*out*/ readonly arn!: pulumi.Output<string>;
    /**
     * Whether the thing type is deprecated. If true, no new things could be associated with this type.
     */
    public readonly deprecated!: pulumi.Output<boolean | undefined>;
    /**
     * The name of the thing type.
     */
    public readonly name!: pulumi.Output<string>;
    public readonly properties!: pulumi.Output<{ description?: string, searchableAttributes: string[] } | undefined>;

    /**
     * Create a ThingType resource with the given unique name, arguments, and options.
     *
     * @param name The _unique_ name of the resource.
     * @param args The arguments to use to populate this resource's properties.
     * @param opts A bag of options that control this resource's behavior.
     */
    constructor(name: string, args?: ThingTypeArgs, opts?: pulumi.CustomResourceOptions)
    constructor(name: string, argsOrState?: ThingTypeArgs | ThingTypeState, opts?: pulumi.CustomResourceOptions) {
        let inputs: pulumi.Inputs = {};
        if (opts && opts.id) {
            const state = argsOrState as ThingTypeState | undefined;
            inputs["arn"] = state ? state.arn : undefined;
            inputs["deprecated"] = state ? state.deprecated : undefined;
            inputs["name"] = state ? state.name : undefined;
            inputs["properties"] = state ? state.properties : undefined;
        } else {
            const args = argsOrState as ThingTypeArgs | undefined;
            inputs["deprecated"] = args ? args.deprecated : undefined;
            inputs["name"] = args ? args.name : undefined;
            inputs["properties"] = args ? args.properties : undefined;
            inputs["arn"] = undefined /*out*/;
        }
        super(ThingType.__pulumiType, name, inputs, opts);
    }
}


/**
 * Input properties used for looking up and filtering ThingType resources.
 */
export interface ThingTypeState {
    /**
     * The ARN of the created AWS IoT Thing Type.
     */
    readonly arn?: pulumi.Input<string>;
    /**
     * Whether the thing type is deprecated. If true, no new things could be associated with this type.
     */
    readonly deprecated?: pulumi.Input<boolean>;
    /**
     * The name of the thing type.
     */
    readonly name?: pulumi.Input<string>;
    readonly properties?: pulumi.Input<{ description?: pulumi.Input<string>, searchableAttributes?: pulumi.Input<pulumi.Input<string>[]> }>;
}


/**
 * The set of arguments for constructing a ThingType resource.
 */
export interface ThingTypeArgs {
    /**
     * Whether the thing type is deprecated. If true, no new things could be associated with this type.
     */
    readonly deprecated?: pulumi.Input<boolean>;
    /**
     * The name of the thing type.
     */
    readonly name?: pulumi.Input<string>;
    readonly properties?: pulumi.Input<{ description?: pulumi.Input<string>, searchableAttributes?: pulumi.Input<pulumi.Input<string>[]> }>;
}


/**
     * Get an existing ThingType resource's state with the given name, ID, and optional extra
     * properties used to qualify the lookup.
     *
     * @param name The _unique_ name of the resulting resource.
     * @param id The _unique_ provider ID of the resource to lookup.
     * @param state Any extra arguments used during the lookup.
     */
public static get(name: string, id: pulumi.Input<pulumi.ID>, state?: ThingTypeState, opts?: pulumi.CustomResourceOptions): ThingType {
        return new ThingType(name, <any>state, { ...opts, id: id });
    }


/**
     * Returns true if the given object is an instance of ThingType.  This is designed to work even
     * when multiple copies of the Pulumi SDK have been loaded into the same process.
     */
public static isInstance(obj: any): obj is ThingType {
        if (obj === undefined || obj === null) {
            return false;
        }
        return obj['__pulumiType'] === ThingType.__pulumiType;
    }


(target) => this.api.getData(target, range)


/**
 * Datasource
 */
export class DataSource extends DataSourceApi<Query, DataSourceOptions> {
  /**
   * Api
   *
   * @type {Api} api
   */
  api: Api;

  /**
   * Constructor
   */
  constructor(instanceSettings: DataSourceInstanceSettings<DataSourceOptions>) {
    super(instanceSettings);
    this.api = new Api(instanceSettings);
  }

  /**
   * Query
   */
  async query(options: DataQueryRequest<Query>): Promise<DataQueryResponse> {
    const { range } = options;

    /**
     * Process targets
     */
    const data = options.targets.map((target) => this.api.getData(target, range));

    /**
     * Return data
     */
    return { data };
  }

  /**
   * Health Check
   */
  async testDatasource() {
    const isStatusOk = true;

    /**
     * Return
     */
    return {
      status: isStatusOk ? DataSourceTestStatus.SUCCESS : DataSourceTestStatus.ERROR,
      message: isStatusOk ? `Connected...` : "Error. Can't connect.",
    };
  }
}


/**
   * Query
   */
async query(options: DataQueryRequest<Query>): Promise<DataQueryResponse> {
    const { range } = options;

    /**
     * Process targets
     */
    const data = options.targets.map((target) => this.api.getData(target, range));

    /**
     * Return data
     */
    return { data };
  }


/**
   * Health Check
   */
async testDatasource() {
    const isStatusOk = true;

    /**
     * Return
     */
    return {
      status: isStatusOk ? DataSourceTestStatus.SUCCESS : DataSourceTestStatus.ERROR,
      message: isStatusOk ? `Connected...` : "Error. Can't connect.",
    };
  }


(ev: Event) => {
    const input = ev.target as HTMLInputElement | null;
    if (input) {
      this.value = input.value || '';
    }
    this.fwInput.emit(ev as KeyboardEvent);
  }


() => {
    this.hasFocus = true;
    this.fwFocus.emit();
  }


() => {
    this.hasFocus = false;
    this.fwBlur.emit();
  }


@Component({
  tag: 'fw-textarea',
  styleUrl: 'textarea.scss',
  shadow: true,
})
export class Textarea {
  private nativeInput?: HTMLTextAreaElement;

  @State() hasFocus = false;
  /**
   * Label displayed on the interface, for the component.
   */
  @Prop() label = '';
  /**
   * Default value displayed in the input box.
   */
  @Prop({ mutable: true }) value?: string | null = '';
  /**
   * Width of the input box, specified as number of columns.
   */
  @Prop() cols?: number;
  /**
   * Height of the input box, specified as number of rows.
   */
  @Prop() rows?: number;
  /**
   * Maximum number of characters a user can enter in the input box.
   */
  @Prop() maxlength?: number;
  /**
   * Minimum number of characters a user must enter in the input box for the value to be valid.
   */
  @Prop() minlength?: number;
  /**
   * Name of the component, saved as part of form data.
   */
  @Prop() name = '';
  /**
   * Text displayed in the input box before a user enters a value.
   */
  @Prop() placeholder?: string | null;
  /**
   * Theme based on which the input box is styled.
   */
  @Prop() state: 'normal' | 'warning' | 'error' = 'normal';
  /**
   * Type of text wrapping used by the input box. If the value is hard, the text in the textarea is wrapped (contains line breaks) when the form data is saved. If the value is soft, the text in the textarea is saved as a single line, when the form data is saved.
   */
  @Prop() wrap: 'soft' | 'hard' = 'soft';
  /**
   * Descriptive or instructional text displayed below the input box.
   */
  @Prop() stateText = '';
  /**
   * If true, the textarea is autofocused
   */
  @Prop() autofocus = false;
  /**
   * If true, the user cannot enter a value in the input box. If the attribute’s value is undefined, the value is set to false.
   */
  @Prop() readonly = false;
  /**
   * Specifies the input box as a mandatory field and displays an asterisk next to the label. If the attribute’s value is undefined, the value is set to false.
   */
  @Prop() required = false;
  /**
   * Disables the text area on the interface. If the attribute’s value is undefined, the value is set to false.
   */
  @Prop() disabled = false;
  /**
   * Triggered when the value in the input box is modified.
   */
  @Event() fwChange: EventEmitter;
  /**
   * Triggered when the input box comes into focus.
   */
  @Event() fwFocus: EventEmitter<void>;
  /**
   * Triggered when the input box loses focus.
   */
  @Event() fwBlur: EventEmitter<void>;
  /**
   * Triggered when a value is entered in the input box.
   */
  @Event() fwInput: EventEmitter<KeyboardEvent>;

  @Watch('value')
  watchHandler(newValue: string) {
    this.fwChange.emit({ value: newValue });
  }

  private onInput = (ev: Event) => {
    const input = ev.target as HTMLInputElement | null;
    if (input) {
      this.value = input.value || '';
    }
    this.fwInput.emit(ev as KeyboardEvent);
  }

  private onFocus = () => {
    this.hasFocus = true;
    this.fwFocus.emit();
  }

  private onBlur = () => {
    this.hasFocus = false;
    this.fwBlur.emit();
  }

  private getValue(): string {
    return this.value || '';
  }

  private hasValue(): boolean {
    return this.getValue().length > 0;
  }

  /**
   * Sets focus on a specific `fw-textarea`. Use this method instead of the global `input.focus()`.
   */
  @Method()
  async setFocus() {
    if (this.nativeInput) {
      this.nativeInput.focus();
    }
  }

  render() {
    return (
      <Host
        aria-disabled={this.disabled}
        class={{
          'has-value': this.hasValue(),
          'has-focus': this.hasFocus,
        }}
      >

          {this.label !== '' ? {this.label} : ''}

            <textarea
              class={{
                'responsive': this.cols === undefined,
              }}
              ref={input => this.nativeInput = input}
              disabled={this.disabled}
              name={this.name}
              placeholder={this.placeholder || ''}
              minLength={this.minlength}
              maxLength={this.maxlength}
              readOnly={this.readonly}
              autoFocus={this.autofocus}
              required={this.required}
              value={this.value}
              onInput={e => this.onInput(e)}
              onBlur={this.onBlur}
              onFocus={this.onFocus}
              rows={this.rows}
              cols={this.cols}
              wrap={this.wrap}
            />

          {this.stateText !== '' ?
            {this.stateText} : ''}

      </Host>
    );
  }
}


@Watch('value')
  watchHandler(newValue: string) {
    this.fwChange.emit({ value: newValue });
  }


private getValue(): string {
    return this.value || '';
  }


private hasValue(): boolean {
    return this.getValue().length > 0;
  }


/**
   * Sets focus on a specific `fw-textarea`. Use this method instead of the global `input.focus()`.
   */
@Method()
  async setFocus() {
    if (this.nativeInput) {
      this.nativeInput.focus();
    }
  }


render() {
    return (
      <Host
        aria-disabled={this.disabled}
        class={{
          'has-value': this.hasValue(),
          'has-focus': this.hasFocus,
        }}
      >

          {this.label !== '' ? {this.label} : ''}

            <textarea
              class={{
                'responsive': this.cols === undefined,
              }}
              ref={input => this.nativeInput = input}
              disabled={this.disabled}
              name={this.name}
              placeholder={this.placeholder || ''}
              minLength={this.minlength}
              maxLength={this.maxlength}
              readOnly={this.readonly}
              autoFocus={this.autofocus}
              required={this.required}
              value={this.value}
              onInput={e => this.onInput(e)}
              onBlur={this.onBlur}
              onFocus={this.onFocus}
              rows={this.rows}
              cols={this.cols}
              wrap={this.wrap}
            />

          {this.stateText !== '' ?
            {this.stateText} : ''}

      </Host>
    );
  }
}


export class AttachmentDto {
  @IsString()
  @IsOptional()
  _id: string;

  @IsString()
  @IsNotEmpty()
  readonly file_hash: string;

  @IsString()
  @IsOptional()
  readonly file_name: string;

  @IsString()
  @IsOptional()
  readonly file_description: string;
}


function linkTo(name: string, params: IParameters = {}) {
      return router.findByName(name).composeURL(params);
    }


() => {
  it('should setup application correctly with the use of router', async () => {
    const app = express();
    const router = new Router([
      {
        action: async (_c, _p, _b) => await Promise.resolve({ body: { message: 'ok' } }),
        name: 'home',
        path: '/',
      },
      {
        action: async (_c, _p, _b) => await Promise.resolve({
          body: { message: 'ok' },
          status: 304,
        }),
        name: 'notModified',
        path: '/304',
      },
      {
        action: async (_c, _p, _b) => await Promise.resolve({ redirect: '/' }),
        name: 'redirect',
        path: '/redirect',
      },
      {
        action: async () => {
          throw new Error('TESTING 500');
          await Promise.resolve('BAD');
        },
        name: 'serverError',
        path: '/500',
      },
      {
        action: async () => {
          throw new NotAuthorisedError('DENIED 403');
          await Promise.resolve('BAD');
        },
        name: 'notAuthorisedError',
        path: '/403',
      },
      {
        action: async (_c, p, _b) => await Promise.resolve({
          body: { message: `Hello, ${p.name}!` },
        }),
        name: 'hello',
        path: '/hello/:name',
      },
      {
        action: async (_c, _p, _b) => await Promise.resolve({
          download: { data: 'text', name: 'download.txt' },
        }),
        name: 'download',
        path: '/download',
      },
      {
        action: async (_c, _p, _b) => await Promise.resolve({ mimeType: 'image/png' }),
        name: 'png-mimetype',
        path: '/image',
      },
      {
        action: async (_c, _p, _b) => await Promise.resolve({ mimeType: 'text/csv' }),
        name: 'csv-mimetype',
        path: '/csv',
      },
    ]);

    function linkTo(name: string, params: IParameters = {}) {
      return router.findByName(name).composeURL(params);
    }

    app.use(
      (
        req: express.Request,
        _res: express.Response,
        next: express.NextFunction,
      ) => {
        req.csrfToken = () => '';
        next();
      },
    );

    app.use(routerMiddleware(router, config));

    app.use(
      (
        err: Error,
        _req: express.Request,
        res: express.Response,
        _next: express.NextFunction,
      ) => {
        if (err instanceof NotFoundError) {
          return res.status(404).send({ message: err.message });
        }
        if (err instanceof NotAuthorisedError) {
          return res.status(403).send({ message: err.message });
        }

        res.status(500).send({ message: err.message });
      },
    );

    const agent = request.agent(app);

    const okResponse = await agent.get('/');
    const helloResponse = await agent.get('/hello/World');
    const notModifiedResponse = await agent.get('/304');
    const redirectResponse = await agent.get('/redirect');
    const notAuthorisedResponse = await agent.get('/403');
    const notFoundResponse = await agent.get('/404');
    const serverErrorResponse = await agent.get('/500');
    const downloadResponse = await agent.get('/download');
    const imgResponse = await agent.get('/image');
    const csvResponse = await agent.get('/csv');

    expect(linkTo('hello', { name: 'World' })).toEqual('/hello/World');
    expect(linkTo('home')).toEqual('/');
    expect(okResponse.status).toEqual(200);
    expect(helloResponse.status).toEqual(200);
    expect(helloResponse.text).toContain('Hello, World!');
    expect(notModifiedResponse.status).toEqual(304);
    expect(redirectResponse.header.location).toEqual('/');
    expect(notAuthorisedResponse.status).toEqual(403);
    expect(notFoundResponse.status).toEqual(404);
    expect(serverErrorResponse.status).toEqual(500);
    expect(downloadResponse.header['content-disposition']).toEqual(
      'attachment; filename="download.txt"',
    );
    expect(imgResponse.header['content-type']).toEqual('image/png');
    expect(csvResponse.header['content-type']).toEqual(
      'text/csv; charset=utf-8',
    );
  });
}


async () => {
    const app = express();
    const router = new Router([
      {
        action: async (_c, _p, _b) => await Promise.resolve({ body: { message: 'ok' } }),
        name: 'home',
        path: '/',
      },
      {
        action: async (_c, _p, _b) => await Promise.resolve({
          body: { message: 'ok' },
          status: 304,
        }),
        name: 'notModified',
        path: '/304',
      },
      {
        action: async (_c, _p, _b) => await Promise.resolve({ redirect: '/' }),
        name: 'redirect',
        path: '/redirect',
      },
      {
        action: async () => {
          throw new Error('TESTING 500');
          await Promise.resolve('BAD');
        },
        name: 'serverError',
        path: '/500',
      },
      {
        action: async () => {
          throw new NotAuthorisedError('DENIED 403');
          await Promise.resolve('BAD');
        },
        name: 'notAuthorisedError',
        path: '/403',
      },
      {
        action: async (_c, p, _b) => await Promise.resolve({
          body: { message: `Hello, ${p.name}!` },
        }),
        name: 'hello',
        path: '/hello/:name',
      },
      {
        action: async (_c, _p, _b) => await Promise.resolve({
          download: { data: 'text', name: 'download.txt' },
        }),
        name: 'download',
        path: '/download',
      },
      {
        action: async (_c, _p, _b) => await Promise.resolve({ mimeType: 'image/png' }),
        name: 'png-mimetype',
        path: '/image',
      },
      {
        action: async (_c, _p, _b) => await Promise.resolve({ mimeType: 'text/csv' }),
        name: 'csv-mimetype',
        path: '/csv',
      },
    ]);

    function linkTo(name: string, params: IParameters = {}) {
      return router.findByName(name).composeURL(params);
    }

    app.use(
      (
        req: express.Request,
        _res: express.Response,
        next: express.NextFunction,
      ) => {
        req.csrfToken = () => '';
        next();
      },
    );

    app.use(routerMiddleware(router, config));

    app.use(
      (
        err: Error,
        _req: express.Request,
        res: express.Response,
        _next: express.NextFunction,
      ) => {
        if (err instanceof NotFoundError) {
          return res.status(404).send({ message: err.message });
        }
        if (err instanceof NotAuthorisedError) {
          return res.status(403).send({ message: err.message });
        }

        res.status(500).send({ message: err.message });
      },
    );

    const agent = request.agent(app);

    const okResponse = await agent.get('/');
    const helloResponse = await agent.get('/hello/World');
    const notModifiedResponse = await agent.get('/304');
    const redirectResponse = await agent.get('/redirect');
    const notAuthorisedResponse = await agent.get('/403');
    const notFoundResponse = await agent.get('/404');
    const serverErrorResponse = await agent.get('/500');
    const downloadResponse = await agent.get('/download');
    const imgResponse = await agent.get('/image');
    const csvResponse = await agent.get('/csv');

    expect(linkTo('hello', { name: 'World' })).toEqual('/hello/World');
    expect(linkTo('home')).toEqual('/');
    expect(okResponse.status).toEqual(200);
    expect(helloResponse.status).toEqual(200);
    expect(helloResponse.text).toContain('Hello, World!');
    expect(notModifiedResponse.status).toEqual(304);
    expect(redirectResponse.header.location).toEqual('/');
    expect(notAuthorisedResponse.status).toEqual(403);
    expect(notFoundResponse.status).toEqual(404);
    expect(serverErrorResponse.status).toEqual(500);
    expect(downloadResponse.header['content-disposition']).toEqual(
      'attachment; filename="download.txt"',
    );
    expect(imgResponse.header['content-type']).toEqual('image/png');
    expect(csvResponse.header['content-type']).toEqual(
      'text/csv; charset=utf-8',
    );
  }


async (_c, _p, _b) => await Promise.resolve({ body: { message: 'ok' } })


async (_c, _p, _b) => await Promise.resolve({
          body: { message: 'ok' },
          status: 304,
        })


async (_c, _p, _b) => await Promise.resolve({ redirect: '/' })


async () => {
          throw new Error('TESTING 500');
          await Promise.resolve('BAD');
        }


async () => {
          throw new NotAuthorisedError('DENIED 403');
          await Promise.resolve('BAD');
        }


async (_c, p, _b) => await Promise.resolve({
          body: { message: `Hello, ${p.name}!` },
        })


async (_c, _p, _b) => await Promise.resolve({
          download: { data: 'text', name: 'download.txt' },
        })


async (_c, _p, _b) => await Promise.resolve({ mimeType: 'image/png' })


async (_c, _p, _b) => await Promise.resolve({ mimeType: 'text/csv' })


(
        req: express.Request,
        _res: express.Response,
        next: express.NextFunction,
      ) => {
        req.csrfToken = () => '';
        next();
      }


(
        err: Error,
        _req: express.Request,
        res: express.Response,
        _next: express.NextFunction,
      ) => {
        if (err instanceof NotFoundError) {
          return res.status(404).send({ message: err.message });
        }
        if (err instanceof NotAuthorisedError) {
          return res.status(403).send({ message: err.message });
        }

        res.status(500).send({ message: err.message });
      }


async (
    type: string,
    path: string,
    isLibrary: boolean
  ) => {
    let result;
    if (videosType.includes(type)) {
      result = await assetsManager.addAsset(AssetsType.VIDEO, path, isLibrary);
    } else if (imagesType.includes(type)) {
      result = await assetsManager.addAsset(AssetsType.IMAGE, path, isLibrary);
    } else if (audiosType.includes(type)) {
      result = await assetsManager.addAsset(AssetsType.AUDIO, path, isLibrary);
    } else if (lottiesType.includes(type)) {
      result = await assetsManager.addAsset(AssetsType.LOTTIE, path, isLibrary);
    }
    return result;
  }


() => {
    return new Promise((resolve, reject) => {
      if (window.__WORK_DIR__) {
        fs.getFiles(
          window.__WORK_DIR__,
          async (err: any, results: string[]) => {
            for (let i = 0; i < results?.length; i++) {
              // eslint-disable-next-line no-await-in-loop
              const type = await getFileType(results[i]);
              // eslint-disable-next-line no-await-in-loop
              await handleAssetsType(type, results[i], false);
            }
            resolve('done');
          }
        );
      } else {
        // eslint-disable-next-line prefer-promise-reject-errors
        reject('遍历文件出错，无法获取 __WORK_DIR__');
      }
    });
  }


(resolve, reject) => {
      if (window.__WORK_DIR__) {
        fs.getFiles(
          window.__WORK_DIR__,
          async (err: any, results: string[]) => {
            for (let i = 0; i < results?.length; i++) {
              // eslint-disable-next-line no-await-in-loop
              const type = await getFileType(results[i]);
              // eslint-disable-next-line no-await-in-loop
              await handleAssetsType(type, results[i], false);
            }
            resolve('done');
          }
        );
      } else {
        // eslint-disable-next-line prefer-promise-reject-errors
        reject('遍历文件出错，无法获取 __WORK_DIR__');
      }
    }


async (err: any, results: string[]) => {
            for (let i = 0; i < results?.length; i++) {
              // eslint-disable-next-line no-await-in-loop
              const type = await getFileType(results[i]);
              // eslint-disable-next-line no-await-in-loop
              await handleAssetsType(type, results[i], false);
            }
            resolve('done');
          }


(id: string) => {
    const path = `${window.__WORK_DIR__}${UserDatePath}/vframes/${id}`;
    return fs.existsSync(path);
  }


async (id: string, type: AssetsType) => {
    // 删除 assetStore
    assetsDispatch.deleteAsset(type, id);

    // 视频资源检测
    if (type === AssetsType.VIDEO) {
      // 检查所有视频轨
      const videoTracks = nativeQueryTool.getAllVideoTracks();
      videoTracks.forEach((track) => {
        const idx = track.videos.indexOf(id);
        if (idx !== -1) {
          nativeDispatch.deleteVideoOnVideoTrackId(
            track.videos[idx],
            track.uid
          );
        }
      });
    }

    // 音频资源检测
    if (type === AssetsType.AUDIO) {
      const interactNodes = nativeQueryTool.getAllInteractNodes();

      // 节点使用情况检测
      interactNodes.forEach((node) => {
        if (
          node.node.type === INative.InteractsType.SIMPLE_SELECT ||
          node.node.type === INative.InteractsType.DRAGNDROP ||
          node.node.type === INative.InteractsType.AUDIO_RECORD
        ) {
          if (node.node.correctSoundFilename === id) {
            const _node = _.cloneDeep(node);
            (_node.node as any).correctSoundFilename = '';
            nativeDispatch.updateInteractiveNode(_node);
          } else if (node.node.wrongSoundFilename === id) {
            const _node = _.cloneDeep(node);
            (_node.node as any).correctSoundFilename = '';
            nativeDispatch.updateInteractiveNode(_node);
          }
        }
      });
    }

    // 图片资源检测
    if (type === AssetsType.IMAGE) {
      const interactNodes = nativeQueryTool.getAllInteractNodes();
      interactNodes.forEach((node) => {
        if (node.node.type === INative.InteractsType.DRAGNDROP) {
          const _node = _.cloneDeep(node);
          (_node.node as INative.DragNDrop).dragItems.forEach((item) => {
            if (item.imgFilename === id) {
              item.imgFilename = '';
            }
          });
          nativeDispatch.updateInteractiveNode(_node);
        }
        if (node.node.type === INative.InteractsType.VIDEO_RECORD) {
          if (node.node.decorateImage.imgFilename === id) {
            const _node = _.cloneDeep(node);
            (_node.node as INative.VideoRecord).decorateImage.imgFilename = '';
            nativeDispatch.updateInteractiveNode(_node);
          }
        }
      });
    }

    // 动效资源检测
    if (type === AssetsType.LOTTIE) {
      const interactNodes = nativeQueryTool.getAllInteractNodes();
      interactNodes.forEach((node) => {
        if (node.node.type === INative.InteractsType.AUDIO_RECORD) {
          if (node.node.correctEffectFileName === id) {
            const _node = _.cloneDeep(node);
            (_node.node as INative.AudioRecord).correctEffectFileName = '';
            nativeDispatch.updateInteractiveNode(_node);
          }
          if (node.node.wrongEffectFileName === id) {
            const _node = _.cloneDeep(node);
            (_node.node as INative.AudioRecord).wrongEffectFileName = '';
            nativeDispatch.updateInteractiveNode(_node);
          }
        }
      });
    }
  }


(track) => {
        const idx = track.videos.indexOf(id);
        if (idx !== -1) {
          nativeDispatch.deleteVideoOnVideoTrackId(
            track.videos[idx],
            track.uid
          );
        }
      }


(node) => {
        if (
          node.node.type === INative.InteractsType.SIMPLE_SELECT ||
          node.node.type === INative.InteractsType.DRAGNDROP ||
          node.node.type === INative.InteractsType.AUDIO_RECORD
        ) {
          if (node.node.correctSoundFilename === id) {
            const _node = _.cloneDeep(node);
            (_node.node as any).correctSoundFilename = '';
            nativeDispatch.updateInteractiveNode(_node);
          } else if (node.node.wrongSoundFilename === id) {
            const _node = _.cloneDeep(node);
            (_node.node as any).correctSoundFilename = '';
            nativeDispatch.updateInteractiveNode(_node);
          }
        }
      }


(node) => {
        if (node.node.type === INative.InteractsType.DRAGNDROP) {
          const _node = _.cloneDeep(node);
          (_node.node as INative.DragNDrop).dragItems.forEach((item) => {
            if (item.imgFilename === id) {
              item.imgFilename = '';
            }
          });
          nativeDispatch.updateInteractiveNode(_node);
        }
        if (node.node.type === INative.InteractsType.VIDEO_RECORD) {
          if (node.node.decorateImage.imgFilename === id) {
            const _node = _.cloneDeep(node);
            (_node.node as INative.VideoRecord).decorateImage.imgFilename = '';
            nativeDispatch.updateInteractiveNode(_node);
          }
        }
      }


(item) => {
            if (item.imgFilename === id) {
              item.imgFilename = '';
            }
          }


(node) => {
        if (node.node.type === INative.InteractsType.AUDIO_RECORD) {
          if (node.node.correctEffectFileName === id) {
            const _node = _.cloneDeep(node);
            (_node.node as INative.AudioRecord).correctEffectFileName = '';
            nativeDispatch.updateInteractiveNode(_node);
          }
          if (node.node.wrongEffectFileName === id) {
            const _node = _.cloneDeep(node);
            (_node.node as INative.AudioRecord).wrongEffectFileName = '';
            nativeDispatch.updateInteractiveNode(_node);
          }
        }
      }


async () => {
      assetsManager.cleanAssets();
      await getFiles();
    }


() => {
      dispatch(actionInitialAssets());
      return assetsManager;
    }


async (src: string): Promise<IAddAssetResult | undefined> => {
      const name = src.split('/').pop();
      const path = `${window.__WORK_DIR__}/${name}`;
      const type = await getFileType(src);
      let result;
      if (fs.existsSync(src)) {
        if (!fs.existsSync(path)) {
          fs.copyFile(src, path);
          // 由于复制视频到系统后，系统需要隔一段时间才能读取到文件
          const wait = () => {
            return new Promise((resolve, reject) => {
              setTimeout(() => {
                const result = handleAssetsType(type, path, false);
                resolve(result);
              }, 500);
            });
          };
          result = await wait();
        } else {
          const value = assetsQueryTool.getAssetByPath(path);
          if (value) {
            result = {
              type,
              value,
            };
          }
        }
      }
      return result;
    }


() => {
            return new Promise((resolve, reject) => {
              setTimeout(() => {
                const result = handleAssetsType(type, path, false);
                resolve(result);
              }, 500);
            });
          }


(resolve, reject) => {
              setTimeout(() => {
                const result = handleAssetsType(type, path, false);
                resolve(result);
              }, 500);
            }


() => {
                const result = handleAssetsType(type, path, false);
                resolve(result);
              }


async (type: AssetsType, path: string, isLibrary: boolean) => {
      // 应用生成的用户数据不纳入资源计算范围，例如：视频帧
      if (isUserDataPath(path) || isScreenshotPath(path)) return undefined;

      console.log('path: ', path);
      const md5 = fs.getFileMd5(path);
      console.log('md5: ', md5);
      const name = path.split('/').pop() ?? '';

      const { size } = fs.statSync(path);
      let libraryValues = {};
      if (isLibrary) {
        const findKey = name.split('.')[0];
        const { fileName, cdnUrl } = mapLibraryAssetsName[findKey] || {};
        if (!cdnUrl) return;
        libraryValues = {
          name: fileName,
          cdnUrl,
        };
      }
      // state 里面有值的话，覆盖属性
      const assetState = assetsQueryTool.getAssetByMd5(md5);
      const value = {
        uid: md5,
        path,
        isValid: true,
        name,
        cdnUrl: assetState?.cdnUrl ?? '',
        duration: 0,
        size,
        pixels: { width: 0, height: 0 },
        isLibrary,
        ...libraryValues,
        next: assetState?.next ?? null,
        allowChangeProgress: assetState?.allowChangeProgress ?? true,
      };
      const params = {
        type,
        value,
      };

      if (type === AssetsType.VIDEO) {
        const metadata = await getMediaInfo(path);
        console.log('metadata: ', metadata);
        const meta = metadata.streams.filter(
          (item) => item.codec_type === 'video'
        );
        const { duration } = metadata.format;
        const { width, height } = meta[0];

        if (width && height && duration) {
          params.value.duration = Number(duration);
          params.value.pixels = { width, height };
          // 获取视频帧
          await assetsManager.getVideosIFrams([value]);
        } else {
          message.warning('非法资源，已自动删除');
          assetsDispatch.deleteAsset(type, md5);
          fs.deleteFile(path);
          return;
        }
      } else if (type === AssetsType.AUDIO) {
        const metadata = await getMediaInfo(path);
        const { duration = 0 } = metadata.streams[0];
        params.value.duration = Number(duration);
        delete params.value.pixels;
        delete params.value.next;
        delete params.value.allowChangeProgress;
      } else {
        delete params.value.duration;
        delete params.value.pixels;
        delete params.value.next;
        delete params.value.allowChangeProgress;
      }
      console.log('params: ', params);

      dispatch(actionAddAsset(params));

      return params;
    }


(item) => item.codec_type === 'video'


(id: string, index: number) => {
      if (!hasVideoVFrames(id)) {
        message.warning('获取视频 vframes 失败');
        return '';
      }
      const path = `${window.__WORK_DIR__}${UserDatePath}/vframes/${id}/index-${index}.png`;
      return fs.existsSync(path) ? path : '';
    }


async (videos: Video[]) => {
      console.log('videos:', videos);
      const getItemIFrames = async () => {
        for (let i = 0; i < videos.length; i++) {
          const video = videos[i];
          const folder = `${window.__WORK_DIR__}/.userData/vframes/${video.uid}`;
          await getVideoIFrameByTime(
            video?.path,
            `index-${i + 1}.png`,
            1,
            folder
          );
        }
      };
      await getItemIFrames();
    }


async () => {
        for (let i = 0; i < videos.length; i++) {
          const video = videos[i];
          const folder = `${window.__WORK_DIR__}/.userData/vframes/${video.uid}`;
          await getVideoIFrameByTime(
            video?.path,
            `index-${i + 1}.png`,
            1,
            folder
          );
        }
      }


async () => {
      // 遍历工程文件夹下所有资源，以更新资源；
      await getFiles();

      const {
        videoDict,
        imageDict,
        audioDict,
        lottieDict,
      } = assetsQueryTool.getAllAssets();

      const videos = Object.values(videoDict);
      const audios = Object.values(audioDict);
      const images = Object.values(imageDict);
      const lotties = Object.values(lottieDict);

      // 检测资源丢失情况
      // 视频资源检测
      console.log('准备进行资源丢失检测');
      for (let i = 0; i < videos.length; i++) {
        const video = videos[i];
        const isExist = await fs.exists(video.path);
        console.info(video.path, ':', isExist);
        !isExist && handleUnExistAsset(video.uid, AssetsType.VIDEO);
      }

      for (let i = 0; i < audios.length; i++) {
        const audio = audios[i];
        const isExist = await fs.exists(audio.path);
        !isExist && handleUnExistAsset(audio.uid, AssetsType.AUDIO);
      }

      for (let i = 0; i < images.length; i++) {
        const image = images[i];
        const isExist = await fs.exists(image.path);
        !isExist && handleUnExistAsset(image.uid, AssetsType.IMAGE);
      }

      for (let i = 0; i < lotties.length; i++) {
        const lottie = lotties[i];
        const isExist = await fs.exists(lottie.path);
        !isExist && handleUnExistAsset(lottie.uid, AssetsType.LOTTIE);
      }
    }


() => {
      return new Promise((resolve, reject) => {
        const dirPath =
          process.env.NODE_ENV === 'production'
            ? `${getAppResources()}/src${path.sep}assets`
            : path.join(__dirname, './assets');
        fs.getFiles(dirPath, async (err: any, results: string[]) => {
          for (let i = 0; i < results?.length; i++) {
            // eslint-disable-next-line no-await-in-loop
            const type = await getFileType(results[i]);
            // 这里拿到的是素材库的素材
            // eslint-disable-next-line no-await-in-loop
            await handleAssetsType(type, results[i], true);
          }
          resolve('done');
        });
      });
    }


(resolve, reject) => {
        const dirPath =
          process.env.NODE_ENV === 'production'
            ? `${getAppResources()}/src${path.sep}assets`
            : path.join(__dirname, './assets');
        fs.getFiles(dirPath, async (err: any, results: string[]) => {
          for (let i = 0; i < results?.length; i++) {
            // eslint-disable-next-line no-await-in-loop
            const type = await getFileType(results[i]);
            // 这里拿到的是素材库的素材
            // eslint-disable-next-line no-await-in-loop
            await handleAssetsType(type, results[i], true);
          }
          resolve('done');
        });
      }


async (err: any, results: string[]) => {
          for (let i = 0; i < results?.length; i++) {
            // eslint-disable-next-line no-await-in-loop
            const type = await getFileType(results[i]);
            // 这里拿到的是素材库的素材
            // eslint-disable-next-line no-await-in-loop
            await handleAssetsType(type, results[i], true);
          }
          resolve('done');
        }


() => {
    const {
      videoDict,
      audioDict,
      imageDict,
      lottieDict,
    } = store.getState().assetsState;

    const filteredVideos = _.filter(videoDict, (asset) => !asset.cdnUrl);
    const filteredAudios = _.filter(audioDict, (asset) => !asset.cdnUrl);
    const filteredImages = _.filter(imageDict, (asset) => !asset.cdnUrl);
    const filteredlotties = _.filter(lottieDict, (asset) => !asset.cdnUrl);

    return {
      filteredVideos,
      filteredAudios,
      filteredImages,
      filteredlotties,
    };
  }


(asset) => !asset.cdnUrl


() => {
    const {
      filteredVideos,
      filteredAudios,
      filteredImages,
      filteredlotties,
    } = getLocalAssets();

    // 检测是否所有需要上传的文件都存在
    let isAllExists = true;
    const isNotExistsFileName: string[] = [];
    const handleCheckExists = (path: string) => {
      if (!fs.existsSync(path)) {
        isNotExistsFileName.push(`${path}; `);
        isAllExists = false;
      }
    };
    for (let i = 0; i < filteredVideos.length; i++) {
      const video = filteredVideos[i];
      handleCheckExists(video.path);
    }
    for (let i = 0; i < filteredAudios.length; i++) {
      const audio = filteredAudios[i];
      handleCheckExists(audio.path);
    }
    for (let i = 0; i < filteredImages.length; i++) {
      const image = filteredImages[i];
      handleCheckExists(image.path);
    }
    for (let i = 0; i < filteredlotties.length; i++) {
      const lottie = filteredlotties[i];
      handleCheckExists(lottie.path);
    }
    if (!isAllExists) {
      Modal.warning({
        title: '以下资源丢失',
        content: isNotExistsFileName,
      });
    }
  }


(path: string) => {
      if (!fs.existsSync(path)) {
        isNotExistsFileName.push(`${path}; `);
        isAllExists = false;
      }
    }


interface IAddAssetResult {
  type: AssetsType;
  value: {
    uid: string;
    path: string;
    isValid: boolean;
    name: string;
    duration?: number;
  };
}


(command) => this._secureSend(command, upstreamSender)


() => upstreamSender.packetCapacity() - 20


(evt) => evt.packet.tryAs(SecurityV1.SecurityNonceReport)


export class SecurityS0Layer implements Layer {
	private _requester = new Requester();
	private _nonceStore: NonceStore;
	private _codec: SecurityS0Codec;

	constructor(codec: SecurityS0Codec, nonceStore: NonceStore) {
		this._nonceStore = nonceStore;
		this._codec = codec;
	}

	async dispatch(
		event: LayerEvent<Packet>,
		next: DispatchNext,
		sender: Sender
	): Promise<void> {
		this._requester.dispatch(event);

		// NonceGet is just responded to, not passed through
		if (event.packet.is(SecurityV1.SecurityNonceGet)) {
			return this._handleSecurityNonceGet(event, sender);
		}

		// NonceReport is handled in _secureSend, not passed through
		if (event.packet.is(SecurityV1.SecurityNonceReport)) {
			return;
		}

		// Encapsulation is decoded and handled, EncapNonceGet is replied to afterwards
		const encapPacket =
			event.packet.tryAs(SecurityV1.SecurityMessageEncapsulation) ??
			event.packet.tryAs(SecurityV1.SecurityMessageEncapsulationNonceGet);

		// Non-secure packet, just pass through (but allow sender to still encrypt
		// messages if necessary)
		if (!encapPacket) {
			sender = this._makeSecureSender(sender);
			return next(event, sender);
		}

		// Encapsulated packet, decode it and forward decoded event
		const decoded = this._codec.decapsulate(
			encapPacket,
			event.endpoint.nodeId,
			1
		);
		const decodedEvent: LayerEvent<Packet> = {
			packetType: event.packetType,
			endpoint: event.endpoint,
			packet: decoded,
		};

		if (event.packet.is(SecurityV1.SecurityMessageEncapsulationNonceGet)) {
			// It doesn't seem to be mentioned in the docs, but at least one of my devices doesn't
			// send back e.g. a Report to a Get when receiving a MessageEncapsulationNonceGet (only
			// the nonce report is sent). Let's mimick that behavior here.
			await this._handleSecurityNonceGet(event, sender);
			sender = {
				send(command) {
					return false;
				},
				packetCapacity() {
					return 0;
				},
			};
		} else {
			// TODO Force sends to always be secure (except in very special cases)
			sender = this._makeSecureSender(sender);
		}

		log("decoded", layerEventToString(decodedEvent));
		return next(decodedEvent, sender);
	}

	async send(
		command: LayerCommand,
		next: SendNext,
		send: Sender
	): Promise<boolean> {
		const secureSend = this._makeSecureSender(send);
		return await next(command, secureSend);
	}

	private _makeSecureSender(upstreamSender: Sender): Sender {
		return {
			send: (command) => this._secureSend(command, upstreamSender),
			packetCapacity: () => upstreamSender.packetCapacity() - 20,
		};
	}

	private async _secureSend(
		command: LayerCommand,
		send: Sender
	): Promise<boolean> {
		if (command.secure) {
			log(`fetch nonce, endpoint=${endPointToString(command.endpoint)}`);
			const nonceEvent = await this._requester.sendAndWaitFor(
				{
					endpoint: command.endpoint,
					packet: new SecurityV1.SecurityNonceGet(),
				},
				send.send,
				(evt) => evt.packet.tryAs(SecurityV1.SecurityNonceReport)
			);
			if (!nonceEvent) {
				// Send was suppressed, stop further processing.
				return false;
			}
			const destination = command.endpoint.nodeId;
			const senderNonce = randomBytes(8);
			const encapsulated = this._codec.encapsulate(
				command.packet,
				1,
				destination,
				senderNonce,
				nonceEvent.packet.data.nonce,
				false
			);
			const encapCmd: LayerCommand = {
				...command,
				packet: encapsulated,
			};
			log(
				`received nonce, encoded command`,
				layerCommandToString(encapCmd)
			);
			return send.send(encapCmd);
		} else {
			return send.send(command);
		}
	}

	private async _handleSecurityNonceGet(
		event: LayerEvent<Packet>,
		send: Sender
	): Promise<void> {
		if (this._nonceStore.canGenerateNonce()) {
			const nonce = this._nonceStore.generate(event.endpoint.nodeId);
			const nonceCmd: LayerCommand = {
				endpoint: { nodeId: event.endpoint.nodeId },
				packet: new SecurityV1.SecurityNonceReport({
					nonce: nonce.data,
				}),
			};
			log(
				`send nonce report to=${endPointToString(
					nonceCmd.endpoint
				)} nonceId=${toHex(nonce.id, 2)} nonce=[${bufferToString(
					nonce.data
				)}]`
			);
			await send.send(nonceCmd);
		} else {
			log(
				`warn`,
				`received nonce get request from ${event.endpoint}, but nonce store is full, ignoring request`
			);
		}
	}
}


async dispatch(
		event: LayerEvent<Packet>,
		next: DispatchNext,
		sender: Sender
	): Promise<void> {
		this._requester.dispatch(event);

		// NonceGet is just responded to, not passed through
		if (event.packet.is(SecurityV1.SecurityNonceGet)) {
			return this._handleSecurityNonceGet(event, sender);
		}

		// NonceReport is handled in _secureSend, not passed through
		if (event.packet.is(SecurityV1.SecurityNonceReport)) {
			return;
		}

		// Encapsulation is decoded and handled, EncapNonceGet is replied to afterwards
		const encapPacket =
			event.packet.tryAs(SecurityV1.SecurityMessageEncapsulation) ??
			event.packet.tryAs(SecurityV1.SecurityMessageEncapsulationNonceGet);

		// Non-secure packet, just pass through (but allow sender to still encrypt
		// messages if necessary)
		if (!encapPacket) {
			sender = this._makeSecureSender(sender);
			return next(event, sender);
		}

		// Encapsulated packet, decode it and forward decoded event
		const decoded = this._codec.decapsulate(
			encapPacket,
			event.endpoint.nodeId,
			1
		);
		const decodedEvent: LayerEvent<Packet> = {
			packetType: event.packetType,
			endpoint: event.endpoint,
			packet: decoded,
		};

		if (event.packet.is(SecurityV1.SecurityMessageEncapsulationNonceGet)) {
			// It doesn't seem to be mentioned in the docs, but at least one of my devices doesn't
			// send back e.g. a Report to a Get when receiving a MessageEncapsulationNonceGet (only
			// the nonce report is sent). Let's mimick that behavior here.
			await this._handleSecurityNonceGet(event, sender);
			sender = {
				send(command) {
					return false;
				},
				packetCapacity() {
					return 0;
				},
			};
		} else {
			// TODO Force sends to always be secure (except in very special cases)
			sender = this._makeSecureSender(sender);
		}

		log("decoded", layerEventToString(decodedEvent));
		return next(decodedEvent, sender);
	}


send(command) {
					return false;
				}


packetCapacity() {
					return 0;
				}


async send(
		command: LayerCommand,
		next: SendNext,
		send: Sender
	): Promise<boolean> {
		const secureSend = this._makeSecureSender(send);
		return await next(command, secureSend);
	}


private _makeSecureSender(upstreamSender: Sender): Sender {
		return {
			send: (command) => this._secureSend(command, upstreamSender),
			packetCapacity: () => upstreamSender.packetCapacity() - 20,
		};
	}


private async _secureSend(
		command: LayerCommand,
		send: Sender
	): Promise<boolean> {
		if (command.secure) {
			log(`fetch nonce, endpoint=${endPointToString(command.endpoint)}`);
			const nonceEvent = await this._requester.sendAndWaitFor(
				{
					endpoint: command.endpoint,
					packet: new SecurityV1.SecurityNonceGet(),
				},
				send.send,
				(evt) => evt.packet.tryAs(SecurityV1.SecurityNonceReport)
			);
			if (!nonceEvent) {
				// Send was suppressed, stop further processing.
				return false;
			}
			const destination = command.endpoint.nodeId;
			const senderNonce = randomBytes(8);
			const encapsulated = this._codec.encapsulate(
				command.packet,
				1,
				destination,
				senderNonce,
				nonceEvent.packet.data.nonce,
				false
			);
			const encapCmd: LayerCommand = {
				...command,
				packet: encapsulated,
			};
			log(
				`received nonce, encoded command`,
				layerCommandToString(encapCmd)
			);
			return send.send(encapCmd);
		} else {
			return send.send(command);
		}
	}


private async _handleSecurityNonceGet(
		event: LayerEvent<Packet>,
		send: Sender
	): Promise<void> {
		if (this._nonceStore.canGenerateNonce()) {
			const nonce = this._nonceStore.generate(event.endpoint.nodeId);
			const nonceCmd: LayerCommand = {
				endpoint: { nodeId: event.endpoint.nodeId },
				packet: new SecurityV1.SecurityNonceReport({
					nonce: nonce.data,
				}),
			};
			log(
				`send nonce report to=${endPointToString(
					nonceCmd.endpoint
				)} nonceId=${toHex(nonce.id, 2)} nonce=[${bufferToString(
					nonce.data
				)}]`
			);
			await send.send(nonceCmd);
		} else {
			log(
				`warn`,
				`received nonce get request from ${event.endpoint}, but nonce store is full, ignoring request`
			);
		}
	}


(props: LayoutBoxProps) => {
  const ComponentTag = props.tag || 'div'
  return <ComponentTag css={rootStyle(props)}>{props.children}</ComponentTag>
}


(props: LayoutBoxProps) => {
  return css`
    padding: ${props.padding};
    color: ${props.color};
    background-color: ${props.backgroundColor};
    border-color: ${props.borderColor};
    border-style: solid;
    border-width: ${props.borderWidth || 0};
    outline: solid 0.125rem transparent;
    outline-offset: -0.125rem;
  `
}


// ----------------------------------------
// Type
// ----------------------------------------
export type LayoutBoxProps = {
  /** Custom tag used for the root element. */
  tag?: ElementType
  /** Content of the component. */
  children?: ReactNode
  /** Defines the padding around component. */
  padding?: CSSProperties['padding']
  /** Color used for text. */
  color?: CSSProperties['color']
  /** Color used for the background. */
  backgroundColor?: CSSProperties['backgroundColor']
  /** Color used for the border. */
  borderColor?: CSSProperties['borderColor']
  /** Defines the width of the border. */
  borderWidth?: CSSProperties['borderWidth']
}


export default class Provider<S = any> extends Component<Props<S>, {}> {
  getChildContext() {
    return { store: this.props.store };
  }
  render() {
    return (
      (this.props.children && this.props.children[0]) ||
      (this.props.children as JSX.Element)
    );
  }
}


getChildContext() {
    return { store: this.props.store };
  }


render() {
    return (
      (this.props.children && this.props.children[0]) ||
      (this.props.children as JSX.Element)
    );
  }


() => {
    describe("Solve puzzle correctly", () => {
        it("Should accept the answer and close the modal when correct.", function() {
            this.timeout(0);
            let test = async (url) => {
                return new Nightmare({show:false})
                .goto(url)
                .wait(2000)
                .type('#puzzleModal input[type=text]', '5')
                .click('#submitPuzzle')
                .wait(2000)
                .end()
                .evaluate(() => {
                    return document.querySelector("#puzzleModal") === null;
                })
                .end();
            }
            return  test(url).should.eventually.equal(true)
        });
    });
    describe("Solve puzzle incorrectly", () => {
        it("Should reject the answer and not close the modal when incorrect.", function() {
            this.timeout(0);
            let test = async (url) => {
                return new Nightmare({show:false})
                .goto(url)
                .wait(2000)
                .type('#puzzleModal input[type=text]', '11')
                .click('#submitPuzzle')
                .wait(2000)
                .end()
                .evaluate(() => {
                    let isVisible = document.querySelector("#puzzleModal") !== null;
                    let hasError = document.querySelector("#puzzleModal .invalid-feedback").innerText.length > 0;
                    return isVisible && hasError;
                })
                .end();
            }
            return  test(url).should.eventually.equal(true)
        });
    });
    describe("Run Sync", () => {
        it("Should run the process synchronously.", function(){
            this.timeout(0);
            let test = async (url) => {
                return new Nightmare({show:false})
                .goto(url)
                .wait(2000)
                .type('#puzzleModal input[type=text]', '5')
                .click('#submitPuzzle')
                .wait(2000)
                .click('#runSync')
                .wait(9000)
                .end()
                .evaluate(() => {
                    let resultOutput = document.getElementById("resultOutput");
                    return resultOutput.children.length === 17;
                })
                .end();
            }
            return  test(url).should.eventually.equal(true)
        });
    });
    describe("Run Async", () => {
        it("Should run the process asynchronously.", function(){
            this.timeout(0);
            let test = async (url) => {
                return new Nightmare({show:false})
                .goto(url)
                .wait(2000)
                .type('#puzzleModal input[type=text]', '5')
                .click('#submitPuzzle')
                .wait(2000)
                .click('#runAsync')
                .wait(7000)
                .end()
                .evaluate(() => {
                    let resultOutput = document.getElementById("resultOutput");
                    return resultOutput.children.length === 17;
                })
                .end();
            }
            return  test(url).should.eventually.equal(true)
        });
    })
}


() => {
        it("Should accept the answer and close the modal when correct.", function() {
            this.timeout(0);
            let test = async (url) => {
                return new Nightmare({show:false})
                .goto(url)
                .wait(2000)
                .type('#puzzleModal input[type=text]', '5')
                .click('#submitPuzzle')
                .wait(2000)
                .end()
                .evaluate(() => {
                    return document.querySelector("#puzzleModal") === null;
                })
                .end();
            }
            return  test(url).should.eventually.equal(true)
        });
    }


async (url) => {
                return new Nightmare({show:false})
                .goto(url)
                .wait(2000)
                .type('#puzzleModal input[type=text]', '5')
                .click('#submitPuzzle')
                .wait(2000)
                .end()
                .evaluate(() => {
                    return document.querySelector("#puzzleModal") === null;
                })
                .end();
            }


() => {
                    return document.querySelector("#puzzleModal") === null;
                }


() => {
        it("Should reject the answer and not close the modal when incorrect.", function() {
            this.timeout(0);
            let test = async (url) => {
                return new Nightmare({show:false})
                .goto(url)
                .wait(2000)
                .type('#puzzleModal input[type=text]', '11')
                .click('#submitPuzzle')
                .wait(2000)
                .end()
                .evaluate(() => {
                    let isVisible = document.querySelector("#puzzleModal") !== null;
                    let hasError = document.querySelector("#puzzleModal .invalid-feedback").innerText.length > 0;
                    return isVisible && hasError;
                })
                .end();
            }
            return  test(url).should.eventually.equal(true)
        });
    }


async (url) => {
                return new Nightmare({show:false})
                .goto(url)
                .wait(2000)
                .type('#puzzleModal input[type=text]', '11')
                .click('#submitPuzzle')
                .wait(2000)
                .end()
                .evaluate(() => {
                    let isVisible = document.querySelector("#puzzleModal") !== null;
                    let hasError = document.querySelector("#puzzleModal .invalid-feedback").innerText.length > 0;
                    return isVisible && hasError;
                })
                .end();
            }


() => {
                    let isVisible = document.querySelector("#puzzleModal") !== null;
                    let hasError = document.querySelector("#puzzleModal .invalid-feedback").innerText.length > 0;
                    return isVisible && hasError;
                }


() => {
        it("Should run the process synchronously.", function(){
            this.timeout(0);
            let test = async (url) => {
                return new Nightmare({show:false})
                .goto(url)
                .wait(2000)
                .type('#puzzleModal input[type=text]', '5')
                .click('#submitPuzzle')
                .wait(2000)
                .click('#runSync')
                .wait(9000)
                .end()
                .evaluate(() => {
                    let resultOutput = document.getElementById("resultOutput");
                    return resultOutput.children.length === 17;
                })
                .end();
            }
            return  test(url).should.eventually.equal(true)
        });
    }


async (url) => {
                return new Nightmare({show:false})
                .goto(url)
                .wait(2000)
                .type('#puzzleModal input[type=text]', '5')
                .click('#submitPuzzle')
                .wait(2000)
                .click('#runSync')
                .wait(9000)
                .end()
                .evaluate(() => {
                    let resultOutput = document.getElementById("resultOutput");
                    return resultOutput.children.length === 17;
                })
                .end();
            }


() => {
                    let resultOutput = document.getElementById("resultOutput");
                    return resultOutput.children.length === 17;
                }


() => {
        it("Should run the process asynchronously.", function(){
            this.timeout(0);
            let test = async (url) => {
                return new Nightmare({show:false})
                .goto(url)
                .wait(2000)
                .type('#puzzleModal input[type=text]', '5')
                .click('#submitPuzzle')
                .wait(2000)
                .click('#runAsync')
                .wait(7000)
                .end()
                .evaluate(() => {
                    let resultOutput = document.getElementById("resultOutput");
                    return resultOutput.children.length === 17;
                })
                .end();
            }
            return  test(url).should.eventually.equal(true)
        });
    }


async (url) => {
                return new Nightmare({show:false})
                .goto(url)
                .wait(2000)
                .type('#puzzleModal input[type=text]', '5')
                .click('#submitPuzzle')
                .wait(2000)
                .click('#runAsync')
                .wait(7000)
                .end()
                .evaluate(() => {
                    let resultOutput = document.getElementById("resultOutput");
                    return resultOutput.children.length === 17;
                })
                .end();
            }


@Entity()
@Index(["first_name", "last_name"], { unique: true })
export class Author extends BaseEntity {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  first_name: string;

  @Column({
    nullable: true,
  })
  last_name: string;
}


(value?: string | string[]) => {
  let params = '';
  if (value) {
    if (typeof value === 'string') params = `/${value}`;
    else {
      value.forEach((k) => {
        params = `${params}/${k}`;
      });
    }
  }
  return params;
}


(k) => {
        params = `${params}/${k}`;
      }


(url: {
  endpoint: string;
  params?: string | string[];
  query?:
    | {
        page?: number;
        perPage?: number;
        with?: string[];
        search?: string;
      }
    | Record<string, unknown>;
}) => {
  // set url to base api address/endpoint
  let endpoint = `${url.endpoint}${getParams(url.params)}`;

  if (url.query) {
    endpoint = `${endpoint}?${axiosClient.getQuery(url.query)}`;
  }
  return endpoint;
}


<
  V extends HttpVerb,
  T = unknown,
  D = unknown,
  R = AxiosResponse<ResourceResponceI<T>>
>(
  verb: V,
  endpoint: string,
  data?: D,
  options?: {
    params?: string | string[];
    query?:
      | {
          page?: number;
          perPage?: number;
          with?: string[];
          search?: string;
        }
      | Record<string, unknown>;
  },
  config?: AxiosRequestConfig<D>
): Promise<R> => {
  const url = `${import.meta.env.VITE_BASE_API_URL}/${prepareUrl({
    endpoint,
    ...options,
  })}`;
  if (verb === 'post') {
    return axiosClient.apiPost<R, R, D>(url, data, config);
  }
  if (verb === 'delete') {
    return axiosClient.apiDelete<R, R, D>(url, config);
  }
  if (verb === 'put') {
    return axiosClient.apiPut<R, R, D>(url, data, config);
  }
  return axiosClient.apiGet<R, R, D>(url, config);
}


(meta?: PaginationMetaI): State.Pagination | undefined =>
  meta
    ? {
        page: meta.current_page,
        perPage: meta.per_page,
        lastPage: meta.last_page,
        from: meta.from,
        to: meta.to,
        total: meta.total,
        links: [...meta.links],
      }
    : undefined


@Component({
  templateUrl: 'main.html'
})
class IntroPage {
  continueText: string = "Skip";
  startingIndex: number = 1;
  mySlideOptions;
  showSlide: boolean = true;

  constructor(private nav: NavController) {
    this.mySlideOptions = {
      paginationClickable: true,
      lazyLoading: true,
      preloadImages: false,
      initialSlide: this.startingIndex
    };
  }

  onSlideChanged(slider) {
    console.log("Slide changed", slider);
  }

  onSlideChangeStart(slider) {
    console.log("Slide change start", slider);
    slider.isEnd ? this.continueText = "Continue" : this.continueText = "Skip";
  }

  onSlideMove(slider) {
    console.log("Slide move", slider);
  }

  toggleLastSlide() {
    this.showSlide = !this.showSlide;
  }

  skip() {
    this.nav.push(MainPage);
  }
}


@Component({
  template: `
  <ion-header>
    <ion-navbar>
      <ion-title>Slides</ion-title>
    </ion-navbar>
  </ion-header>

  <ion-content padding>

Another Page

  </ion-content>
  `
})
class MainPage {}


@Component({
  template: '<ion-nav [root]="root"></ion-nav>'
})
class E2EApp {
  root = IntroPage;
}


onSlideChanged(slider) {
    console.log("Slide changed", slider);
  }


onSlideChangeStart(slider) {
    console.log("Slide change start", slider);
    slider.isEnd ? this.continueText = "Continue" : this.continueText = "Skip";
  }


onSlideMove(slider) {
    console.log("Slide move", slider);
  }


toggleLastSlide() {
    this.showSlide = !this.showSlide;
  }


skip() {
    this.nav.push(MainPage);
  }


@Component({
  selector: 'nlg-sieve-eratosthenes',
  templateUrl: './sieve-eratosthenes.component.html',
  styleUrls: ['./sieve-eratosthenes.component.scss']
})
export class SieveEratosthenesComponent {

  prime: number;
  primes = [];
  limit = 100;

  constructor() { }

  initPrimeArray(limit) {

    for (let i = 0; i < limit; i++) {
      this.primes[i] = true;
    }
  }
  prevDef(e?: Event) {
    e.preventDefault();
  }
  eratosthenes() {

    this.initPrimeArray(this.limit);

    let loopLimit = this.primes.length * .5;
    let limit = this.primes.length;

    for (let i = 2; i < loopLimit; i++) {
      for (let j = 2; j < (limit / i); j++) {
        this.primes[(i * j)] = false;
      }
    }
  }

  eratosthenesOpti() {

    this.initPrimeArray(this.limit);

    let prime = 2;

    while ((prime * prime) < this.limit) {

      if (this.primes[prime]) {
        let noPrimeIndex = prime * prime;

        while (noPrimeIndex < this.limit) {

          this.primes[noPrimeIndex] = false;
          noPrimeIndex += prime;
        }
      }
      prime++;
    }

    for (let i = 2; i < this.limit; i++) {

      if (this.primes[i]) {
        console.log((i.toString(2).padStart(9, '0')));
      }
    }
  }

  * erastosSieveGenerator() {
    let n = 2;

    while (true) {
      if (this.isPrime(n++)) { yield n; }
    }
  }

  /** Checks if `n` is prime.
   * @param n The value to check
   * @returns Returns `true` if `n` is prime
   */
  isPrime(n: number): boolean {

    let nSqrt = Math.sqrt(n);
    for (let i = 2; i <= nSqrt; i++) {
      if (n % i === 0) { return false; }
    }
    return true;
  }

}


initPrimeArray(limit) {

    for (let i = 0; i < limit; i++) {
      this.primes[i] = true;
    }
  }


prevDef(e?: Event) {
    e.preventDefault();
  }


eratosthenes() {

    this.initPrimeArray(this.limit);

    let loopLimit = this.primes.length * .5;
    let limit = this.primes.length;

    for (let i = 2; i < loopLimit; i++) {
      for (let j = 2; j < (limit / i); j++) {
        this.primes[(i * j)] = false;
      }
    }
  }


eratosthenesOpti() {

    this.initPrimeArray(this.limit);

    let prime = 2;

    while ((prime * prime) < this.limit) {

      if (this.primes[prime]) {
        let noPrimeIndex = prime * prime;

        while (noPrimeIndex < this.limit) {

          this.primes[noPrimeIndex] = false;
          noPrimeIndex += prime;
        }
      }
      prime++;
    }

    for (let i = 2; i < this.limit; i++) {

      if (this.primes[i]) {
        console.log((i.toString(2).padStart(9, '0')));
      }
    }
  }


* erastosSieveGenerator() {
    let n = 2;

    while (true) {
      if (this.isPrime(n++)) { yield n; }
    }
  }


/** Checks if `n` is prime.
   * @param n The value to check
   * @returns Returns `true` if `n` is prime
   */
isPrime(n: number): boolean {

    let nSqrt = Math.sqrt(n);
    for (let i = 2; i <= nSqrt; i++) {
      if (n % i === 0) { return false; }
    }
    return true;
  }


@Component({
  selector: 'app-sidebar',
  templateUrl: './sidebar.component.html'
})
export class SidebarComponent implements OnInit {
  menuItems: any[];

  constructor() { }

  ngOnInit() {
    this.menuItems = ROUTES.filter(menuItem => menuItem);
  }
  isMobileMenu() {
      if ($(window).width() > 991) {
          return false;
      }
      return true;
  };
}


declare interface RouteInfo {
    path: string;
    title: string;
    icon: string;
    class: string;
}


ngOnInit() {
    this.menuItems = ROUTES.filter(menuItem => menuItem);
  }


isMobileMenu() {
      if ($(window).width() > 991) {
          return false;
      }
      return true;
  }


(args: ComponentProps) => <Component {...args} />


type ComponentProps = React.ComponentProps<typeof Component>


type cases = [
  Expect<Equal<GreaterThan<1, 0>, true>>,
  Expect<Equal<GreaterThan<5, 4>, true>>,
  Expect<Equal<GreaterThan<4, 5>, false>>,
  Expect<Equal<GreaterThan<0, 0>, false>>,
  Expect<Equal<GreaterThan<20, 20>, false>>
]


@NgModule({
  imports: [
    CommonModule,
    TabGroupModule,
    CodeModule,
    TableModule,
    GridModule,
    ButtonModule,
    ContextmenuModule,
    MainContextRoutingModule,
    LanguageModule
  ],
  declarations: [MainContextmenuComponent]
})

export class MainContextmenuModule {}


function requireAllPages() {
  const req = require.context('../pages');
  return req
    .keys()
    .reduce(
      (acc, filename) =>
        Object.assign(acc, {
          [filename.replace('./', '').replace('.mdx', '')]: req(filename),
        }),
      {}
    );
}


(acc, filename) =>
        Object.assign(acc, {
          [filename.replace('./', '').replace('.mdx', '')]: req(filename),
        })


() => {
  const pages: Record<string, Page> = requireAllPages();
  const sections: Record<string, (Page & { name: string })[]> = {};

  Object.entries(pages)
    .sort(
      (page1, page2) =>
        (page1[1].data.order || 100) - (page2[1].data.order || 100)
    )
    .forEach(([pageName, page]) => {
      const section = page.data.section;
      if (!section) {
        throw new Error(`
Put ${pageName}.mdx in a section! E.g:
---
section: My section
---
      `);
      }

      sections[section] = sections[section] || [];
      sections[section].push({
        ...page,
        name: pageName,
      });
    });

  return Object.entries(sections)
    .sort(([a], [b]) => {
      const aOrder = Number(a.match(/^(\d+)-/)[1] || 100);
      const bOrder = Number(b.match(/^(\d+)-/)[1] || 100);
      return aOrder - bOrder;
    })
    .map(([name, pages]) => ({
      name: name,
      pages,
    }));
}


(page1, page2) =>
        (page1[1].data.order || 100) - (page2[1].data.order || 100)


([pageName, page]) => {
      const section = page.data.section;
      if (!section) {
        throw new Error(`
Put ${pageName}.mdx in a section! E.g:
---
section: My section
---
      `);
      }

      sections[section] = sections[section] || [];
      sections[section].push({
        ...page,
        name: pageName,
      });
    }


([a], [b]) => {
      const aOrder = Number(a.match(/^(\d+)-/)[1] || 100);
      const bOrder = Number(b.match(/^(\d+)-/)[1] || 100);
      return aOrder - bOrder;
    }


([name, pages]) => ({
      name: name,
      pages,
    })


() => {
  const name = location.pathname.split('/')[2] || 'what-is-compiled';
  return `https://github.com/compiled/compiled-website/tree/master/packages/docs/src/pages/${name}.mdx`;
}


(slug: string) => {
  const sections = getSections();
  const name = slug === '/' ? sections[0].pages[0].name : slug.slice(1);
  const pages: Record<string, Page> = requireAllPages();

  const page = pages[name];
  if (!page) {
    return null;
  }

  const sectionIndex = sections.findIndex(
    (section) => section.name === page.data.section
  );
  const previousSection = sections[sectionIndex - 1];
  const nextSection = sections[sectionIndex + 1];
  const section = sections[sectionIndex];
  const pageIndex = section.pages.findIndex((page) => page.name === name);
  const nextPage = section.pages[pageIndex + 1];
  const previousPage = section.pages[pageIndex - 1];

  return {
    name,
    Component: page.default,
    data: page.data,
    next: (nextPage || nextSection) && {
      cta: nextPage ? 'Next' : nextSection.name.replace(/^\d+-/, ''),
      name: nextPage ? nextPage.data.name : nextSection.pages[0].data.name,
      slug: nextPage ? nextPage.name : nextSection.pages[0].name,
    },
    previous: (previousPage || previousSection) && {
      cta: previousPage
        ? 'Previous'
        : previousSection.name.replace(/^\d+-/, ''),
      name: previousPage
        ? previousPage.data.name
        : previousSection.pages[previousSection.pages.length - 1].data.name,
      slug: previousPage
        ? previousPage.name
        : previousSection.pages[previousSection.pages.length - 1].name,
    },
  };
}


(section) => section.name === page.data.section


(page) => page.name === name


() => {
  const location = useLocation();
  const pageSlug = location.pathname;
  const page = getPage(pageSlug);

  return (
    <AnchorProvider>
      <RootLayout
        aside={
          page && (
            <nav aria-label="page">
              <VerticalStack gap={2}>
                <Heading look="h500" as="div">
                  Contents
                </Heading>
                {page.data.headings
                  .filter((heading) => heading.depth < 4)
                  .map((heading, index) => (
                    <ToAnchor
                      depth={heading.depth}
                      key={`${heading.text}-${index}`}>
                      {heading.text}
                    </ToAnchor>


(heading) => heading.depth < 4


(heading, index) => (
                    <ToAnchor
                      depth={heading.depth}


(section, sectionIndex) => (
              <Section
                key={section.name}


(page, pageIndex) => (
                  <LinkItem
                    aria-current={
                      location.pathname === `/${page.name}` ||
                      (location.pathname === '/' &&
                        sectionIndex === 0 &&
                        pageIndex === 0)
                        ? 'page'
                        : undefined
                    }


interface Page {
  default: React.ComponentType<{}>;
  data: {
    headings: {
      depth: number;
      text: string;
    }[];
    order?: number;
    name?: string;
    section: string;
  };
}


getEditUrl()


([,p]) => p.passcode === payload.passcode


export class FigmaWebhooksRouter {
    constructor(private readonly config: BridgeConfigFigma, private readonly queue: MessageQueue) { }

    private onWebhook(req: Request<unknown, unknown, FigmaPayload, unknown>, res: Response<string|{error: string}>) {
        const payload = req.body;
        const instance = Object.entries(this.config.instances).find(([,p]) => p.passcode === payload.passcode);
        if (!instance) {
            // No instance found
            res.sendStatus(401);
            return;
        }
        if (typeof payload.file_name !== "string" || typeof payload.file_key !== "string") {
            res.status(400).send({error: "Missing required object keys file_name, file_key"});
            return;
        }
        res.status(200).send();
        this.queue.push({
            eventName: 'figma.payload',
            data: {
                payload,
                instanceName: instance[0],
            },
            sender: 'GithubWebhooks',
        })
    }

    public getRouter() {
        const router = Router();
        router.use(json());
        router.post("/webhook", this.onWebhook.bind(this));
        return router;
    }
}


private onWebhook(req: Request<unknown, unknown, FigmaPayload, unknown>, res: Response<string|{error: string}>) {
        const payload = req.body;
        const instance = Object.entries(this.config.instances).find(([,p]) => p.passcode === payload.passcode);
        if (!instance) {
            // No instance found
            res.sendStatus(401);
            return;
        }
        if (typeof payload.file_name !== "string" || typeof payload.file_key !== "string") {
            res.status(400).send({error: "Missing required object keys file_name, file_key"});
            return;
        }
        res.status(200).send();
        this.queue.push({
            eventName: 'figma.payload',
            data: {
                payload,
                instanceName: instance[0],
            },
            sender: 'GithubWebhooks',
        })
    }


public getRouter() {
        const router = Router();
        router.use(json());
        router.post("/webhook", this.onWebhook.bind(this));
        return router;
    }


export default function (window: BrowserWindow) {
    setup_chat();
    ipcMain.handle('bitcoin::command', async (event, arg) => {
        const node = await get_bitcoin_node();
        try {
            return { ok: await node.command(arg) };
        } catch (r: any) {
            if (r instanceof RpcError) {
                return {
                    err: { code: r.code, message: r.message, name: r.name },
                };
            } else if (r instanceof Error) {
                return { err: r.toString() };
            }
        }
    });

    ipcMain.handle('emulator::kill', (event) => {
        kill_emulator();
    });
    ipcMain.handle('emulator::start', (event) => {
        start_sapio_oracle();
    });
    ipcMain.handle('emulator::read_log', (event) => {
        return get_emulator_log();
    });

    ipcMain.handle('sapio::load_contract_list', async (event) => {
        const contracts = await sapio.list_contracts();
        return contracts;
    });
    ipcMain.handle(
        'sapio::create_contract',
        async (event, workspace, [which, psbt, args]) => {
            const result = await sapio.create_contract(
                workspace,
                which,
                psbt,
                args
            );
            return result;
        }
    );

    ipcMain.handle('sapio::show_config', async (event) => {
        return await sapio.show_config();
    });

    ipcMain.handle('sapio::load_wasm_plugin', (event) => {
        const plugins = dialog.showOpenDialogSync({
            properties: ['openFile', 'multiSelections'],
            filters: [{ extensions: ['wasm'], name: 'WASM' }],
        });
        const errs = [];
        if (!plugins || !plugins.length) return { err: 'No Plugin Selected' };
        for (const plugin of plugins) {
            const loaded = sapio.load_contract_file_name(plugin);
            if ('err' in loaded) {
                return loaded;
            }
        }
        return { ok: null };
    });

    ipcMain.handle('sapio::open_contract_from_file', (event) => {
        const file = dialog.showOpenDialogSync(window, {
            properties: ['openFile'],
            filters: [
                {
                    extensions: ['json'],
                    name: 'Sapio Contract Object',
                },
            ],
        });
        if (file && file.length === 1) {
            const data = readFileSync(file[0]!, {
                encoding: 'utf-8',
            });
            return { ok: data };
        }
    });
    ipcMain.handle(
        'sapio::compiled_contracts::list',
        async (event, workspace) => {
            return (
                await SapioWorkspace.new(workspace)
            ).list_compiled_contracts();
        }
    );
    ipcMain.handle(
        'sapio::compiled_contracts::trash',
        async (event, workspace, file_name) => {
            return (
                await SapioWorkspace.new(workspace)
            ).trash_compiled_contract(file_name);
        }
    );
    ipcMain.handle('sapio::psbt::finalize', (event, psbt) => {
        return sapio.psbt_finalize(psbt);
    });
    ipcMain.handle(
        'sapio::compiled_contracts::open',
        async (event, workspace_name, file_name) => {
            const workspace = await SapioWorkspace.new(workspace_name);
            const data = await workspace.read_bound_data_for(file_name);
            const name = await workspace.read_module_for(file_name);
            const args = await workspace.read_args_for(file_name);
            return { ok: { data, name, args } };
        }
    );

    ipcMain.handle('sapio::workspaces::init', async (event, workspace) => {
        await SapioWorkspace.new(workspace);
    });
    ipcMain.handle('sapio::workspaces::list', async (event) => {
        return await SapioWorkspace.list_all();
    });
    ipcMain.handle('sapio::workspaces::trash', async (event, workspace) => {
        return (await SapioWorkspace.new(workspace)).trash_workspace(workspace);
    });

    ipcMain.handle('write_clipboard', (event, s: string) => {
        clipboard.writeText(s);
    });

    ipcMain.handle('save_psbt', async (event, psbt) => {
        const path = await dialog.showSaveDialog(window, {
            filters: [
                {
                    extensions: ['psbt'],
                    name: 'Partially Signed Bitcoin Transaction',
                },
            ],
        });
        if (path.filePath) {
            await writeFile(path.filePath, psbt);
        }
    });
    ipcMain.handle('fetch_psbt', async (event, psbt) => {
        const path = await dialog.showOpenDialog(window, {
            filters: [
                {
                    extensions: ['psbt'],
                    name: 'Partially Signed Bitcoin Transaction',
                },
            ],
        });
        if (path && path.filePaths.length) {
            return await readFile(path.filePaths[0]!, { encoding: 'utf-8' });
        }
    });
    ipcMain.handle('save_contract', async (event, psbt) => {
        const path = await dialog.showSaveDialog(window, {
            filters: [{ extensions: ['json'], name: 'Sapio Contract Object' }],
        });
        if (path.filePath) {
            await writeFile(path.filePath, psbt);
        }
    });

    ipcMain.handle(
        'save_settings',
        async (event, which: Prefs, data: string) => {
            return preferences.save(which, JSON.parse(data));
        }
    );

    ipcMain.handle('load_settings_sync', (event, which: Prefs) => {
        return preferences.data[which];
    });
    ipcMain.handle('select_filename', async (event) => {
        const path = await dialog.showOpenDialog(window);
        if (path && path.filePaths.length == 1) {
            return path.filePaths[0]!;
        }
        return null;
    });
}


async (event, arg) => {
        const node = await get_bitcoin_node();
        try {
            return { ok: await node.command(arg) };
        } catch (r: any) {
            if (r instanceof RpcError) {
                return {
                    err: { code: r.code, message: r.message, name: r.name },
                };
            } else if (r instanceof Error) {
                return { err: r.toString() };
            }
        }
    }


(event) => {
        kill_emulator();
    }


(event) => {
        start_sapio_oracle();
    }


(event) => {
        return get_emulator_log();
    }


async (event) => {
        const contracts = await sapio.list_contracts();
        return contracts;
    }


async (event, workspace, [which, psbt, args]) => {
            const result = await sapio.create_contract(
                workspace,
                which,
                psbt,
                args
            );
            return result;
        }


async (event) => {
        return await sapio.show_config();
    }


(event) => {
        const plugins = dialog.showOpenDialogSync({
            properties: ['openFile', 'multiSelections'],
            filters: [{ extensions: ['wasm'], name: 'WASM' }],
        });
        const errs = [];
        if (!plugins || !plugins.length) return { err: 'No Plugin Selected' };
        for (const plugin of plugins) {
            const loaded = sapio.load_contract_file_name(plugin);
            if ('err' in loaded) {
                return loaded;
            }
        }
        return { ok: null };
    }


(event) => {
        const file = dialog.showOpenDialogSync(window, {
            properties: ['openFile'],
            filters: [
                {
                    extensions: ['json'],
                    name: 'Sapio Contract Object',
                },
            ],
        });
        if (file && file.length === 1) {
            const data = readFileSync(file[0]!, {
                encoding: 'utf-8',
            });
            return { ok: data };
        }
    }


async (event, workspace) => {
            return (
                await SapioWorkspace.new(workspace)
            ).list_compiled_contracts();
        }


async (event, workspace, file_name) => {
            return (
                await SapioWorkspace.new(workspace)
            ).trash_compiled_contract(file_name);
        }


(event, psbt) => {
        return sapio.psbt_finalize(psbt);
    }


async (event, workspace_name, file_name) => {
            const workspace = await SapioWorkspace.new(workspace_name);
            const data = await workspace.read_bound_data_for(file_name);
            const name = await workspace.read_module_for(file_name);
            const args = await workspace.read_args_for(file_name);
            return { ok: { data, name, args } };
        }


async (event, workspace) => {
        await SapioWorkspace.new(workspace);
    }


async (event) => {
        return await SapioWorkspace.list_all();
    }


async (event, workspace) => {
        return (await SapioWorkspace.new(workspace)).trash_workspace(workspace);
    }


(event, s: string) => {
        clipboard.writeText(s);
    }


async (event, psbt) => {
        const path = await dialog.showSaveDialog(window, {
            filters: [
                {
                    extensions: ['psbt'],
                    name: 'Partially Signed Bitcoin Transaction',
                },
            ],
        });
        if (path.filePath) {
            await writeFile(path.filePath, psbt);
        }
    }


async (event, psbt) => {
        const path = await dialog.showOpenDialog(window, {
            filters: [
                {
                    extensions: ['psbt'],
                    name: 'Partially Signed Bitcoin Transaction',
                },
            ],
        });
        if (path && path.filePaths.length) {
            return await readFile(path.filePaths[0]!, { encoding: 'utf-8' });
        }
    }


async (event, psbt) => {
        const path = await dialog.showSaveDialog(window, {
            filters: [{ extensions: ['json'], name: 'Sapio Contract Object' }],
        });
        if (path.filePath) {
            await writeFile(path.filePath, psbt);
        }
    }


async (event, which: Prefs, data: string) => {
            return preferences.save(which, JSON.parse(data));
        }


(event, which: Prefs) => {
        return preferences.data[which];
    }


async (event) => {
        const path = await dialog.showOpenDialog(window);
        if (path && path.filePaths.length == 1) {
            return path.filePaths[0]!;
        }
        return null;
    }


export class FilesApiMock implements FilesAPI {
  public static readonly CLASS_NAME = "FilesApiMock";

  private readonly log: Logger;
  private readonly data: Map<string, Buffer>;

  public get className(): string {
    return FilesApiMock.CLASS_NAME;
  }

  constructor(public readonly options: IFilesApiMockOptions) {
    const fnTag = `${this.className}#constructor()`;
    Checks.truthy(options, `${fnTag} arg options`);

    this.data = new Map();

    const level = this.options.logLevel || "INFO";
    const label = this.className;
    this.log = LoggerProvider.getOrCreate({ level, label });
    this.log.debug(`Instantiated ${this.className} OK`);
  }

  public async chmod(
    path: string,
    mode: string | number,
    options?: ChmodOptions | undefined,
  ): Promise<void> {
    throw new RuntimeError("Method chmod() not implemented");
  }

  public async cp(
    from: IPFSPath | IPFSPath[],
    to: string,
    options?: CpOptions | undefined,
  ): Promise<void> {
    throw new RuntimeError("Method cp() not implemented");
  }

  public async mkdir(
    path: string,
    options?: MkdirOptions | undefined,
  ): Promise<void> {
    throw new RuntimeError("Method mkdir() not implemented");
  }

  public async stat(
    ipfsPath: IPFSPath,
    options?: StatOptions | undefined,
  ): Promise<StatResult> {
    if (typeof ipfsPath !== "string") {
      throw new RuntimeError("Sorry, the mock only supports string IPFS paths");
    }
    if (this.data.has(ipfsPath)) {
      return {} as StatResult;
    } else {
      throw new RuntimeError(K_IPFS_JS_HTTP_ERROR_FILE_DOES_NOT_EXIST);
    }
  }

  public async touch(
    ipfsPath: string,
    options?: TouchOptions | undefined,
  ): Promise<void> {
    throw new RuntimeError("Method touch() not implemented");
  }

  public async rm(
    ipfsPaths: string | string[],
    options?: RmOptions | undefined,
  ): Promise<void> {
    throw new RuntimeError("Method rm() not implemented");
  }

  public async *read(
    ipfsPath: IPFSPath,
    options?: ReadOptions | undefined,
  ): AsyncIterable<Uint8Array> {
    if (typeof ipfsPath !== "string") {
      throw new RuntimeError("Sorry, the mock only supports string IPFS paths");
    }
    const buffer = this.data.get(ipfsPath);
    if (!buffer) {
      throw new RuntimeError(K_IPFS_JS_HTTP_ERROR_FILE_DOES_NOT_EXIST);
    }
    yield buffer;
  }

  public async write(
    ipfsPath: string,
    content:
      | string
      | Uint8Array
      | AsyncIterable<Uint8Array>
      | Blob
      | Iterable<Uint8Array>,
    options?: WriteOptions | undefined,
  ): Promise<void> {
    if (!(content instanceof Buffer)) {
      throw new RuntimeError("Sorry, this mock only supports Buffer content.");
    }
    this.data.set(ipfsPath, content);
  }

  public async mv(
    from: string | string[],
    to: string,
    options?: MvOptions | undefined,
  ): Promise<void> {
    throw new RuntimeError("Method mv() not implemented");
  }

  public async flush(
    ipfsPath: string,
    options?: AbortOptions | undefined,
  ): Promise<import("cids")> {
    throw new RuntimeError("Method flush() not implemented");
  }

  public ls(
    ipfsPath: IPFSPath,
    options?: AbortOptions | undefined,
  ): AsyncIterable<MFSEntry> {
    throw new RuntimeError("Method ls() not implemented");
  }
}


export interface IFilesApiMockOptions {
  logLevel?: LogLevelDesc;
}


public async chmod(
    path: string,
    mode: string | number,
    options?: ChmodOptions | undefined,
  ): Promise<void> {
    throw new RuntimeError("Method chmod() not implemented");
  }


public async cp(
    from: IPFSPath | IPFSPath[],
    to: string,
    options?: CpOptions | undefined,
  ): Promise<void> {
    throw new RuntimeError("Method cp() not implemented");
  }


public async mkdir(
    path: string,
    options?: MkdirOptions | undefined,
  ): Promise<void> {
    throw new RuntimeError("Method mkdir() not implemented");
  }


public async stat(
    ipfsPath: IPFSPath,
    options?: StatOptions | undefined,
  ): Promise<StatResult> {
    if (typeof ipfsPath !== "string") {
      throw new RuntimeError("Sorry, the mock only supports string IPFS paths");
    }
    if (this.data.has(ipfsPath)) {
      return {} as StatResult;
    } else {
      throw new RuntimeError(K_IPFS_JS_HTTP_ERROR_FILE_DOES_NOT_EXIST);
    }
  }


public async touch(
    ipfsPath: string,
    options?: TouchOptions | undefined,
  ): Promise<void> {
    throw new RuntimeError("Method touch() not implemented");
  }


public async rm(
    ipfsPaths: string | string[],
    options?: RmOptions | undefined,
  ): Promise<void> {
    throw new RuntimeError("Method rm() not implemented");
  }


public async *read(
    ipfsPath: IPFSPath,
    options?: ReadOptions | undefined,
  ): AsyncIterable<Uint8Array> {
    if (typeof ipfsPath !== "string") {
      throw new RuntimeError("Sorry, the mock only supports string IPFS paths");
    }
    const buffer = this.data.get(ipfsPath);
    if (!buffer) {
      throw new RuntimeError(K_IPFS_JS_HTTP_ERROR_FILE_DOES_NOT_EXIST);
    }
    yield buffer;
  }


public async write(
    ipfsPath: string,
    content:
      | string
      | Uint8Array
      | AsyncIterable<Uint8Array>
      | Blob
      | Iterable<Uint8Array>,
    options?: WriteOptions | undefined,
  ): Promise<void> {
    if (!(content instanceof Buffer)) {
      throw new RuntimeError("Sorry, this mock only supports Buffer content.");
    }
    this.data.set(ipfsPath, content);
  }


public async mv(
    from: string | string[],
    to: string,
    options?: MvOptions | undefined,
  ): Promise<void> {
    throw new RuntimeError("Method mv() not implemented");
  }


public async flush(
    ipfsPath: string,
    options?: AbortOptions | undefined,
  ): Promise<import("cids")> {
    throw new RuntimeError("Method flush() not implemented");
  }


public ls(
    ipfsPath: IPFSPath,
    options?: AbortOptions | undefined,
  ): AsyncIterable<MFSEntry> {
    throw new RuntimeError("Method ls() not implemented");
  }


() => {
  let store$: StoreWithSnapshots;

  beforeEach(() => {
    const configurationServiceMock = mock(ConfigurationService);
    when(configurationServiceMock.getServerConfiguration()).thenReturn(of({}));

    TestBed.configureTestingModule({
      imports: [CoreStoreModule.forTesting(['serverConfig'], [ServerConfigEffects])],
      providers: [
        provideStoreSnapshots(),
        { provide: ConfigurationService, useFactory: () => instance(configurationServiceMock) },
      ],
    });

    store$ = TestBed.inject(StoreWithSnapshots);
  });

  it('should be created', () => {
    expect(store$).toBeTruthy();
  });

  it('should trigger the loading of config data on the first page', () => {
    store$.dispatch(routerTestNavigationAction({ routerState: { url: '/any' } }));

    expect(store$.actionsArray()).toMatchInlineSnapshot(`
      @ngrx/router-store/navigation: /any
      [Configuration Internal] Get the ICM configuration
      [Configuration API] Get the ICM configuration Success:
        config: {}
    `);
  });
}


() => {
    const configurationServiceMock = mock(ConfigurationService);
    when(configurationServiceMock.getServerConfiguration()).thenReturn(of({}));

    TestBed.configureTestingModule({
      imports: [CoreStoreModule.forTesting(['serverConfig'], [ServerConfigEffects])],
      providers: [
        provideStoreSnapshots(),
        { provide: ConfigurationService, useFactory: () => instance(configurationServiceMock) },
      ],
    });

    store$ = TestBed.inject(StoreWithSnapshots);
  }


() => instance(configurationServiceMock)


() => {
    expect(store$).toBeTruthy();
  }


() => {
    store$.dispatch(routerTestNavigationAction({ routerState: { url: '/any' } }));

    expect(store$.actionsArray()).toMatchInlineSnapshot(`
      @ngrx/router-store/navigation: /any
      [Configuration Internal] Get the ICM configuration
      [Configuration API] Get the ICM configuration Success:
        config: {}
    `);
  }


() => {
  let actions$: Observable<Action>;
  let effects: ServerConfigEffects;
  let store$: MockStore;
  let configurationServiceMock: ConfigurationService;

  beforeEach(() => {
    configurationServiceMock = mock(ConfigurationService);

    TestBed.configureTestingModule({
      providers: [
        ServerConfigEffects,
        provideMockStore(),
        provideMockActions(() => actions$),
        { provide: ConfigurationService, useFactory: () => instance(configurationServiceMock) },
      ],
    });

    effects = TestBed.inject(ServerConfigEffects);
    store$ = TestBed.inject(MockStore);
  });

  describe('loadServerConfigOnInit$', () => {
    it('should trigger the loading of config data on the first page', () => {
      store$.overrideSelector(isServerConfigurationLoaded, false);

      const action = routerTestNavigationAction({});
      const expected = loadServerConfig();

      actions$ = hot('a', { a: action });
      expect(effects.loadServerConfigOnInit$).toBeObservable(cold('a', { a: expected }));
    });

    it('should not trigger the loading of config data on the second page', () => {
      store$.overrideSelector(isServerConfigurationLoaded, true);

      const action = routerTestNavigationAction({});
      actions$ = hot('        ----a---a--a', { a: action });
      const expected$ = cold('------------');

      expect(effects.loadServerConfigOnInit$).toBeObservable(expected$);
    });
  });

  describe('loadServerConfig$', () => {
    beforeEach(() => {
      when(configurationServiceMock.getServerConfiguration()).thenReturn(of({}));
    });

    it('should map to action of type ApplyConfiguration', () => {
      const action = loadServerConfig();
      const completion = loadServerConfigSuccess({ config: {} });

      actions$ = hot('-a-a-a', { a: action });
      const expected$ = cold('-c-c-c', { c: completion });

      expect(effects.loadServerConfig$).toBeObservable(expected$);
    });

    it('should map invalid request to action of type LoadServerConfigFail', () => {
      when(configurationServiceMock.getServerConfiguration()).thenReturn(
        throwError(makeHttpError({ message: 'invalid' }))
      );

      const action = loadServerConfig();
      const completion = loadServerConfigFail({ error: makeHttpError({ message: 'invalid' }) });
      actions$ = hot('-a-a-a', { a: action });
      const expected$ = cold('-c-c-c', { c: completion });

      expect(effects.loadServerConfig$).toBeObservable(expected$);
    });
  });
}


() => {
    configurationServiceMock = mock(ConfigurationService);

    TestBed.configureTestingModule({
      providers: [
        ServerConfigEffects,
        provideMockStore(),
        provideMockActions(() => actions$),
        { provide: ConfigurationService, useFactory: () => instance(configurationServiceMock) },
      ],
    });

    effects = TestBed.inject(ServerConfigEffects);
    store$ = TestBed.inject(MockStore);
  }


() => {
    it('should trigger the loading of config data on the first page', () => {
      store$.overrideSelector(isServerConfigurationLoaded, false);

      const action = routerTestNavigationAction({});
      const expected = loadServerConfig();

      actions$ = hot('a', { a: action });
      expect(effects.loadServerConfigOnInit$).toBeObservable(cold('a', { a: expected }));
    });

    it('should not trigger the loading of config data on the second page', () => {
      store$.overrideSelector(isServerConfigurationLoaded, true);

      const action = routerTestNavigationAction({});
      actions$ = hot('        ----a---a--a', { a: action });
      const expected$ = cold('------------');

      expect(effects.loadServerConfigOnInit$).toBeObservable(expected$);
    });
  }


() => {
      store$.overrideSelector(isServerConfigurationLoaded, false);

      const action = routerTestNavigationAction({});
      const expected = loadServerConfig();

      actions$ = hot('a', { a: action });
      expect(effects.loadServerConfigOnInit$).toBeObservable(cold('a', { a: expected }));
    }


() => {
      store$.overrideSelector(isServerConfigurationLoaded, true);

      const action = routerTestNavigationAction({});
      actions$ = hot('        ----a---a--a', { a: action });
      const expected$ = cold('------------');

      expect(effects.loadServerConfigOnInit$).toBeObservable(expected$);
    }


() => {
    beforeEach(() => {
      when(configurationServiceMock.getServerConfiguration()).thenReturn(of({}));
    });

    it('should map to action of type ApplyConfiguration', () => {
      const action = loadServerConfig();
      const completion = loadServerConfigSuccess({ config: {} });

      actions$ = hot('-a-a-a', { a: action });
      const expected$ = cold('-c-c-c', { c: completion });

      expect(effects.loadServerConfig$).toBeObservable(expected$);
    });

    it('should map invalid request to action of type LoadServerConfigFail', () => {
      when(configurationServiceMock.getServerConfiguration()).thenReturn(
        throwError(makeHttpError({ message: 'invalid' }))
      );

      const action = loadServerConfig();
      const completion = loadServerConfigFail({ error: makeHttpError({ message: 'invalid' }) });
      actions$ = hot('-a-a-a', { a: action });
      const expected$ = cold('-c-c-c', { c: completion });

      expect(effects.loadServerConfig$).toBeObservable(expected$);
    });
  }