diff --git a/cli/package.json b/cli/package.json index 5047299..66a9bb2 100644 --- a/cli/package.json +++ b/cli/package.json @@ -33,17 +33,18 @@ "LICENSE" ], "dependencies": { - "commander": "^12.0.0", - "chalk": "^5.3.0", "ajv": "^8.12.0", - "js-yaml": "^4.1.0", + "ajv-formats": "^3.0.1", + "chalk": "^5.3.0", + "commander": "^12.0.0", "dotenv": "^16.4.0", - "glob": "^13.0.6" + "glob": "^13.0.6", + "js-yaml": "^4.1.0" }, "devDependencies": { - "typescript": "^5.4.0", - "@types/node": "^20.11.0", "@types/js-yaml": "^4.0.9", + "@types/node": "^20.11.0", + "typescript": "^5.4.0", "vitest": "^1.6.0" } } diff --git a/cli/schema/rep-manifest.schema.json b/cli/schema/rep-manifest.schema.json index f279232..4e57203 100644 --- a/cli/schema/rep-manifest.schema.json +++ b/cli/schema/rep-manifest.schema.json @@ -38,8 +38,8 @@ "default": false }, "default": { - "type": "string", - "description": "Default value if the environment variable is not set. Only valid for non-required variables." + "type": ["string", "number", "boolean"], + "description": "Default value if the environment variable is not set. Only valid for non-required variables. Non-string values are coerced to strings." }, "description": { "type": "string", diff --git a/cli/src/utils/__tests__/manifest.test.ts b/cli/src/utils/__tests__/manifest.test.ts index ace8646..7e4e13c 100644 --- a/cli/src/utils/__tests__/manifest.test.ts +++ b/cli/src/utils/__tests__/manifest.test.ts @@ -159,6 +159,38 @@ describe('manifest', () => { expect(() => validateManifest(manifest)).not.toThrow(); }); + it('should accept boolean default values', () => { + const manifest = { + version: '0.1.0', + variables: { + COMING_SOON: { + tier: 'public', + type: 'boolean', + required: false, + default: false, + }, + }, + }; + + expect(() => validateManifest(manifest)).not.toThrow(); + }); + + it('should accept number default values', () => { + const manifest = { + version: '0.1.0', + variables: { + MAX_RETRIES: { + tier: 'public', + type: 'number', + required: false, + default: 3, + }, + }, + }; + + expect(() => validateManifest(manifest)).not.toThrow(); + }); + it('should accept settings object', () => { const manifest = { version: '0.1.0', @@ -172,8 +204,36 @@ describe('manifest', () => { session_key_ttl: '30s', }, }; - + expect(() => validateManifest(manifest)).not.toThrow(); }); + + it('should validate allowed_origins as URIs', () => { + const manifest = { + version: '0.1.0', + variables: { + API_URL: { tier: 'public' }, + }, + settings: { + allowed_origins: ['https://app.example.com'], + }, + }; + + expect(() => validateManifest(manifest)).not.toThrow(); + }); + + it('should reject invalid URIs in allowed_origins', () => { + const manifest = { + version: '0.1.0', + variables: { + API_URL: { tier: 'public' }, + }, + settings: { + allowed_origins: ['not a uri'], + }, + }; + + expect(() => validateManifest(manifest)).toThrow(); + }); }); }); diff --git a/cli/src/utils/manifest.ts b/cli/src/utils/manifest.ts index a02aeb2..134ad9f 100644 --- a/cli/src/utils/manifest.ts +++ b/cli/src/utils/manifest.ts @@ -7,12 +7,13 @@ import * as fs from 'fs'; import * as path from 'path'; import * as yaml from 'js-yaml'; import Ajv, { type ValidateFunction } from 'ajv'; +import addFormats from 'ajv-formats'; export interface ManifestVariable { tier: 'public' | 'sensitive' | 'server'; type?: 'string' | 'url' | 'number' | 'boolean' | 'csv' | 'json' | 'enum'; required?: boolean; - default?: string; + default?: string | number | boolean; description?: string; example?: string; pattern?: string; @@ -58,6 +59,7 @@ function getSchemaValidator(): ValidateFunction { const schema = JSON.parse(schemaContent); const ajv = new Ajv({ allErrors: true, strict: false }); + addFormats(ajv); schemaValidator = ajv.compile(schema); return schemaValidator; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d2ddc38..353d1db 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -86,6 +86,9 @@ importers: ajv: specifier: ^8.12.0 version: 8.18.0 + ajv-formats: + specifier: ^3.0.1 + version: 3.0.1(ajv@8.18.0) chalk: specifier: ^5.3.0 version: 5.6.2 @@ -1689,6 +1692,14 @@ packages: ajv: optional: true + ajv-formats@3.0.1: + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + ajv@8.18.0: resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} @@ -5527,6 +5538,10 @@ snapshots: optionalDependencies: ajv: 8.18.0 + ajv-formats@3.0.1(ajv@8.18.0): + optionalDependencies: + ajv: 8.18.0 + ajv@8.18.0: dependencies: fast-deep-equal: 3.1.3 diff --git a/schema/rep-manifest.schema.json b/schema/rep-manifest.schema.json index f279232..4e57203 100644 --- a/schema/rep-manifest.schema.json +++ b/schema/rep-manifest.schema.json @@ -38,8 +38,8 @@ "default": false }, "default": { - "type": "string", - "description": "Default value if the environment variable is not set. Only valid for non-required variables." + "type": ["string", "number", "boolean"], + "description": "Default value if the environment variable is not set. Only valid for non-required variables. Non-string values are coerced to strings." }, "description": { "type": "string",