🚀. Socket Launch Week Day 2:Introducing Manifest Alerts.Learn more
Sign In

@contractkit/plugin-typescript

Package Overview
Dependencies
Maintainers
1
Versions
23
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.16.1
to
0.17.0
+10
-24
.turbo/turbo-build$colon$ci.log

@@ -0,23 +1,9 @@

 WARN  Issue while reading "/home/runner/work/ContractKit/ContractKit/.npmrc". Failed to replace env in config: ${NPM_TOKEN}

> @contractkit/plugin-typescript@0.16.0 build:ci /Users/robert/projects/contractkit/packages/plugin-typescript
> @contractkit/plugin-typescript@0.17.0 build:ci /home/runner/work/ContractKit/ContractKit/packages/plugin-typescript
> eslint --max-warnings=0 && pnpm run build
=============
 WARN  Issue while reading "/home/runner/work/ContractKit/ContractKit/.npmrc". Failed to replace env in config: ${NPM_TOKEN}
WARNING: You are currently running a version of TypeScript which is not officially supported by @typescript-eslint/typescript-estree.
* @typescript-eslint/typescript-estree version: 8.57.2
* Supported TypeScript versions: >=4.8.4 <6.0.0
* Your TypeScript version: 6.0.2
Please only submit bug reports when using the officially supported version.
=============
> @contractkit/plugin-typescript@0.16.0 build /Users/robert/projects/contractkit/packages/plugin-typescript
> @contractkit/plugin-typescript@0.17.0 build /home/runner/work/ContractKit/ContractKit/packages/plugin-typescript
> tsup src/index.ts --format esm --sourcemap --dts && tsc --emitDeclarationOnly --declaration

@@ -30,7 +16,7 @@

ESM Build start
ESM dist/index.js 123.76 KB
ESM dist/index.js.map 267.25 KB
ESM ⚡️ Build success in 156ms
DTS Build start
DTS ⚡️ Build success in 702ms
DTS dist/index.d.ts 3.44 KB
ESM dist/index.js 124.43 KB
ESM dist/index.js.map 269.86 KB
ESM ⚡️ Build success in 321ms
DTS Build start
DTS ⚡️ Build success in 5018ms
DTS dist/index.d.ts 3.44 KB

@@ -0,81 +1,37 @@

 WARN  Issue while reading "/home/runner/work/ContractKit/ContractKit/.npmrc". Failed to replace env in config: ${NPM_TOKEN}

> @contractkit/plugin-typescript@0.16.0 test:ci /Users/robert/projects/contractkit/packages/plugin-typescript
> @contractkit/plugin-typescript@0.17.0 test:ci /home/runner/work/ContractKit/ContractKit/packages/plugin-typescript
> vitest run --coverage
[?25l
 RUN  v4.1.5 /Users/robert/projects/contractkit/packages/plugin-typescript
 RUN  v4.1.5 /home/runner/work/ContractKit/ContractKit/packages/plugin-typescript
Coverage enabled with v8
[?2026h
 ❯ tests/codegen-operation.test.ts [queued]
✓ tests/codegen-operation.test.ts (88 tests) 117ms
✓ tests/codegen-contract.test.ts (122 tests) 181ms
✓ tests/codegen-sdk.test.ts (123 tests) 181ms
✓ tests/codegen-plain-types.test.ts (58 tests) 35ms
✓ tests/codegen-server.test.ts (15 tests) 19ms
✓ tests/pipeline.test.ts (25 tests) 162ms
 Test Files 0 passed (6)
 Tests 0 passed (0)
 Start at 08:51:09
 Duration 206ms
[?2026l[?2026h
 ❯ tests/codegen-contract.test.ts [queued]
 ❯ tests/codegen-operation.test.ts [queued]
 ❯ tests/codegen-plain-types.test.ts [queued]
 ❯ tests/codegen-sdk.test.ts [queued]
 ❯ tests/codegen-server.test.ts [queued]
 ❯ tests/pipeline.test.ts [queued]
 Test Files 0 passed (6)
 Tests 0 passed (0)
 Start at 08:51:09
 Duration 481ms
[?2026l[?2026h
 ❯ tests/codegen-contract.test.ts [queued]
 ❯ tests/codegen-operation.test.ts [queued]
 ❯ tests/codegen-plain-types.test.ts [queued]
 ❯ tests/codegen-sdk.test.ts [queued]
 ❯ tests/codegen-server.test.ts [queued]
 ❯ tests/pipeline.test.ts [queued]
 Test Files 0 passed (6)
 Tests 0 passed (0)
 Start at 08:51:09
 Duration 928ms
[?2026l[?2026h
 ❯ tests/codegen-contract.test.ts [queued]
 ❯ tests/codegen-operation.test.ts [queued]
 ❯ tests/codegen-plain-types.test.ts [queued]
 ❯ tests/codegen-sdk.test.ts [queued]
 ❯ tests/codegen-server.test.ts [queued]
 ❯ tests/pipeline.test.ts 0/25
 Test Files 0 passed (6)
 Tests 0 passed (25)
 Start at 08:51:09
 Duration 1.13s
[?2026l ✓ tests/codegen-plain-types.test.ts (58 tests) 8ms
✓ tests/codegen-contract.test.ts (120 tests) 11ms
✓ tests/codegen-server.test.ts (15 tests) 10ms
✓ tests/codegen-operation.test.ts (86 tests) 28ms
✓ tests/pipeline.test.ts (25 tests) 47ms
✓ tests/codegen-sdk.test.ts (123 tests) 13ms
 Test Files  6 passed (6)
 Tests  427 passed (427)
 Start at  08:51:09
 Duration  1.31s (transform 1.99s, setup 0ms, import 4.86s, tests 118ms, environment 0ms)
 Tests  431 passed (431)
 Start at  20:56:41
 Duration  6.44s (transform 3.92s, setup 0ms, import 13.45s, tests 694ms, environment 1ms)
 % Coverage report from v8
-------------------------|---------|----------|---------|---------|-----------------------------------------------------------------------------------------------------------------------------------------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-------------------------|---------|----------|---------|---------|-----------------------------------------------------------------------------------------------------------------------------------------------------
All files  |  77.37 |  73.85 |  82.17 |  79.25 |  
 src  |  77.05 |  73.45 |  81.57 |  78.88 |  
 codegen-contract.ts  |  87.38 |  81.19 |  90.16 |  88.24 | 102,259,348-349,414-416,418,420,432-434,439,494,595,642-646,654-658,664-674,724-726,730,739-755,796-797,807-816,830-831,898,964-969,974-975,980-981
 codegen-operation.ts  |  78.18 |  74.26 |  78.08 |  79.69 | 30,44-77,85-94,164,172-173,312,387,423-426,500-502,511-514,577-597,618,632-646,651-652,668-679,684-685 
 codegen-plain-types.ts |  87.5 |  76.76 |  92.85 |  91.66 | 50-55,91,111,185,199,211,217,269 
 codegen-sdk.ts  |  88.75 |  83.6 |  85 |  91.49 | 23,308-309,578-585,594-599,606-623,678,712-729 
 index.ts  |  23.35 |  19.51 |  33.33 |  25.69 | 136,139,175-375,391,394,397,415,418,421 
 path-utils.ts  |  23.47 |  18.84 |  58.33 |  23.71 | 24-25,51,54-55,80-187 
 ts-render.ts  |  82.19 |  85.39 |  72.22 |  87.09 | 56,90,126,157-165 
 tests  |  90.38 |  90.9 |  89.28 |  93.47 |  
 helpers.ts  |  90.38 |  90.9 |  89.28 |  93.47 | 110-118 
-------------------------|---------|----------|---------|---------|-----------------------------------------------------------------------------------------------------------------------------------------------------
[?25h
-------------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-------------------|---------|----------|---------|---------|-------------------
All files | 77.47 | 74.06 | 82.02 | 79.41 |
src | 77.16 | 73.68 | 81.4 | 79.06 |
...n-contract.ts | 87.57 | 81.63 | 89.91 | 88.57 | ...1025,1030-1031
...-operation.ts | 78.18 | 74.26 | 78.08 | 79.69 | ...68-679,684-685
...lain-types.ts | 87.5 | 76.76 | 92.85 | 91.66 | ...99,211,217,269
codegen-sdk.ts | 88.75 | 83.6 | 85 | 91.49 | ...23,678,712-729
index.ts | 23.35 | 19.51 | 33.33 | 25.69 | ...97,415,418,421
path-utils.ts | 23.47 | 18.84 | 58.33 | 23.71 | ...1,54-55,80-187
ts-render.ts | 82.19 | 85.39 | 72.22 | 87.09 | 56,90,126,157-165
tests | 90.38 | 90.9 | 89.28 | 93.47 |
helpers.ts | 90.38 | 90.9 | 89.28 | 93.47 | 110-118
-------------------|---------|----------|---------|---------|-------------------
# @contractkit/contractkit-plugin-typescript
## 0.17.0
### Minor Changes
- b3f7da9: Fix `ref & ref` type alias intersections generating `ZodIntersection` instead of `ZodObject`
Contracts like `contract Foo: A & B` (two model refs, no inline fields) previously emitted `A.and(B)`, producing a `ZodIntersection`. This broke `.strict()` calls on the result and caused each strict schema to reject the other's keys at runtime.
All three rendering paths (`renderIntersection`, `renderInputType`, `renderQueryType`) now emit `.extend(B.shape)` chains for any `ref & (ref | inlineObject)*` intersection, matching the pattern already used for multi-base model inheritance.
## 0.16.1

@@ -4,0 +14,0 @@

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

<div class='fl pad1y space-right2'>
<span class="strong">77.37% </span>
<span class="strong">77.47% </span>
<span class="quiet">Statements</span>
<span class='fraction'>1686/2179</span>
<span class='fraction'>1696/2189</span>
</div>

@@ -34,5 +34,5 @@

<div class='fl pad1y space-right2'>
<span class="strong">73.85% </span>
<span class="strong">74.06% </span>
<span class="quiet">Branches</span>
<span class='fraction'>1076/1457</span>
<span class='fraction'>1091/1473</span>
</div>

@@ -42,5 +42,5 @@

<div class='fl pad1y space-right2'>
<span class="strong">82.17% </span>
<span class="strong">82.02% </span>
<span class="quiet">Functions</span>
<span class='fraction'>295/359</span>
<span class='fraction'>292/356</span>
</div>

@@ -50,5 +50,5 @@

<div class='fl pad1y space-right2'>
<span class="strong">79.25% </span>
<span class="strong">79.41% </span>
<span class="quiet">Lines</span>
<span class='fraction'>1463/1846</span>
<span class='fraction'>1478/1861</span>
</div>

@@ -87,13 +87,13 @@

<td class="file medium" data-value="src"><a href="src/index.html">src</a></td>
<td data-value="77.05" class="pic medium">
<td data-value="77.16" class="pic medium">
<div class="chart"><div class="cover-fill" style="width: 77%"></div><div class="cover-empty" style="width: 23%"></div></div>
</td>
<td data-value="77.05" class="pct medium">77.05%</td>
<td data-value="2127" class="abs medium">1639/2127</td>
<td data-value="73.45" class="pct medium">73.45%</td>
<td data-value="1424" class="abs medium">1046/1424</td>
<td data-value="81.57" class="pct high">81.57%</td>
<td data-value="331" class="abs high">270/331</td>
<td data-value="78.88" class="pct medium">78.88%</td>
<td data-value="1800" class="abs medium">1420/1800</td>
<td data-value="77.16" class="pct medium">77.16%</td>
<td data-value="2137" class="abs medium">1649/2137</td>
<td data-value="73.68" class="pct medium">73.68%</td>
<td data-value="1440" class="abs medium">1061/1440</td>
<td data-value="81.4" class="pct high">81.4%</td>
<td data-value="328" class="abs high">267/328</td>
<td data-value="79.06" class="pct medium">79.06%</td>
<td data-value="1815" class="abs medium">1435/1815</td>
</tr>

@@ -124,3 +124,3 @@

<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2026-04-29T12:51:10.318Z
at 2026-04-30T20:56:48.353Z
</div>

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

@@ -889,3 +889,3 @@

<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2026-04-29T12:51:10.318Z
at 2026-04-30T20:56:48.353Z
</div>

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

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

<div class='fl pad1y space-right2'>
<span class="strong">77.05% </span>
<span class="strong">77.16% </span>
<span class="quiet">Statements</span>
<span class='fraction'>1639/2127</span>
<span class='fraction'>1649/2137</span>
</div>

@@ -34,5 +34,5 @@

<div class='fl pad1y space-right2'>
<span class="strong">73.45% </span>
<span class="strong">73.68% </span>
<span class="quiet">Branches</span>
<span class='fraction'>1046/1424</span>
<span class='fraction'>1061/1440</span>
</div>

@@ -42,5 +42,5 @@

<div class='fl pad1y space-right2'>
<span class="strong">81.57% </span>
<span class="strong">81.4% </span>
<span class="quiet">Functions</span>
<span class='fraction'>270/331</span>
<span class='fraction'>267/328</span>
</div>

@@ -50,5 +50,5 @@

<div class='fl pad1y space-right2'>
<span class="strong">78.88% </span>
<span class="strong">79.06% </span>
<span class="quiet">Lines</span>
<span class='fraction'>1420/1800</span>
<span class='fraction'>1435/1815</span>
</div>

@@ -87,13 +87,13 @@

<td class="file high" data-value="codegen-contract.ts"><a href="codegen-contract.ts.html">codegen-contract.ts</a></td>
<td data-value="87.38" class="pic high">
<td data-value="87.57" class="pic high">
<div class="chart"><div class="cover-fill" style="width: 87%"></div><div class="cover-empty" style="width: 13%"></div></div>
</td>
<td data-value="87.38" class="pct high">87.38%</td>
<td data-value="642" class="abs high">561/642</td>
<td data-value="81.19" class="pct high">81.19%</td>
<td data-value="436" class="abs high">354/436</td>
<td data-value="90.16" class="pct high">90.16%</td>
<td data-value="122" class="abs high">110/122</td>
<td data-value="88.24" class="pct high">88.24%</td>
<td data-value="519" class="abs high">458/519</td>
<td data-value="87.57" class="pct high">87.57%</td>
<td data-value="652" class="abs high">571/652</td>
<td data-value="81.63" class="pct high">81.63%</td>
<td data-value="452" class="abs high">369/452</td>
<td data-value="89.91" class="pct high">89.91%</td>
<td data-value="119" class="abs high">107/119</td>
<td data-value="88.57" class="pct high">88.57%</td>
<td data-value="534" class="abs high">473/534</td>
</tr>

@@ -199,3 +199,3 @@

<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2026-04-29T12:51:10.318Z
at 2026-04-30T20:56:48.353Z
</div>

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

@@ -637,3 +637,3 @@

<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2026-04-29T12:51:10.318Z
at 2026-04-30T20:56:48.353Z
</div>

@@ -640,0 +640,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-04-29T12:51:10.318Z
at 2026-04-30T20:56:48.353Z
</div>

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

@@ -344,7 +344,7 @@

<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">832x</span>
<span class="cline-any cline-yes">847x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">303x</span>
<span class="cline-any cline-yes">308x</span>
<span class="cline-any cline-neutral">&nbsp;</span>

@@ -393,3 +393,3 @@ <span class="cline-any cline-neutral">&nbsp;</span>

<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">237x</span>
<span class="cline-any cline-yes">242x</span>
<span class="cline-any cline-neutral">&nbsp;</span>

@@ -406,3 +406,3 @@ <span class="cline-any cline-neutral">&nbsp;</span>

<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">160x</span>
<span class="cline-any cline-yes">166x</span>
<span class="cline-any cline-neutral">&nbsp;</span>

@@ -417,3 +417,3 @@ <span class="cline-any cline-neutral">&nbsp;</span>

<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">130x</span>
<span class="cline-any cline-yes">132x</span>
<span class="cline-any cline-neutral">&nbsp;</span>

@@ -468,14 +468,14 @@ <span class="cline-any cline-neutral">&nbsp;</span>

<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">52x</span>
<span class="cline-any cline-yes">52x</span>
<span class="cline-any cline-yes">43x</span>
<span class="cline-any cline-yes">5x</span>
<span class="cline-any cline-yes">5x</span>
<span class="cline-any cline-yes">54x</span>
<span class="cline-any cline-yes">54x</span>
<span class="cline-any cline-yes">45x</span>
<span class="cline-any cline-yes">7x</span>
<span class="cline-any cline-yes">7x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">201x</span>
<span class="cline-any cline-yes">201x</span>
<span class="cline-any cline-yes">201x</span>
<span class="cline-any cline-yes">201x</span>
<span class="cline-any cline-yes">203x</span>
<span class="cline-any cline-yes">203x</span>
<span class="cline-any cline-yes">203x</span>
<span class="cline-any cline-yes">203x</span>
<span class="cline-any cline-neutral">&nbsp;</span>

@@ -494,8 +494,8 @@ <span class="cline-any cline-neutral">&nbsp;</span>

<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">191x</span>
<span class="cline-any cline-yes">191x</span>
<span class="cline-any cline-yes">193x</span>
<span class="cline-any cline-yes">193x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">185x</span>
<span class="cline-any cline-yes">187x</span>
<span class="cline-any cline-neutral">&nbsp;</span>

@@ -820,3 +820,3 @@ <span class="cline-any cline-neutral">&nbsp;</span>

<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2026-04-29T12:51:10.318Z
at 2026-04-30T20:56:48.353Z
</div>

@@ -823,0 +823,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-04-29T12:51:10.318Z
at 2026-04-30T20:56:48.353Z
</div>

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

import type { ContractRootNode, ModelNode, ContractTypeNode, ObjectMode } from '@contractkit/core';
/**
* Maps a ContractKit object mode to its Zod constructor name.
*
* @returns `"z.strictObject"` | `"z.object"` | `"z.looseObject"`
*/
export declare function modeToWrapper(mode: ObjectMode): string;

@@ -21,3 +26,21 @@ export interface ContractCodegenContext {

export declare function computeModelsWithInput(models: ModelNode[], externalModelsWithInput?: Set<string>): Set<string>;
/**
* Generate a TypeScript module containing Zod schemas for every model in `root`.
*
* Emits up to three schemas per model when visibility modifiers are present:
* `ModelBase` (all fields), `Model` (read — no writeonly), `ModelInput` (write — no readonly).
*
* @param root - The parsed contract root node.
* @param context - Optional cross-file context for import resolution and Input/Output variant tracking.
* @returns The full TypeScript source as a string.
*/
export declare function generateContract(root: ContractRootNode, context?: ContractCodegenContext): string;
/**
* Render a ContractKit AST type node as a Zod schema expression string.
*
* @param parseCaseTransform - When set, generates a `.transform()` that remaps incoming keys from
* the given casing (`'snake'` | `'pascal'`) to camelCase for `inlineObject` types.
* @param defaultMode - Fallback object mode (`'strict'` | `'strip'` | `'loose'`) when the node
* doesn't specify its own mode.
*/
export declare function renderType(type: ContractTypeNode, parseCaseTransform?: 'snake' | 'pascal', defaultMode?: ObjectMode): string;

@@ -24,0 +47,0 @@ /**

@@ -1,1 +0,1 @@

{"version":3,"file":"codegen-contract.d.ts","sourceRoot":"","sources":["../src/codegen-contract.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACR,gBAAgB,EAChB,SAAS,EAET,gBAAgB,EAWhB,UAAU,EACb,MAAM,mBAAmB,CAAC;AAO3B,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAStD;AAID,MAAM,WAAW,sBAAsB;IACnC,sDAAsD;IACtD,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,8DAA8D;IAC9D,cAAc,EAAE,MAAM,CAAC;IACvB,qFAAqF;IACrF,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC9B,oFAAoF;IACpF,gBAAgB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC/B,6GAA6G;IAC7G,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAChC;AAID;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,uBAAuB,GAAE,GAAG,CAAC,MAAM,CAAa,GAAG,GAAG,CAAC,MAAM,CAAC,CAsCzH;AAkBD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,gBAAgB,EAAE,OAAO,CAAC,EAAE,sBAAsB,GAAG,MAAM,CAkEjG;AA+RD,wBAAgB,UAAU,CAAC,IAAI,EAAE,gBAAgB,EAAE,kBAAkB,CAAC,EAAE,OAAO,GAAG,QAAQ,EAAE,WAAW,CAAC,EAAE,UAAU,GAAG,MAAM,CA6B5H;AAiND;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,gBAAgB,EAAE,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC,EAAE,UAAU,GAAG,MAAM,CA8CvH;AA2BD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,gBAAgB,EAAE,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC,EAAE,UAAU,GAAG,MAAM,CA6BvH;AA0CD,wBAAgB,eAAe,CAAC,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAuB7E;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAE7E;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAiBjE;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,gBAAgB,GAAG,MAAM,EAAE,CAcpE;AAED,0EAA0E;AAC1E,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,gBAAgB,EAAE,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM,EAAE,CA4BvG;AAmCD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,SAAS,EAAE,CA6D/D;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,sBAAsB,GAAG,MAAM,CAgB3F;AAED,gGAAgG;AAChG,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEpD"}
{"version":3,"file":"codegen-contract.d.ts","sourceRoot":"","sources":["../src/codegen-contract.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACR,gBAAgB,EAChB,SAAS,EAET,gBAAgB,EAWhB,UAAU,EACb,MAAM,mBAAmB,CAAC;AAO3B;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAStD;AAID,MAAM,WAAW,sBAAsB;IACnC,sDAAsD;IACtD,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,8DAA8D;IAC9D,cAAc,EAAE,MAAM,CAAC;IACvB,qFAAqF;IACrF,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC9B,oFAAoF;IACpF,gBAAgB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC/B,6GAA6G;IAC7G,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAChC;AAID;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,uBAAuB,GAAE,GAAG,CAAC,MAAM,CAAa,GAAG,GAAG,CAAC,MAAM,CAAC,CAsCzH;AAkBD;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,gBAAgB,EAAE,OAAO,CAAC,EAAE,sBAAsB,GAAG,MAAM,CAkEjG;AA+RD;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,gBAAgB,EAAE,kBAAkB,CAAC,EAAE,OAAO,GAAG,QAAQ,EAAE,WAAW,CAAC,EAAE,UAAU,GAAG,MAAM,CA6B5H;AA0ND;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,gBAAgB,EAAE,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC,EAAE,UAAU,GAAG,MAAM,CAuDvH;AA2BD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,gBAAgB,EAAE,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC,EAAE,UAAU,GAAG,MAAM,CAsCvH;AA0CD,wBAAgB,eAAe,CAAC,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAuB7E;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAE7E;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAiBjE;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,gBAAgB,GAAG,MAAM,EAAE,CAcpE;AAED,0EAA0E;AAC1E,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,gBAAgB,EAAE,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM,EAAE,CA4BvG;AAmCD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,SAAS,EAAE,CA6D/D;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,sBAAsB,GAAG,MAAM,CAgB3F;AAED,gGAAgG;AAChG,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEpD"}
{
"name": "@contractkit/plugin-typescript",
"version": "0.16.1",
"version": "0.17.0",
"description": "ContractKit built-in plugin: TypeScript codegen (SDK clients, Koa routers, Zod schemas, plain types)",

@@ -5,0 +5,0 @@ "author": {

@@ -25,2 +25,7 @@ import { relative, dirname } from 'node:path';

/**
* Maps a ContractKit object mode to its Zod constructor name.
*
* @returns `"z.strictObject"` | `"z.object"` | `"z.looseObject"`
*/
export function modeToWrapper(mode: ObjectMode): string {

@@ -115,2 +120,12 @@ switch (mode) {

/**
* Generate a TypeScript module containing Zod schemas for every model in `root`.
*
* Emits up to three schemas per model when visibility modifiers are present:
* `ModelBase` (all fields), `Model` (read — no writeonly), `ModelInput` (write — no readonly).
*
* @param root - The parsed contract root node.
* @param context - Optional cross-file context for import resolution and Input/Output variant tracking.
* @returns The full TypeScript source as a string.
*/
export function generateContract(root: ContractRootNode, context?: ContractCodegenContext): string {

@@ -469,2 +484,10 @@ const needsDateTime = rootNeedsDateTime(root);

/**
* Render a ContractKit AST type node as a Zod schema expression string.
*
* @param parseCaseTransform - When set, generates a `.transform()` that remaps incoming keys from
* the given casing (`'snake'` | `'pascal'`) to camelCase for `inlineObject` types.
* @param defaultMode - Fallback object mode (`'strict'` | `'strip'` | `'loose'`) when the node
* doesn't specify its own mode.
*/
export function renderType(type: ContractTypeNode, parseCaseTransform?: 'snake' | 'pascal', defaultMode?: ObjectMode): string {

@@ -637,21 +660,30 @@ switch (type.kind) {

const [first, ...rest] = i.members;
// When the pattern is ref & { inlineObject(s) }, use .extend() to produce a
// single merged ZodObject. Using .and(z.strictObject) breaks because each
// strict side rejects the other side's keys during intersection parsing.
if (first && first.kind === 'ref' && rest.length > 0 && rest.every(m => m.kind === 'inlineObject')) {
const allFields = rest.flatMap(m => (m as InlineObjectTypeNode).fields);
const fieldLines =
parseCaseTransform === 'snake'
? renderFieldsAsSnakeCase(allFields, defaultMode)
.map(l => ` ${l}`)
.join('\n')
: parseCaseTransform === 'pascal'
? renderFieldsAsPascalCase(allFields, defaultMode)
.map(l => ` ${l}`)
.join('\n')
: allFields
.flatMap(f => renderField(f, defaultMode))
.map(l => ` ${l}`)
.join('\n');
return `${first.name}.extend({\n${fieldLines}\n})`;
// When the pattern is ref & (ref | inlineObject)*, use .extend() chains to
// produce a single ZodObject. .and() breaks strict objects — each strict side
// rejects the other side's keys during intersection parsing, and ZodIntersection
// has no .strict() method.
if (first && first.kind === 'ref' && rest.length > 0 && rest.every(m => m.kind === 'ref' || m.kind === 'inlineObject')) {
let expr = first.name;
for (const member of rest) {
if (member.kind === 'ref') {
expr += `.extend(${member.name}.shape)`;
} else {
const m = member as InlineObjectTypeNode;
const fieldLines =
parseCaseTransform === 'snake'
? renderFieldsAsSnakeCase(m.fields, defaultMode)
.map(l => ` ${l}`)
.join('\n')
: parseCaseTransform === 'pascal'
? renderFieldsAsPascalCase(m.fields, defaultMode)
.map(l => ` ${l}`)
.join('\n')
: m.fields
.flatMap(f => renderField(f, defaultMode))
.map(l => ` ${l}`)
.join('\n');
expr += `.extend({\n${fieldLines}\n})`;
}
}
return expr;
}

@@ -737,7 +769,16 @@ let expr = renderType(first!, parseCaseTransform, defaultMode);

const [first, ...rest] = type.members;
if (first && first.kind === 'ref' && rest.length > 0 && rest.every(m => m.kind === 'inlineObject')) {
const base = modelsWithInput?.has(first.name) ? `${first.name}Input` : first.name;
const allFields = rest.flatMap(m => (m as InlineObjectTypeNode).fields);
const fieldLines = allFields.map(f => ` ${renderInputField(f, modelsWithInput ?? new Set(), defaultMode)}`).join('\n');
return `${base}.extend({\n${fieldLines}\n})`;
if (first && first.kind === 'ref' && rest.length > 0 && rest.every(m => m.kind === 'ref' || m.kind === 'inlineObject')) {
let expr = modelsWithInput?.has(first.name) ? `${first.name}Input` : first.name;
for (const member of rest) {
if (member.kind === 'ref') {
const name = modelsWithInput?.has(member.name) ? `${member.name}Input` : member.name;
expr += `.extend(${name}.shape)`;
} else {
const fieldLines = (member as InlineObjectTypeNode).fields
.map(f => ` ${renderInputField(f, modelsWithInput ?? new Set(), defaultMode)}`)
.join('\n');
expr += `.extend({\n${fieldLines}\n})`;
}
}
return expr;
}

@@ -806,7 +847,16 @@ let expr = renderInputType(first!, modelsWithInput, defaultMode);

const [first, ...rest] = type.members;
if (first && first.kind === 'ref' && rest.length > 0 && rest.every(m => m.kind === 'inlineObject')) {
const base = modelsWithInput?.has(first.name) ? `${first.name}Input` : first.name;
const allFields = rest.flatMap(m => (m as InlineObjectTypeNode).fields);
const fieldLines = allFields.map(f => ` ${renderQueryField(f, modelsWithInput, defaultMode)}`).join('\n');
return `${base}.extend({\n${fieldLines}\n})`;
if (first && first.kind === 'ref' && rest.length > 0 && rest.every(m => m.kind === 'ref' || m.kind === 'inlineObject')) {
let expr = modelsWithInput?.has(first.name) ? `${first.name}Input` : first.name;
for (const member of rest) {
if (member.kind === 'ref') {
const name = modelsWithInput?.has(member.name) ? `${member.name}Input` : member.name;
expr += `.extend(${name}.shape)`;
} else {
const fieldLines = (member as InlineObjectTypeNode).fields
.map(f => ` ${renderQueryField(f, modelsWithInput, defaultMode)}`)
.join('\n');
expr += `.extend({\n${fieldLines}\n})`;
}
}
return expr;
}

@@ -813,0 +863,0 @@ let expr = renderQueryType(first!, modelsWithInput, defaultMode);

@@ -716,2 +716,40 @@ import { generateContract, renderType } from '../src/codegen-contract.js';

});
it('ref & ref type alias uses .extend(B.shape) instead of .and()', () => {
const root = contractRoot([
model('Pagination', [field('page', scalarType('int'))]),
model('Filter', [field('status', scalarType('string'), { optional: true })]),
model('ListQuery', [], {
type: {
kind: 'intersection',
members: [
{ kind: 'ref', name: 'Pagination' },
{ kind: 'ref', name: 'Filter' },
],
},
}),
]);
const output = generateContract(root);
expect(output).toContain('export const ListQuery = Pagination.extend(Filter.shape)');
expect(output).not.toContain('.and(');
});
it('ref & ref type alias substitutes Input variant when base has Input', () => {
const root = contractRoot([
model('Pagination', [field('page', scalarType('int')), field('total', scalarType('int'), { visibility: 'readonly' })]),
model('Filter', [field('status', scalarType('string'), { optional: true })]),
model('ListQuery', [], {
type: {
kind: 'intersection',
members: [
{ kind: 'ref', name: 'Pagination' },
{ kind: 'ref', name: 'Filter' },
],
},
}),
]);
const output = generateContract(root);
expect(output).toContain('export const ListQuery = Pagination.extend(Filter.shape)');
expect(output).toContain('export const ListQueryInput = PaginationInput.extend(Filter.shape)');
});
});

@@ -718,0 +756,0 @@

@@ -327,2 +327,41 @@ import { describe, it, expect } from 'vitest';

it('ref & ref intersection query uses .extend(B.shape) not .and()', () => {
const root = opRoot([
opRoute('/persons', [
opOperation('get', {
query: {
kind: 'intersection',
members: [
{ kind: 'ref', name: 'Pagination' },
{ kind: 'ref', name: 'PersonQuery' },
],
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any,
}),
]),
]);
const output = generateOp(root);
expect(output).toContain('Pagination.extend(PersonQuery.shape)');
expect(output).not.toContain('.and(');
});
it('ref & ref intersection query substitutes Input variants', () => {
const root = opRoot([
opRoute('/persons', [
opOperation('get', {
query: {
kind: 'intersection',
members: [
{ kind: 'ref', name: 'Pagination' },
{ kind: 'ref', name: 'PersonQuery' },
],
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any,
}),
]),
]);
const output = generateOp(root, { modelsWithInput: new Set(['Pagination']) });
expect(output).toContain('PaginationInput.extend(PersonQuery.shape)');
});
it('wraps ContractTypeNode intersection query with array fields using z.preprocess', () => {

@@ -329,0 +368,0 @@ const root = opRoot([

> @contractkit/plugin-typescript@0.16.0 build /Users/robert/projects/contractkit/packages/plugin-typescript
> tsup src/index.ts --format esm --sourcemap --dts && tsc --emitDeclarationOnly --declaration
CLI Building entry: src/index.ts
CLI Using tsconfig: tsconfig.json
CLI tsup v8.5.1
CLI Target: esnext
ESM Build start
ESM dist/index.js 123.76 KB
ESM dist/index.js.map 267.25 KB
ESM ⚡️ Build success in 95ms
DTS Build start
DTS ⚡️ Build success in 744ms
DTS dist/index.d.ts 3.44 KB
> @contractkit/plugin-typescript@0.16.0 test /Users/robert/projects/contractkit/packages/plugin-typescript
> vitest run
 RUN  v4.1.5 /Users/robert/projects/contractkit/packages/plugin-typescript
✓ tests/codegen-contract.test.ts (120 tests) 10ms
✓ tests/codegen-plain-types.test.ts (58 tests) 10ms
✓ tests/codegen-sdk.test.ts (123 tests) 16ms
✓ tests/codegen-operation.test.ts (86 tests) 9ms
✓ tests/pipeline.test.ts (25 tests) 29ms
✓ tests/codegen-server.test.ts (15 tests) 6ms
 Test Files  6 passed (6)
 Tests  427 passed (427)
 Start at  08:51:34
 Duration  472ms (transform 591ms, setup 0ms, import 1.33s, tests 80ms, environment 0ms)

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