@contractkit/plugin-typescript 0.34.1 → 0.36.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build$colon$ci.log +5 -5
- package/.turbo/turbo-test$colon$ci.log +25 -18
- package/CHANGELOG.md +56 -0
- package/README.md +4 -3
- package/dist/codegen-mcp.d.ts +8 -1
- package/dist/codegen-mcp.d.ts.map +1 -1
- package/dist/codegen-operation.d.ts +13 -5
- package/dist/codegen-operation.d.ts.map +1 -1
- package/dist/index.d.ts +12 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +370 -91
- package/dist/index.js.map +1 -1
- package/dist/server-framework-fastify.d.ts +8 -0
- package/dist/server-framework-fastify.d.ts.map +1 -0
- package/dist/server-framework-koa.d.ts +7 -0
- package/dist/server-framework-koa.d.ts.map +1 -0
- package/dist/server-framework.d.ts +101 -0
- package/dist/server-framework.d.ts.map +1 -0
- package/llms.txt +1 -1
- package/package.json +2 -2
- package/src/codegen-mcp.ts +11 -25
- package/src/codegen-operation.ts +146 -77
- package/src/index.ts +37 -5
- package/src/server-framework-fastify.ts +128 -0
- package/src/server-framework-koa.ts +114 -0
- package/src/server-framework.ts +129 -0
- package/tests/codegen-mcp.test.ts +12 -0
- package/tests/codegen-operation-framework.test.ts +184 -0
- package/tests/codegen-operation.test.ts +74 -0
- package/tests/codegen-server.test.ts +56 -0
- package/tests/server-framework-fastify.test.ts +148 -0
- package/tests/server-framework-koa.test.ts +115 -0
- package/tests/server-framework.test.ts +25 -0
package/dist/index.js
CHANGED
|
@@ -1453,6 +1453,138 @@ __name(pascalToDotCase, "pascalToDotCase");
|
|
|
1453
1453
|
// src/codegen-operation.ts
|
|
1454
1454
|
import { resolveModifiers, resolveSecurity, SECURITY_NONE, classifyContentType, emittedResponses, PATH_PARAM_RE_G, toIdentifier } from "@contractkit/core";
|
|
1455
1455
|
import { basename, dirname as dirname3, relative as relative3 } from "path";
|
|
1456
|
+
|
|
1457
|
+
// src/server-framework-koa.ts
|
|
1458
|
+
var KOA_RUNTIME_MODULE = "@maroonedsoftware/koa";
|
|
1459
|
+
var KOA_RUNTIME_SYMBOLS = [
|
|
1460
|
+
"ServerKitRouter",
|
|
1461
|
+
"bodyParserMiddleware",
|
|
1462
|
+
"requirePolicy",
|
|
1463
|
+
"requireSignature"
|
|
1464
|
+
];
|
|
1465
|
+
var KOA_SERVER_FRAMEWORK = {
|
|
1466
|
+
name: "koa",
|
|
1467
|
+
imports(uses) {
|
|
1468
|
+
const symbols = KOA_RUNTIME_SYMBOLS.filter(uses);
|
|
1469
|
+
return symbols.length > 0 ? [
|
|
1470
|
+
`import { ${symbols.join(", ")} } from '${KOA_RUNTIME_MODULE}';`
|
|
1471
|
+
] : [];
|
|
1472
|
+
},
|
|
1473
|
+
routerDeclaration(routerName) {
|
|
1474
|
+
return `export const ${routerName} = ServerKitRouter();`;
|
|
1475
|
+
},
|
|
1476
|
+
pathParam(identifier) {
|
|
1477
|
+
return `:${identifier}`;
|
|
1478
|
+
},
|
|
1479
|
+
handlerLocals: [
|
|
1480
|
+
"ctx"
|
|
1481
|
+
],
|
|
1482
|
+
routeOpen(routerName, method, path, middlewares) {
|
|
1483
|
+
const middlewareStr = middlewares.length > 0 ? `, ${middlewares.join(", ")},` : ",";
|
|
1484
|
+
return `${routerName}.${method}('${path}'${middlewareStr} async ctx => {`;
|
|
1485
|
+
},
|
|
1486
|
+
routeClose() {
|
|
1487
|
+
return [
|
|
1488
|
+
"});"
|
|
1489
|
+
];
|
|
1490
|
+
},
|
|
1491
|
+
middleware: {
|
|
1492
|
+
policy(args) {
|
|
1493
|
+
return `requirePolicy(${args})`;
|
|
1494
|
+
},
|
|
1495
|
+
bodyParser(tokensExpr) {
|
|
1496
|
+
return `bodyParserMiddleware([${tokensExpr}])`;
|
|
1497
|
+
},
|
|
1498
|
+
signature(args) {
|
|
1499
|
+
return `requireSignature(${args})`;
|
|
1500
|
+
}
|
|
1501
|
+
},
|
|
1502
|
+
request: {
|
|
1503
|
+
params: "ctx.params",
|
|
1504
|
+
query: "ctx.query",
|
|
1505
|
+
headers: "ctx.headers",
|
|
1506
|
+
// Not `ctx.request.body`: the ServerKit body parser drains the stream and writes its result
|
|
1507
|
+
// here, and in Koa `ctx.body` is the *response* body.
|
|
1508
|
+
parsedBody: "ctx.parsedBody",
|
|
1509
|
+
// Koa strips the parameters off `Content-Type` for this accessor already.
|
|
1510
|
+
contentType: "ctx.request.type"
|
|
1511
|
+
},
|
|
1512
|
+
resolveService(className) {
|
|
1513
|
+
return `ctx.container.get(${className})`;
|
|
1514
|
+
},
|
|
1515
|
+
response: {
|
|
1516
|
+
status(expr) {
|
|
1517
|
+
return `ctx.status = ${expr};`;
|
|
1518
|
+
},
|
|
1519
|
+
header(name, valueExpr) {
|
|
1520
|
+
return `ctx.set('${name}', ${valueExpr});`;
|
|
1521
|
+
},
|
|
1522
|
+
type(expr) {
|
|
1523
|
+
return `ctx.type = ${expr};`;
|
|
1524
|
+
},
|
|
1525
|
+
send(bodyExpr) {
|
|
1526
|
+
return bodyExpr === void 0 ? [] : [
|
|
1527
|
+
`ctx.body = ${bodyExpr};`
|
|
1528
|
+
];
|
|
1529
|
+
},
|
|
1530
|
+
caseEnd() {
|
|
1531
|
+
return [
|
|
1532
|
+
"break;"
|
|
1533
|
+
];
|
|
1534
|
+
}
|
|
1535
|
+
},
|
|
1536
|
+
mcpRouter({ path }) {
|
|
1537
|
+
return `import { ServerKitRouter, bodyParserMiddleware, requireSignature } from '${KOA_RUNTIME_MODULE}';
|
|
1538
|
+
import { McpDispatcher, createMcpRequestContext, MCP_AUTH_POLICY } from '@maroonedsoftware/mcp';
|
|
1539
|
+
|
|
1540
|
+
/** Mount the MCP endpoint onto a ServerKit router. Bind \`registerMcpTools\` to the \`McpToolHandlerMap\` token. */
|
|
1541
|
+
export function mountMcp(router: ReturnType<typeof ServerKitRouter>): void {
|
|
1542
|
+
router.post('${path}', bodyParserMiddleware(['json']), requireSignature('mcp', { policy: MCP_AUTH_POLICY }), async (ctx) => {
|
|
1543
|
+
const dispatcher = ctx.container.get(McpDispatcher);
|
|
1544
|
+
const context = createMcpRequestContext({ requestId: ctx.requestId, logger: ctx.logger });
|
|
1545
|
+
if (dispatcher.sessionMode === 'stateful') {
|
|
1546
|
+
ctx.respond = false;
|
|
1547
|
+
await dispatcher.dispatchStateful(
|
|
1548
|
+
{ req: ctx.req, res: ctx.res, body: ctx.parsedBody, sessionId: ctx.get('mcp-session-id') },
|
|
1549
|
+
context,
|
|
1550
|
+
);
|
|
1551
|
+
} else {
|
|
1552
|
+
const response = await dispatcher.dispatch(JSON.parse(String(ctx.rawBody)), context);
|
|
1553
|
+
if (response) ctx.body = response;
|
|
1554
|
+
else ctx.status = 202; // a notification \u2014 nothing to return
|
|
1555
|
+
}
|
|
1556
|
+
});
|
|
1557
|
+
}
|
|
1558
|
+
`;
|
|
1559
|
+
}
|
|
1560
|
+
};
|
|
1561
|
+
|
|
1562
|
+
// src/codegen-operation.ts
|
|
1563
|
+
var GENERATOR_HANDLER_LOCALS = [
|
|
1564
|
+
"service",
|
|
1565
|
+
"result",
|
|
1566
|
+
"body",
|
|
1567
|
+
"multipartBody",
|
|
1568
|
+
"params",
|
|
1569
|
+
"query",
|
|
1570
|
+
"headers"
|
|
1571
|
+
];
|
|
1572
|
+
function bindPathParams(nodes, handlerLocals) {
|
|
1573
|
+
const reserved = /* @__PURE__ */ new Set([
|
|
1574
|
+
...handlerLocals,
|
|
1575
|
+
...GENERATOR_HANDLER_LOCALS
|
|
1576
|
+
]);
|
|
1577
|
+
const taken = /* @__PURE__ */ new Set();
|
|
1578
|
+
const bindings = /* @__PURE__ */ new Map();
|
|
1579
|
+
for (const node of nodes) {
|
|
1580
|
+
let local = toIdentifier(node.name);
|
|
1581
|
+
while (reserved.has(local) || taken.has(local)) local += "_";
|
|
1582
|
+
taken.add(local);
|
|
1583
|
+
bindings.set(node.name, local);
|
|
1584
|
+
}
|
|
1585
|
+
return bindings;
|
|
1586
|
+
}
|
|
1587
|
+
__name(bindPathParams, "bindPathParams");
|
|
1456
1588
|
function bodyParserToken(contentType) {
|
|
1457
1589
|
switch (classifyContentType(contentType)) {
|
|
1458
1590
|
case "urlencoded":
|
|
@@ -1525,6 +1657,11 @@ function bodyTypesStructurallyEqual(a, b) {
|
|
|
1525
1657
|
}
|
|
1526
1658
|
__name(bodyTypesStructurallyEqual, "bodyTypesStructurallyEqual");
|
|
1527
1659
|
function generateOp(root, options = {}) {
|
|
1660
|
+
const resolved = {
|
|
1661
|
+
...options,
|
|
1662
|
+
framework: options.framework ?? KOA_SERVER_FRAMEWORK
|
|
1663
|
+
};
|
|
1664
|
+
const framework = resolved.framework;
|
|
1528
1665
|
const types = collectTypes(root, options.modelsWithInput, options.modelsWithOutput);
|
|
1529
1666
|
const services = collectServices(root);
|
|
1530
1667
|
const routerName = deriveRouterName(root.file);
|
|
@@ -1533,13 +1670,13 @@ function generateOp(root, options = {}) {
|
|
|
1533
1670
|
lines.push("/**");
|
|
1534
1671
|
lines.push(` * generated from ${sourceLink(basename(root.file), options.outPath, root.file)}`);
|
|
1535
1672
|
lines.push("*/");
|
|
1536
|
-
lines.push(
|
|
1673
|
+
lines.push(framework.routerDeclaration(routerName));
|
|
1537
1674
|
lines.push("");
|
|
1538
1675
|
const includeInternal = options.includeInternal ?? true;
|
|
1539
1676
|
for (const route of root.routes) {
|
|
1540
1677
|
for (const op of route.operations) {
|
|
1541
1678
|
if (!includeInternal && resolveModifiers(route, op).includes("internal")) continue;
|
|
1542
|
-
lines.push(...generateHandler(route, op, root,
|
|
1679
|
+
lines.push(...generateHandler(route, op, root, resolved));
|
|
1543
1680
|
lines.push("");
|
|
1544
1681
|
}
|
|
1545
1682
|
}
|
|
@@ -1574,15 +1711,7 @@ function generateOp(root, options = {}) {
|
|
|
1574
1711
|
].join("\n");
|
|
1575
1712
|
const uses = /* @__PURE__ */ __name((symbol) => new RegExp(`\\b${symbol}\\b`).test(generated), "uses");
|
|
1576
1713
|
const body = [];
|
|
1577
|
-
|
|
1578
|
-
"ServerKitRouter",
|
|
1579
|
-
"bodyParserMiddleware",
|
|
1580
|
-
"requirePolicy",
|
|
1581
|
-
"requireSignature"
|
|
1582
|
-
].filter(uses);
|
|
1583
|
-
if (koaImports.length > 0) {
|
|
1584
|
-
body.push(`import { ${koaImports.join(", ")} } from '@maroonedsoftware/koa';`);
|
|
1585
|
-
}
|
|
1714
|
+
body.push(...framework.imports(uses));
|
|
1586
1715
|
for (const svc of services.filter(uses)) {
|
|
1587
1716
|
const modulePath = root.services?.[svc] ?? root.meta[svc] ?? deriveModulePath(svc, options.servicePathTemplate);
|
|
1588
1717
|
body.push(`import { ${svc} } from '${modulePath}';`);
|
|
@@ -1626,6 +1755,7 @@ function generateHandler(route, op, root, options) {
|
|
|
1626
1755
|
const file = root.file;
|
|
1627
1756
|
const outPath = options.outPath;
|
|
1628
1757
|
const modelsWithInput = options.modelsWithInput;
|
|
1758
|
+
const framework = options.framework;
|
|
1629
1759
|
lines.push("/**");
|
|
1630
1760
|
const desc = op.description ?? route.description;
|
|
1631
1761
|
if (desc) {
|
|
@@ -1641,7 +1771,7 @@ function generateHandler(route, op, root, options) {
|
|
|
1641
1771
|
if (mods.includes("deprecated")) lines.push(` * @deprecated`);
|
|
1642
1772
|
lines.push("*/");
|
|
1643
1773
|
const method = op.method;
|
|
1644
|
-
const path = route.path.replace(PATH_PARAM_RE_G, (_m, name) =>
|
|
1774
|
+
const path = route.path.replace(PATH_PARAM_RE_G, (_m, name) => framework.pathParam(toIdentifier(name)));
|
|
1645
1775
|
const bodies = op.request?.bodies ?? [];
|
|
1646
1776
|
const hasBody = bodies.length > 0;
|
|
1647
1777
|
const isSingleMultipart = bodies.length === 1 && bodies[0].contentType === "multipart/form-data";
|
|
@@ -1649,42 +1779,42 @@ function generateHandler(route, op, root, options) {
|
|
|
1649
1779
|
if (effectiveSecurity !== SECURITY_NONE) {
|
|
1650
1780
|
const policy = effectiveSecurity?.policy;
|
|
1651
1781
|
const args = policy === void 0 ? "" : policy === false ? "{ policy: false }" : `{ policy: '${policy}' }`;
|
|
1652
|
-
middlewares.push(
|
|
1782
|
+
middlewares.push(framework.middleware.policy(args));
|
|
1653
1783
|
}
|
|
1654
1784
|
if (hasBody) {
|
|
1655
1785
|
const parserTokens = Array.from(new Set(bodies.map((b) => bodyParserToken(b.contentType))));
|
|
1656
1786
|
const tokensExpr = parserTokens.map((t) => `'${t}'`).join(", ");
|
|
1657
|
-
middlewares.push(
|
|
1787
|
+
middlewares.push(framework.middleware.bodyParser(tokensExpr));
|
|
1658
1788
|
}
|
|
1659
1789
|
if (op.signature) {
|
|
1660
1790
|
const sigArgs = op.signaturePolicy ? `'${escapeSingleQuoted(op.signature)}', { policy: '${escapeSingleQuoted(op.signaturePolicy)}' }` : `'${escapeSingleQuoted(op.signature)}'`;
|
|
1661
|
-
middlewares.push(
|
|
1791
|
+
middlewares.push(framework.middleware.signature(sigArgs));
|
|
1662
1792
|
}
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
lines.push(...generateParamValidation(route.params, "
|
|
1666
|
-
lines.push(...generateParamValidation(op.query, "
|
|
1667
|
-
lines.push(...generateParamValidation(op.headers, "
|
|
1793
|
+
lines.push(framework.routeOpen(deriveRouterName(file), method, path, middlewares));
|
|
1794
|
+
const pathBindings = route.params?.kind === "params" ? bindPathParams(route.params.nodes, framework.handlerLocals) : void 0;
|
|
1795
|
+
lines.push(...generateParamValidation(route.params, "params", framework.request.params, route.paramsMode ?? "strict", "", modelsWithInput, pathBindings));
|
|
1796
|
+
lines.push(...generateParamValidation(op.query, "query", framework.request.query, op.queryMode ?? "strict", "", modelsWithInput));
|
|
1797
|
+
lines.push(...generateParamValidation(op.headers, "headers", framework.request.headers, op.headersMode ?? "strip", "", modelsWithInput));
|
|
1668
1798
|
if (hasBody && op.request) {
|
|
1669
1799
|
if (isSingleMultipart) {
|
|
1670
|
-
lines.push(` const multipartBody =
|
|
1800
|
+
lines.push(` const multipartBody = ${framework.request.parsedBody} as MultipartBody;`);
|
|
1671
1801
|
lines.push("");
|
|
1672
1802
|
} else if (bodies.length === 1) {
|
|
1673
|
-
lines.push(` const body = await parseAndValidate(
|
|
1803
|
+
lines.push(` const body = await parseAndValidate(${framework.request.parsedBody}, ${renderInputType(bodies[0].bodyType, modelsWithInput)});`);
|
|
1674
1804
|
lines.push("");
|
|
1675
1805
|
} else if (bodies.every((b) => bodyTypesStructurallyEqual(b.bodyType, bodies[0].bodyType))) {
|
|
1676
|
-
lines.push(` const body = await parseAndValidate(
|
|
1806
|
+
lines.push(` const body = await parseAndValidate(${framework.request.parsedBody}, ${renderInputType(bodies[0].bodyType, modelsWithInput)});`);
|
|
1677
1807
|
lines.push("");
|
|
1678
1808
|
} else {
|
|
1679
1809
|
const annotation = bodies.map((b) => b.contentType === "multipart/form-data" ? "MultipartBody" : `z.infer<typeof ${renderInputType(b.bodyType, modelsWithInput)}>`).join(" | ");
|
|
1680
1810
|
lines.push(` let body!: ${annotation};`);
|
|
1681
|
-
lines.push(` switch (
|
|
1811
|
+
lines.push(` switch (${framework.request.contentType}) {`);
|
|
1682
1812
|
for (const b of bodies) {
|
|
1683
1813
|
lines.push(` case '${b.contentType}':`);
|
|
1684
1814
|
if (b.contentType === "multipart/form-data") {
|
|
1685
|
-
lines.push(` body =
|
|
1815
|
+
lines.push(` body = ${framework.request.parsedBody} as MultipartBody;`);
|
|
1686
1816
|
} else {
|
|
1687
|
-
lines.push(` body = await parseAndValidate(
|
|
1817
|
+
lines.push(` body = await parseAndValidate(${framework.request.parsedBody}, ${renderInputType(b.bodyType, modelsWithInput)});`);
|
|
1688
1818
|
}
|
|
1689
1819
|
lines.push(` break;`);
|
|
1690
1820
|
}
|
|
@@ -1694,17 +1824,18 @@ function generateHandler(route, op, root, options) {
|
|
|
1694
1824
|
}
|
|
1695
1825
|
const emitted = emittedResponses(op);
|
|
1696
1826
|
const serviceParts = inferService(op, route, file);
|
|
1697
|
-
const call = `await service.${serviceParts.methodName}(${buildArgs(route, op)})`;
|
|
1827
|
+
const call = `await service.${serviceParts.methodName}(${buildArgs(route, op, pathBindings)})`;
|
|
1698
1828
|
if (emitted.length > 1) {
|
|
1699
1829
|
lines.push(...generateMultiStatusResult(emitted, serviceParts.className, call, options));
|
|
1700
1830
|
} else {
|
|
1701
1831
|
lines.push(...generateSingleStatusResult(emitted[0], op, serviceParts.className, call, options));
|
|
1702
1832
|
}
|
|
1703
|
-
lines.push(
|
|
1833
|
+
lines.push(...framework.routeClose());
|
|
1704
1834
|
return lines;
|
|
1705
1835
|
}
|
|
1706
1836
|
__name(generateHandler, "generateHandler");
|
|
1707
1837
|
function generateSingleStatusResult(resp, op, className, call, options) {
|
|
1838
|
+
const framework = options.framework;
|
|
1708
1839
|
const lines = [];
|
|
1709
1840
|
const bodies = resp ? resp.bodies : [];
|
|
1710
1841
|
const respHeaders = resp?.headers ?? [];
|
|
@@ -1715,7 +1846,7 @@ function generateSingleStatusResult(resp, op, className, call, options) {
|
|
|
1715
1846
|
const { annotation, prelude } = formatTypeAnnotation(bodies[0].bodyType, options.modelsWithOutput);
|
|
1716
1847
|
if (prelude) lines.push(` ${prelude}`);
|
|
1717
1848
|
bodySchema = responseBodySchema(bodies[0].bodyType, options, prelude ? "resultType" : void 0);
|
|
1718
|
-
lines.push(` const service =
|
|
1849
|
+
lines.push(` const service = ${framework.resolveService(className)};`);
|
|
1719
1850
|
if (hasRespHeaders) {
|
|
1720
1851
|
lines.push(` const result: { body: ${annotation}; headers: ${headersAnnotation} } = ${call};`);
|
|
1721
1852
|
} else {
|
|
@@ -1729,10 +1860,10 @@ function generateSingleStatusResult(resp, op, className, call, options) {
|
|
|
1729
1860
|
const { members, preludes } = rendered;
|
|
1730
1861
|
bodySchema = rendered.bodySchema;
|
|
1731
1862
|
for (const prelude of preludes) lines.push(` ${prelude}`);
|
|
1732
|
-
lines.push(` const service =
|
|
1863
|
+
lines.push(` const service = ${framework.resolveService(className)};`);
|
|
1733
1864
|
lines.push(` const result: ${members.join(" | ")} = ${call};`);
|
|
1734
1865
|
} else {
|
|
1735
|
-
lines.push(` const service =
|
|
1866
|
+
lines.push(` const service = ${framework.resolveService(className)};`);
|
|
1736
1867
|
if (hasRespHeaders) {
|
|
1737
1868
|
lines.push(` const result: { headers: ${headersAnnotation} } = ${call};`);
|
|
1738
1869
|
} else {
|
|
@@ -1740,19 +1871,22 @@ function generateSingleStatusResult(resp, op, className, call, options) {
|
|
|
1740
1871
|
}
|
|
1741
1872
|
}
|
|
1742
1873
|
lines.push("");
|
|
1743
|
-
lines.push(`
|
|
1744
|
-
lines.push(...headerSetLines(respHeaders, " "));
|
|
1874
|
+
lines.push(` ${framework.response.status(String(resp?.statusCode ?? 204))}`);
|
|
1875
|
+
lines.push(...headerSetLines(respHeaders, " ", framework));
|
|
1745
1876
|
if (bodies.length === 1) {
|
|
1746
|
-
lines.push(`
|
|
1747
|
-
lines.push(
|
|
1877
|
+
lines.push(` ${framework.response.type(`'${bodies[0].contentType}'`)}`);
|
|
1878
|
+
lines.push(...indent(framework.response.send(responseBodyExpr(hasRespHeaders ? "result.body" : "result", bodySchema)), " "));
|
|
1748
1879
|
} else if (bodies.length > 1) {
|
|
1749
|
-
lines.push(`
|
|
1750
|
-
lines.push(
|
|
1880
|
+
lines.push(` ${framework.response.type("result.contentType")}`);
|
|
1881
|
+
lines.push(...indent(framework.response.send(responseBodyExpr("result.body", bodySchema)), " "));
|
|
1882
|
+
} else {
|
|
1883
|
+
lines.push(...indent(framework.response.send(void 0), " "));
|
|
1751
1884
|
}
|
|
1752
1885
|
return lines;
|
|
1753
1886
|
}
|
|
1754
1887
|
__name(generateSingleStatusResult, "generateSingleStatusResult");
|
|
1755
1888
|
function generateMultiStatusResult(emitted, className, call, options) {
|
|
1889
|
+
const framework = options.framework;
|
|
1756
1890
|
const lines = [];
|
|
1757
1891
|
const members = [];
|
|
1758
1892
|
const preludes = [];
|
|
@@ -1767,21 +1901,23 @@ function generateMultiStatusResult(emitted, className, call, options) {
|
|
|
1767
1901
|
bodySchemas.set(resp.statusCode, rendered.bodySchema);
|
|
1768
1902
|
}
|
|
1769
1903
|
for (const prelude of preludes) lines.push(` ${prelude}`);
|
|
1770
|
-
lines.push(` const service =
|
|
1904
|
+
lines.push(` const service = ${framework.resolveService(className)};`);
|
|
1771
1905
|
lines.push(` const result:`);
|
|
1772
1906
|
for (const member of members) lines.push(` | ${member}`);
|
|
1773
1907
|
lines.push(` = ${call};`);
|
|
1774
1908
|
lines.push("");
|
|
1775
|
-
lines.push(`
|
|
1909
|
+
lines.push(` ${framework.response.status("result.status")}`);
|
|
1776
1910
|
lines.push(` switch (result.status) {`);
|
|
1777
1911
|
for (const resp of emitted) {
|
|
1778
1912
|
lines.push(` case ${resp.statusCode}:`);
|
|
1779
|
-
lines.push(...headerSetLines(resp.headers ?? [], " "));
|
|
1913
|
+
lines.push(...headerSetLines(resp.headers ?? [], " ", framework));
|
|
1780
1914
|
if (resp.bodies.length > 0) {
|
|
1781
|
-
lines.push(`
|
|
1782
|
-
lines.push(
|
|
1915
|
+
lines.push(` ${framework.response.type("result.contentType")}`);
|
|
1916
|
+
lines.push(...indent(framework.response.send(responseBodyExpr("result.body", bodySchemas.get(resp.statusCode))), " "));
|
|
1917
|
+
} else {
|
|
1918
|
+
lines.push(...indent(framework.response.send(void 0), " "));
|
|
1783
1919
|
}
|
|
1784
|
-
lines.push(
|
|
1920
|
+
lines.push(...indent(framework.response.caseEnd(), " "));
|
|
1785
1921
|
}
|
|
1786
1922
|
lines.push(` }`);
|
|
1787
1923
|
return lines;
|
|
@@ -1848,13 +1984,18 @@ function renderHeadersAnnotation(headers, modelsWithOutput) {
|
|
|
1848
1984
|
return `{ ${fields.join("; ")} }`;
|
|
1849
1985
|
}
|
|
1850
1986
|
__name(renderHeadersAnnotation, "renderHeadersAnnotation");
|
|
1851
|
-
function headerSetLines(headers,
|
|
1987
|
+
function headerSetLines(headers, pad, framework) {
|
|
1852
1988
|
return headers.map((h) => {
|
|
1853
1989
|
const accessor = `result.headers[${JSON.stringify(headerNameToProperty(h.name))}]`;
|
|
1854
|
-
|
|
1990
|
+
const write = framework.response.header(h.name, `String(${accessor})`);
|
|
1991
|
+
return h.optional ? `${pad}if (${accessor} !== undefined) ${write}` : `${pad}${write}`;
|
|
1855
1992
|
});
|
|
1856
1993
|
}
|
|
1857
1994
|
__name(headerSetLines, "headerSetLines");
|
|
1995
|
+
function indent(lines, pad) {
|
|
1996
|
+
return lines.map((line) => `${pad}${line}`);
|
|
1997
|
+
}
|
|
1998
|
+
__name(indent, "indent");
|
|
1858
1999
|
function inferService(op, route, file) {
|
|
1859
2000
|
if (op.service) {
|
|
1860
2001
|
const [cls = "", method] = op.service.split(".");
|
|
@@ -1890,11 +2031,11 @@ function inferMethodName(method, path) {
|
|
|
1890
2031
|
}
|
|
1891
2032
|
}
|
|
1892
2033
|
__name(inferMethodName, "inferMethodName");
|
|
1893
|
-
function buildArgs(route, op) {
|
|
2034
|
+
function buildArgs(route, op, bindings) {
|
|
1894
2035
|
const args = [];
|
|
1895
2036
|
if (route.params) {
|
|
1896
2037
|
if (route.params.kind === "params") {
|
|
1897
|
-
args.push(...route.params.nodes.map((p) => toIdentifier(p.name)));
|
|
2038
|
+
args.push(...route.params.nodes.map((p) => bindings?.get(p.name) ?? toIdentifier(p.name)));
|
|
1898
2039
|
} else {
|
|
1899
2040
|
args.push("params");
|
|
1900
2041
|
}
|
|
@@ -2014,21 +2155,25 @@ function responseBodyExpr(value, schema) {
|
|
|
2014
2155
|
return schema ? `await parseAndValidate(${value}, ${schema}, 500)` : value;
|
|
2015
2156
|
}
|
|
2016
2157
|
__name(responseBodyExpr, "responseBodyExpr");
|
|
2017
|
-
function generateParamValidation(source,
|
|
2158
|
+
function generateParamValidation(source, kind, sourceExpr, mode, suffix = "", modelsWithInput, bindings) {
|
|
2018
2159
|
if (!source) return [];
|
|
2019
2160
|
const lines = [];
|
|
2020
|
-
const isQuery =
|
|
2161
|
+
const isQuery = kind === "query";
|
|
2162
|
+
const isPathParams = kind === "params";
|
|
2021
2163
|
if (source.kind === "ref") {
|
|
2022
2164
|
const typeName = modelsWithInput?.has(source.name) ? `${source.name}Input` : source.name;
|
|
2023
|
-
lines.push(` const ${
|
|
2165
|
+
lines.push(` const ${kind} = await parseAndValidate(${sourceExpr}, ${typeName}.${mode}());`);
|
|
2024
2166
|
lines.push("");
|
|
2025
2167
|
} else if (source.kind === "params") {
|
|
2026
2168
|
if (source.nodes.length > 0) {
|
|
2027
|
-
const isPathParams = ctxExpr === "ctx.params";
|
|
2028
2169
|
const bind = /* @__PURE__ */ __name((name) => isPathParams ? toIdentifier(name) : name, "bind");
|
|
2029
|
-
const lhs =
|
|
2170
|
+
const lhs = isPathParams ? `{ ${source.nodes.map((p) => {
|
|
2171
|
+
const wire = bind(p.name);
|
|
2172
|
+
const local = bindings?.get(p.name) ?? wire;
|
|
2173
|
+
return wire === local ? wire : `${wire}: ${local}`;
|
|
2174
|
+
}).join(", ")} }` : kind;
|
|
2030
2175
|
lines.push(` const ${lhs} = await parseAndValidate(`);
|
|
2031
|
-
lines.push(` ${
|
|
2176
|
+
lines.push(` ${sourceExpr},`);
|
|
2032
2177
|
lines.push(` ${modeToWrapper(mode)}({`);
|
|
2033
2178
|
for (const param of source.nodes) {
|
|
2034
2179
|
const bound = bind(param.name);
|
|
@@ -2042,7 +2187,7 @@ function generateParamValidation(source, ctxExpr, varName, mode, suffix = "", mo
|
|
|
2042
2187
|
}
|
|
2043
2188
|
} else {
|
|
2044
2189
|
const schema = isQuery ? renderQueryType(source.node, modelsWithInput) : renderInputType(source.node, modelsWithInput);
|
|
2045
|
-
lines.push(` const ${
|
|
2190
|
+
lines.push(` const ${kind} = await parseAndValidate(${sourceExpr}, (${schema}).${mode}());`);
|
|
2046
2191
|
lines.push("");
|
|
2047
2192
|
}
|
|
2048
2193
|
return lines;
|
|
@@ -2896,7 +3041,7 @@ function sdkResponseMembers(resp, modelsWithOutput, includeStatus) {
|
|
|
2896
3041
|
].join("; ")} }`);
|
|
2897
3042
|
}
|
|
2898
3043
|
__name(sdkResponseMembers, "sdkResponseMembers");
|
|
2899
|
-
function sdkReturnLines(resp, modelsWithOutput,
|
|
3044
|
+
function sdkReturnLines(resp, modelsWithOutput, indent2, includeStatus, revive, where) {
|
|
2900
3045
|
const bodies = resp.bodies;
|
|
2901
3046
|
const headers = resp.headers ?? [];
|
|
2902
3047
|
const leading = includeStatus ? [
|
|
@@ -2907,7 +3052,7 @@ function sdkReturnLines(resp, modelsWithOutput, indent, includeStatus, revive, w
|
|
|
2907
3052
|
] : [];
|
|
2908
3053
|
if (bodies.length === 0) {
|
|
2909
3054
|
return [
|
|
2910
|
-
`${
|
|
3055
|
+
`${indent2}return { ${[
|
|
2911
3056
|
...leading,
|
|
2912
3057
|
...trailing
|
|
2913
3058
|
].join(", ")} };`
|
|
@@ -2921,7 +3066,7 @@ function sdkReturnLines(resp, modelsWithOutput, indent, includeStatus, revive, w
|
|
|
2921
3066
|
...trailing
|
|
2922
3067
|
];
|
|
2923
3068
|
return [
|
|
2924
|
-
`${
|
|
3069
|
+
`${indent2}return { ${fields.join(", ")} };`
|
|
2925
3070
|
];
|
|
2926
3071
|
}
|
|
2927
3072
|
const dataTypes = bodies.map((b) => sdkDataType(b, modelsWithOutput));
|
|
@@ -2934,11 +3079,11 @@ function sdkReturnLines(resp, modelsWithOutput, indent, includeStatus, revive, w
|
|
|
2934
3079
|
...trailing
|
|
2935
3080
|
];
|
|
2936
3081
|
return [
|
|
2937
|
-
`${
|
|
3082
|
+
`${indent2}return { ${fields.join(", ")} };`
|
|
2938
3083
|
];
|
|
2939
3084
|
}
|
|
2940
3085
|
const lines = [
|
|
2941
|
-
`${
|
|
3086
|
+
`${indent2}switch (readContentType(result)) {`
|
|
2942
3087
|
];
|
|
2943
3088
|
for (const [i, body] of bodies.slice(1).entries()) {
|
|
2944
3089
|
const fields = [
|
|
@@ -2947,8 +3092,8 @@ function sdkReturnLines(resp, modelsWithOutput, indent, includeStatus, revive, w
|
|
|
2947
3092
|
`data: ${sdkReadExpr(body, modelsWithOutput, hint(revive, `${resp.statusCode}_${i + 1}`))}`,
|
|
2948
3093
|
...trailing
|
|
2949
3094
|
];
|
|
2950
|
-
lines.push(`${
|
|
2951
|
-
lines.push(`${
|
|
3095
|
+
lines.push(`${indent2} case '${body.contentType}':`);
|
|
3096
|
+
lines.push(`${indent2} return { ${fields.join(", ")} };`);
|
|
2952
3097
|
}
|
|
2953
3098
|
const first = bodies[0];
|
|
2954
3099
|
const fallbackFields = [
|
|
@@ -2957,9 +3102,9 @@ function sdkReturnLines(resp, modelsWithOutput, indent, includeStatus, revive, w
|
|
|
2957
3102
|
`data: ${sdkReadExpr(first, modelsWithOutput, hint(revive, `${resp.statusCode}_0`))}`,
|
|
2958
3103
|
...trailing
|
|
2959
3104
|
];
|
|
2960
|
-
lines.push(`${
|
|
2961
|
-
lines.push(`${
|
|
2962
|
-
lines.push(`${
|
|
3105
|
+
lines.push(`${indent2} default:`);
|
|
3106
|
+
lines.push(`${indent2} return { ${fallbackFields.join(", ")} };`);
|
|
3107
|
+
lines.push(`${indent2}}`);
|
|
2963
3108
|
return lines;
|
|
2964
3109
|
}
|
|
2965
3110
|
__name(sdkReturnLines, "sdkReturnLines");
|
|
@@ -4103,6 +4248,142 @@ function renderOutputField(field, outputCase, modelsWithOutput, target) {
|
|
|
4103
4248
|
}
|
|
4104
4249
|
__name(renderOutputField, "renderOutputField");
|
|
4105
4250
|
|
|
4251
|
+
// src/server-framework-fastify.ts
|
|
4252
|
+
var FASTIFY_RUNTIME_MODULE = "@maroonedsoftware/fastify";
|
|
4253
|
+
var FASTIFY_RUNTIME_SYMBOLS = [
|
|
4254
|
+
"ServerKitRouter",
|
|
4255
|
+
"bodyParserMiddleware",
|
|
4256
|
+
"requirePolicy",
|
|
4257
|
+
"requireSignature",
|
|
4258
|
+
"requestMediaType"
|
|
4259
|
+
];
|
|
4260
|
+
var FASTIFY_SERVER_FRAMEWORK = {
|
|
4261
|
+
name: "fastify",
|
|
4262
|
+
imports(uses) {
|
|
4263
|
+
const symbols = FASTIFY_RUNTIME_SYMBOLS.filter(uses);
|
|
4264
|
+
return symbols.length > 0 ? [
|
|
4265
|
+
`import { ${symbols.join(", ")} } from '${FASTIFY_RUNTIME_MODULE}';`
|
|
4266
|
+
] : [];
|
|
4267
|
+
},
|
|
4268
|
+
routerDeclaration(routerName) {
|
|
4269
|
+
return `export const ${routerName} = ServerKitRouter();`;
|
|
4270
|
+
},
|
|
4271
|
+
pathParam(identifier) {
|
|
4272
|
+
return `:${identifier}`;
|
|
4273
|
+
},
|
|
4274
|
+
handlerLocals: [
|
|
4275
|
+
"request",
|
|
4276
|
+
"reply"
|
|
4277
|
+
],
|
|
4278
|
+
routeOpen(routerName, method, path, middlewares) {
|
|
4279
|
+
const middlewareStr = middlewares.length > 0 ? `, ${middlewares.join(", ")},` : ",";
|
|
4280
|
+
return `${routerName}.${method}('${path}'${middlewareStr} async (request, reply) => {`;
|
|
4281
|
+
},
|
|
4282
|
+
routeClose() {
|
|
4283
|
+
return [
|
|
4284
|
+
"});"
|
|
4285
|
+
];
|
|
4286
|
+
},
|
|
4287
|
+
middleware: {
|
|
4288
|
+
policy(args) {
|
|
4289
|
+
return `requirePolicy(${args})`;
|
|
4290
|
+
},
|
|
4291
|
+
bodyParser(tokensExpr) {
|
|
4292
|
+
return `bodyParserMiddleware([${tokensExpr}])`;
|
|
4293
|
+
},
|
|
4294
|
+
signature(args) {
|
|
4295
|
+
return `requireSignature(${args})`;
|
|
4296
|
+
}
|
|
4297
|
+
},
|
|
4298
|
+
request: {
|
|
4299
|
+
params: "request.params",
|
|
4300
|
+
query: "request.query",
|
|
4301
|
+
headers: "request.headers",
|
|
4302
|
+
// ServerKit parses lazily per route, so Fastify's own `request.body` is never populated.
|
|
4303
|
+
parsedBody: "request.parsedBody",
|
|
4304
|
+
// A call, not a property: the raw header carries `; charset=utf-8`, which would match none of
|
|
4305
|
+
// the declared MIME literals the generated switch compares against.
|
|
4306
|
+
contentType: "requestMediaType(request)"
|
|
4307
|
+
},
|
|
4308
|
+
resolveService(className) {
|
|
4309
|
+
return `request.container.get(${className})`;
|
|
4310
|
+
},
|
|
4311
|
+
response: {
|
|
4312
|
+
status(expr) {
|
|
4313
|
+
return `reply.status(${expr});`;
|
|
4314
|
+
},
|
|
4315
|
+
header(name, valueExpr) {
|
|
4316
|
+
return `reply.header('${name}', ${valueExpr});`;
|
|
4317
|
+
},
|
|
4318
|
+
type(expr) {
|
|
4319
|
+
return `reply.type(${expr});`;
|
|
4320
|
+
},
|
|
4321
|
+
send(bodyExpr) {
|
|
4322
|
+
return bodyExpr === void 0 ? [
|
|
4323
|
+
"return reply.send();"
|
|
4324
|
+
] : [
|
|
4325
|
+
`return reply.send(${bodyExpr});`
|
|
4326
|
+
];
|
|
4327
|
+
},
|
|
4328
|
+
caseEnd() {
|
|
4329
|
+
return [];
|
|
4330
|
+
}
|
|
4331
|
+
},
|
|
4332
|
+
mcpRouter({ path }) {
|
|
4333
|
+
return `import { type ServerKitRouterType, bodyParserMiddleware, requireSignature, requestHeader } from '${FASTIFY_RUNTIME_MODULE}';
|
|
4334
|
+
import { McpDispatcher, createMcpRequestContext, MCP_AUTH_POLICY } from '@maroonedsoftware/mcp';
|
|
4335
|
+
|
|
4336
|
+
/** Mount the MCP endpoint onto a ServerKit router. Bind \`registerMcpTools\` to the \`McpToolHandlerMap\` token. */
|
|
4337
|
+
export function mountMcp(router: ServerKitRouterType): void {
|
|
4338
|
+
router.post('${path}', bodyParserMiddleware(['json']), requireSignature('mcp', { policy: MCP_AUTH_POLICY }), async (request, reply) => {
|
|
4339
|
+
const dispatcher = request.container.get(McpDispatcher);
|
|
4340
|
+
const context = createMcpRequestContext({ requestId: request.requestId, logger: request.logger });
|
|
4341
|
+
if (dispatcher.sessionMode === 'stateful') {
|
|
4342
|
+
// Fastify's equivalent of Koa's \`ctx.respond = false\`: the dispatcher writes the raw
|
|
4343
|
+
// response itself, and the request scope is disposed on the raw socket close instead.
|
|
4344
|
+
reply.hijack();
|
|
4345
|
+
await dispatcher.dispatchStateful(
|
|
4346
|
+
{
|
|
4347
|
+
req: request.raw,
|
|
4348
|
+
res: reply.raw,
|
|
4349
|
+
body: request.parsedBody,
|
|
4350
|
+
// \`requestHeader\` returns '' for an absent header; the session id is optional.
|
|
4351
|
+
sessionId: requestHeader(request, 'mcp-session-id') || undefined,
|
|
4352
|
+
},
|
|
4353
|
+
context,
|
|
4354
|
+
);
|
|
4355
|
+
return;
|
|
4356
|
+
}
|
|
4357
|
+
const response = await dispatcher.dispatch(JSON.parse(String(request.rawBody)), context);
|
|
4358
|
+
if (response) return reply.send(response);
|
|
4359
|
+
reply.status(202); // a notification \u2014 nothing to return
|
|
4360
|
+
return reply.send();
|
|
4361
|
+
});
|
|
4362
|
+
}
|
|
4363
|
+
`;
|
|
4364
|
+
}
|
|
4365
|
+
};
|
|
4366
|
+
|
|
4367
|
+
// src/server-framework.ts
|
|
4368
|
+
var SERVER_FRAMEWORK_NAMES = [
|
|
4369
|
+
"koa",
|
|
4370
|
+
"fastify"
|
|
4371
|
+
];
|
|
4372
|
+
var DEFAULT_SERVER_FRAMEWORK_NAME = "koa";
|
|
4373
|
+
var SERVER_FRAMEWORKS = {
|
|
4374
|
+
koa: KOA_SERVER_FRAMEWORK,
|
|
4375
|
+
fastify: FASTIFY_SERVER_FRAMEWORK
|
|
4376
|
+
};
|
|
4377
|
+
function resolveServerFramework(name) {
|
|
4378
|
+
const resolved = name ?? DEFAULT_SERVER_FRAMEWORK_NAME;
|
|
4379
|
+
const framework = SERVER_FRAMEWORKS[resolved];
|
|
4380
|
+
if (!framework) {
|
|
4381
|
+
throw new Error(`plugin-typescript: server.framework '${resolved}' is not supported \u2014 expected one of: ${SERVER_FRAMEWORK_NAMES.join(", ")}.`);
|
|
4382
|
+
}
|
|
4383
|
+
return framework;
|
|
4384
|
+
}
|
|
4385
|
+
__name(resolveServerFramework, "resolveServerFramework");
|
|
4386
|
+
|
|
4106
4387
|
// src/codegen-mcp.ts
|
|
4107
4388
|
import { resolveModifiers as resolveModifiers3, emittedResponses as emittedResponses2, toIdentifier as toIdentifier3 } from "@contractkit/core";
|
|
4108
4389
|
import { basename as basename3, dirname as dirname5, relative as relative5 } from "path";
|
|
@@ -4523,29 +4804,10 @@ function generateMcpAggregator(entries) {
|
|
|
4523
4804
|
}
|
|
4524
4805
|
__name(generateMcpAggregator, "generateMcpAggregator");
|
|
4525
4806
|
function generateMcpRouter(options = {}) {
|
|
4526
|
-
const
|
|
4527
|
-
return
|
|
4528
|
-
|
|
4529
|
-
|
|
4530
|
-
/** Mount the MCP endpoint onto a ServerKit router. Bind \`registerMcpTools\` to the \`McpToolHandlerMap\` token. */
|
|
4531
|
-
export function mountMcp(router: ReturnType<typeof ServerKitRouter>): void {
|
|
4532
|
-
router.post('${path}', bodyParserMiddleware(['json']), requireSignature('mcp', { policy: MCP_AUTH_POLICY }), async (ctx) => {
|
|
4533
|
-
const dispatcher = ctx.container.get(McpDispatcher);
|
|
4534
|
-
const context = createMcpRequestContext({ requestId: ctx.requestId, logger: ctx.logger });
|
|
4535
|
-
if (dispatcher.sessionMode === 'stateful') {
|
|
4536
|
-
ctx.respond = false;
|
|
4537
|
-
await dispatcher.dispatchStateful(
|
|
4538
|
-
{ req: ctx.req, res: ctx.res, body: ctx.parsedBody, sessionId: ctx.get('mcp-session-id') },
|
|
4539
|
-
context,
|
|
4540
|
-
);
|
|
4541
|
-
} else {
|
|
4542
|
-
const response = await dispatcher.dispatch(JSON.parse(String(ctx.rawBody)), context);
|
|
4543
|
-
if (response) ctx.body = response;
|
|
4544
|
-
else ctx.status = 202; // a notification \u2014 nothing to return
|
|
4545
|
-
}
|
|
4546
|
-
});
|
|
4547
|
-
}
|
|
4548
|
-
`;
|
|
4807
|
+
const framework = options.framework ?? KOA_SERVER_FRAMEWORK;
|
|
4808
|
+
return framework.mcpRouter({
|
|
4809
|
+
path: options.path ?? "/mcp"
|
|
4810
|
+
});
|
|
4549
4811
|
}
|
|
4550
4812
|
__name(generateMcpRouter, "generateMcpRouter");
|
|
4551
4813
|
|
|
@@ -4779,6 +5041,10 @@ function createTypescriptPlugin(config, rootDir) {
|
|
|
4779
5041
|
}
|
|
4780
5042
|
__name(createTypescriptPlugin, "createTypescriptPlugin");
|
|
4781
5043
|
function assertValidConfig(config) {
|
|
5044
|
+
const framework = config.server?.framework;
|
|
5045
|
+
if (framework !== void 0 && !SERVER_FRAMEWORK_NAMES.includes(framework)) {
|
|
5046
|
+
throw new Error(`plugin-typescript: server.framework '${String(framework)}' is not supported \u2014 expected one of: ${SERVER_FRAMEWORK_NAMES.join(", ")}.`);
|
|
5047
|
+
}
|
|
4782
5048
|
if (config.server?.validateResponses && !config.server.zod) {
|
|
4783
5049
|
throw new Error("plugin-typescript: server.validateResponses requires server.zod: true \u2014 without it output.types emits plain TypeScript interfaces, which are types with no runtime schema value for the router to validate against.");
|
|
4784
5050
|
}
|
|
@@ -4908,6 +5174,7 @@ function sliceModelSet(refs, ownNames, set) {
|
|
|
4908
5174
|
__name(sliceModelSet, "sliceModelSet");
|
|
4909
5175
|
function collectServerOutput(config, rootDir, inputs, units) {
|
|
4910
5176
|
const serverBase = resolve2(rootDir, config.baseDir ?? ".");
|
|
5177
|
+
const framework = resolveServerFramework(config.framework);
|
|
4911
5178
|
const modelsWithInput = inputs.modelsWithInput;
|
|
4912
5179
|
const modelsWithOutput = inputs.modelsWithOutput;
|
|
4913
5180
|
const modelsWithTransform = computeModelsWithCaseTransform(inputs.contractRoots.flatMap((r) => r.models));
|
|
@@ -4956,7 +5223,7 @@ function collectServerOutput(config, rootDir, inputs, units) {
|
|
|
4956
5223
|
currentOutPath: typeOutPath,
|
|
4957
5224
|
modelsWithInput,
|
|
4958
5225
|
modelsWithOutput,
|
|
4959
|
-
// These types are consumed by
|
|
5226
|
+
// These types are consumed by server handlers, so `binary` is a Buffer, not a Blob.
|
|
4960
5227
|
target: "server"
|
|
4961
5228
|
};
|
|
4962
5229
|
const content = config.zod ? generateContract(ast, renderCtx) : generatePlainTypes(ast, renderCtx);
|
|
@@ -4987,6 +5254,9 @@ function collectServerOutput(config, rootDir, inputs, units) {
|
|
|
4987
5254
|
// this router's output with no change to `root` or the config.
|
|
4988
5255
|
modelsWithTransform: sliceModelSet(refs, /* @__PURE__ */ new Set(), modelsWithTransform),
|
|
4989
5256
|
validateResponses: config.validateResponses ?? false,
|
|
5257
|
+
// Covered by `sub` already, which is the whole sub-config; explicit for the same reason
|
|
5258
|
+
// `validateResponses` is — the inputs that change a router's text read at a glance.
|
|
5259
|
+
framework: framework.name,
|
|
4990
5260
|
sub: subConfigKey
|
|
4991
5261
|
});
|
|
4992
5262
|
units.push({
|
|
@@ -5003,7 +5273,8 @@ function collectServerOutput(config, rootDir, inputs, units) {
|
|
|
5003
5273
|
modelsWithOutput,
|
|
5004
5274
|
modelsWithTransform,
|
|
5005
5275
|
includeInternal: config.includeInternal,
|
|
5006
|
-
validateResponses: config.validateResponses
|
|
5276
|
+
validateResponses: config.validateResponses,
|
|
5277
|
+
framework
|
|
5007
5278
|
})
|
|
5008
5279
|
}
|
|
5009
5280
|
], "render")
|
|
@@ -5625,10 +5896,12 @@ function collectMcpOutput(config, fullConfig, rootDir, inputs, units, globalFile
|
|
|
5625
5896
|
});
|
|
5626
5897
|
if (config.emitRouter !== false) {
|
|
5627
5898
|
const routerPath = join2(mcpBase, config.output?.router ?? "mcp.router.ts");
|
|
5899
|
+
const framework = resolveServerFramework(fullConfig.server?.framework);
|
|
5628
5900
|
globalFiles.push({
|
|
5629
5901
|
relativePath: routerPath,
|
|
5630
5902
|
content: generateMcpRouter({
|
|
5631
|
-
path: config.path
|
|
5903
|
+
path: config.path,
|
|
5904
|
+
framework
|
|
5632
5905
|
})
|
|
5633
5906
|
});
|
|
5634
5907
|
}
|
|
@@ -5686,8 +5959,14 @@ function stableSubConfig(config) {
|
|
|
5686
5959
|
}
|
|
5687
5960
|
__name(stableSubConfig, "stableSubConfig");
|
|
5688
5961
|
export {
|
|
5962
|
+
DEFAULT_SERVER_FRAMEWORK_NAME,
|
|
5963
|
+
FASTIFY_SERVER_FRAMEWORK,
|
|
5964
|
+
KOA_SERVER_FRAMEWORK,
|
|
5965
|
+
SERVER_FRAMEWORKS,
|
|
5966
|
+
SERVER_FRAMEWORK_NAMES,
|
|
5689
5967
|
TYPESCRIPT_CODEGEN_VERSION,
|
|
5690
5968
|
createTypescriptPlugin,
|
|
5691
|
-
index_default as default
|
|
5969
|
+
index_default as default,
|
|
5970
|
+
resolveServerFramework
|
|
5692
5971
|
};
|
|
5693
5972
|
//# sourceMappingURL=index.js.map
|