openapi-sync 5.0.7 → 6.0.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.
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import {o,l,m,j,k as k$1}from'../chunk-TTLQP4UN.mjs';import'../chunk-L52BXDAC.mjs';import {c,b}from'../chunk-3VKPQNDD.mjs';import {e,f}from'../chunk-VLACEFT4.mjs';import {McpServer}from'@modelcontextprotocol/sdk/server/mcp.js';import {StdioServerTransport}from'@modelcontextprotocol/sdk/server/stdio.js';import {z}from'zod';import f$1 from'path';import x from'fs';var k=e(c$1=>{o();c();var s=(...t)=>process.stderr.write(t.join(" ")+`
3
+ `),d=process.cwd(),a=new McpServer({name:"openapi-sync",version:"1.0.0"});a.tool("openapi_sync_validate","Validate the openapi-sync config file and all configured API specs without writing any files to disk. Use this as a pre-flight check before syncing. Returns a structured result with per-API validity and endpoint counts.",{},()=>f(null,null,function*(){s("[openapi-sync-mcp] Running validate...");try{let t=yield l({silent:!0});return {content:[{type:"text",text:JSON.stringify(t,null,2)}]}}catch(t){return {content:[{type:"text",text:JSON.stringify({valid:false,configErrors:[t.message],apis:{}})}],isError:true}}}));a.tool("openapi_sync_list_endpoints","Fetch and parse all configured OpenAPI specs, then return a structured list of every endpoint (name, HTTP method, path, tags, summary). No files are written. Use this to understand the API surface before deciding on a client type or tag filters.",{apiName:z.string().optional().describe("Limit results to a specific API name from the config. Omit to list endpoints for all configured APIs."),tags:z.array(z.string()).optional().describe("Filter endpoints to only those with one of these OpenAPI tags.")},n=>f(null,[n],function*({apiName:t,tags:r}){s("[openapi-sync-mcp] Listing endpoints...");try{let o=yield m({apiName:t,tags:r,silent:!0});return {content:[{type:"text",text:JSON.stringify(o,null,2)}]}}catch(o){return {content:[{type:"text",text:`Error: ${o.message}`}],isError:true}}}));a.tool("openapi_sync_sync","Run a full openapi-sync \u2014 fetches all configured OpenAPI specs and writes TypeScript types, endpoint builder functions, and optional validation schemas (Zod/Yup/Joi) to disk. Returns a SyncResult with the list of files written and any errors. Run this after validating your config.",{refetchInterval:z.number().optional().describe("If set, enables continuous auto-sync at this interval (ms). Omit for a single one-time sync.")},r=>f(null,[r],function*({refetchInterval:t}){s("[openapi-sync-mcp] Running sync...");try{let n=yield j({refetchInterval:t,silent:!0});return {content:[{type:"text",text:JSON.stringify(n,null,2)}],isError:!n.success}}catch(n){return {content:[{type:"text",text:JSON.stringify({success:false,apis:[],filesWritten:[],endpointCount:0,warnings:[],errors:[n.message]})}],isError:true}}}));a.tool("openapi_sync_generate_client","Generate a fully-typed API client for one or all configured APIs. Supports: fetch, axios, react-query, swr, rtk-query. Syncs the latest spec first, then writes client files to disk. Returns a SyncResult with the list of files written.",{type:z.enum(["fetch","axios","react-query","swr","rtk-query"]).describe("The type of API client to generate."),apiName:z.string().optional().describe("API name from the config to generate a client for. Omit to generate for all configured APIs."),baseURL:z.string().optional().describe("Base URL to bake into the generated client (e.g. https://api.example.com). Can be overridden at runtime in the generated code."),tags:z.array(z.string()).optional().describe("Only generate client methods for endpoints with these tags."),endpoints:z.array(z.string()).optional().describe("Only generate client methods for these specific endpoint names / operationIds."),outputDir:z.string().optional().describe("Custom output directory for the generated client files. Defaults to the path set in your openapi.sync config.")},I=>f(null,[I],function*({type:t,apiName:r,baseURL:n,tags:o,endpoints:u,outputDir:y}){s(`[openapi-sync-mcp] Generating ${t} client...`);try{let p=yield k$1({type:t,apiName:r,baseURL:n,tags:o,endpoints:u,outputDir:y,silent:!0});return {content:[{type:"text",text:JSON.stringify(p,null,2)}],isError:!p.success}}catch(p){return {content:[{type:"text",text:JSON.stringify({success:false,apis:[],filesWritten:[],endpointCount:0,warnings:[],errors:[p.message]})}],isError:true}}}));a.tool("openapi_sync_init","Create an openapi.sync config file in the current working directory without any interactive prompts. Use this to set up a project from scratch. After calling this tool, run openapi_sync_validate and then openapi_sync_sync.",{apiName:z.string().describe("A short identifier for this API used as a folder name and config key (e.g. 'petstore', 'my-api'). Letters, numbers, hyphens and underscores only."),apiSource:z.string().describe("URL to the OpenAPI spec (https://...) or relative path to a local file (e.g. ./api/openapi.yaml)."),outputFolder:z.string().optional().default("./src/api").describe("Output folder for generated files (default: ./src/api)."),configFormat:z.enum(["typescript","json","javascript"]).optional().default("typescript").describe("Config file format (default: typescript)."),clientType:z.enum(["react-query","swr","fetch","axios","rtk-query"]).optional().describe("Client type to pre-configure in the config. Omit to skip client generation."),validationLibrary:z.enum(["zod","yup","joi"]).optional().describe("Validation library to pre-configure. Omit to skip validation schema generation."),folderSplit:z.boolean().optional().default(false).describe("Organize generated files into folders by OpenAPI tags."),typesPrefix:z.string().optional().default("I").describe("Prefix for generated TypeScript interface names (default: 'I', e.g. IPet)."),excludeTags:z.array(z.string()).optional().describe("Tags to exclude from generation (e.g. ['deprecated', 'internal'])."),runSync:z.boolean().optional().default(false).describe("If true, immediately run a full sync after creating the config file.")},$=>f(null,[$],function*({apiName:t,apiSource:r,outputFolder:n,configFormat:o,clientType:u,validationLibrary:y,folderSplit:I,typesPrefix:p,excludeTags:S,runSync:O}){s("[openapi-sync-mcp] Creating config...");try{let l=yield b({apiName:t,apiSource:r,outputFolder:n,configFormat:o,clientType:u,validationLibrary:y,folderSplit:I,typesPrefix:p,excludeTags:S,runSync:O,silent:!0});return {content:[{type:"text",text:JSON.stringify(l,null,2)}],isError:!l.success}}catch(l){return {content:[{type:"text",text:JSON.stringify({success:false,configFile:"",message:l.message,errors:[l.message]})}],isError:true}}}));a.tool("openapi_sync_read_config","Read the current openapi.sync config file from the working directory and return its contents as a string. Useful to inspect what APIs are configured before running sync or generate-client.",{},()=>f(null,null,function*(){s("[openapi-sync-mcp] Reading config...");let t=[f$1.join(d,"openapi.sync.ts"),f$1.join(d,"openapi.sync.js"),f$1.join(d,"openapi.sync.json")];for(let r of t)if(x.existsSync(r))try{let n=x.readFileSync(r,"utf-8");return {content:[{type:"text",text:JSON.stringify({found:!0,file:f$1.basename(r),path:r,content:n},null,2)}]}}catch(n){return {content:[{type:"text",text:JSON.stringify({found:false,error:`Could not read ${r}: ${n.message}`})}],isError:true}}return {content:[{type:"text",text:JSON.stringify({found:false,searched:t,message:"No openapi.sync config file found. Use the openapi_sync_init tool to create one."})}]}}));function R(){return f(this,null,function*(){let t=new StdioServerTransport;yield a.connect(t),s("[openapi-sync-mcp] Server running on stdio. Ready for tool calls.");})}R().catch(t=>{s("[openapi-sync-mcp] Fatal error:",t),process.exit(1);});});var server = k();
4
+ export{server as default};
@@ -0,0 +1,11 @@
1
+ import {o,d as d$1,f as f$1}from'./chunk-L52BXDAC.mjs';import {d,f,c}from'./chunk-VLACEFT4.mjs';import l from'path';import $ from'fs';import P from'axios';import w from'@apidevtools/swagger-parser';var g,E,b,O,S=d(()=>{o();g=process.cwd(),E=()=>{let i=l.join(g,"openapi.sync.js"),d=l.join(g,"openapi.sync.ts"),r=l.join(g,"openapi.sync.json"),o=[i,d,r];try{c("esbuild-register");}catch(s){}for(let s of o)if($.existsSync(s))try{let n=c(s);return Object.keys(n).length===1&&n.default&&(n=n.default),typeof n=="function"&&(n=n()),{config:n,errors:[]}}catch(n){let c=n instanceof Error?n.message:String(n);return {config:null,errors:[`Failed to parse ${s}: ${c}`]}}return {config:null,errors:[`No config file found. Searched: ${o.join(", ")}. Run \`npx openapi-sync init --no-interactive\` to create one.`]}},b=(i,d)=>f(null,null,function*(){var c;let r;try{if(i.startsWith("http://")||i.startsWith("https://"))r=(yield P.get(i,{timeout:15e3})).data;else {let a=l.isAbsolute(i)?i:l.join(g,i);r=yield $.promises.readFile(a,"utf-8");}}catch(t){return {endpointCount:0,error:`Could not fetch/read spec: ${t instanceof Error?t.message:String(t)}`}}let o;try{let t=d$1(r)?r:f$1(r);o=yield w.parse(t);}catch(t){return {endpointCount:0,error:`Could not parse spec: ${t instanceof Error?t.message:String(t)}`}}let s=0,n=(o==null?void 0:o.paths)||{};for(let t of Object.keys(n)){let a=["get","post","put","patch","delete","head","options"];for(let e of a)(c=n[t])!=null&&c[e]&&s++;}return {endpointCount:s}}),O=i=>f(null,null,function*(){var a;let r=((a=i==null?void 0:i.silent)!=null?a:false)?{log:()=>{},warn:()=>{},error:()=>{}}:{log:console.log,warn:console.warn,error:console.error},o={valid:false,apis:{},configErrors:[]},{config:s,errors:n}=E();if(n.length>0)return o.configErrors=n,r.error(`\u274C Config errors:
2
+ ${n.map(e=>` \u2022 ${e}`).join(`
3
+ `)}`),o;if(!s)return o.configErrors=["Config loaded but is empty or null."],o;if((!s.api||Object.keys(s.api).length===0)&&o.configErrors.push('Config must have at least one API defined under the "api" key.'),o.configErrors.length>0)return r.error(`\u274C Config validation errors:
4
+ ${o.configErrors.map(e=>` \u2022 ${e}`).join(`
5
+ `)}`),o;let c=Object.keys(s.api);r.log(`
6
+ \u{1F50D} Validating ${c.length} API spec(s)...
7
+ `);let t=true;return yield Promise.all(c.map(e=>f(null,null,function*(){let h=s.api[e];r.log(` \u{1F50E} ${e}: ${h}`);let{endpointCount:m,error:p}=yield b(h,e);p?(o.apis[e]={valid:false,endpointCount:0,error:p},r.error(` \u274C ${e}: ${p}`),t=false):(o.apis[e]={valid:true,endpointCount:m},r.log(` \u2705 ${e}: ${m} endpoint(s) found`));}))),o.valid=t&&o.configErrors.length===0,o.valid?r.log(`
8
+ \u2705 All checks passed \u2014 config and specs are valid.
9
+ `):r.error(`
10
+ \u274C Validation failed \u2014 see errors above.
11
+ `),o});});S();export{O as validateConfig};
package/llms.txt ADDED
@@ -0,0 +1,334 @@
1
+ # openapi-sync
2
+
3
+ A CLI + Node.js library that generates TypeScript types, fully-typed API clients,
4
+ and runtime validation schemas from OpenAPI (Swagger) specifications.
5
+
6
+ Supports: TypeScript, Python, Fetch, Axios, React Query, SWR, RTK Query, Zod, Yup, Joi.
7
+ npm: https://www.npmjs.com/package/openapi-sync
8
+ Docs: https://openapi-sync.com
9
+ GitHub: https://github.com/akintomiwa-fisayo/openapi-sync
10
+
11
+
12
+ ## AGENT QUICK-START
13
+
14
+ Minimum working setup (no interactive prompts needed):
15
+
16
+ ```bash
17
+ # 1. Create config (agent-safe, no prompts)
18
+ npx openapi-sync init --no-interactive \
19
+ --api-name petstore \
20
+ --api-url https://petstore3.swagger.io/api/v3/openapi.json \
21
+ --output-folder ./src/api \
22
+ --config-format typescript
23
+
24
+ # 2. Validate config and spec before writing any files
25
+ npx openapi-sync validate --json
26
+
27
+ # 3. Sync — generates types, endpoints, validation schemas
28
+ npx openapi-sync --json
29
+
30
+ # 4. (Optional) generate a typed API client
31
+ npx openapi-sync generate-client --type react-query --json
32
+ ```
33
+
34
+ All commands support `--json` (machine-readable stdout) and exit codes:
35
+ 0 = success | 1 = config/validation error | 2 = network error | 3 = generation error
36
+
37
+
38
+ ## CLI COMMANDS
39
+
40
+ ### Agent-safe commands (no stdin required)
41
+
42
+ | Command | Description |
43
+ |---------|-------------|
44
+ | `npx openapi-sync` | Sync specs, generate types + endpoints + schemas |
45
+ | `npx openapi-sync validate` | Validate config + specs; no files written |
46
+ | `npx openapi-sync list-endpoints` | List all endpoints from spec; no files written |
47
+ | `npx openapi-sync generate-client --type <TYPE>` | Generate typed API client |
48
+ | `npx openapi-sync init --no-interactive [FLAGS]` | Create config file without prompts |
49
+
50
+ ### Interactive-only (requires human stdin — NOT safe for agents)
51
+
52
+ | Command | Description |
53
+ |---------|-------------|
54
+ | `npx openapi-sync init` | Interactive wizard |
55
+
56
+ ### Global flags (all commands)
57
+
58
+ | Flag | Description |
59
+ |------|-------------|
60
+ | `--json` | Emit a single JSON object to stdout; suppress emoji logs |
61
+ | `--silent` | Suppress all console output |
62
+ | `--dry-run` | Show what would be written without writing (sync, generate-client) |
63
+ | `--help, -h` | Help |
64
+ | `--version, -v` | Version |
65
+
66
+ ### `init` flags (used with `--no-interactive`)
67
+
68
+ | Flag | Default | Description |
69
+ |------|---------|-------------|
70
+ | `--api-name` | `"myapi"` | API key in config |
71
+ | `--api-url` | — | URL to OpenAPI spec |
72
+ | `--api-file` | — | Path to local spec file |
73
+ | `--output-folder` | `"./src/api"` | Generated files output |
74
+ | `--config-format` | `"typescript"` | `typescript`, `json`, `javascript` |
75
+ | `--client-type` | — | `react-query`, `swr`, `fetch`, `axios`, `rtk-query` |
76
+ | `--validation-library` | — | `zod`, `yup`, `joi` |
77
+ | `--folder-split` | `false` | Organise by OpenAPI tags |
78
+ | `--types-prefix` | `"I"` | TS interface name prefix |
79
+ | `--use-operation-id` | `true` | Use operationId for names |
80
+ | `--exclude-tags` | — | Comma-separated tags to exclude |
81
+ | `--show-curl` | `true` | Include cURL in JSDoc |
82
+ | `--refetch-interval` | `0` | Auto-refetch ms (0=disabled) |
83
+ | `--run-sync` | `false` | Run sync after config creation |
84
+
85
+ ### `generate-client` flags
86
+
87
+ | Flag | Alias | Description |
88
+ |------|-------|-------------|
89
+ | `--type` | `-t` | Client type (required) |
90
+ | `--api` | `-a` | Specific API from config |
91
+ | `--tags` | | Filter endpoints by tags |
92
+ | `--endpoints` | `-e` | Filter by endpoint names |
93
+ | `--output` | `-o` | Output directory |
94
+ | `--base-url` | `-b` | Base URL baked into client |
95
+
96
+ ### `list-endpoints` flags
97
+
98
+ | Flag | Description |
99
+ |------|-------------|
100
+ | `--api` | Limit to a specific API |
101
+ | `--tags` | Comma-separated tag filter |
102
+
103
+
104
+ ## PROGRAMMATIC API
105
+
106
+ All functions are exported from the `openapi-sync` package.
107
+
108
+ ```typescript
109
+ import {
110
+ Init, // Sync specs → types, endpoints, schemas
111
+ GenerateClient, // Generate typed API client
112
+ ValidateConfig, // Pre-flight check (no file writes)
113
+ ListEndpoints, // Inspect API surface (no file writes)
114
+ InteractiveInit, // NOT agent-safe — requires stdin
115
+ } from "openapi-sync";
116
+ ```
117
+
118
+ ### Init(options?) → Promise<SyncResult>
119
+ Agent-safe. Returns SyncResult.
120
+
121
+ ```typescript
122
+ const result = await Init({ silent: true });
123
+ // result: { success, apis, filesWritten, endpointCount, warnings, errors }
124
+ ```
125
+
126
+ ### GenerateClient(options) → Promise<SyncResult>
127
+ Agent-safe. Syncs then generates a typed client.
128
+
129
+ ```typescript
130
+ const result = await GenerateClient({
131
+ type: "react-query", // "fetch"|"axios"|"react-query"|"swr"|"rtk-query"
132
+ apiName: "petstore", // optional; defaults to all APIs
133
+ baseURL: "https://api.example.com",
134
+ tags: ["pets"], // optional tag filter
135
+ silent: true,
136
+ });
137
+ ```
138
+
139
+ ### ValidateConfig(options?) → Promise<ValidationResult>
140
+ Agent-safe. No files written.
141
+
142
+ ```typescript
143
+ const validation = await ValidateConfig({ silent: true });
144
+ // validation: { valid, apis: { [name]: { valid, endpointCount, error? } }, configErrors }
145
+ if (!validation.valid) process.exit(1);
146
+ ```
147
+
148
+ ### ListEndpoints(options?) → Promise<Record<string, EndpointSummary[]>>
149
+ Agent-safe. No files written.
150
+
151
+ ```typescript
152
+ const map = await ListEndpoints({ apiName: "petstore", tags: ["pet"] });
153
+ // map: { petstore: [{ name, method, path, operationId, tags, summary }] }
154
+ ```
155
+
156
+ ### InteractiveInit() → Promise<void>
157
+ NOT agent-safe — blocks on stdin prompts.
158
+ Use CLI `init --no-interactive` instead.
159
+
160
+
161
+ ## CONFIG FILE FORMAT
162
+
163
+ Config file: `openapi.sync.ts` | `openapi.sync.js` | `openapi.sync.json`
164
+ JSON Schema: https://openapi-sync.com/schema/openapi.sync.schema.json
165
+
166
+ ### Minimal config
167
+
168
+ ```json
169
+ {
170
+ "folder": "./src/api",
171
+ "api": {
172
+ "petstore": "https://petstore3.swagger.io/api/v3/openapi.json"
173
+ }
174
+ }
175
+ ```
176
+
177
+ ### Full TypeScript config
178
+
179
+ ```typescript
180
+ import { IConfig } from "openapi-sync";
181
+ const config: IConfig = {
182
+ folder: "./src/api",
183
+ api: { petstore: "https://petstore3.swagger.io/api/v3/openapi.json" },
184
+ refetchInterval: 5000,
185
+ language: "typescript", // "typescript" | "python"
186
+ folderSplit: { byTags: true },
187
+ types: { name: { prefix: "I", useOperationId: true } },
188
+ endpoints: {
189
+ doc: { showCurl: true },
190
+ exclude: { tags: ["deprecated"] },
191
+ },
192
+ validations: { library: "zod" }, // "zod" | "yup" | "joi"
193
+ clientGeneration: {
194
+ enabled: true,
195
+ type: "react-query", // "fetch"|"axios"|"react-query"|"swr"|"rtk-query"
196
+ baseURL: "https://api.example.com",
197
+ reactQuery: { version: 5, mutations: true },
198
+ },
199
+ customCode: { enabled: true, position: "bottom" },
200
+ };
201
+ export default config;
202
+ ```
203
+
204
+ ### Key IConfig fields
205
+
206
+ | Field | Type | Description |
207
+ |-------|------|-------------|
208
+ | `api` | `Record<string, string>` | **Required.** Map of API name → spec URL or file path |
209
+ | `folder` | `string` | Output folder (default: `"api"`) |
210
+ | `language` | `"typescript"\|"python"` | Output language |
211
+ | `refetchInterval` | `number` | Auto-refetch ms |
212
+ | `folderSplit.byTags` | `boolean` | Split output by OpenAPI tags |
213
+ | `validations.library` | `"zod"\|"yup"\|"joi"` | Validation schema library |
214
+ | `clientGeneration.type` | string | Client type to generate |
215
+ | `clientGeneration.baseURL` | `string` | Base URL for generated client |
216
+ | `endpoints.exclude.tags` | `string[]` | Tags to exclude from generation |
217
+
218
+
219
+ ## STRUCTURED RETURN TYPES
220
+
221
+ ### SyncResult (returned by Init, GenerateClient)
222
+
223
+ ```typescript
224
+ type SyncResult = {
225
+ success: boolean;
226
+ apis: string[];
227
+ filesWritten: string[];
228
+ endpointCount: number;
229
+ warnings: string[];
230
+ errors: string[];
231
+ };
232
+ ```
233
+
234
+ ### ValidationResult (returned by ValidateConfig)
235
+
236
+ ```typescript
237
+ type ValidationResult = {
238
+ valid: boolean;
239
+ apis: Record<string, { valid: boolean; endpointCount: number; error?: string }>;
240
+ configErrors: string[];
241
+ };
242
+ ```
243
+
244
+ ### EndpointSummary (items in ListEndpoints result)
245
+
246
+ ```typescript
247
+ type EndpointSummary = {
248
+ name: string;
249
+ method: string;
250
+ path: string;
251
+ operationId?: string;
252
+ tags?: string[];
253
+ summary?: string;
254
+ };
255
+ ```
256
+
257
+
258
+ ## GENERATED FILE STRUCTURE
259
+
260
+ ```
261
+ ./src/api/
262
+ {apiName}/
263
+ types.ts ← TypeScript interfaces for all schemas
264
+ endpoints.ts ← Typed endpoint URL builder functions
265
+ shared.ts ← Shared component types
266
+ validations.ts ← Zod/Yup/Joi schemas (if enabled)
267
+ client/
268
+ client.ts ← Fetch/Axios client instance
269
+ hooks.ts ← React Query / SWR hooks (if enabled)
270
+ ```
271
+
272
+ When `folderSplit.byTags` is true, types/endpoints/validations are split into
273
+ tag-named subdirectories with aggregator index files for easy imports.
274
+
275
+
276
+ ## ERROR CODES (typed errors)
277
+
278
+ | Code | Class | Trigger |
279
+ |------|-------|---------|
280
+ | `CONFIG_NOT_FOUND` | ConfigNotFoundError | No config file in cwd |
281
+ | `CONFIG_PARSE_FAILED` | ConfigParseError | Config file parse error |
282
+ | `CONFIG_INVALID` | ConfigValidationError | Bad config field value |
283
+ | `SPEC_FETCH_FAILED` | SpecFetchError | Network error fetching spec |
284
+ | `SPEC_READ_FAILED` | SpecReadError | Local file read error |
285
+ | `SPEC_PARSE_FAILED` | SpecParseError | Not a valid OpenAPI spec |
286
+ | `GENERATION_FAILED` | GenerationError | File write error |
287
+ | `UNKNOWN_API` | UnknownApiError | API name not in config |
288
+
289
+ All error classes extend `OpenApiSyncError` and expose `.code`, `.message`, and `.toJSON()`.
290
+
291
+
292
+ ## COMMON AGENT WORKFLOWS
293
+
294
+ ### Workflow 1: First-time setup
295
+
296
+ ```bash
297
+ npx openapi-sync init --no-interactive \
298
+ --api-name myapi \
299
+ --api-url https://api.example.com/openapi.json \
300
+ --output-folder ./src/api \
301
+ --client-type react-query \
302
+ --validation-library zod \
303
+ --config-format typescript \
304
+ --json
305
+ npx openapi-sync validate --json
306
+ npx openapi-sync --json
307
+ npx openapi-sync generate-client --type react-query --json
308
+ ```
309
+
310
+ ### Workflow 2: Inspect before generating
311
+
312
+ ```bash
313
+ # See what endpoints exist before committing to a client type
314
+ npx openapi-sync list-endpoints --json | jq '.petstore | map(.method + " " + .path)'
315
+
316
+ # Preview what would be written
317
+ npx openapi-sync --dry-run --json
318
+ npx openapi-sync generate-client --type fetch --dry-run --json
319
+ ```
320
+
321
+ ### Workflow 3: Programmatic (TypeScript)
322
+
323
+ ```typescript
324
+ import { ValidateConfig, Init, GenerateClient } from "openapi-sync";
325
+
326
+ const validation = await ValidateConfig({ silent: true });
327
+ if (!validation.valid) throw new Error(JSON.stringify(validation));
328
+
329
+ const syncResult = await Init({ silent: true });
330
+ if (!syncResult.success) throw new Error(JSON.stringify(syncResult));
331
+
332
+ const clientResult = await GenerateClient({ type: "axios", silent: true });
333
+ console.log(JSON.stringify(clientResult));
334
+ ```