UNPKG

scenario-mock-server

Version:
1,017 lines (997 loc) 30.8 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __defProps = Object.defineProperties; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropDescs = Object.getOwnPropertyDescriptors; var __getOwnPropNames = Object.getOwnPropertyNames; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); var __objRest = (source, exclude) => { var target = {}; for (var prop in source) if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0) target[prop] = source[prop]; if (source != null && __getOwnPropSymbols) for (var prop of __getOwnPropSymbols(source)) { if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop)) target[prop] = source[prop]; } return target; }; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var src_exports = {}; __export(src_exports, { createExpressApp: () => createExpressApp, run: () => run }); module.exports = __toCommonJS(src_exports); // src/run.ts var import_server_with_kill = require("server-with-kill"); // src/express.ts var import_node_path = __toESM(require("path")); var import_cookie_parser = __toESM(require("cookie-parser")); var import_cors = __toESM(require("cors")); var import_express = __toESM(require("express")); var import_zod3 = __toESM(require("zod")); // src/ui.tsx var import_react2 = __toESM(require("react")); var import_server = require("react-dom/server"); // src/Html.tsx var import_react = __toESM(require("react")); function Html({ updatedScenarioName, uiPath, scenarios, groups }) { return /* @__PURE__ */ import_react.default.createElement("html", { lang: "en" }, /* @__PURE__ */ import_react.default.createElement("head", null, /* @__PURE__ */ import_react.default.createElement("meta", { charSet: "utf-8" }), /* @__PURE__ */ import_react.default.createElement("meta", { name: "viewport", content: "width=device-width,initial-scale=1" }), /* @__PURE__ */ import_react.default.createElement("title", null, `${updatedScenarioName ? "Updated - " : ""}Scenarios - Scenario Mock Server`), /* @__PURE__ */ import_react.default.createElement( "link", { rel: "stylesheet", href: `${uiPath}${uiPath.slice(-1) === "/" ? "" : "/"}index.css` } )), /* @__PURE__ */ import_react.default.createElement("body", null, /* @__PURE__ */ import_react.default.createElement("main", null, /* @__PURE__ */ import_react.default.createElement(ScenarioUpdateInfo, { updatedScenarioName }), /* @__PURE__ */ import_react.default.createElement("form", { className: "stack-1", method: "POST", action: uiPath }, /* @__PURE__ */ import_react.default.createElement("p", null, /* @__PURE__ */ import_react.default.createElement("a", { href: uiPath }, "Refresh page")), /* @__PURE__ */ import_react.default.createElement(CallToActionButton, null), /* @__PURE__ */ import_react.default.createElement("fieldset", { className: "stack-3" }, /* @__PURE__ */ import_react.default.createElement("legend", null, /* @__PURE__ */ import_react.default.createElement("h1", null, "Scenarios")), scenarios.some(({ group }) => group !== null) ? /* @__PURE__ */ import_react.default.createElement(GroupedScenarios, { groups, scenarios }) : /* @__PURE__ */ import_react.default.createElement(ScenarioList, { scenarios })), /* @__PURE__ */ import_react.default.createElement(CallToActionButton, null))))); } function ScenarioUpdateInfo({ updatedScenarioName }) { if (!updatedScenarioName) { return null; } return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, "Updated to the following scenario: ", updatedScenarioName); } function CallToActionButton() { return /* @__PURE__ */ import_react.default.createElement("button", { type: "submit", name: "button", value: "modify" }, "Select scenario"); } var NULL_GROUP_ID = "sms-other"; function GroupedScenarios({ groups, scenarios }) { const groupedScenarios = {}; scenarios.forEach((scenario) => { const group = scenario.group === null ? NULL_GROUP_ID : scenario.group; groupedScenarios[group] = groupedScenarios[group] || []; groupedScenarios[group].push(scenario); }); const groupsWithLabelIds = Object.keys(groups); const groupsWithoutLabelIds = Object.keys(groupedScenarios).filter( (groupId) => !groupsWithLabelIds.includes(groupId) && groupId !== NULL_GROUP_ID ); const groupEntries = Object.entries(groups).concat(groupsWithoutLabelIds.map((groupId) => [groupId, groupId])).concat([[NULL_GROUP_ID, "Other"]]); return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, groupEntries.map(([groupId, groupName]) => /* @__PURE__ */ import_react.default.createElement("div", { key: groupId }, /* @__PURE__ */ import_react.default.createElement("h2", null, groupName), /* @__PURE__ */ import_react.default.createElement(ScenarioList, { scenarios: groupedScenarios[groupId] })))); } function ScenarioList({ scenarios }) { return /* @__PURE__ */ import_react.default.createElement("div", { className: "stack-3" }, scenarios.map((scenario) => /* @__PURE__ */ import_react.default.createElement("div", { key: scenario.id }, /* @__PURE__ */ import_react.default.createElement( "input", { type: "radio", id: scenario.id, name: "scenarioId", value: scenario.id, defaultChecked: scenario.selected } ), /* @__PURE__ */ import_react.default.createElement("label", { htmlFor: scenario.id }, scenario.name), scenario.description ? /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("br", null), /* @__PURE__ */ import_react.default.createElement("details", null, /* @__PURE__ */ import_react.default.createElement("summary", null, "Description"), /* @__PURE__ */ import_react.default.createElement("div", { className: "description" }, scenario.description))) : null))); } // src/cookies.ts var import_zod = __toESM(require("zod")); var CONTEXT_AND_SCENARIO_COOKIE_NAME = "scenario-mock-server"; function getScenarioIdFromCookie({ getCookie, setCookie, defaultValue }) { let cookieValue = defaultValue; const cookie = getCookie(CONTEXT_AND_SCENARIO_COOKIE_NAME); if (cookie) { try { cookieValue = JSON.parse(cookie); } catch (error) { setCookie(CONTEXT_AND_SCENARIO_COOKIE_NAME, JSON.stringify(cookieValue)); } } return cookieValue.scenarioId; } var cookieValueSchema = import_zod.default.object({ context: import_zod.default.object({}).passthrough(), scenarioId: import_zod.default.string() }); function getScenarioMockServerCookie({ getCookie, initialScenarioId, initialContext }) { const cookie = getCookie(CONTEXT_AND_SCENARIO_COOKIE_NAME); if (cookie) { try { const parsedCookie = JSON.parse(cookie); return cookieValueSchema.parse(parsedCookie); } catch (error) { console.error("Cookie value could not be parsed"); } } return { scenarioId: initialScenarioId, context: initialContext }; } function setScenarioMockServerCookie({ setCookie, value }) { setCookie(CONTEXT_AND_SCENARIO_COOKIE_NAME, JSON.stringify(value)); } // src/utils/get-scenario-ids.ts function getScenarioIds(scenarioIds, scenario, scenarioMap) { const scenarioIdsResult = [scenario.id].concat(scenarioIds); if (scenario.extend) { const scenarioToExtend = scenarioMap[scenario.extend]; if (scenarioToExtend) { return getScenarioIds(scenarioIdsResult, scenarioToExtend, scenarioMap); } } return scenarioIdsResult; } // src/utils/get-context-from-scenario.ts function getContextFromScenario(scenario, scenarioMap) { const scenarioIds = getScenarioIds([], scenario, scenarioMap); let context = {}; for (const scenarioId of scenarioIds) { const scenario2 = scenarioMap[scenarioId]; if (scenario2.context) { context = __spreadValues(__spreadValues({}, context), scenario2.context); } } return context; } // src/apis.ts function getScenarios({ getCookie, setCookie, getServerScenarioId, cookieMode, initialScenarioId, initialContext, scenarios }) { const scenarioId = getScenarioId({ getCookie, setCookie, getServerScenarioId, cookieMode, initialScenarioId, initialContext }); const allScenarios = scenarios.map( ({ id, name, description, group }) => ({ id, name, description: description === void 0 ? null : description, selected: id === scenarioId, group: group === void 0 ? null : group }) ); return { status: 200, headers: { "content-type": "application/json" }, data: allScenarios }; } function selectScenario({ scenarioId, scenarioMap, cookieMode, setCookie, setServerContext, setServerScenarioId }) { const updatedScenario = scenarioMap[scenarioId]; if (!updatedScenario) { return { status: 400, headers: { "content-type": "application/json" }, data: { message: `Scenario id "${scenarioId}" does not exist` } }; } updateScenarioAndContext({ cookieMode, scenarioId, scenarioMap, setCookie, setServerContext, setServerScenarioId }); return { status: 204 }; } function updateScenarioAndContext({ scenarioId, scenarioMap, cookieMode, setCookie, setServerContext, setServerScenarioId }) { const context = getContextFromScenario(scenarioMap[scenarioId], scenarioMap); if (cookieMode) { setScenarioMockServerCookie({ setCookie, value: { scenarioId, context } }); return; } setServerContext(context); setServerScenarioId(scenarioId); } function getScenarioId({ getCookie, setCookie, getServerScenarioId, cookieMode, initialScenarioId, initialContext }) { if (cookieMode) { return getScenarioIdFromCookie({ getCookie, setCookie, defaultValue: { scenarioId: initialScenarioId, context: initialContext } }); } return getServerScenarioId(); } // src/ui.tsx function getUi({ uiPath, cookieMode, scenarios, initialScenarioId, initialContext, getCookie, setCookie, getServerScenarioId, groups }) { const { data } = getScenarios({ cookieMode, initialContext, initialScenarioId, scenarios, getCookie, getServerScenarioId, setCookie }); const html = (0, import_server.renderToStaticMarkup)( /* @__PURE__ */ import_react2.default.createElement(Html, { uiPath, scenarios: data, groups }) ); return "<!DOCTYPE html>\n" + html; } function updateUi({ scenarioId, uiPath, scenarioMap, scenarios, cookieMode, setCookie, setServerContext, setServerScenarioId, groups }) { const updatedScenarioName = scenarioMap[scenarioId].name; selectScenario({ cookieMode, scenarioId, scenarioMap, setCookie, setServerContext, setServerScenarioId }); const allScenarios = scenarios.map(({ id, name, description, group }) => ({ id, name, description: description === void 0 ? null : description, selected: id === scenarioId, group: group === void 0 ? null : group })); const html = (0, import_server.renderToStaticMarkup)( /* @__PURE__ */ import_react2.default.createElement( Html, { uiPath, scenarios: allScenarios, groups, updatedScenarioName } ) ); return "<!DOCTYPE html>\n" + html; } // src/handle-request.ts var import_crypto = require("crypto"); // src/graph-ql.ts var import_graphql = require("graphql"); var import_graphql_tag = __toESM(require("graphql-tag")); var import_zod2 = __toESM(require("zod")); // src/create-handler.ts var DEFAULT_STATUS = 200; var DEFAULT_DELAY = 0; function createHandler({ response = {}, updateContext: updateContext2, getContext }) { return async (req) => { const result = isResponseFunction(response) ? await response(__spreadProps(__spreadValues({}, req), { updateContext: updateContext2, context: getContext() })) : response; const { status = DEFAULT_STATUS, data, delay = DEFAULT_DELAY } = result; const headers = lowerCaseKeys(result.headers || {}); await wait(delay); if (data !== void 0 && !headers["content-type"]) { headers["content-type"] = "application/json"; } return { status, data, headers }; }; } function wait(responseDelay) { return new Promise((res) => setTimeout(res, responseDelay)); } function isResponseFunction(response) { return typeof response === "function"; } function lowerCaseKeys(obj) { return Object.fromEntries( Object.entries(obj).map(([key, value]) => [key.toLowerCase(), value]) ); } // src/graph-ql.ts function isGraphQlMock(mock) { return mock.method === "GRAPHQL"; } function getGraphQlMocks(mocks) { const graphQlMocksByPathAndOperations = {}; mocks.filter(isGraphQlMock).forEach(({ path: path2, operations }) => { const operationsByTypeAndName = graphQlMocksByPathAndOperations[path2] ? graphQlMocksByPathAndOperations[path2] : {}; operations.forEach((operation) => { operationsByTypeAndName[`${operation.type}${operation.name}`] = operation; }); graphQlMocksByPathAndOperations[path2] = operationsByTypeAndName; }); return Object.entries(graphQlMocksByPathAndOperations).map( ([path2, operationsByTypeAndName]) => ({ method: "GRAPHQL", path: path2, operations: Object.values(operationsByTypeAndName) }) ); } var bodySchema = import_zod2.default.object({ query: import_zod2.default.string().optional(), operationName: import_zod2.default.string().optional(), variables: import_zod2.default.object({}).passthrough().optional() }).default({}).catch({}); function getGraphQlMock(path2, graphqlMocks) { return graphqlMocks.find((graphQlMock) => graphQlMock.path === path2) || null; } function isOperationDefinition(definition) { return definition.kind === import_graphql.Kind.OPERATION_DEFINITION; } async function graphQlRequestHandler({ req, graphQlMock, updateContext: updateContext2, getContext }) { var _a; if (!["GET", "POST"].includes(req.method)) { return { status: 404 }; } let query; const body = bodySchema.parse(req.body); if (req.headers["content-type"] === "application/graphql") { query = typeof req.body === "string" ? req.body : ""; } else { query = body.query || (Array.isArray(req.query.query) ? "" : req.query.query) || ""; } let graphqlAst; try { graphqlAst = (0, import_graphql_tag.default)(query); } catch (error) { return { status: 400, headers: { "Content-Type": "application/json" }, data: { message: `query "${query}" is not a valid GraphQL query` } }; } const operationDefitions = graphqlAst.definitions.filter( isOperationDefinition ); if (operationDefitions.length > 1 && !body.operationName && !req.query.operationName) { return { status: 400, headers: { "Content-Type": "application/json" }, data: { message: `operationName required` } }; } const operationName = body.operationName || req.query.operationName || ((_a = operationDefitions[0].name) == null ? void 0 : _a.value); if (typeof operationName !== "string") { return { status: 400, headers: { "Content-Type": "application/json" }, data: { message: `Operation name required` } }; } const operationDefinition = operationDefitions.find( (definition) => { var _a2; return ((_a2 = definition.name) == null ? void 0 : _a2.value) === operationName; } ); if (!operationDefinition) { return { status: 400, headers: { "Content-Type": "application/json" }, data: { message: `Operation "${operationName}" could not be found` } }; } const operationType = operationDefinition.operation; if (operationType === "subscription") { return { status: 400, headers: { "Content-Type": "application/json" }, data: { message: `Subscriptions are not supported` } }; } if (operationType === "mutation" && req.method === "GET") { return { status: 400, headers: { "Content-Type": "application/json" }, data: { message: `Mutations cannot be resolved over GET` } }; } let variables = body.variables; if (variables === void 0 && req.query.variables && !Array.isArray(req.query.variables)) { try { variables = JSON.parse(req.query.variables); } catch (e) { } } variables = variables || {}; const operation = graphQlMock.operations.find( ({ type, name }) => operationType === type && name === operationName ); if (!operation) { return { status: 404 }; } const handler = createHandler({ getContext, updateContext: updateContext2, response: operation.response }); return handler({ variables, headers: req.headers }); } // src/http.ts var import_path_to_regexp = require("path-to-regexp"); function isHttpMock(mock) { return mock.method !== "GRAPHQL"; } function getHttpMocks(mocks) { const httpMocksByPathAndMethod = {}; mocks.filter(isHttpMock).forEach((mock) => { const { path: path2, method } = mock; httpMocksByPathAndMethod[`${String(path2)}${method}`] = mock; }); return Object.values(httpMocksByPathAndMethod); } function httpRequestHandler({ req, httpMock, params, updateContext: updateContext2, getContext }) { const _a = req, { body } = _a, restOfReq = __objRest(_a, ["body"]); const handler = createHandler(__spreadProps(__spreadValues({}, httpMock), { getContext, updateContext: updateContext2 })); return handler(__spreadProps(__spreadValues({}, restOfReq), { params, body: typeof body === "string" ? {} : body })); } function getHttpMockAndParams(req, httpMocks) { for (const httpMock of httpMocks) { if (httpMock.method !== req.method) { continue; } const { match, params } = getMatchAndParams(req.path, httpMock.path); if (match) { return { httpMock, params }; } } return { httpMock: null, params: {} }; } function getMatchAndParams(reqPath, mockUrl) { const params = {}; const keys = []; const regex = (0, import_path_to_regexp.pathToRegexp)(mockUrl, keys); const match = regex.exec(reqPath); if (!match) { return { match: false, params }; } for (let i = 1; i < match.length; i++) { const key = keys[i - 1]; const prop = key.name; params[prop] = decodeURIComponent(match[i]); } return { match: true, params }; } // src/handle-request.ts var HEADER_SCENARIO_ID = "sms-scenario-id"; var HEADER_CONTEXT_ID = "sms-context-id"; function updateContext(context, partialContext) { const newContext = __spreadValues(__spreadValues({}, context), typeof partialContext === "function" ? partialContext(context) : partialContext); return newContext; } function getMocksFromScenario(scenario, scenarioMap) { const scenarioIds = getScenarioIds([], scenario, scenarioMap); const mocks = scenarioIds.map((scenarioId) => scenarioMap[scenarioId].mocks).reduce((result, mocks2) => result.concat(mocks2), []); const httpMocks = getHttpMocks(mocks); const graphQlMocks = getGraphQlMocks(mocks); return { httpMocks, graphQlMocks }; } async function handleRequest({ req, getServerScenarioId, initialScenarioId, initialContext, scenarioMap, getServerContext, setServerContext, getCookie, cookieMode, setCookie, contextCache }) { const headerScenarioId = req.headers[HEADER_SCENARIO_ID]; const headerContextId = req.headers[HEADER_CONTEXT_ID] || (0, import_crypto.randomUUID)(); if (headerScenarioId && cookieMode) { return { status: 400, data: { message: `Cannot use "${HEADER_SCENARIO_ID}" header when cookie mode is enabled` } }; } const scenarioMockServerCookie = getScenarioMockServerCookie({ initialContext, initialScenarioId, getCookie }); const getScenarioId2 = headerScenarioId ? () => headerScenarioId : cookieMode ? () => scenarioMockServerCookie.scenarioId : getServerScenarioId; const getContext = headerScenarioId ? () => { const context = contextCache.get(headerContextId); if (!context) { return getContextFromScenario( scenarioMap[headerScenarioId], scenarioMap ); } return context; } : cookieMode ? () => scenarioMockServerCookie.context : getServerContext; const setContext = req.headers[HEADER_SCENARIO_ID] ? (context) => { contextCache.set(headerContextId, context); } : cookieMode ? (context) => { scenarioMockServerCookie.context = context; } : setServerContext; const scenarioId = getScenarioId2(); const scenario = scenarioMap[scenarioId]; const { httpMocks, graphQlMocks } = getMocksFromScenario( scenario, scenarioMap ); const graphQlMock = getGraphQlMock(req.path, graphQlMocks); let result = { status: 404 }; if (graphQlMock) { result = await graphQlRequestHandler({ req, graphQlMock, updateContext: localUpdateContext, getContext }); } else { const { httpMock, params } = getHttpMockAndParams(req, httpMocks); if (httpMock) { result = await httpRequestHandler({ req, httpMock, params, getContext, updateContext: localUpdateContext }); } } if (cookieMode) { setScenarioMockServerCookie({ setCookie, value: scenarioMockServerCookie }); } return result; function localUpdateContext(partialContext) { const newContext = updateContext(getContext(), partialContext); setContext(newContext); return newContext; } } // src/utils/lru-cache.ts var LruCache = class { constructor(size) { this._map = /* @__PURE__ */ new Map(); if (size <= 0 || size - Math.floor(size) != 0) { throw new Error("size must be a positive integer"); } this._size = size; } get(id) { if (!this._map.has(id)) { return null; } const value = this._map.get(id); this._map.delete(id); this._map.set(id, value); return value; } set(id, value) { if (this._map.has(id)) { this._map.delete(id); } if (this._map.size === this._size) { const [[id2]] = this._map.entries(); this._map.delete(id2); } this._map.set(id, value); } delete(id) { this._map.delete(id); } clear() { this._map.clear(); } }; // src/express.ts function scenarioHasGroup(scenario) { return !Array.isArray(scenario) && scenario.group != null; } function validateAllGroupsHaveNames({ scenarios, groups }) { const uniqueGroups = Array.from( Object.values(scenarios).filter(scenarioHasGroup).reduce((set, { group }) => { set.add(group); return set; }, /* @__PURE__ */ new Set()).values() ); const groupsWithoutNames = uniqueGroups.filter((group) => !groups[group]); if (groupsWithoutNames.length > 0) { console.warn( `The following groups do not have a name: ${groupsWithoutNames.join( ", " )}` ); } } function createExpressApp({ scenarios: externalScenarioMap, options = {}, groups = {} }) { const { uiPath = "/", selectScenarioPath = "/select-scenario", scenariosPath = "/scenarios", groupsPath = "/groups", cookieMode = false, parallelContextSize = 10 } = options; validateAllGroupsHaveNames({ scenarios: externalScenarioMap, groups }); const { scenarios, scenarioMap } = generateScenarios(externalScenarioMap); const { initialScenarioId, initialContext } = generatInitialValues( scenarios, scenarioMap ); let serverScenarioId = initialScenarioId; let serverContext = initialContext; const contextCache = new LruCache(parallelContextSize); const app = (0, import_express.default)(); app.use((0, import_cors.default)({ credentials: true })); app.use((0, import_cookie_parser.default)()); app.use(uiPath, import_express.default.static(import_node_path.default.join(__dirname, "assets"))); app.use(import_express.default.urlencoded({ extended: false })); app.use(import_express.default.json()); app.use(import_express.default.text({ type: "application/graphql" })); app.get(uiPath, (req, res) => { const html = getUi({ uiPath, cookieMode, initialScenarioId, initialContext, getCookie: expressGetCookie(req), setCookie: expressSetCookie(res), getServerScenarioId, scenarios, groups }); res.send(html); }); app.post(uiPath, ({ body }, res) => { const scenarioId = import_zod3.default.string().parse(body.scenarioId); const html = updateUi({ uiPath, initialScenarioId, scenarioId, scenarioMap, scenarios, cookieMode, setCookie: expressSetCookie(res), setServerContext, setServerScenarioId, groups }); res.send(html); }); app.put(selectScenarioPath, ({ body }, res) => { const scenarioId = import_zod3.default.string().parse(body.scenarioId); const result = selectScenario({ scenarioId, cookieMode, scenarioMap, setCookie: expressSetCookie(res), setServerContext, setServerScenarioId }); expressResponse(res, result); }); app.get(scenariosPath, (req, res) => { const result = getScenarios({ getCookie: expressGetCookie(req), setCookie: expressSetCookie(res), getServerScenarioId, cookieMode, initialScenarioId, initialContext, scenarios }); expressResponse(res, result); }); app.get(groupsPath, (_, res) => { expressResponse(res, { status: 200, headers: { "content-type": "application/json" }, data: Object.entries(groups).map(([id, name]) => ({ id, name })) }); }); app.use(async (req, res) => { const internalRequest = { body: req.body, headers: expressCleanHeaders(req.headers || {}), method: req.method, path: req.path, query: req.query }; try { const result = await handleRequest({ req: internalRequest, getServerScenarioId, initialScenarioId, initialContext, scenarioMap, getServerContext, setServerContext, cookieMode, getCookie: expressGetCookie(req), setCookie: expressSetCookie(res), contextCache }); expressResponse(res, result); } catch (error) { if (error instanceof Error) { expressResponse(res, { status: 500, data: { message: error.message } }); return; } console.error(error); expressResponse(res, { status: 500, data: { message: "Unknown error - check logs" } }); } }); return app; function getServerScenarioId() { return serverScenarioId; } function getServerContext() { return serverContext; } function setServerContext(context) { serverContext = context; } function setServerScenarioId(scenarioId) { serverScenarioId = scenarioId; } } function generateScenarios(externalScenarioMap) { const scenarioMap = {}; const scenarios = []; for (const [id, scenario] of Object.entries(externalScenarioMap)) { let internalScenario = { id, name: id, mocks: [] }; if (Array.isArray(scenario)) { internalScenario.mocks = scenario; } else { internalScenario = __spreadValues(__spreadValues({}, internalScenario), scenario); } scenarioMap[id] = internalScenario; scenarios.push(internalScenario); } if (scenarios.length === 0) { throw new Error("No scenarios defined"); } return { scenarios, scenarioMap }; } function generatInitialValues(scenarios, scenarioMap) { const initialScenario = scenarios[0]; const initialScenarioId = initialScenario.id; const initialContext = getContextFromScenario(initialScenario, scenarioMap); return { initialScenarioId, initialContext }; } function expressSetCookie(res) { return (cookieName, cookieValue) => { res.cookie(cookieName, cookieValue, { encode: String }); }; } function expressGetCookie(req) { return (cookieName) => req.cookies[cookieName]; } function expressCleanHeaders(headers) { return Object.fromEntries( Object.entries(headers).filter( (keyValuePair) => typeof keyValuePair[1] === "string" ) ); } function expressResponse(res, { status, headers, data }) { res.set(headers).status(status).send( headers && headers["content-type"] === "application/json" ? JSON.stringify(data) : data ); } // src/run.ts function run({ scenarios, options = {}, groups = {} }) { const _a = options, { port = 3e3 } = _a, restOfOptions = __objRest(_a, ["port"]); const app = createExpressApp({ scenarios, options: restOfOptions, groups }); return (0, import_server_with_kill.transform)( app.listen(port, () => { console.log(`Server running on port ${port}`); }) ); } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { createExpressApp, run });