"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PageFetchAction = exports.RETRIABLE_ERRORS = void 0;
const exception_js_1 = require("../../exception.js");
const index_js_1 = require("../model/index.js");
const index_js_2 = require("../services/index.js");
exports.RETRIABLE_ERRORS = ['ECONNRESET', 'ECONNREFUSED', 'ETIMEDOUT', 'EPIPE', 'ERR_STREAM_DESTROYED'];
class PageFetchAction extends index_js_1.Action {
    constructor() {
        super(...arguments);
        this.$request = undefined;
        this.$response = undefined;
        this.mode = 'Fetch';
        this.rejectHttpErrors = true;
        this.requestBodyFormat = 'none';
        this.responseBodyFormat = 'none';
        this.timeout = 120000;
        this.retries = 3;
        this.$result = undefined;
    }
    get $fetch() {
        return this.$engine.get(index_js_2.FetchService);
    }
    reset() {
        super.reset();
        this.$result = undefined;
        this.$request = undefined;
        this.$response = undefined;
    }
    hasChildren() {
        return true;
    }
    async resolveChildrenScope() {
        if (!this.$result) {
            return [];
        }
        const inputSet = await this.resolveScope();
        return inputSet.map(el => el.clone(this.$result));
    }
    afterRun() {
        if (this.$runtime.bypassed) {
            this.skip();
        }
        else {
            this.enter();
        }
    }
    async exec() {
        const data = await this.retry(async () => {
            const el = await this.selectOne(this.pipeline);
            return el.value;
        });
        const requestSpec = await this.$fetch.prepareRequestSpec({
            mode: this.mode,
            requestBodyFormat: this.requestBodyFormat,
            responseBodyFormat: this.responseBodyFormat,
            timeout: this.timeout,
            retries: this.retries,
            ...data
        });
        const response = await this.$fetch.sendWithRetries(requestSpec);
        if (response.status >= 400 && this.rejectHttpErrors) {
            throw new exception_js_1.Exception({
                name: 'SendNetworkRequestFailed',
                message: `Request failed: HTTP ${response.status} ${response.statusText}`,
                retry: false,
            });
        }
        this.$result = {
            request: requestSpec,
            response,
        };
        this.$request = requestSpec;
        this.$response = response;
    }
}
PageFetchAction.$type = 'Page.fetch';
PageFetchAction.$icon = 'fas fa-wifi';
PageFetchAction.$help = `
Sends a new network request from currently open webpage.

The request is configured using pipeline's output object (see below).
The pipeline should return a single element with object value, which configures the request.

The request follows Content Security Policy (CSP) of the current web page.
This can be a limiting factor, for example when trying to send a request to a domain which is not whitelisted
in \`Access-Control-Allow-Origin\` header (the same applies to other CORS and CSP headers).
If you are experiencing problems due to CORS and/or CSP, it is recommended to use "nodejs" or "nodejs+proxy" mode.

The request configuration object (the output value of pipeline) can consist of:

- \`method\`: HTTP request method; \`GET\`, \`POST\`, \`PUT\` or \`DELETE\` are used most commonly
- \`protocol\` (e.g. \`http:\`, \`https:\`)
- \`host\` (e.g. \`example.com\`, \`example.com: 3123\`)
- \`hostname\` (e.g. \`example.com\`)
- \`pathname\` (e.g. \`/path/to/resource\`)
- \`query\`: an object with query parameters (e.g. \`{ hello: 'world' }\`)
- \`headers\`: an object with headers (e.g. \`{ 'content-type': 'text/plain' }\`)
- \`body\`: an object which configures request body, depends on Request Body Format parameter:
  - none: the body is not included in request (the value is ignored)
  - json: the object is sent as JSON; content type header is set to \`application/json\`
    if not overridden with \`headers\`
  - urlencoded: the object is encoded in a similar way to \`query\` above and is sent in request body;
    content type header is set to \`application/x-www-form-urlencoded\` if not overridden with \`headers\`
  - multipart: the content type header is set to \`multipart/form-data\` and the request body is encoded
    as multipart/form-data stream following the rules:
      - each object key is a separate field
      - if the value is string, a text field is appended to form data
      - if the value is Blob object, the file field is appended to form data (see [Fetch Blob](#fetch-blob) for details)
  - text: the value is sent as a plain text

Note: it is handy to configure the request object using Compose pipe.

### Parameters

- mode: Fetch or XHR (Fetch is preferred, but XHR may be necessary if website replaces \`window.fetch\`
  with its own incompatible implementation)
- request body format: specifies how to treat \`/ body\` of request object (see above)
- response body format: specifies how to parse the response

### Use For

- advanced scripting, to send network requests manually
`;
__decorate([
    index_js_1.params.Pipeline(),
    __metadata("design:type", index_js_1.Pipeline)
], PageFetchAction.prototype, "pipeline", void 0);
__decorate([
    index_js_1.params.Outcome({
        label: 'Request',
        placeholder: 'Run the action to send a request.',
        hidden: true,
    }),
    __metadata("design:type", Object)
], PageFetchAction.prototype, "$request", void 0);
__decorate([
    index_js_1.params.Outcome({
        label: 'Response',
        placeholder: 'Run the action to send a request.',
    }),
    __metadata("design:type", Object)
], PageFetchAction.prototype, "$response", void 0);
__decorate([
    index_js_1.params.Enum({
        enum: ['Fetch', 'Node', 'Node+Proxy']
    }),
    __metadata("design:type", String)
], PageFetchAction.prototype, "mode", void 0);
__decorate([
    index_js_1.params.Boolean(),
    __metadata("design:type", Boolean)
], PageFetchAction.prototype, "rejectHttpErrors", void 0);
__decorate([
    index_js_1.params.Enum({
        enum: ['none', 'json', 'urlencoded', 'multipart', 'text', 'base64']
    }),
    __metadata("design:type", String)
], PageFetchAction.prototype, "requestBodyFormat", void 0);
__decorate([
    index_js_1.params.Enum({
        enum: ['none', 'json', 'urlencoded', 'text', 'base64', 'blob']
    }),
    __metadata("design:type", String)
], PageFetchAction.prototype, "responseBodyFormat", void 0);
__decorate([
    index_js_1.params.Number({ min: 0 }),
    __metadata("design:type", Number)
], PageFetchAction.prototype, "timeout", void 0);
__decorate([
    index_js_1.params.Number({ min: 0 }),
    __metadata("design:type", Number)
], PageFetchAction.prototype, "retries", void 0);
exports.PageFetchAction = PageFetchAction;
//# sourceMappingURL=Page.fetch.js.map