|
| 1 | +/** |
| 2 | + * Build script to generate pre-compiled ajv validators. |
| 3 | + * |
| 4 | + * This generates static validation functions that don't use `new Function()`, |
| 5 | + * making them compatible with Cloudflare Workers and other restricted environments. |
| 6 | + * |
| 7 | + * Usage: node scripts/build-validators.js |
| 8 | + */ |
| 9 | + |
| 10 | +const Ajv = require('ajv'); |
| 11 | +const standaloneCode = require('ajv/dist/standalone').default; |
| 12 | +const fs = require('fs'); |
| 13 | +const path = require('path'); |
| 14 | + |
| 15 | +const flagsSchema = require('../flagd-schemas/json/flags.json'); |
| 16 | +const targetingSchema = require('../flagd-schemas/json/targeting.json'); |
| 17 | + |
| 18 | +// Configure ajv with source code generation enabled. |
| 19 | +// CJS output (esm: false, the default) so the file can be directly required |
| 20 | +// in both Node.js and Jest (CJS) environments. When bundled for Workers with |
| 21 | +// a bundler like esbuild/rollup, the CJS export will be rewritten to ESM |
| 22 | +// automatically by the bundler. |
| 23 | +// strict: false is required — the upstream flagd schema uses 1-tuple `items` |
| 24 | +// without additionalItems/minItems/maxItems (e.g. varRule), which AJV strict |
| 25 | +// mode rejects. |
| 26 | +const ajv = new Ajv({ |
| 27 | + strict: false, |
| 28 | + code: { source: true, esm: false }, |
| 29 | +}); |
| 30 | + |
| 31 | +// Add targeting schema first (referenced by flags schema) |
| 32 | +ajv.addSchema(targetingSchema, 'targeting'); |
| 33 | + |
| 34 | +// Compile the flags schema |
| 35 | +const validate = ajv.compile(flagsSchema); |
| 36 | + |
| 37 | +// Generate standalone code |
| 38 | +const moduleCode = standaloneCode(ajv, validate); |
| 39 | + |
| 40 | +// Create output directory |
| 41 | +const outputDir = path.join(__dirname, '../src/lib/generated'); |
| 42 | +fs.mkdirSync(outputDir, { recursive: true }); |
| 43 | + |
| 44 | +// Write the generated validators |
| 45 | +const outputPath = path.join(outputDir, 'validators.js'); |
| 46 | +fs.writeFileSync( |
| 47 | + outputPath, |
| 48 | + `// AUTO-GENERATED FILE - DO NOT EDIT |
| 49 | +// Generated by scripts/build-validators.js |
| 50 | +// Run 'npm run build:validators' to regenerate |
| 51 | +// |
| 52 | +// These pre-compiled validators are compatible with Cloudflare Workers |
| 53 | +// and other environments that restrict dynamic code generation. |
| 54 | +
|
| 55 | +${moduleCode} |
| 56 | +`, |
| 57 | +); |
| 58 | + |
| 59 | +// Also create a TypeScript declaration file. |
| 60 | +// The type is inlined (rather than importing ValidateFunction from ajv) so that |
| 61 | +// downstream consumers don't need ajv installed for type resolution. |
| 62 | +const dtsPath = path.join(outputDir, 'validators.d.ts'); |
| 63 | +fs.writeFileSync( |
| 64 | + dtsPath, |
| 65 | + `// AUTO-GENERATED FILE - DO NOT EDIT |
| 66 | +// Type declarations for pre-compiled validators |
| 67 | +
|
| 68 | +interface ValidateFunction { |
| 69 | + (data: unknown): boolean; |
| 70 | + errors?: null | Array<Record<string, unknown>>; |
| 71 | +} |
| 72 | +
|
| 73 | +declare const validate: ValidateFunction; |
| 74 | +export = validate; |
| 75 | +`, |
| 76 | +); |
| 77 | + |
| 78 | +console.log('Generated pre-compiled validators:'); |
| 79 | +console.log(' -', outputPath); |
| 80 | +console.log(' -', dtsPath); |
0 commit comments