@contractkit/plugin-typescript 0.24.0 → 0.25.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 +11 -11
- package/CHANGELOG.md +42 -0
- package/coverage/clover.xml +270 -269
- package/coverage/coverage-final.json +1 -1
- package/coverage/index.html +8 -8
- package/coverage/src/codegen-contract.ts.html +1 -1
- package/coverage/src/codegen-operation.ts.html +29 -11
- package/coverage/src/codegen-plain-types.ts.html +1 -1
- package/coverage/src/codegen-sdk.ts.html +1 -1
- package/coverage/src/index.html +9 -9
- package/coverage/src/index.ts.html +1 -1
- package/coverage/src/path-utils.ts.html +1 -1
- package/coverage/src/ts-render.ts.html +1 -1
- package/coverage/tests/helpers.ts.html +1 -1
- package/coverage/tests/index.html +1 -1
- package/dist/index.js +7 -6
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/codegen-operation.ts +11 -5
- package/tests/codegen-operation.test.ts +36 -36
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contractkit/plugin-typescript",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.25.0",
|
|
4
4
|
"description": "ContractKit built-in plugin: TypeScript codegen (SDK clients, Koa routers, Zod schemas, plain types)",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Marooned Software",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
".": "./dist/index.js"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@contractkit/core": "0.
|
|
29
|
+
"@contractkit/core": "0.18.0"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@repo/config-eslint": "0.3.1",
|
package/src/codegen-operation.ts
CHANGED
|
@@ -137,9 +137,9 @@ export function generateOp(root: OpRootNode, options: OpCodegenOptions = {}): st
|
|
|
137
137
|
// before deciding whether to emit the zod import.
|
|
138
138
|
const body: string[] = [];
|
|
139
139
|
const needsSignature = fileNeedsSignature(root);
|
|
140
|
-
const
|
|
140
|
+
const needsPolicy = fileNeedsPolicy(root);
|
|
141
141
|
const koaImports = ['ServerKitRouter', 'bodyParserMiddleware'];
|
|
142
|
-
if (
|
|
142
|
+
if (needsPolicy) koaImports.push('requirePolicy');
|
|
143
143
|
if (needsSignature) koaImports.push('requireSignature');
|
|
144
144
|
body.push(`import { ${koaImports.join(', ')} } from '@maroonedsoftware/koa';`);
|
|
145
145
|
|
|
@@ -241,8 +241,14 @@ function generateHandler(route: OpRouteNode, op: OpOperationNode, root: OpRootNo
|
|
|
241
241
|
// Middleware list
|
|
242
242
|
const middlewares: string[] = [];
|
|
243
243
|
if (effectiveSecurity !== SECURITY_NONE) {
|
|
244
|
-
const
|
|
245
|
-
|
|
244
|
+
const policy = effectiveSecurity?.policy;
|
|
245
|
+
const args =
|
|
246
|
+
policy === undefined
|
|
247
|
+
? ''
|
|
248
|
+
: policy === false
|
|
249
|
+
? '{ policy: false }'
|
|
250
|
+
: `{ policy: '${policy}' }`;
|
|
251
|
+
middlewares.push(`requirePolicy(${args})`);
|
|
246
252
|
}
|
|
247
253
|
if (hasBody) {
|
|
248
254
|
const parserTokens = Array.from(new Set(bodies.map(b => bodyParserToken(b.contentType))));
|
|
@@ -761,7 +767,7 @@ function routeNeedsValidation(root: OpRootNode): boolean {
|
|
|
761
767
|
);
|
|
762
768
|
}
|
|
763
769
|
|
|
764
|
-
function
|
|
770
|
+
function fileNeedsPolicy(root: OpRootNode): boolean {
|
|
765
771
|
return root.routes.some(route => route.operations.some(op => resolveSecurity(route, op, root) !== SECURITY_NONE));
|
|
766
772
|
}
|
|
767
773
|
|
|
@@ -807,9 +807,9 @@ describe('generateOp — route modifiers JSDoc', () => {
|
|
|
807
807
|
expect(out).toContain('anonymous access, no security required');
|
|
808
808
|
});
|
|
809
809
|
|
|
810
|
-
it('emits no annotation for security with
|
|
810
|
+
it('emits no annotation for security with policy', () => {
|
|
811
811
|
const op = opOperation('get', {
|
|
812
|
-
security: {
|
|
812
|
+
security: { policy: 'paymentsWrite', loc: { file: 'test.op', line: 1 } },
|
|
813
813
|
});
|
|
814
814
|
const root = opRoot([opRoute('/users', [op])]);
|
|
815
815
|
const out = generateOp(root);
|
|
@@ -845,7 +845,7 @@ describe('generateOp — route modifiers JSDoc', () => {
|
|
|
845
845
|
});
|
|
846
846
|
const root = opRoot([opRoute('/webhooks', [op])]);
|
|
847
847
|
const out = generateOp(root);
|
|
848
|
-
expect(out).toContain(`import { ServerKitRouter, bodyParserMiddleware,
|
|
848
|
+
expect(out).toContain(`import { ServerKitRouter, bodyParserMiddleware, requirePolicy, requireSignature }`);
|
|
849
849
|
expect(out).toContain(`requireSignature('MODERN_TREASURY_WEBHOOK')`);
|
|
850
850
|
});
|
|
851
851
|
|
|
@@ -867,63 +867,63 @@ describe('generateOp — route modifiers JSDoc', () => {
|
|
|
867
867
|
|
|
868
868
|
it('does not import requireSignature when no signature is set', () => {
|
|
869
869
|
const op = opOperation('get', {
|
|
870
|
-
security: {
|
|
870
|
+
security: { policy: 'paymentsWrite', loc: { file: 'test.op', line: 1 } },
|
|
871
871
|
});
|
|
872
872
|
const root = opRoot([opRoute('/users', [op])]);
|
|
873
873
|
const out = generateOp(root);
|
|
874
874
|
expect(out).not.toContain('requireSignature');
|
|
875
|
-
expect(out).toContain(`import { ServerKitRouter, bodyParserMiddleware,
|
|
875
|
+
expect(out).toContain(`import { ServerKitRouter, bodyParserMiddleware, requirePolicy }`);
|
|
876
876
|
});
|
|
877
877
|
});
|
|
878
878
|
|
|
879
|
-
// ───
|
|
879
|
+
// ─── Policy middleware ──────────────────────────────────────────
|
|
880
880
|
|
|
881
|
-
describe('
|
|
882
|
-
it('injects
|
|
881
|
+
describe('policy middleware', () => {
|
|
882
|
+
it('injects requirePolicy() with no args for unannotated routes', () => {
|
|
883
883
|
const op = opOperation('get');
|
|
884
884
|
const root = opRoot([opRoute('/users', [op])]);
|
|
885
885
|
const out = generateOp(root);
|
|
886
|
-
expect(out).toContain(`import { ServerKitRouter, bodyParserMiddleware,
|
|
887
|
-
expect(out).toContain(`
|
|
886
|
+
expect(out).toContain(`import { ServerKitRouter, bodyParserMiddleware, requirePolicy }`);
|
|
887
|
+
expect(out).toContain(`requirePolicy()`);
|
|
888
888
|
});
|
|
889
889
|
|
|
890
|
-
it('injects
|
|
890
|
+
it('injects requirePolicy with a named policy when set', () => {
|
|
891
891
|
const op = opOperation('get', {
|
|
892
|
-
security: {
|
|
892
|
+
security: { policy: 'paymentsWrite', loc: { file: 'test.op', line: 1 } },
|
|
893
893
|
});
|
|
894
894
|
const root = opRoot([opRoute('/users', [op])]);
|
|
895
895
|
const out = generateOp(root);
|
|
896
|
-
expect(out).toContain(`
|
|
896
|
+
expect(out).toContain(`requirePolicy({ policy: 'paymentsWrite' })`);
|
|
897
897
|
});
|
|
898
898
|
|
|
899
|
-
it('injects
|
|
899
|
+
it('injects requirePolicy with policy: false when explicitly bypassed', () => {
|
|
900
900
|
const op = opOperation('get', {
|
|
901
|
-
security: {
|
|
901
|
+
security: { policy: false, loc: { file: 'test.op', line: 1 } },
|
|
902
902
|
});
|
|
903
903
|
const routeLine = generateOp(opRoot([opRoute('/users', [op])]))
|
|
904
904
|
.split('\n')
|
|
905
905
|
.find(l => l.includes('.get('));
|
|
906
|
-
expect(routeLine).toContain(`
|
|
906
|
+
expect(routeLine).toContain(`requirePolicy({ policy: false })`);
|
|
907
907
|
});
|
|
908
908
|
|
|
909
|
-
it('does not inject
|
|
909
|
+
it('does not inject requirePolicy for public (security: none) routes', () => {
|
|
910
910
|
const op = opOperation('get', { security: SECURITY_NONE });
|
|
911
911
|
const root = opRoot([opRoute('/health', [op])]);
|
|
912
912
|
const out = generateOp(root);
|
|
913
|
-
expect(out).not.toContain('
|
|
913
|
+
expect(out).not.toContain('requirePolicy');
|
|
914
914
|
});
|
|
915
915
|
|
|
916
|
-
it('does not import
|
|
916
|
+
it('does not import requirePolicy when all routes are public', () => {
|
|
917
917
|
const op = opOperation('get', { security: SECURITY_NONE });
|
|
918
918
|
const root = opRoot([opRoute('/health', [op])]);
|
|
919
919
|
const out = generateOp(root);
|
|
920
920
|
expect(out).toContain(`import { ServerKitRouter, bodyParserMiddleware }`);
|
|
921
|
-
expect(out).not.toContain('
|
|
921
|
+
expect(out).not.toContain('requirePolicy');
|
|
922
922
|
});
|
|
923
923
|
|
|
924
|
-
it('places
|
|
924
|
+
it('places requirePolicy before bodyParserMiddleware in the route line', () => {
|
|
925
925
|
const op = opOperation('post', {
|
|
926
|
-
security: {
|
|
926
|
+
security: { policy: 'paymentsWrite', loc: { file: 'test.op', line: 1 } },
|
|
927
927
|
request: opRequest('Payload'),
|
|
928
928
|
});
|
|
929
929
|
const root = opRoot([opRoute('/users', [op])]);
|
|
@@ -931,16 +931,16 @@ describe('generateOp — route modifiers JSDoc', () => {
|
|
|
931
931
|
.split('\n')
|
|
932
932
|
.find(l => l.includes('.post('));
|
|
933
933
|
expect(routeLine).toBeDefined();
|
|
934
|
-
const
|
|
934
|
+
const polIdx = routeLine!.indexOf(`requirePolicy`);
|
|
935
935
|
const bodyIdx = routeLine!.indexOf(`bodyParserMiddleware`);
|
|
936
|
-
expect(
|
|
937
|
-
expect(bodyIdx).toBeGreaterThan(
|
|
936
|
+
expect(polIdx).toBeGreaterThan(-1);
|
|
937
|
+
expect(bodyIdx).toBeGreaterThan(polIdx);
|
|
938
938
|
});
|
|
939
939
|
|
|
940
|
-
it('places
|
|
940
|
+
it('places requirePolicy before requireSignature when both are set', () => {
|
|
941
941
|
const op = opOperation('post', {
|
|
942
942
|
signature: 'MY_KEY',
|
|
943
|
-
security: {
|
|
943
|
+
security: { policy: 'paymentsWrite', loc: { file: 'test.op', line: 1 } },
|
|
944
944
|
request: opRequest('Payload'),
|
|
945
945
|
});
|
|
946
946
|
const root = opRoot([opRoute('/webhooks', [op])]);
|
|
@@ -948,30 +948,30 @@ describe('generateOp — route modifiers JSDoc', () => {
|
|
|
948
948
|
.split('\n')
|
|
949
949
|
.find(l => l.includes('.post('));
|
|
950
950
|
expect(routeLine).toBeDefined();
|
|
951
|
-
const
|
|
951
|
+
const polIdx = routeLine!.indexOf(`requirePolicy`);
|
|
952
952
|
const sigIdx = routeLine!.indexOf(`requireSignature`);
|
|
953
|
-
expect(
|
|
954
|
-
expect(sigIdx).toBeGreaterThan(
|
|
953
|
+
expect(polIdx).toBeGreaterThan(-1);
|
|
954
|
+
expect(sigIdx).toBeGreaterThan(polIdx);
|
|
955
955
|
});
|
|
956
956
|
|
|
957
|
-
it('imports both
|
|
957
|
+
it('imports both requirePolicy and requireSignature when both are set', () => {
|
|
958
958
|
const op = opOperation('post', {
|
|
959
959
|
signature: 'MY_KEY',
|
|
960
|
-
security: {
|
|
960
|
+
security: { policy: 'paymentsWrite', loc: { file: 'test.op', line: 1 } },
|
|
961
961
|
request: opRequest('Payload'),
|
|
962
962
|
});
|
|
963
963
|
const root = opRoot([opRoute('/webhooks', [op])]);
|
|
964
964
|
const out = generateOp(root);
|
|
965
|
-
expect(out).toContain(`import { ServerKitRouter, bodyParserMiddleware,
|
|
965
|
+
expect(out).toContain(`import { ServerKitRouter, bodyParserMiddleware, requirePolicy, requireSignature }`);
|
|
966
966
|
});
|
|
967
967
|
|
|
968
|
-
it('works with route-level
|
|
968
|
+
it('works with route-level policy security', () => {
|
|
969
969
|
const op = opOperation('get');
|
|
970
970
|
const route = opRoute('/users', [op]);
|
|
971
|
-
route.security = {
|
|
971
|
+
route.security = { policy: 'paymentsWrite', loc: { file: 'test.op', line: 1 } };
|
|
972
972
|
const root = opRoot([route]);
|
|
973
973
|
const out = generateOp(root);
|
|
974
|
-
expect(out).toContain(`
|
|
974
|
+
expect(out).toContain(`requirePolicy({ policy: 'paymentsWrite' })`);
|
|
975
975
|
});
|
|
976
976
|
});
|
|
977
977
|
});
|