Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@contractkit/plugin-typescript

Package Overview
Dependencies
Maintainers
1
Versions
21
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@contractkit/plugin-typescript - npm Package Compare versions

Comparing version
0.24.0
to
0.25.0
+5
-5
.turbo/turbo-build$colon$ci.log
> @contractkit/plugin-typescript@0.24.0 build:ci /home/runner/work/ContractKit/ContractKit/packages/plugin-typescript
> @contractkit/plugin-typescript@0.25.0 build:ci /home/runner/work/ContractKit/ContractKit/packages/plugin-typescript
> eslint --max-warnings=0 && pnpm run build
> @contractkit/plugin-typescript@0.24.0 build /home/runner/work/ContractKit/ContractKit/packages/plugin-typescript
> @contractkit/plugin-typescript@0.25.0 build /home/runner/work/ContractKit/ContractKit/packages/plugin-typescript
> tsup src/index.ts --format esm --sourcemap --dts && tsc --emitDeclarationOnly --declaration

@@ -15,6 +15,6 @@

ESM dist/index.js 149.94 KB
ESM dist/index.js.map 327.56 KB
ESM ⚡️ Build success in 339ms
ESM dist/index.js.map 327.66 KB
ESM ⚡️ Build success in 449ms
DTS Build start
DTS ⚡️ Build success in 4724ms
DTS ⚡️ Build success in 5889ms
DTS dist/index.d.ts 1.82 KB
> @contractkit/plugin-typescript@0.24.0 test:ci /home/runner/work/ContractKit/ContractKit/packages/plugin-typescript
> @contractkit/plugin-typescript@0.25.0 test:ci /home/runner/work/ContractKit/ContractKit/packages/plugin-typescript
> vitest run --coverage

@@ -9,13 +9,13 @@

✓ tests/codegen-operation.test.ts (88 tests) 104ms
✓ tests/codegen-contract.test.ts (127 tests) 116ms
✓ tests/codegen-sdk.test.ts (132 tests) 134ms
✓ tests/codegen-operation.test.ts (88 tests) 149ms
✓ tests/codegen-contract.test.ts (127 tests) 132ms
✓ tests/codegen-sdk.test.ts (132 tests) 175ms
✓ tests/codegen-plain-types.test.ts (61 tests) 34ms
✓ tests/pipeline.test.ts (25 tests) 149ms
✓ tests/codegen-server.test.ts (19 tests) 31ms
✓ tests/pipeline.test.ts (25 tests) 199ms
✓ tests/codegen-server.test.ts (19 tests) 67ms
 Test Files  6 passed (6)
 Tests  452 passed (452)
 Start at  15:00:19
 Duration  5.63s (transform 3.00s, setup 0ms, import 10.57s, tests 567ms, environment 4ms)
 Start at  17:08:46
 Duration  6.33s (transform 4.31s, setup 0ms, import 12.82s, tests 757ms, environment 9ms)

@@ -26,6 +26,6 @@  % Coverage report from v8

-------------------|---------|----------|---------|---------|-------------------
All files | 80 | 75.28 | 83.5 | 82.61 |
src | 79.79 | 74.96 | 83.06 | 82.37 |
All files | 80.01 | 75.28 | 83.5 | 82.62 |
src | 79.79 | 74.96 | 83.06 | 82.38 |
...n-contract.ts | 87.92 | 82.7 | 90.08 | 89.06 | ...1085,1090-1091
...-operation.ts | 78.13 | 74.26 | 77.77 | 79.69 | ...69-680,685-686
...-operation.ts | 78.18 | 74.26 | 77.77 | 79.75 | ...75-686,691-692
...lain-types.ts | 89.08 | 78.37 | 96.66 | 92.3 | ...31,243,249,301

@@ -32,0 +32,0 @@ codegen-sdk.ts | 88.09 | 82.56 | 87.32 | 91.32 | ...1108-1109,1112

# @contractkit/contractkit-plugin-typescript
## 0.25.0
### Minor Changes
- dd8197b: **Breaking:** Replace the `requireMfa: boolean` field in `security: { ... }` blocks with `policy: <ident|none>`, and switch the generated Koa router middleware from `requireSecurity` to ServerKit's new `requirePolicy`.
The `security` declaration on operations, routes, and the file-level `options { security: { ... } }` block no longer accepts a `requireMfa:` line. The new field is `policy:` and takes a bare identifier (the named policy) or the keyword `none` to explicitly bypass policy enforcement. Existing `.ck` files that use `requireMfa:` will fail to parse.
```ck
# Before
security: {
requireMfa: true
}
# After
security: {
policy: paymentsWrite
}
# Explicit bypass
security: {
policy: none
}
```
**`@contractkit/core`** — `SecurityFields` interface drops `requireMfa` / `requireMfaDescription` and adds `policy?: string | false` / `policyDescription?: string`. The grammar's `SecurityRequireMfaLine` is replaced by `SecurityPolicyLine` (`policyKw ":" (noneKw | identifier)`). `security: none` (the route-level public sentinel) is unchanged.
**`@contractkit/plugin-typescript`** — Generated Koa routers now import `requirePolicy` from `@maroonedsoftware/koa` (previously `requireSecurity`) and emit `requirePolicy({ policy: 'name' })`, `requirePolicy({ policy: false })`, or bare `requirePolicy()`. Consumers must upgrade ServerKit alongside.
**`@contractkit/prettier-plugin`** — Formats `policy: <name>` and `policy: none` lines inside security blocks. Files containing `requireMfa:` will no longer round-trip and will surface as parse errors.
**`@contractkit/plugin-markdown`** — The "Security: authenticated" admonition now shows `policy: <name|none>` instead of `requireMfa: <bool>`.
**`@contractkit/openapi-to-ck`** — Non-empty OpenAPI `security` requirements continue to collapse to an empty `security: {}` (authenticated, default policy); the serializer now emits `policy:` lines when the field is set.
**`contractkit-vscode-extension`** — TextMate grammar highlights `policy:` inside the security block; LSP completion offers `policy` instead of `requireMfa`. Re-run `pnpm run vscode:install` to pick up the change.
### Patch Changes
- Updated dependencies [dd8197b]
- @contractkit/core@0.18.0
## 0.24.0

@@ -4,0 +46,0 @@

@@ -26,5 +26,5 @@

<div class='fl pad1y space-right2'>
<span class="strong">80% </span>
<span class="strong">80.01% </span>
<span class="quiet">Statements</span>
<span class='fraction'>2033/2541</span>
<span class='fraction'>2034/2542</span>
</div>

@@ -48,5 +48,5 @@

<div class='fl pad1y space-right2'>
<span class="strong">82.61% </span>
<span class="strong">82.62% </span>
<span class="quiet">Lines</span>
<span class='fraction'>1782/2157</span>
<span class='fraction'>1783/2158</span>
</div>

@@ -89,3 +89,3 @@

<td data-value="79.79" class="pct medium">79.79%</td>
<td data-value="2489" class="abs medium">1986/2489</td>
<td data-value="2490" class="abs medium">1987/2490</td>
<td data-value="74.96" class="pct medium">74.96%</td>

@@ -95,4 +95,4 @@ <td data-value="1606" class="abs medium">1204/1606</td>

<td data-value="372" class="abs high">309/372</td>
<td data-value="82.37" class="pct high">82.37%</td>
<td data-value="2111" class="abs high">1739/2111</td>
<td data-value="82.38" class="pct high">82.38%</td>
<td data-value="2112" class="abs high">1740/2112</td>
</tr>

@@ -123,3 +123,3 @@

<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2026-05-12T15:00:24.685Z
at 2026-05-13T17:08:53.091Z
</div>

@@ -126,0 +126,0 @@ <script src="prettify.js"></script>

@@ -985,3 +985,3 @@

<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2026-05-12T15:00:24.685Z
at 2026-05-13T17:08:53.091Z
</div>

@@ -988,0 +988,0 @@ <script src="../prettify.js"></script>

@@ -28,3 +28,3 @@

<span class="quiet">Statements</span>
<span class='fraction'>1986/2489</span>
<span class='fraction'>1987/2490</span>
</div>

@@ -48,5 +48,5 @@

<div class='fl pad1y space-right2'>
<span class="strong">82.37% </span>
<span class="strong">82.38% </span>
<span class="quiet">Lines</span>
<span class='fraction'>1739/2111</span>
<span class='fraction'>1740/2112</span>
</div>

@@ -100,7 +100,7 @@

<td class="file medium" data-value="codegen-operation.ts"><a href="codegen-operation.ts.html">codegen-operation.ts</a></td>
<td data-value="78.13" class="pic medium">
<td data-value="78.18" class="pic medium">
<div class="chart"><div class="cover-fill" style="width: 78%"></div><div class="cover-empty" style="width: 22%"></div></div>
</td>
<td data-value="78.13" class="pct medium">78.13%</td>
<td data-value="462" class="abs medium">361/462</td>
<td data-value="78.18" class="pct medium">78.18%</td>
<td data-value="463" class="abs medium">362/463</td>
<td data-value="74.26" class="pct medium">74.26%</td>

@@ -110,4 +110,4 @@ <td data-value="338" class="abs medium">251/338</td>

<td data-value="72" class="abs medium">56/72</td>
<td data-value="79.69" class="pct medium">79.69%</td>
<td data-value="399" class="abs medium">318/399</td>
<td data-value="79.75" class="pct medium">79.75%</td>
<td data-value="400" class="abs medium">319/400</td>
</tr>

@@ -198,3 +198,3 @@

<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2026-05-12T15:00:24.685Z
at 2026-05-13T17:08:53.091Z
</div>

@@ -201,0 +201,0 @@ <script src="../prettify.js"></script>

@@ -733,3 +733,3 @@

<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2026-05-12T15:00:24.685Z
at 2026-05-13T17:08:53.091Z
</div>

@@ -736,0 +736,0 @@ <script src="../prettify.js"></script>

@@ -580,3 +580,3 @@

<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2026-05-12T15:00:24.685Z
at 2026-05-13T17:08:53.091Z
</div>

@@ -583,0 +583,0 @@ <script src="../prettify.js"></script>

@@ -814,3 +814,3 @@

<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2026-05-12T15:00:24.685Z
at 2026-05-13T17:08:53.091Z
</div>

@@ -817,0 +817,0 @@ <script src="../prettify.js"></script>

@@ -104,3 +104,3 @@

<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2026-05-12T15:00:24.685Z
at 2026-05-13T17:08:53.091Z
</div>

@@ -107,0 +107,0 @@ <script src="../prettify.js"></script>

{
"name": "@contractkit/plugin-typescript",
"version": "0.24.0",
"version": "0.25.0",
"description": "ContractKit built-in plugin: TypeScript codegen (SDK clients, Koa routers, Zod schemas, plain types)",

@@ -29,3 +29,3 @@ "author": {

"dependencies": {
"@contractkit/core": "0.17.0"
"@contractkit/core": "0.18.0"
},

@@ -32,0 +32,0 @@ "devDependencies": {

@@ -140,5 +140,5 @@ import type { OpRootNode, OpRouteNode, OpOperationNode, ContractTypeNode, ParamSource, ObjectMode } from '@contractkit/core';

const needsSignature = fileNeedsSignature(root);
const needsSecurity = fileNeedsSecurity(root);
const needsPolicy = fileNeedsPolicy(root);
const koaImports = ['ServerKitRouter', 'bodyParserMiddleware'];
if (needsSecurity) koaImports.push('requireSecurity');
if (needsPolicy) koaImports.push('requirePolicy');
if (needsSignature) koaImports.push('requireSignature');

@@ -245,4 +245,10 @@ body.push(`import { ${koaImports.join(', ')} } from '@maroonedsoftware/koa';`);

if (effectiveSecurity !== SECURITY_NONE) {
const args = effectiveSecurity && effectiveSecurity.requireMfa !== undefined ? `{ requireMfa: ${effectiveSecurity.requireMfa} }` : '';
middlewares.push(`requireSecurity(${args})`);
const policy = effectiveSecurity?.policy;
const args =
policy === undefined
? ''
: policy === false
? '{ policy: false }'
: `{ policy: '${policy}' }`;
middlewares.push(`requirePolicy(${args})`);
}

@@ -766,3 +772,3 @@ if (hasBody) {

function fileNeedsSecurity(root: OpRootNode): boolean {
function fileNeedsPolicy(root: OpRootNode): boolean {
return root.routes.some(route => route.operations.some(op => resolveSecurity(route, op, root) !== SECURITY_NONE));

@@ -769,0 +775,0 @@ }

@@ -810,5 +810,5 @@ import { describe, it, expect } from 'vitest';

it('emits no annotation for security with requireMfa', () => {
it('emits no annotation for security with policy', () => {
const op = opOperation('get', {
security: { requireMfa: true, loc: { file: 'test.op', line: 1 } },
security: { policy: 'paymentsWrite', loc: { file: 'test.op', line: 1 } },
});

@@ -849,3 +849,3 @@ const root = opRoot([opRoute('/users', [op])]);

const out = generateOp(root);
expect(out).toContain(`import { ServerKitRouter, bodyParserMiddleware, requireSecurity, requireSignature }`);
expect(out).toContain(`import { ServerKitRouter, bodyParserMiddleware, requirePolicy, requireSignature }`);
expect(out).toContain(`requireSignature('MODERN_TREASURY_WEBHOOK')`);

@@ -872,3 +872,3 @@ });

const op = opOperation('get', {
security: { requireMfa: true, loc: { file: 'test.op', line: 1 } },
security: { policy: 'paymentsWrite', loc: { file: 'test.op', line: 1 } },
});

@@ -878,29 +878,29 @@ const root = opRoot([opRoute('/users', [op])]);

expect(out).not.toContain('requireSignature');
expect(out).toContain(`import { ServerKitRouter, bodyParserMiddleware, requireSecurity }`);
expect(out).toContain(`import { ServerKitRouter, bodyParserMiddleware, requirePolicy }`);
});
});
// ─── Security (requireMfa) middleware ───────────────────────────
// ─── Policy middleware ──────────────────────────────────────────
describe('security middleware', () => {
it('injects requireSecurity() with no args for unannotated routes', () => {
describe('policy middleware', () => {
it('injects requirePolicy() with no args for unannotated routes', () => {
const op = opOperation('get');
const root = opRoot([opRoute('/users', [op])]);
const out = generateOp(root);
expect(out).toContain(`import { ServerKitRouter, bodyParserMiddleware, requireSecurity }`);
expect(out).toContain(`requireSecurity()`);
expect(out).toContain(`import { ServerKitRouter, bodyParserMiddleware, requirePolicy }`);
expect(out).toContain(`requirePolicy()`);
});
it('injects requireSecurity with requireMfa: true when set', () => {
it('injects requirePolicy with a named policy when set', () => {
const op = opOperation('get', {
security: { requireMfa: true, loc: { file: 'test.op', line: 1 } },
security: { policy: 'paymentsWrite', loc: { file: 'test.op', line: 1 } },
});
const root = opRoot([opRoute('/users', [op])]);
const out = generateOp(root);
expect(out).toContain(`requireSecurity({ requireMfa: true })`);
expect(out).toContain(`requirePolicy({ policy: 'paymentsWrite' })`);
});
it('injects requireSecurity with requireMfa: false when set', () => {
it('injects requirePolicy with policy: false when explicitly bypassed', () => {
const op = opOperation('get', {
security: { requireMfa: false, loc: { file: 'test.op', line: 1 } },
security: { policy: false, loc: { file: 'test.op', line: 1 } },
});

@@ -910,13 +910,13 @@ const routeLine = generateOp(opRoot([opRoute('/users', [op])]))

.find(l => l.includes('.get('));
expect(routeLine).toContain(`requireSecurity({ requireMfa: false })`);
expect(routeLine).toContain(`requirePolicy({ policy: false })`);
});
it('does not inject requireSecurity for public (security: none) routes', () => {
it('does not inject requirePolicy for public (security: none) routes', () => {
const op = opOperation('get', { security: SECURITY_NONE });
const root = opRoot([opRoute('/health', [op])]);
const out = generateOp(root);
expect(out).not.toContain('requireSecurity');
expect(out).not.toContain('requirePolicy');
});
it('does not import requireSecurity when all routes are public', () => {
it('does not import requirePolicy when all routes are public', () => {
const op = opOperation('get', { security: SECURITY_NONE });

@@ -926,8 +926,8 @@ const root = opRoot([opRoute('/health', [op])]);

expect(out).toContain(`import { ServerKitRouter, bodyParserMiddleware }`);
expect(out).not.toContain('requireSecurity');
expect(out).not.toContain('requirePolicy');
});
it('places requireSecurity before bodyParserMiddleware in the route line', () => {
it('places requirePolicy before bodyParserMiddleware in the route line', () => {
const op = opOperation('post', {
security: { requireMfa: true, loc: { file: 'test.op', line: 1 } },
security: { policy: 'paymentsWrite', loc: { file: 'test.op', line: 1 } },
request: opRequest('Payload'),

@@ -940,12 +940,12 @@ });

expect(routeLine).toBeDefined();
const secIdx = routeLine!.indexOf(`requireSecurity`);
const polIdx = routeLine!.indexOf(`requirePolicy`);
const bodyIdx = routeLine!.indexOf(`bodyParserMiddleware`);
expect(secIdx).toBeGreaterThan(-1);
expect(bodyIdx).toBeGreaterThan(secIdx);
expect(polIdx).toBeGreaterThan(-1);
expect(bodyIdx).toBeGreaterThan(polIdx);
});
it('places requireSecurity before requireSignature when both are set', () => {
it('places requirePolicy before requireSignature when both are set', () => {
const op = opOperation('post', {
signature: 'MY_KEY',
security: { requireMfa: true, loc: { file: 'test.op', line: 1 } },
security: { policy: 'paymentsWrite', loc: { file: 'test.op', line: 1 } },
request: opRequest('Payload'),

@@ -958,12 +958,12 @@ });

expect(routeLine).toBeDefined();
const secIdx = routeLine!.indexOf(`requireSecurity`);
const polIdx = routeLine!.indexOf(`requirePolicy`);
const sigIdx = routeLine!.indexOf(`requireSignature`);
expect(secIdx).toBeGreaterThan(-1);
expect(sigIdx).toBeGreaterThan(secIdx);
expect(polIdx).toBeGreaterThan(-1);
expect(sigIdx).toBeGreaterThan(polIdx);
});
it('imports both requireSecurity and requireSignature when both are set', () => {
it('imports both requirePolicy and requireSignature when both are set', () => {
const op = opOperation('post', {
signature: 'MY_KEY',
security: { requireMfa: true, loc: { file: 'test.op', line: 1 } },
security: { policy: 'paymentsWrite', loc: { file: 'test.op', line: 1 } },
request: opRequest('Payload'),

@@ -973,14 +973,14 @@ });

const out = generateOp(root);
expect(out).toContain(`import { ServerKitRouter, bodyParserMiddleware, requireSecurity, requireSignature }`);
expect(out).toContain(`import { ServerKitRouter, bodyParserMiddleware, requirePolicy, requireSignature }`);
});
it('works with route-level requireMfa security', () => {
it('works with route-level policy security', () => {
const op = opOperation('get');
const route = opRoute('/users', [op]);
route.security = { requireMfa: true, loc: { file: 'test.op', line: 1 } };
route.security = { policy: 'paymentsWrite', loc: { file: 'test.op', line: 1 } };
const root = opRoot([route]);
const out = generateOp(root);
expect(out).toContain(`requireSecurity({ requireMfa: true })`);
expect(out).toContain(`requirePolicy({ policy: 'paymentsWrite' })`);
});
});
});

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display