@nahkies/typescript-koa-runtime
Version:
Runtime package for code generated by @nahkies/openapi-code-generator using the typescript-koa template
186 lines (185 loc) • 6.45 kB
JavaScript
import { t as KoaRuntimeError } from "./errors-CrlMSPkU.mjs";
import Cors from "@koa/cors";
import getRawBody from "raw-body";
import Koa from "koa";
import { koaBody } from "koa-body";
//#region ../typescript-common-runtime/dist/esm/request-bodies/octet-stream.mjs
async function parseOctetStreamRequestBody(req, opts) {
const contentLength = opts.contentLength ?? (req.headers["content-length"] ? parseInt(req.headers["content-length"], 10) : void 0);
if (!contentLength) throw new Error("No content length provided");
const body = await getRawBody(req, {
length: contentLength,
limit: opts.sizeLimit
});
if (!body) return;
if (!Buffer.isBuffer(body)) throw new Error("body must be a buffer");
return new Blob([new Uint8Array(body)], { type: "application/octet-stream" });
}
//#endregion
//#region ../typescript-common-runtime/dist/esm/types.mjs
const SkipResponse = Symbol.for("skip response processing");
//#endregion
//#region ../typescript-common-runtime/dist/esm/query-parser.mjs
const separators = {
deepObject: ",",
form: ",",
pipeDelimited: "|",
spaceDelimited: " "
};
function parseCsvPairsToObject(str, sep) {
const split = str.split(sep);
const result = {};
for (let i = 0; i < split.length; i += 2) {
const key = split[i];
const value = split[i + 1];
if (key) result[key] = value;
}
return result;
}
function escapeStringForRegExp(str) {
return str.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&").replace(/-/g, "\\x2d");
}
/**
* Returns an ordered list of keys that match the provided key with array notation. E.g.,
* For key "foo" return ["foo[0]", "foo[1]", "foo[2]", ...]
*/
function extractArrayNotationKeys(key, keys) {
const regex = new RegExp(`^${escapeStringForRegExp(key)}\\[[0-9]+]$`);
return Array.from(keys).filter((it) => regex.test(it)).sort();
}
function parseObjectQueryParameter(key, schema, encoding, query) {
const type = schema.type;
if (encoding.explode) if (encoding.style === "deepObject") {
if (type === "object") {
const result = {};
for (const [subKey, subSchema] of Object.entries(schema.properties)) if (subSchema.type === "object") result[subKey] = parseObjectQueryParameter(`${key}[${subKey}]`, subSchema, encoding, query);
else result[subKey] = query.get(`${key}[${subKey}]`) ?? void 0;
return result;
}
throw new Error(`[expected to be unreachable] parseObjectQueryParameter: unsupported parameter type: ${type} for explode: ${encoding.explode}, style: ${encoding.style}`);
} else {
if (type === "object") {
const result = {};
for (const it of Object.keys(schema.properties)) result[it] = query.get(it) ?? void 0;
return result;
}
throw new Error(`[expected to be unreachable] parseObjectQueryParameter: unsupported parameter type: ${type} for explode: ${encoding.explode}, style: ${encoding.style}`);
}
else if ([
"form",
"spaceDelimited",
"pipeDelimited"
].includes(encoding.style)) {
const sep = separators[encoding.style];
return parseCsvPairsToObject(query.get(key) ?? "", sep);
} else return JSON.parse(query.get(key) ?? "null");
}
function parseArrayQueryParameter(key, _schema, encoding, query) {
if (encoding.explode) {
if (encoding.style === "deepObject") return extractArrayNotationKeys(key, query.keys()).map((it) => query.get(it) ?? void 0);
return query.getAll(key);
} else return query.get(key)?.split(separators[encoding.style]);
}
function parsePrimitiveQueryParameter(key, query) {
return query.get(key) ?? void 0;
}
function parseQueryParameter({ name, schema, explode, style }, query) {
const encoding = {
style: style ?? "form",
explode: explode ?? true
};
const type = schema.type;
switch (type) {
case "object": return parseObjectQueryParameter(name, schema, encoding, query);
case "array": return parseArrayQueryParameter(name, schema, encoding, query);
case "string":
case "number":
case "boolean":
case "null": return parsePrimitiveQueryParameter(name, query);
default: throw new Error(`unsupported parameter type: ${type}`);
}
}
function parseQueryParameters(rawQuery, parameters) {
const query = new URLSearchParams(rawQuery);
const result = {};
for (const parameter of parameters) result[parameter.name] = parseQueryParameter(parameter, query);
return result;
}
//#endregion
//#region src/server.ts
var KoaRuntimeResponse = class {
status;
_body;
constructor(status) {
this.status = status;
}
body(body) {
this._body = body;
return this;
}
unpack() {
return {
status: this.status,
body: this._body
};
}
};
function handleResponse(ctx, validator) {
return async (response) => {
if (response === SkipResponse) return;
const { status, body } = response instanceof KoaRuntimeResponse ? response.unpack() : response;
ctx.body = validator(status, body);
ctx.status = status;
};
}
function handleImplementationError(err) {
throw KoaRuntimeError.HandlerError(err);
}
async function parseOctetStream(ctx, sizeLimit) {
const body = await parseOctetStreamRequestBody(ctx.req, { sizeLimit });
ctx.body = body;
return body;
}
/**
* Starts a Koa server and listens on `port` or a randomly allocated port if none provided.
* Enables CORS and body parsing by default. It's recommended to customize the CORS options
* for production usage.
*
* If you need more control over your Koa server, you should avoid calling this function
* and instead mount the router from your generated codes `createRouter` call directly
* onto a server you have constructed.
*/
async function startServer({ middleware = [], cors = void 0, body = void 0, port = 0, router }) {
const app = new Koa();
if (cors !== "disabled") app.use(Cors(cors));
if (body !== "disabled") app.use(koaBody(body));
for (const it of middleware) app.use(it);
app.use(router.routes());
app.use(router.allowedMethods());
return new Promise((resolve, reject) => {
try {
const server = app.listen(port);
server.once("listening", () => {
try {
const address = server.address();
if (!address || typeof address !== "object") throw new Error("failed to bind port");
resolve({
app,
server,
address
});
} catch (err) {
reject(err);
}
});
server.once("error", (err) => {
reject(err);
});
} catch (err) {
reject(err);
}
});
}
//#endregion
export { KoaRuntimeResponse, SkipResponse, handleImplementationError, handleResponse, parseOctetStream, parseQueryParameters, startServer };
//# sourceMappingURL=server.mjs.map