diff --git a/README.md b/README.md index 1a00a11..7ee075f 100644 --- a/README.md +++ b/README.md @@ -132,6 +132,39 @@ playground('.selector', options) instance.getCode() // function for getting code from snippet. ``` +- `onRequest(request)` — Request interceptor that allows you to modify HTTP requests before they are sent. + _request_ — request with the following properties: + - `url` — the request URL + - `method` — HTTP method (GET, POST, etc.) + - `headers` — request headers + - `body` — request body (if applicable) + + The interceptor should return the modified request + + ```js + const options = { + onRequest: async (request) => { + request.headers = { + ...request.headers, + 'Authorization': 'Basic a290bGluLXBsYXlncm91bmQ6MTIzNA==' + }; + + if (request.url.includes("/compiler/highlight")) + request.url = request.url + '?severity=WARNING'; + + if (request.body && request.url.includes("/compiler/complete")) { + const body = JSON.parse(request.body); + body.type = 'CLASS'; + request.body = JSON.stringify(body); + } + + return request; + } + }; + + playground('.selector', options); + ``` + ## Customizing editors diff --git a/src/config.js b/src/config.js index 86a5b5b..7e9ee20 100644 --- a/src/config.js +++ b/src/config.js @@ -3,7 +3,12 @@ import { TargetPlatforms } from './utils/platforms'; const currentScript = getCurrentScript(); -export const RUNTIME_CONFIG = { ...getConfigFromElement(currentScript) }; +export const RUNTIME_CONFIG = { + ...getConfigFromElement(currentScript), + interceptor: { + onRequest: null, + } +}; /** * API Paths diff --git a/src/index.js b/src/index.js index 39ffa21..ada0330 100644 --- a/src/index.js +++ b/src/index.js @@ -22,6 +22,7 @@ polyfill(); * @property {Function} onConsoleOpen * @property {Function} onConsoleClose * @property {Function} callBack + * @property {Function} onRequest * * @param {string} selector * @param {Object} options @@ -29,6 +30,11 @@ polyfill(); */ export default function create(selector, options = {}) { API_URLS.server = options.server || API_URLS.server; + + if (options.onRequest) { + RUNTIME_CONFIG.interceptor.onRequest = options.onRequest; + } + return ExecutableCode.create(selector, options); } diff --git a/src/interceptor/fetch-interceptor.js b/src/interceptor/fetch-interceptor.js new file mode 100644 index 0000000..558132c --- /dev/null +++ b/src/interceptor/fetch-interceptor.js @@ -0,0 +1,23 @@ +import { fetch } from 'whatwg-fetch'; +import { RUNTIME_CONFIG } from '../config'; + +export const fetchWithInterceptor = async function(url, config = {}) { + const onRequest = RUNTIME_CONFIG.interceptor?.onRequest; + + const request = { + url, + method: config.method || 'GET', + headers: { ...config.headers }, + body: config.body, + }; + + const modified = typeof onRequest === 'function' + ? await onRequest(request).catch(() => null) ?? request + : request; + + return fetch(modified.url, { + method: modified.method, + headers: modified.headers, + body: modified.body, + }); +}; diff --git a/src/js-executor/index.js b/src/js-executor/index.js index 2998510..30f5750 100644 --- a/src/js-executor/index.js +++ b/src/js-executor/index.js @@ -4,7 +4,7 @@ import {showJsException} from '../view/output-view'; import {processingHtmlBrackets} from '../utils'; import {isWasmRelated, TargetPlatforms} from '../utils/platforms'; import {executeJs, executeWasmCode, executeWasmCodeWithSkiko, executeWasmCodeWithStdlib} from './execute-es-module'; -import {fetch} from "whatwg-fetch"; +import {fetchWithInterceptor} from '../interceptor/fetch-interceptor'; const INIT_SCRIPT = 'if(kotlin.BufferedOutput!==undefined){kotlin.out = new kotlin.BufferedOutput()}' + @@ -183,13 +183,13 @@ export default class JsExecutor { } if (targetPlatform === TargetPlatforms.COMPOSE_WASM) { - const skikoStdlib = fetch(API_URLS.RESOURCE_VERSIONS(),{ + const skikoStdlib = fetchWithInterceptor(API_URLS.RESOURCE_VERSIONS(),{ method: 'GET' }).then(response => response.json()) .then(versions => { const skikoVersion = versions["skiko"]; - const skikoExports = fetch(API_URLS.SKIKO_MJS(skikoVersion), { + const skikoExports = fetchWithInterceptor(API_URLS.SKIKO_MJS(skikoVersion), { method: 'GET', headers: { 'Content-Type': 'text/javascript', @@ -208,7 +208,7 @@ export default class JsExecutor { const stdlibVersion = versions["stdlib"]; - const stdlibExports = fetch(API_URLS.STDLIB_MJS(stdlibVersion), { + const stdlibExports = fetchWithInterceptor(API_URLS.STDLIB_MJS(stdlibVersion), { method: 'GET', headers: { 'Content-Type': 'text/javascript', diff --git a/src/webdemo-api.js b/src/webdemo-api.js index 64bade9..6d35f0b 100644 --- a/src/webdemo-api.js +++ b/src/webdemo-api.js @@ -1,4 +1,3 @@ -import { fetch } from 'whatwg-fetch'; import { API_URLS } from './config'; import flatten from 'flatten'; import { isWasmRelated, TargetPlatforms } from './utils/platforms'; @@ -9,6 +8,7 @@ import { processJUnitResults, processJVMOutput, } from './view/output-view'; +import { fetchWithInterceptor } from './interceptor/fetch-interceptor'; /** * @typedef {Object} KotlinVersion @@ -31,7 +31,7 @@ export default class WebDemoApi { */ static getCompilerVersions() { if (!CACHE.compilerVersions) { - CACHE.compilerVersions = fetch(API_URLS.VERSIONS) + CACHE.compilerVersions = fetchWithInterceptor(API_URLS.VERSIONS) .then((response) => response.json()) .catch(() => (CACHE.compilerVersions = null)); } @@ -268,7 +268,7 @@ function executeCode( ...(options || {}), }; - return fetch(url, { + return fetchWithInterceptor(url, { method: 'POST', body: JSON.stringify(body), headers: {