@contractkit/plugin-typescript 0.34.1 → 0.35.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 +23 -18
- package/CHANGELOG.md +22 -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 +12 -4
- package/dist/codegen-operation.d.ts.map +1 -1
- package/dist/index.d.ts +11 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +215 -88
- package/dist/index.js.map +1 -1
- 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 +94 -0
- package/dist/server-framework.d.ts.map +1 -0
- package/llms.txt +1 -1
- package/package.json +1 -1
- package/src/codegen-mcp.ts +11 -25
- package/src/codegen-operation.ts +99 -74
- package/src/index.ts +36 -5
- package/src/server-framework-koa.ts +112 -0
- package/src/server-framework.ts +119 -0
- package/tests/codegen-mcp.test.ts +12 -0
- package/tests/codegen-operation-framework.test.ts +140 -0
- package/tests/codegen-operation.test.ts +29 -0
- package/tests/codegen-server.test.ts +56 -0
- package/tests/server-framework-koa.test.ts +115 -0
- package/tests/server-framework.test.ts +24 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { KOA_SERVER_FRAMEWORK as koa } from '../src/server-framework-koa.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The Koa adapter is the reference implementation: every string here is one the generator emitted
|
|
6
|
+
* inline before the seam existed. Pinning them individually means a change to any one of them shows
|
|
7
|
+
* up as a failure here, naming the piece, rather than only as a snapshot diff two packages away.
|
|
8
|
+
*/
|
|
9
|
+
describe('KOA_SERVER_FRAMEWORK', () => {
|
|
10
|
+
it('is named koa', () => {
|
|
11
|
+
expect(koa.name).toBe('koa');
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
describe('imports', () => {
|
|
15
|
+
it('emits one line naming every symbol the body uses', () => {
|
|
16
|
+
expect(koa.imports(() => true)).toEqual([
|
|
17
|
+
"import { ServerKitRouter, bodyParserMiddleware, requirePolicy, requireSignature } from '@maroonedsoftware/koa';",
|
|
18
|
+
]);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('narrows to the symbols the body actually references', () => {
|
|
22
|
+
expect(koa.imports(s => s === 'requirePolicy')).toEqual(["import { requirePolicy } from '@maroonedsoftware/koa';"]);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('emits nothing when the body references none of them', () => {
|
|
26
|
+
expect(koa.imports(() => false)).toEqual([]);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('declares the router', () => {
|
|
31
|
+
expect(koa.routerDeclaration('UsersRouter')).toBe('export const UsersRouter = ServerKitRouter();');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('renders a path parameter with a colon', () => {
|
|
35
|
+
expect(koa.pathParam('userId')).toBe(':userId');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe('routeOpen', () => {
|
|
39
|
+
it('omits the middleware list when there is none', () => {
|
|
40
|
+
expect(koa.routeOpen('UsersRouter', 'get', '/users', [])).toBe("UsersRouter.get('/users', async ctx => {");
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('places the middleware between the path and the handler', () => {
|
|
44
|
+
expect(koa.routeOpen('UsersRouter', 'post', '/users', ['requirePolicy()', "bodyParserMiddleware(['json'])"])).toBe(
|
|
45
|
+
"UsersRouter.post('/users', requirePolicy(), bodyParserMiddleware(['json']), async ctx => {",
|
|
46
|
+
);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('closes a route', () => {
|
|
51
|
+
expect(koa.routeClose()).toEqual(['});']);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('renders the route middleware factories', () => {
|
|
55
|
+
expect(koa.middleware.policy('')).toBe('requirePolicy()');
|
|
56
|
+
expect(koa.middleware.policy("{ policy: 'admin' }")).toBe("requirePolicy({ policy: 'admin' })");
|
|
57
|
+
expect(koa.middleware.bodyParser("'json', 'multipart'")).toBe("bodyParserMiddleware(['json', 'multipart'])");
|
|
58
|
+
expect(koa.middleware.signature("'slack'")).toBe("requireSignature('slack')");
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('reads the request off the context', () => {
|
|
62
|
+
expect(koa.request).toEqual({
|
|
63
|
+
params: 'ctx.params',
|
|
64
|
+
query: 'ctx.query',
|
|
65
|
+
headers: 'ctx.headers',
|
|
66
|
+
parsedBody: 'ctx.parsedBody',
|
|
67
|
+
contentType: 'ctx.request.type',
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('resolves a service from the request container', () => {
|
|
72
|
+
expect(koa.resolveService('PaymentService')).toBe('ctx.container.get(PaymentService)');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe('response', () => {
|
|
76
|
+
it('assigns the status', () => {
|
|
77
|
+
expect(koa.response.status('200')).toBe('ctx.status = 200;');
|
|
78
|
+
expect(koa.response.status('result.status')).toBe('ctx.status = result.status;');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('sets a header as a single statement, so the optional guard can wrap it', () => {
|
|
82
|
+
expect(koa.response.header('x-request-id', 'String(result.headers["xRequestId"])')).toBe(
|
|
83
|
+
'ctx.set(\'x-request-id\', String(result.headers["xRequestId"]));',
|
|
84
|
+
);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('assigns the content type', () => {
|
|
88
|
+
expect(koa.response.type("'application/json'")).toBe("ctx.type = 'application/json';");
|
|
89
|
+
expect(koa.response.type('result.contentType')).toBe('ctx.type = result.contentType;');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('assigns the body, and writes nothing at all when there is none', () => {
|
|
93
|
+
expect(koa.response.send('result')).toEqual(['ctx.body = result;']);
|
|
94
|
+
// Koa ends the response on its own once the handler resolves, so a 204 needs no statement.
|
|
95
|
+
expect(koa.response.send(undefined)).toEqual([]);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('closes a status case with a break', () => {
|
|
99
|
+
expect(koa.response.caseEnd()).toEqual(['break;']);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe('mcpRouter', () => {
|
|
104
|
+
it('mounts the dispatcher at the given path', () => {
|
|
105
|
+
const out = koa.mcpRouter({ path: '/tools' });
|
|
106
|
+
expect(out).toContain("router.post('/tools',");
|
|
107
|
+
expect(out).toContain("import { ServerKitRouter, bodyParserMiddleware, requireSignature } from '@maroonedsoftware/koa';");
|
|
108
|
+
expect(out).toContain('const dispatcher = ctx.container.get(McpDispatcher);');
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('escapes the backticks in its own doc comment rather than closing the template', () => {
|
|
112
|
+
expect(koa.mcpRouter({ path: '/mcp' })).toContain('Bind `registerMcpTools` to the `McpToolHandlerMap` token.');
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { resolveServerFramework, SERVER_FRAMEWORK_NAMES, SERVER_FRAMEWORKS, DEFAULT_SERVER_FRAMEWORK_NAME } from '../src/server-framework.js';
|
|
3
|
+
|
|
4
|
+
describe('resolveServerFramework', () => {
|
|
5
|
+
it('defaults to koa when the config names none', () => {
|
|
6
|
+
expect(resolveServerFramework(undefined).name).toBe('koa');
|
|
7
|
+
expect(DEFAULT_SERVER_FRAMEWORK_NAME).toBe('koa');
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it('resolves a supported name', () => {
|
|
11
|
+
expect(resolveServerFramework('koa').name).toBe('koa');
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('rejects an unsupported name, naming what is supported', () => {
|
|
15
|
+
expect(() => resolveServerFramework('express')).toThrow(/server\.framework 'express' is not supported/);
|
|
16
|
+
expect(() => resolveServerFramework('express')).toThrow(/expected one of: koa/);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('has an adapter for every declared name', () => {
|
|
20
|
+
for (const name of SERVER_FRAMEWORK_NAMES) {
|
|
21
|
+
expect(SERVER_FRAMEWORKS[name]?.name).toBe(name);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
});
|