UNPKG

@nahkies/typescript-express-runtime

Version:

Runtime package for code generated by @nahkies/openapi-code-generator using the typescript-express template

1 lines 15.8 kB
{"version":3,"file":"server.mjs","names":[],"sources":["../../../typescript-common-runtime/dist/esm/types.mjs","../../../typescript-common-runtime/dist/esm/query-parser.mjs","../../../typescript-common-runtime/dist/esm/request-bodies/octet-stream.mjs","../../src/server.ts"],"sourcesContent":["//#region src/types.ts\nconst SkipResponse = Symbol.for(\"skip response processing\");\nfunction isNonEmptyArray(it) {\n\treturn Array.isArray(it) && it.length > 0;\n}\n//#endregion\nexport { SkipResponse, isNonEmptyArray };\n\n//# sourceMappingURL=types.mjs.map","//#region src/query-parser.ts\nconst separators = {\n\tdeepObject: \",\",\n\tform: \",\",\n\tpipeDelimited: \"|\",\n\tspaceDelimited: \" \"\n};\nfunction parseCsvPairsToObject(str, sep) {\n\tconst split = str.split(sep);\n\tconst result = {};\n\tfor (let i = 0; i < split.length; i += 2) {\n\t\tconst key = split[i];\n\t\tconst value = split[i + 1];\n\t\tif (key) result[key] = value;\n\t}\n\treturn result;\n}\nfunction escapeStringForRegExp(str) {\n\treturn str.replace(/[|\\\\{}()[\\]^$+*?.]/g, \"\\\\$&\").replace(/-/g, \"\\\\x2d\");\n}\n/**\n* Returns an ordered list of keys that match the provided key with array notation. E.g.,\n* For key \"foo\" return [\"foo[0]\", \"foo[1]\", \"foo[2]\", ...]\n*/\nfunction extractArrayNotationKeys(key, keys) {\n\tconst regex = new RegExp(`^${escapeStringForRegExp(key)}\\\\[[0-9]+]$`);\n\treturn Array.from(keys).filter((it) => regex.test(it)).sort();\n}\nfunction parseObjectQueryParameter(key, schema, encoding, query) {\n\tconst type = schema.type;\n\tif (encoding.explode) if (encoding.style === \"deepObject\") {\n\t\tif (type === \"object\") {\n\t\t\tconst result = {};\n\t\t\tfor (const [subKey, subSchema] of Object.entries(schema.properties)) if (subSchema.type === \"object\") result[subKey] = parseObjectQueryParameter(`${key}[${subKey}]`, subSchema, encoding, query);\n\t\t\telse result[subKey] = query.get(`${key}[${subKey}]`) ?? void 0;\n\t\t\treturn result;\n\t\t}\n\t\tthrow new Error(`[expected to be unreachable] parseObjectQueryParameter: unsupported parameter type: ${type} for explode: ${encoding.explode}, style: ${encoding.style}`);\n\t} else {\n\t\tif (type === \"object\") {\n\t\t\tconst result = {};\n\t\t\tfor (const it of Object.keys(schema.properties)) result[it] = query.get(it) ?? void 0;\n\t\t\treturn result;\n\t\t}\n\t\tthrow new Error(`[expected to be unreachable] parseObjectQueryParameter: unsupported parameter type: ${type} for explode: ${encoding.explode}, style: ${encoding.style}`);\n\t}\n\telse if ([\n\t\t\"form\",\n\t\t\"spaceDelimited\",\n\t\t\"pipeDelimited\"\n\t].includes(encoding.style)) {\n\t\tconst sep = separators[encoding.style];\n\t\treturn parseCsvPairsToObject(query.get(key) ?? \"\", sep);\n\t} else return JSON.parse(query.get(key) ?? \"null\");\n}\nfunction parseArrayQueryParameter(key, _schema, encoding, query) {\n\tif (encoding.explode) {\n\t\tif (encoding.style === \"deepObject\") return extractArrayNotationKeys(key, query.keys()).map((it) => query.get(it) ?? void 0);\n\t\treturn query.getAll(key);\n\t} else return query.get(key)?.split(separators[encoding.style]);\n}\nfunction parsePrimitiveQueryParameter(key, query) {\n\treturn query.get(key) ?? void 0;\n}\nfunction parseQueryParameter({ name, schema, explode, style }, query) {\n\tconst encoding = {\n\t\tstyle: style ?? \"form\",\n\t\texplode: explode ?? true\n\t};\n\tconst type = schema.type;\n\tswitch (type) {\n\t\tcase \"object\": return parseObjectQueryParameter(name, schema, encoding, query);\n\t\tcase \"array\": return parseArrayQueryParameter(name, schema, encoding, query);\n\t\tcase \"string\":\n\t\tcase \"number\":\n\t\tcase \"boolean\":\n\t\tcase \"null\": return parsePrimitiveQueryParameter(name, query);\n\t\tdefault: throw new Error(`unsupported parameter type: ${type}`);\n\t}\n}\nfunction parseQueryParameters(rawQuery, parameters) {\n\tconst query = new URLSearchParams(rawQuery);\n\tconst result = {};\n\tfor (const parameter of parameters) result[parameter.name] = parseQueryParameter(parameter, query);\n\treturn result;\n}\n//#endregion\nexport { extractArrayNotationKeys, parseCsvPairsToObject, parseQueryParameter, parseQueryParameters };\n\n//# sourceMappingURL=query-parser.mjs.map","import getRawBody from \"raw-body\";\n//#region src/request-bodies/octet-stream.ts\nasync function parseOctetStreamRequestBody(req, opts) {\n\tconst contentLength = opts.contentLength ?? (req.headers[\"content-length\"] ? parseInt(req.headers[\"content-length\"], 10) : void 0);\n\tif (!contentLength) throw new Error(\"No content length provided\");\n\tconst body = await getRawBody(req, {\n\t\tlength: contentLength,\n\t\tlimit: opts.sizeLimit\n\t});\n\tif (!body) return;\n\tif (!Buffer.isBuffer(body)) throw new Error(\"body must be a buffer\");\n\treturn new Blob([new Uint8Array(body)], { type: \"application/octet-stream\" });\n}\n//#endregion\nexport { parseOctetStreamRequestBody };\n\n//# sourceMappingURL=octet-stream.mjs.map","import type {Server} from \"node:http\"\nimport type {AddressInfo, ListenOptions} from \"node:net\"\nimport {\n type Res,\n SkipResponse,\n type StatusCode,\n} from \"@nahkies/typescript-common-runtime/types\"\nimport type {OptionsJson, OptionsText, OptionsUrlencoded} from \"body-parser\"\nimport Cors, {type CorsOptions, type CorsOptionsDelegate} from \"cors\"\nimport type {Response} from \"express\"\nimport express, {\n type ErrorRequestHandler,\n type Express,\n type Response as ExpressResponse,\n type Request,\n type RequestHandler,\n type Router,\n} from \"express\"\nimport {ExpressRuntimeError} from \"./errors.ts\"\n\nexport {parseQueryParameters} from \"@nahkies/typescript-common-runtime/query-parser\"\n\nimport {\n parseOctetStreamRequestBody,\n type SizeLimit,\n} from \"@nahkies/typescript-common-runtime/request-bodies/octet-stream\"\n\nexport {\n type Params,\n type Res,\n SkipResponse,\n type StatusCode,\n type StatusCode1xx,\n type StatusCode2xx,\n type StatusCode3xx,\n type StatusCode4xx,\n type StatusCode5xx,\n} from \"@nahkies/typescript-common-runtime/types\"\n\n// biome-ignore lint/suspicious/noExplicitAny: needed\nexport type ResponseValidator = (status: number, value: unknown) => any\n\nexport class ExpressRuntimeResponse<Type> {\n private _body?: Type\n\n constructor(private readonly status: StatusCode) {}\n\n body(body: Type): this {\n this._body = body\n return this\n }\n\n unpack(): Res<StatusCode, Type | undefined> {\n return {status: this.status, body: this._body}\n }\n}\n\nexport function handleResponse(\n res: Response,\n validator: (status: number, value: unknown) => unknown,\n) {\n return async (\n response:\n | ExpressRuntimeResponse<unknown>\n | typeof SkipResponse\n | Res<StatusCode, unknown>,\n ): Promise<void> => {\n // escape hatch to allow responses to be sent by the implementation handler\n if (response === SkipResponse) {\n return\n }\n\n const {status, body} =\n response instanceof ExpressRuntimeResponse ? response.unpack() : response\n\n res.status(status)\n\n if (body === undefined) {\n res.end()\n return\n }\n\n if (body instanceof Blob) {\n await sendBlob(res, body)\n } else {\n res.json(validator(status, body))\n }\n }\n}\n\nexport function handleImplementationError(err: unknown): never {\n throw ExpressRuntimeError.HandlerError(err)\n}\n\nexport type ExpressRuntimeResponder<\n Status extends StatusCode = StatusCode,\n // biome-ignore lint/suspicious/noExplicitAny: needed\n Type = any,\n> = {\n withStatus: (status: Status) => ExpressRuntimeResponse<Type>\n}\n\nexport type ServerConfig = {\n /**\n * set to \"disabled\" to disable cors middleware, omit or pass undefined for defaults\n *\n * by default, all origins are allowed. you probably don't want this in production,\n * so it's strongly recommended to explicitly configure this.\n **/\n cors?: \"disabled\" | CorsOptions | CorsOptionsDelegate | undefined\n\n /**\n * set to \"disabled\" to disable body parsing middleware, omit or pass undefined for defaults.\n *\n * if disabling, ensure you pass a body parsing middleware that places the parsed\n * body on `req.body` for request body processing to work.\n **/\n body?:\n | \"disabled\"\n | Partial<{\n json: OptionsJson\n text: OptionsText\n urlencoded: OptionsUrlencoded\n }>\n | undefined\n\n /**\n * provide arbitrary express middleware to be mounted before all request handlers\n * useful for mounting logging, alternative body parsers, etc\n */\n middleware?: RequestHandler[]\n\n /**\n * Provide a custom 404 handler\n */\n notFoundHandler?: RequestHandler\n\n /**\n * Provide a custom error handler\n */\n errorHandler?: ErrorRequestHandler\n\n /**\n * the router to use, normally obtained by calling the generated `createRouter`\n * function\n */\n router: Router\n\n /**\n * the port to listen on, a randomly allocated port will be used if none passed\n * alternatively ListenOptions can be passed to control the network interface\n * bound to.\n */\n port?: number | ListenOptions\n}\n\n/**\n * Starts an Express server and listens on `port` or a randomly allocated port if none provided.\n * Enables CORS and body parsing by default. It's recommended to customize the CORS options\n * for production usage.\n *\n * If you need more control over your Express server you should avoid calling this function,\n * and instead mount the router from your generated codes `createRouter` call directly\n * onto a server you have constructed.\n */\nexport async function startServer({\n middleware = [],\n cors = undefined,\n body = undefined,\n port = 0,\n router,\n notFoundHandler,\n errorHandler,\n}: ServerConfig): Promise<{\n app: Express\n server: Server\n address: AddressInfo\n}> {\n const app = express()\n\n if (cors !== \"disabled\") {\n app.use(Cors(cors))\n app.options(\"*route\", Cors(cors))\n }\n\n if (body !== \"disabled\") {\n app.use(express.json(body?.json))\n app.use(express.text(body?.text))\n app.use(express.urlencoded(body?.urlencoded ?? {extended: true}))\n }\n\n if (middleware) {\n for (const it of middleware) {\n app.use(it)\n }\n }\n\n app.use(router)\n\n if (notFoundHandler) {\n app.use(notFoundHandler)\n }\n\n if (errorHandler) {\n app.use(errorHandler)\n }\n\n return new Promise((resolve, reject) => {\n try {\n const server = app.listen(port)\n\n server.once(\"listening\", () => {\n try {\n const address = server.address()\n\n if (!address || typeof address !== \"object\") {\n throw new Error(\"failed to bind port\")\n }\n\n resolve({app, server, address})\n } catch (err) {\n reject(err)\n }\n })\n\n server.once(\"error\", (err) => {\n reject(err)\n })\n } catch (err) {\n reject(err)\n }\n })\n}\n\nexport async function parseOctetStream(\n req: Request,\n sizeLimit: SizeLimit,\n): Promise<Blob | undefined> {\n return parseOctetStreamRequestBody(req, {sizeLimit})\n}\n\nexport async function sendBlob(res: ExpressResponse, body: Blob) {\n const arrayBuffer = await body.arrayBuffer()\n const buffer = Buffer.from(arrayBuffer)\n\n res.setHeader(\"Content-Type\", body.type ?? \"application/octet-stream\")\n res.setHeader(\"Content-Length\", buffer.length)\n\n res.send(buffer)\n}\n"],"mappings":"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"}