Skip to content

Commit 8d17109

Browse files
committed
Include OpenAPI spec and JSON schema sources in package.
1 parent 303f77d commit 8d17109

File tree

3 files changed

+70
-15
lines changed

3 files changed

+70
-15
lines changed

README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,7 @@ Visit `http://localhost:3000` for interactive API docs.
3939
| `npm run validate` | Validate base specs |
4040
| `npm run validate:state` | Validate specs for current STATE |
4141
| `npm run validate:all-states` | Validate all states |
42-
| `npm run clients:generate` | Generate Zodios TypeScript clients |
43-
| `npm run clients:validate` | Type-check generated clients |
42+
| `npm run clients:build-package` | Build state-specific npm package (requires `--state` and `--version`) |
4443
| `npm run postman:generate` | Generate Postman collection |
4544
| `npm run mock:reset` | Reset database to example data |
4645
| `npm test` | Run unit tests |

packages/clients/scripts/build-state-package.js

Lines changed: 68 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,26 @@
1515
*/
1616

1717
import { spawn } from 'child_process';
18-
import { readFileSync, writeFileSync, mkdirSync, rmSync, existsSync, readdirSync, copyFileSync } from 'fs';
18+
import { readFileSync, writeFileSync, mkdirSync, rmSync, existsSync, readdirSync, copyFileSync, statSync } from 'fs';
1919
import { join, dirname } from 'path';
2020
import { fileURLToPath } from 'url';
21+
import yaml from 'js-yaml';
22+
23+
/**
24+
* Recursively copy a directory
25+
*/
26+
function copyDirRecursive(src, dest) {
27+
mkdirSync(dest, { recursive: true });
28+
for (const entry of readdirSync(src)) {
29+
const srcPath = join(src, entry);
30+
const destPath = join(dest, entry);
31+
if (statSync(srcPath).isDirectory()) {
32+
copyDirRecursive(srcPath, destPath);
33+
} else {
34+
copyFileSync(srcPath, destPath);
35+
}
36+
}
37+
}
2138

2239
const __filename = fileURLToPath(import.meta.url);
2340
const __dirname = dirname(__filename);
@@ -185,23 +202,62 @@ async function main() {
185202
console.log(` Generated: ${domain}`);
186203
}
187204

188-
// Step 3: Create index.ts that re-exports all domains and search helpers
189-
console.log('\n3. Creating index exports...');
205+
// Step 3: Copy resolved OpenAPI specs to package
206+
console.log('\n3. Copying OpenAPI specs...');
207+
const openapiDir = join(outputDir, 'openapi');
208+
copyDirRecursive(resolvedDir, openapiDir);
209+
console.log(` Copied resolved specs to openapi/`);
210+
211+
// Step 4: Extract JSON schemas from bundled specs
212+
console.log('\n4. Extracting JSON schemas...');
213+
const jsonSchemaDir = join(outputDir, 'json-schema');
214+
for (const file of specFiles) {
215+
const domain = file.replace('.yaml', '');
216+
const specPath = join(resolvedDir, file);
217+
const domainBundled = join(outputDir, `${domain}-bundled.yaml`);
218+
const domainSchemaDir = join(jsonSchemaDir, domain);
219+
220+
// Bundle spec (dereference $refs) for JSON schema extraction
221+
await exec('npx', [
222+
'@apidevtools/swagger-cli', 'bundle',
223+
specPath,
224+
'-o', domainBundled,
225+
'--dereference'
226+
]);
227+
228+
// Extract schemas from bundled spec
229+
const bundledContent = readFileSync(domainBundled, 'utf8');
230+
const bundledSpec = yaml.load(bundledContent);
231+
const schemas = bundledSpec.components?.schemas || {};
232+
233+
mkdirSync(domainSchemaDir, { recursive: true });
234+
for (const [schemaName, schema] of Object.entries(schemas)) {
235+
const jsonSchemaPath = join(domainSchemaDir, `${schemaName}.json`);
236+
writeFileSync(jsonSchemaPath, JSON.stringify(schema, null, 2));
237+
}
238+
console.log(` Extracted ${Object.keys(schemas).length} schemas for ${domain}`);
239+
240+
// Clean up temp bundled file
241+
rmSync(domainBundled, { force: true });
242+
}
243+
244+
// Step 5: Create index.ts that re-exports all domains and search helpers
245+
console.log('\n5. Creating index exports...');
190246
const domainExports = domains.map(d => `export * as ${d} from './${d}/index.js';`).join('\n');
191247
const indexContent = `${domainExports}
192248
export { q, search } from './search-helpers.js';
193249
`;
194250
writeFileSync(join(srcDir, 'index.ts'), indexContent);
195251
console.log(' Created index.ts');
196252

197-
// Step 3b: Copy search helpers
253+
// Copy search helpers
198254
const searchHelpersSource = join(templatesDir, 'search-helpers.ts');
199255
const searchHelpersDest = join(srcDir, 'search-helpers.ts');
200256
copyFileSync(searchHelpersSource, searchHelpersDest);
201257
console.log(' Copied search-helpers.ts');
202258

203-
// Step 4: Generate package.json from template
204-
console.log('\n4. Generating package.json...');
259+
// Step 6: Generate package.json from template
260+
console.log('\n6. Generating package.json...');
205261
const packageTemplate = readFileSync(join(templatesDir, 'package.template.json'), 'utf8');
206262
const packageJson = packageTemplate
207263
.replace(/\{\{STATE\}\}/g, state)
@@ -210,8 +266,8 @@ export { q, search } from './search-helpers.js';
210266
writeFileSync(join(outputDir, 'package.json'), packageJson);
211267
console.log(' Generated package.json');
212268

213-
// Step 5: Create tsconfig for compilation
214-
console.log('\n5. Setting up TypeScript compilation...');
269+
// Step 7: Create tsconfig for compilation
270+
console.log('\n7. Setting up TypeScript compilation...');
215271
const tsconfig = {
216272
compilerOptions: {
217273
target: 'ES2020',
@@ -230,13 +286,13 @@ export { q, search } from './search-helpers.js';
230286
writeFileSync(join(outputDir, 'tsconfig.json'), JSON.stringify(tsconfig, null, 2));
231287
console.log(' Created tsconfig.json');
232288

233-
// Step 6: Install build dependencies (peer deps needed for type checking)
234-
console.log('\n6. Installing build dependencies...');
289+
// Step 8: Install build dependencies (peer deps needed for type checking)
290+
console.log('\n8. Installing build dependencies...');
235291
await exec('npm', ['install', 'zod@^4.3.5', 'axios@^1.6.0', '--save-dev'], { cwd: outputDir });
236292
console.log(' Dependencies installed');
237293

238-
// Step 7: Compile TypeScript
239-
console.log('\n7. Compiling TypeScript...');
294+
// Step 9: Compile TypeScript
295+
console.log('\n9. Compiling TypeScript...');
240296
try {
241297
await exec('npx', ['tsc'], { cwd: outputDir });
242298
} catch (error) {

packages/clients/templates/package.template.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"type": "module",
66
"main": "./dist/index.js",
77
"types": "./dist/index.d.ts",
8-
"files": ["dist", "src"],
8+
"files": ["dist", "src", "openapi", "json-schema"],
99
"exports": {
1010
".": {
1111
"import": "./dist/index.js",

0 commit comments

Comments
 (0)