{"_id":"@agentkitai/formbridge-schema-normalizer","_rev":"2-62557f1709b79cc18c6696aae1f3e66f","name":"@agentkitai/formbridge-schema-normalizer","dist-tags":{"latest":"0.3.1"},"versions":{"0.3.0":{"name":"@agentkitai/formbridge-schema-normalizer","version":"0.3.0","keywords":["schema","normalization","zod","json-schema","openapi","validation"],"author":{"name":"FormBridge Team"},"license":"MIT","_id":"@agentkitai/formbridge-schema-normalizer@0.3.0","maintainers":[{"name":"amit-paz","email":"amit.paz@gmail.com"}],"dist":{"shasum":"8aae57dc119e10f80704dbf8f899bf4fcab6e526","tarball":"https://registry.npmjs.org/@agentkitai/formbridge-schema-normalizer/-/formbridge-schema-normalizer-0.3.0.tgz","fileCount":42,"integrity":"sha512-yhNZxXnXsRvlNXPLjskniCgWtcLxkFq/Zqii0TeJA7uxAt6uNSOLb9FHAyMhXzJxRdZrgaxyxfdau4tSt6WorA==","signatures":[{"sig":"MEYCIQDzj23wPr1dd6Q04SAHwKvOlMuf+tq9EQ+XpGOjGdfrlAIhAPNKn6OT319BtCjerd0hjSd8NVXN8xPLlrce5TOvj2Wc","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":215583},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"2bd30b08b5f4642ec78674662f9101bbf237869a","scripts":{"test":"vitest","build":"tsc --build","clean":"rm -rf dist","typecheck":"tsc --noEmit","test:watch":"vitest --watch","test:coverage":"vitest --coverage"},"_npmUser":{"name":"amit-paz","email":"amit.paz@gmail.com"},"_npmVersion":"11.5.2","description":"Schema normalization engine that converts Zod schemas, JSON Schema, and OpenAPI specs into a unified IntakeSchema IR","directories":{},"_nodeVersion":"24.12.0","dependencies":{},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"zod":"^3.22.4","vitest":"^1.2.0","typescript":"^5.3.3","@types/node":"^20.11.0"},"peerDependencies":{"zod":"^3.22.0"},"peerDependenciesMeta":{"zod":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/formbridge-schema-normalizer_0.3.0_1782494696116_0.5948159539216642","host":"s3://npm-registry-packages-npm-production"}},"0.3.1":{"name":"@agentkitai/formbridge-schema-normalizer","version":"0.3.1","publishConfig":{"access":"public"},"description":"Schema normalization engine that converts Zod schemas, JSON Schema, and OpenAPI specs into a unified IntakeSchema IR","main":"dist/index.js","types":"dist/index.d.ts","scripts":{"build":"tsc --build","clean":"rm -rf dist","test":"vitest","test:watch":"vitest --watch","test:coverage":"vitest --coverage","typecheck":"tsc --noEmit"},"keywords":["schema","normalization","zod","json-schema","openapi","validation"],"author":{"name":"FormBridge Team"},"license":"MIT","dependencies":{},"devDependencies":{"@types/node":"^20.11.0","typescript":"^5.3.3","vitest":"^1.2.0","zod":"^3.22.4"},"peerDependencies":{"zod":"^3.22.0"},"peerDependenciesMeta":{"zod":{"optional":true}},"repository":{"type":"git","url":"git+https://github.com/agentkitai/formbridge.git","directory":"packages/schema-normalizer"},"gitHead":"875f2feaa2fc057e05bcf42771d124636adc88cb","_id":"@agentkitai/formbridge-schema-normalizer@0.3.1","bugs":{"url":"https://github.com/agentkitai/formbridge/issues"},"homepage":"https://github.com/agentkitai/formbridge#readme","_nodeVersion":"24.18.0","_npmVersion":"12.0.2","dist":{"integrity":"sha512-G7/YSYH6W2sKf5Jsq58eINJuTVKZJB6nDhoS1m32og4teN8IOHFzpbYTyiEJdJcfwLiu7tr3SSPB/tYHAn4imw==","shasum":"99783d77515e59f77857ceabf109b06ec5619704","tarball":"https://registry.npmjs.org/@agentkitai/formbridge-schema-normalizer/-/formbridge-schema-normalizer-0.3.1.tgz","fileCount":11,"unpackedSize":110851,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@agentkitai%2fformbridge-schema-normalizer@0.3.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCeV26A4/FvUVD1Prm7DLcrzupOpLwFkSy1FLNaVyn2BQIgdxlBYxYo2cg0p1tnq8M+Efbdq6MMj2s3mbjcOEye5ko="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:8fd43104-4497-46d0-b553-a7c4a3f65b79"}},"directories":{},"maintainers":[{"name":"amit-paz","email":"amit.paz@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/formbridge-schema-normalizer_0.3.1_1785686905786_0.8281615000659748"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-26T17:24:55.893Z","modified":"2026-08-02T16:08:26.281Z","0.3.0":"2026-06-26T17:24:56.309Z","0.3.1":"2026-08-02T16:08:25.931Z"},"author":{"name":"FormBridge Team"},"license":"MIT","keywords":["schema","normalization","zod","json-schema","openapi","validation"],"description":"Schema normalization engine that converts Zod schemas, JSON Schema, and OpenAPI specs into a unified IntakeSchema IR","maintainers":[{"name":"amit-paz","email":"amit.paz@gmail.com"}],"readme":"# @agentkitai/formbridge-schema-normalizer\n\n> Schema normalization engine that converts Zod schemas, JSON Schema, and OpenAPI specs into a unified IntakeSchema IR\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)\n[![TypeScript](https://img.shields.io/badge/TypeScript-5.3-blue)](https://www.typescriptlang.org/)\n\n## Overview\n\n`@agentkitai/formbridge-schema-normalizer` is a foundational library that enables FormBridge's \"define once, use everywhere\" approach by abstracting over three popular schema formats:\n\n- **Zod schemas** - TypeScript-first validation library\n- **JSON Schema** - Industry-standard JSON schema format (draft-07 and draft-2020-12)\n- **OpenAPI 3.0/3.1** - API specification request body schemas\n\nAll three formats are normalized into a unified **IntakeSchema IR** (Internal Representation) that preserves field definitions, types, constraints, descriptions, required/optional status, nested objects, arrays, and enums. This enables downstream features (validation, form rendering, MCP tool generation) to work from a single canonical schema format.\n\n## Features\n\n✅ **Universal Schema Support**\n- Parse Zod schemas with full constraint extraction\n- Parse JSON Schema (draft-07 and draft-2020-12)\n- Extract request body schemas from OpenAPI 3.0/3.1 documents\n\n✅ **Complete Type Coverage**\n- Primitives: string, number, integer, boolean, null\n- Complex types: objects, arrays, enums\n- Full constraint support: minLength, maxLength, minimum, maximum, pattern, format, etc.\n- Nested objects and arrays with unlimited depth\n\n✅ **Metadata Preservation**\n- Field descriptions and examples\n- Default values\n- Required vs optional fields\n- OpenAPI-specific metadata (operationId, tags, summary)\n\n✅ **Round-trip Conversion**\n- Serialize IntakeSchema IR back to JSON Schema\n- Zero information loss during conversion\n- 398+ test cases ensuring correctness\n\n✅ **Developer Experience**\n- Comprehensive TypeScript types\n- Clear error messages for unsupported constructs\n- Extensive documentation and examples\n\n## Installation\n\n```bash\nnpm install @agentkitai/formbridge-schema-normalizer\n```\n\n### Optional Peer Dependencies\n\nIf you want to parse Zod schemas, install Zod:\n\n```bash\nnpm install zod\n```\n\nZod is an optional peer dependency - you can use JSON Schema and OpenAPI parsers without it.\n\n## Quick Start\n\n### JSON Schema\n\n```typescript\nimport { JSONSchemaParser } from '@agentkitai/formbridge-schema-normalizer';\n\nconst parser = new JSONSchemaParser();\n\nconst jsonSchema = {\n  type: 'object',\n  properties: {\n    username: { type: 'string', minLength: 3, maxLength: 20 },\n    email: { type: 'string', format: 'email' },\n    age: { type: 'integer', minimum: 18 }\n  },\n  required: ['username', 'email']\n};\n\nconst intakeSchema = parser.parse(jsonSchema);\nconsole.log(intakeSchema);\n```\n\n### Zod Schema\n\n```typescript\nimport { ZodParser } from '@agentkitai/formbridge-schema-normalizer';\nimport { z } from 'zod';\n\nconst parser = new ZodParser();\n\nconst zodSchema = z.object({\n  username: z.string().min(3).max(20),\n  email: z.string().email(),\n  age: z.number().int().min(18)\n});\n\nconst intakeSchema = parser.parse(zodSchema);\nconsole.log(intakeSchema);\n```\n\n### OpenAPI Document\n\n```typescript\nimport { OpenAPIParser } from '@agentkitai/formbridge-schema-normalizer';\n\nconst parser = new OpenAPIParser();\n\nconst openApiDoc = {\n  openapi: '3.0.0',\n  info: { title: 'User API', version: '1.0.0' },\n  paths: {\n    '/users': {\n      post: {\n        operationId: 'createUser',\n        requestBody: {\n          content: {\n            'application/json': {\n              schema: {\n                type: 'object',\n                properties: {\n                  username: { type: 'string', minLength: 3 },\n                  email: { type: 'string', format: 'email' }\n                },\n                required: ['username', 'email']\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n};\n\n// Parse by operationId\nconst intakeSchema = parser.parse(openApiDoc, {\n  operationId: 'createUser'\n});\n\n// OR parse by path and method\nconst intakeSchema2 = parser.parse(openApiDoc, {\n  path: '/users',\n  method: 'post'\n});\n```\n\n## Usage Examples\n\n### Full Example with All Field Types\n\n```typescript\nimport { JSONSchemaParser } from '@agentkitai/formbridge-schema-normalizer';\n\nconst parser = new JSONSchemaParser();\n\nconst schema = {\n  type: 'object',\n  title: 'User Registration',\n  description: 'Complete user registration form',\n  properties: {\n    // String with constraints\n    username: {\n      type: 'string',\n      description: 'Unique username',\n      minLength: 3,\n      maxLength: 20,\n      pattern: '^[a-zA-Z0-9_]+$'\n    },\n\n    // String with format\n    email: {\n      type: 'string',\n      format: 'email',\n      description: 'Email address'\n    },\n\n    // Integer with range\n    age: {\n      type: 'integer',\n      minimum: 18,\n      maximum: 120,\n      description: 'User age'\n    },\n\n    // Number with precision\n    salary: {\n      type: 'number',\n      minimum: 0,\n      exclusiveMinimum: true,\n      multipleOf: 0.01,\n      description: 'Annual salary in USD'\n    },\n\n    // Boolean\n    agreeToTerms: {\n      type: 'boolean',\n      description: 'Agree to terms and conditions',\n      default: false\n    },\n\n    // Enum\n    role: {\n      enum: ['admin', 'user', 'guest'],\n      description: 'User role'\n    },\n\n    // Array\n    tags: {\n      type: 'array',\n      items: { type: 'string' },\n      minItems: 1,\n      maxItems: 10,\n      uniqueItems: true,\n      description: 'User tags'\n    },\n\n    // Nested object\n    address: {\n      type: 'object',\n      properties: {\n        street: { type: 'string' },\n        city: { type: 'string' },\n        zipCode: { type: 'string', pattern: '^\\\\d{5}$' }\n      },\n      required: ['city']\n    }\n  },\n  required: ['username', 'email', 'age', 'agreeToTerms']\n};\n\nconst ir = parser.parse(schema);\n\n// Access the normalized schema\nconsole.log(`Schema title: ${ir.title}`);\nconsole.log(`Root type: ${ir.schema.type}`);\n\nif (ir.schema.type === 'object') {\n  for (const [name, field] of Object.entries(ir.schema.properties)) {\n    console.log(`${name}: ${field.type} (${field.required ? 'required' : 'optional'})`);\n  }\n}\n```\n\n### Working with Nested Zod Schemas\n\n```typescript\nimport { ZodParser } from '@agentkitai/formbridge-schema-normalizer';\nimport { z } from 'zod';\n\nconst parser = new ZodParser();\n\nconst addressSchema = z.object({\n  street: z.string().min(1),\n  city: z.string().min(1),\n  zipCode: z.string().regex(/^\\d{5}$/),\n  country: z.string().default('USA')\n});\n\nconst userSchema = z.object({\n  name: z.string().min(1),\n  email: z.string().email(),\n  addresses: z.array(addressSchema).min(1),\n  primaryAddress: addressSchema,\n  newsletter: z.boolean().default(true).optional()\n});\n\nconst ir = parser.parse(userSchema);\nconsole.log(JSON.stringify(ir, null, 2));\n```\n\n### Round-trip Conversion\n\n```typescript\nimport {\n  JSONSchemaParser,\n  JSONSchemaSerializer\n} from '@agentkitai/formbridge-schema-normalizer';\n\nconst parser = new JSONSchemaParser();\nconst serializer = new JSONSchemaSerializer();\n\n// Original JSON Schema\nconst original = {\n  type: 'object',\n  properties: {\n    name: { type: 'string', minLength: 1 },\n    age: { type: 'integer', minimum: 0 }\n  },\n  required: ['name']\n};\n\n// Parse to IR\nconst ir = parser.parse(original);\n\n// Serialize back to JSON Schema\nconst output = serializer.serialize(ir);\n\nconsole.log('Round-trip successful!');\nconsole.log(JSON.stringify(output, null, 2));\n\n// You can also use the convenience function\nimport { serializeToJSONSchema } from '@agentkitai/formbridge-schema-normalizer';\n\nconst jsonSchema = serializeToJSONSchema(ir, {\n  schemaVersion: 'draft-2020-12',\n  includeSchemaVersion: true\n});\n```\n\n### Error Handling\n\n```typescript\nimport {\n  JSONSchemaParser,\n  UnsupportedFeatureError,\n  SchemaValidationError,\n  ParserError\n} from '@agentkitai/formbridge-schema-normalizer';\n\nconst parser = new JSONSchemaParser();\n\ntry {\n  // This will throw - $ref is not supported\n  parser.parse({\n    $ref: '#/definitions/User'\n  });\n} catch (error) {\n  if (error instanceof UnsupportedFeatureError) {\n    console.log('Unsupported feature:', error.message);\n    console.log('Feature:', error.feature);\n    console.log('Suggestion:', error.suggestion);\n  }\n}\n\ntry {\n  // This will throw - anyOf is not supported\n  parser.parse({\n    anyOf: [\n      { type: 'string' },\n      { type: 'number' }\n    ]\n  });\n} catch (error) {\n  if (error instanceof UnsupportedFeatureError) {\n    console.log('Cannot parse union types (anyOf)');\n  }\n}\n\ntry {\n  // This will throw - invalid schema\n  parser.parse({\n    type: 'invalid-type'\n  });\n} catch (error) {\n  if (error instanceof SchemaValidationError) {\n    console.log('Invalid schema:', error.message);\n  }\n}\n```\n\n### Parser Options\n\n```typescript\nimport { JSONSchemaParser } from '@agentkitai/formbridge-schema-normalizer';\n\n// Strict mode (default: true) - fail on unsupported features\nconst strictParser = new JSONSchemaParser({ strict: true });\n\n// Disable metadata inclusion\nconst noMetadataParser = new JSONSchemaParser({\n  includeMetadata: false\n});\n\n// Add custom metadata\nconst customParser = new JSONSchemaParser({\n  customMetadata: {\n    source: 'user-registration-v2',\n    version: '2.0.0',\n    createdAt: new Date().toISOString()\n  }\n});\n\nconst ir = customParser.parse(schema);\nconsole.log(ir.metadata); // Contains your custom metadata\n```\n\n## API Reference\n\n### Parsers\n\n#### `JSONSchemaParser`\n\nParses JSON Schema (draft-07 and draft-2020-12) into IntakeSchema IR.\n\n```typescript\nclass JSONSchemaParser implements Parser<JSONSchema> {\n  constructor(options?: ParserOptions)\n  parse(schema: JSONSchema, options?: ParserOptions): IntakeSchema\n  canParse(schema: JSONSchema): boolean\n}\n```\n\n**Supported JSON Schema features:**\n- All primitive types: `string`, `number`, `integer`, `boolean`, `null`\n- Complex types: `object`, `array`\n- Enum values\n- All standard constraints\n- Nested structures\n\n**Not supported:**\n- `$ref` references\n- `allOf`, `anyOf`, `oneOf`, `not` combinators\n- Tuple validation (`items` as array)\n\n#### `ZodParser`\n\nParses Zod schemas into IntakeSchema IR.\n\n```typescript\nclass ZodParser implements Parser<ZodTypeAny> {\n  constructor(options?: ParserOptions)\n  parse(schema: ZodTypeAny, options?: ParserOptions): IntakeSchema\n  canParse(schema: unknown): boolean\n}\n```\n\n**Supported Zod types:**\n- `z.string()`, `z.number()`, `z.boolean()`, `z.null()`\n- `z.object()`, `z.array()`, `z.enum()`, `z.nativeEnum()`\n- `.optional()`, `.nullable()`, `.default()`\n- `.describe()` for descriptions\n- All constraint methods: `.min()`, `.max()`, `.email()`, `.regex()`, etc.\n\n**Not supported:**\n- `z.union()`, `z.intersection()`, `z.discriminatedUnion()`\n- `z.tuple()`, `z.record()`, `z.map()`, `z.set()`\n- `z.lazy()`, `z.promise()`, `z.function()`\n- `z.any()`, `z.unknown()`, `z.void()`, `z.undefined()`, `z.never()`\n\n#### `OpenAPIParser`\n\nExtracts and parses request body schemas from OpenAPI 3.0/3.1 documents.\n\n```typescript\nclass OpenAPIParser implements Parser<OpenAPIDocument> {\n  constructor(options?: ParserOptions)\n  parse(doc: OpenAPIDocument, options?: OpenAPIParserOptions): IntakeSchema\n  canParse(doc: OpenAPIDocument): boolean\n}\n\ninterface OpenAPIParserOptions extends ParserOptions {\n  operationId?: string;      // Find operation by ID\n  path?: string;             // Find operation by path\n  method?: string;           // HTTP method (with path)\n  mediaType?: string;        // Default: 'application/json'\n}\n```\n\n**Schema extraction modes:**\n1. **By operationId**: `parser.parse(doc, { operationId: 'createUser' })`\n2. **By path + method**: `parser.parse(doc, { path: '/users', method: 'post' })`\n3. **Auto-discovery**: Automatically finds first POST/PUT/PATCH with request body\n\n**OpenAPI metadata preserved:**\n- `operationId`, `summary`, `description`\n- `tags`, `path`, `method`\n\n### Serializers\n\n#### `JSONSchemaSerializer`\n\nSerializes IntakeSchema IR back to JSON Schema.\n\n```typescript\nclass JSONSchemaSerializer {\n  constructor(options?: SerializerOptions)\n  serialize(schema: IntakeSchema): JSONSchema\n}\n\ninterface SerializerOptions {\n  schemaVersion?: 'draft-07' | 'draft-2020-12';  // Default: 'draft-2020-12'\n  includeSchemaVersion?: boolean;                 // Add $schema property\n}\n```\n\n**Convenience function:**\n\n```typescript\nfunction serializeToJSONSchema(\n  schema: IntakeSchema,\n  options?: SerializerOptions\n): JSONSchema\n```\n\n### Type Exports\n\n```typescript\n// IntakeSchema IR types\nexport type {\n  IntakeSchema,\n  IntakeSchemaField,\n  IntakeSchemaFieldType,\n  StringField,\n  NumberField,\n  IntegerField,\n  BooleanField,\n  NullField,\n  ObjectField,\n  ArrayField,\n  EnumField,\n  StringConstraints,\n  NumberConstraints,\n  ArrayConstraints,\n  EnumValue,\n  StringFormat\n}\n\n// Type guards\nexport {\n  isStringField,\n  isNumberField,\n  isIntegerField,\n  isBooleanField,\n  isNullField,\n  isObjectField,\n  isArrayField,\n  isEnumField\n}\n\n// Parser types\nexport type { Parser, ParserOptions }\nexport { ParserError, isParser }\n\n// Error types\nexport {\n  UnsupportedFeatureError,\n  SchemaValidationError,\n  createUnsupportedFeatureError\n}\n```\n\n### Factory Functions\n\nConvenience functions to create parser/serializer instances:\n\n```typescript\nexport function createJSONSchemaParser(options?: ParserOptions): JSONSchemaParser\nexport function createZodParser(options?: ParserOptions): ZodParser\nexport function createOpenAPIParser(options?: ParserOptions): OpenAPIParser\nexport function createJSONSchemaSerializer(options?: SerializerOptions): JSONSchemaSerializer\n```\n\n## IntakeSchema IR Format\n\nThe IntakeSchema IR is the normalized internal representation that all parsers produce.\n\n### Structure\n\n```typescript\ninterface IntakeSchema {\n  version: string;              // IR version (e.g., '1.0.0')\n  schema: IntakeSchemaField;    // Root field (usually ObjectField)\n  title?: string;               // Schema title\n  description?: string;         // Schema description\n  metadata?: Record<string, unknown>;  // Additional metadata\n}\n```\n\n### Field Types\n\nAll fields share a common base structure:\n\n```typescript\ninterface BaseField {\n  type: IntakeSchemaFieldType;\n  description?: string;\n  default?: unknown;\n  examples?: unknown[];\n  required: boolean;            // Is this field required?\n  nullable?: boolean;           // Can this field be null?\n}\n```\n\n#### String Field\n\n```typescript\ninterface StringField extends BaseField {\n  type: 'string';\n  constraints?: {\n    minLength?: number;\n    maxLength?: number;\n    pattern?: string;           // Regular expression\n    format?: StringFormat;      // email, url, uuid, date, etc.\n  };\n  default?: string;\n  examples?: string[];\n}\n```\n\n**Supported formats:**\n- `email`, `uri`, `url`, `uuid`\n- `date`, `date-time`, `time`\n- `ipv4`, `ipv6`, `hostname`\n- `regex`\n\n#### Number/Integer Field\n\n```typescript\ninterface NumberField extends BaseField {\n  type: 'number';\n  constraints?: {\n    minimum?: number;\n    maximum?: number;\n    exclusiveMinimum?: number;\n    exclusiveMaximum?: number;\n    multipleOf?: number;\n  };\n  default?: number;\n  examples?: number[];\n}\n\ninterface IntegerField extends BaseField {\n  type: 'integer';\n  constraints?: NumberConstraints;\n  default?: number;\n  examples?: number[];\n}\n```\n\n#### Boolean Field\n\n```typescript\ninterface BooleanField extends BaseField {\n  type: 'boolean';\n  default?: boolean;\n  examples?: boolean[];\n}\n```\n\n#### Null Field\n\n```typescript\ninterface NullField extends BaseField {\n  type: 'null';\n  default?: null;\n}\n```\n\n#### Object Field\n\n```typescript\ninterface ObjectField extends BaseField {\n  type: 'object';\n  properties: Record<string, IntakeSchemaField>;  // Recursive\n  additionalProperties?: boolean;\n  examples?: Record<string, unknown>[];\n}\n```\n\n#### Array Field\n\n```typescript\ninterface ArrayField extends BaseField {\n  type: 'array';\n  items: IntakeSchemaField;     // Array element schema (recursive)\n  constraints?: {\n    minItems?: number;\n    maxItems?: number;\n    uniqueItems?: boolean;\n  };\n  examples?: unknown[][];\n}\n```\n\n#### Enum Field\n\n```typescript\ninterface EnumField extends BaseField {\n  type: 'enum';\n  values: EnumValue[];          // List of allowed values\n  examples?: Array<string | number>;\n}\n\ninterface EnumValue {\n  value: string | number;       // The actual enum value\n  label?: string;               // Optional display label\n}\n```\n\n### Type Guards\n\nUse type guards to work with IntakeSchema fields:\n\n```typescript\nimport {\n  isStringField,\n  isObjectField,\n  isArrayField\n} from '@agentkitai/formbridge-schema-normalizer';\n\nconst field = ir.schema;\n\nif (isObjectField(field)) {\n  // TypeScript knows field is ObjectField\n  for (const [name, prop] of Object.entries(field.properties)) {\n    if (isStringField(prop) && prop.constraints?.format === 'email') {\n      console.log(`${name} is an email field`);\n    }\n  }\n}\n\nif (isArrayField(field)) {\n  // TypeScript knows field is ArrayField\n  console.log(`Array of ${field.items.type}`);\n}\n```\n\n## Configuration Options\n\n### ParserOptions\n\n```typescript\ninterface ParserOptions {\n  // Strict mode - fail on unsupported features (default: true)\n  strict?: boolean;\n\n  // Include source metadata in parsed IntakeSchema (default: true)\n  includeMetadata?: boolean;\n\n  // Custom metadata to merge into IntakeSchema\n  customMetadata?: Record<string, unknown>;\n}\n```\n\n**Example:**\n\n```typescript\nconst parser = new JSONSchemaParser({\n  strict: true,\n  includeMetadata: true,\n  customMetadata: {\n    source: 'user-api-v2',\n    version: '2.0.0',\n    author: 'API Team'\n  }\n});\n```\n\n### SerializerOptions\n\n```typescript\ninterface SerializerOptions {\n  // Target JSON Schema version (default: 'draft-2020-12')\n  schemaVersion?: 'draft-07' | 'draft-2020-12';\n\n  // Include $schema property in output (default: false)\n  includeSchemaVersion?: boolean;\n}\n```\n\n**Example:**\n\n```typescript\nconst serializer = new JSONSchemaSerializer({\n  schemaVersion: 'draft-07',\n  includeSchemaVersion: true\n});\n\nconst jsonSchema = serializer.serialize(ir);\n// Output includes: { \"$schema\": \"http://json-schema.org/draft-07/schema#\", ... }\n```\n\n## Limitations\n\n### Unsupported JSON Schema Features\n\nThe following JSON Schema features are **not supported** and will throw `UnsupportedFeatureError`:\n\n- **`$ref`** - Schema references and definitions\n  - *Why:* Would require schema resolution and can lead to circular references\n  - *Alternative:* Inline your schemas or use Zod with recursive types\n\n- **`allOf`** - Schema composition (intersection)\n  - *Why:* Complex merging logic, ambiguous constraint resolution\n  - *Alternative:* Flatten your schema manually\n\n- **`anyOf`** - Union types\n  - *Why:* IntakeSchema IR requires single, concrete types for form generation\n  - *Alternative:* Use separate schemas or enum for limited choices\n\n- **`oneOf`** - Exclusive union\n  - *Why:* Same as `anyOf`\n\n- **`not`** - Negation\n  - *Why:* Cannot be represented in positive constraint form\n\n- **Tuple validation** - `items` as array\n  - *Why:* IntakeSchema arrays have homogeneous items\n  - *Alternative:* Use object with numbered properties\n\n### Unsupported Zod Types\n\nThe following Zod types are **not supported**:\n\n- **`z.union()`**, `z.intersection()`, `z.discriminatedUnion()`**\n  - *Reason:* Same as JSON Schema union types\n\n- **`z.tuple()`**\n  - *Reason:* Homogeneous arrays only\n\n- **`z.record()`, `z.map()`, `z.set()`**\n  - *Reason:* Dynamic key structures not supported\n\n- **`z.lazy()`, `z.promise()`, `z.function()`**\n  - *Reason:* Not applicable to static schema representation\n\n- **`z.any()`, `z.unknown()`, `z.void()`, `z.undefined()`, `z.never()`**\n  - *Reason:* Too permissive or not applicable to intake forms\n\n### General Limitations\n\n1. **No circular references** - Deeply nested schemas are supported, but circular references are not\n2. **No conditional schemas** - `if/then/else` in JSON Schema not supported\n3. **No pattern properties** - Object properties must be explicitly defined\n4. **Single type per field** - Union types are not supported\n\n## Testing\n\nThe schema-normalizer package has 398+ comprehensive test cases covering all parsers, serializers, and edge cases.\n\n### Run Tests\n\n```bash\n# Run all tests\nnpm test\n\n# Run specific test file\nnpm test -- json-schema-parser.test.ts\n\n# Run with coverage\nnpm test:coverage\n\n# Watch mode\nnpm test:watch\n```\n\n### Test Coverage\n\n- **JSON Schema Parser**: 83 tests\n- **Zod Parser**: 80 tests\n- **OpenAPI Parser**: 60 tests\n- **Round-trip Tests**: 44 tests\n- **Edge Cases**: 68 tests\n- **Error Handling**: 63 tests\n\n**Total: 398 tests**\n\n### Build and Type Check\n\n```bash\n# Build TypeScript\nnpm run build\n\n# Type check without emitting\nnpm run typecheck\n\n# Clean build artifacts\nnpm run clean\n```\n\n## Examples\n\nSee the [`examples/basic-usage.ts`](./examples/basic-usage.ts) file for comprehensive usage examples including:\n\n1. Parsing JSON Schema\n2. Parsing Zod schemas\n3. Parsing OpenAPI documents\n4. Round-trip serialization\n5. Working with IntakeSchema IR\n6. Error handling\n7. Parser configuration options\n\nRun the examples:\n\n```bash\nnpx tsx examples/basic-usage.ts\n```\n\n## Contributing\n\nContributions are welcome! Please ensure:\n\n1. All tests pass: `npm test`\n2. Code follows TypeScript best practices\n3. New features include comprehensive tests\n4. Documentation is updated\n\n## License\n\nMIT © FormBridge Team\n\n## Related Packages\n\nThis package is part of the FormBridge ecosystem:\n\n- `@agentkitai/formbridge-validator` - Runtime validation using IntakeSchema IR\n- `@agentkitai/formbridge-form-renderer` - Dynamic form generation from IntakeSchema IR\n- `@agentkitai/formbridge-mcp-tools` - MCP tool generation from IntakeSchema IR\n\n## Support\n\n- **Issues**: [GitHub Issues](https://github.com/formbridge/formbridge/issues)\n- **Documentation**: [Full Documentation](https://formbridge.dev/docs)\n- **Examples**: See [`examples/`](./examples/) directory\n\n---\n\n**Built with ❤️ by the FormBridge Team**\n","readmeFilename":"README.md","homepage":"https://github.com/agentkitai/formbridge#readme","repository":{"type":"git","url":"git+https://github.com/agentkitai/formbridge.git","directory":"packages/schema-normalizer"},"bugs":{"url":"https://github.com/agentkitai/formbridge/issues"}}