@cosmneo/onion-lasagna 1.0.0-beta.0 → 1.0.0-beta.2

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @cosmneo/onion-lasagna
2
2
 
3
+ ## 1.0.0-beta.2
4
+
5
+ ## 1.0.0-beta.1
6
+
7
+ ### Patch Changes
8
+
9
+ - f1b086c: **GraphQL SDL: zero-variant unions no longer emit unparseable SDL.** A schema
10
+ shape carrying an empty `anyOf` / `oneOf` — most commonly zod's `z.tuple([])`
11
+ ("always-empty array"), which Zod v4 converts to
12
+ `{ type: 'array', items: { anyOf: [] } }` — made the generator emit
13
+ `union X = ` with no members. That is invalid SDL, so the **entire** generated
14
+ schema failed to parse at server boot, surfacing as a misleading syntax error
15
+ on whatever definition happened to follow the union. Zero-variant unions now
16
+ fall back to the `JSON` scalar, consistent with the generator's handling of
17
+ other unrepresentable shapes (mixed-type unions, empty objects).
18
+
3
19
  ## 1.0.0-beta.0
4
20
 
5
21
  ### Major Changes
@@ -245,6 +245,7 @@ function registerOutputType(typeName, jsonSchema, namedTypes, unionsMetadata) {
245
245
  }
246
246
  function registerUnionType(typeName, variants, namedTypes, unionsMetadata) {
247
247
  if (namedTypes.has(typeName)) return typeName;
248
+ if (variants.length === 0) return "JSON";
248
249
  namedTypes.set(typeName, "");
249
250
  const memberNames = [];
250
251
  const seen = /* @__PURE__ */ new Set();
@@ -393,4 +394,4 @@ export {
393
394
  generateGraphQLSDL,
394
395
  generateGraphQLSDLWithMeta
395
396
  };
396
- //# sourceMappingURL=chunk-FUDTACMG.js.map
397
+ //# sourceMappingURL=chunk-3RX2NJKW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/presentation/graphql/sdl/generate.ts"],"sourcesContent":["/**\n * @fileoverview GraphQL SDL generation from schema definitions.\n *\n * The `generateGraphQLSDL` function creates a complete GraphQL Schema\n * Definition Language string from a schema definition.\n *\n * Input/output schemas are converted to JSON Schema via `toJsonSchema()`,\n * then mapped to GraphQL type definitions.\n *\n * Supported JSON Schema → GraphQL mappings:\n * - `type: 'string' | 'integer' | 'number' | 'boolean'` → scalars\n * - `type: 'array'` → `[T]` (recursing into `items`)\n * - `enum` → `String` (named enums are a future improvement)\n * - `type: 'object'` with `properties` → emitted as a named GraphQL\n * `type`/`input` and reused via the named-types map. Nested object\n * properties get hierarchical names (`OuterType_Property`) so the\n * SDL stays valid without dropping to a `JSON` scalar.\n * - `oneOf` / `anyOf` of object schemas (zod's `discriminatedUnion`\n * and plain `union`) → emitted as a GraphQL `union` of named member\n * types. Output types only — GraphQL spec forbids unions in inputs.\n *\n * Truly unrepresentable shapes (e.g. unions of mixed scalars) still\n * fall back to `JSON`. Empty objects (no properties) also fall back so\n * we don't emit invalid empty SDL types.\n *\n * @module graphql/sdl/generate\n */\n\nimport type { SchemaAdapter, JsonSchema } from '../../http/schema/types';\nimport type {\n GraphQLSchemaConfig,\n GraphQLSchemaDefinition,\n GraphQLFieldDefinition,\n} from '../field/types';\nimport { isSchemaDefinition, collectFields } from '../field/types';\nimport { generateFieldId } from '../field/utils';\nimport type { GraphQLSDLConfig } from './types';\n\n// ============================================================================\n// Public API Types\n// ============================================================================\n\n/**\n * Metadata for a single generated GraphQL union type.\n */\nexport interface GraphQLUnionMetadata {\n /** The generated names of all member types. */\n readonly members: readonly string[];\n /**\n * The JSON Schema property name of the discriminator field, when one was\n * detected. `null` when the union used positional names instead.\n */\n readonly discriminatorField: string | null;\n}\n\n/**\n * The result object returned by `generateGraphQLSDL`.\n *\n * Implements `toString()` so existing callers that treat the return value as\n * a plain string (template literals, string concatenation, etc.) continue to\n * work without modification.\n */\nexport interface GraphQLSDLResult {\n /** The full GraphQL SDL string. */\n readonly sdl: string;\n /**\n * Metadata about all union types that were generated.\n * Keyed by the union type name emitted in the SDL.\n */\n readonly unions: Readonly<Record<string, GraphQLUnionMetadata>>;\n /**\n * Returns the SDL string — allows backward-compatible use in template\n * literals and string concatenation.\n */\n toString(): string;\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\n/**\n * Generates a GraphQL SDL string from a schema definition.\n *\n * Walks the schema structure, extracts JSON schemas from all field\n * definitions, and builds a complete SDL with Query, Mutation, and\n * type definitions.\n *\n * @param schema - Schema definition or schema config\n * @param config - Optional SDL generation configuration\n * @returns Complete GraphQL SDL string\n *\n * @example Basic usage\n * ```typescript\n * import { generateGraphQLSDL } from '@cosmneo/onion-lasagna/graphql/sdl';\n *\n * const sdl = generateGraphQLSDL(projectSchema, {\n * preamble: 'scalar DateTime',\n * });\n *\n * console.log(sdl);\n * // type Query {\n * // projectsGet(input: ProjectsGetInput!): ProjectsGetOutput\n * // projectsList: ProjectsListOutput\n * // }\n * // ...\n * ```\n */\nexport function generateGraphQLSDL<T extends GraphQLSchemaConfig>(\n schema: T | GraphQLSchemaDefinition<T>,\n config?: GraphQLSDLConfig,\n): string {\n return buildSDL(schema, config).sdl;\n}\n\n/**\n * Generates a GraphQL SDL string together with structured metadata about\n * all union types produced during generation.\n *\n * The metadata is needed by server adapters to build `__resolveType`\n * resolvers for each union without re-parsing the SDL string.\n *\n * @param schema - Schema definition or schema config\n * @param config - Optional SDL generation configuration\n * @returns GraphQLSDLResult with the SDL string and union metadata\n *\n * @example\n * ```typescript\n * import { generateGraphQLSDLWithMeta } from '@cosmneo/onion-lasagna/graphql/sdl';\n *\n * const { sdl, unions } = generateGraphQLSDLWithMeta(schema);\n * for (const [unionName, meta] of Object.entries(unions)) {\n * console.log(unionName, meta.members, meta.discriminatorField);\n * }\n * ```\n */\nexport function generateGraphQLSDLWithMeta<T extends GraphQLSchemaConfig>(\n schema: T | GraphQLSchemaDefinition<T>,\n config?: GraphQLSDLConfig,\n): GraphQLSDLResult {\n return buildSDL(schema, config);\n}\n\n/**\n * Core implementation: builds both the SDL string and union metadata.\n * Called by both exported entry points.\n */\nfunction buildSDL<T extends GraphQLSchemaConfig>(\n schema: T | GraphQLSchemaDefinition<T>,\n config?: GraphQLSDLConfig,\n): GraphQLSDLResult {\n const fields = isSchemaDefinition(schema) ? schema.fields : schema;\n const collectedFields = collectFields(fields);\n\n const includeDescriptions = config?.includeDescriptions ?? true;\n const includeDeprecations = config?.includeDeprecations ?? true;\n\n // Partition fields by operation type — detect fieldId collisions early.\n const queries: { fieldId: string; field: GraphQLFieldDefinition }[] = [];\n const mutations: { fieldId: string; field: GraphQLFieldDefinition }[] = [];\n const subscriptions: { fieldId: string; field: GraphQLFieldDefinition }[] = [];\n\n const seenFieldIds = new Set<string>();\n for (const { key, field } of collectedFields) {\n const fieldId = generateFieldId(key);\n if (seenFieldIds.has(fieldId)) {\n throw new Error(\n `Duplicate fieldId \"${fieldId}\" detected in GraphQL schema. ` +\n `Two routes produced the same fieldId (\"${fieldId}\"). ` +\n `Rename one of the conflicting keys to avoid this collision.`,\n );\n }\n seenFieldIds.add(fieldId);\n\n if (field.operation === 'query') {\n queries.push({ fieldId, field });\n } else if (field.operation === 'mutation') {\n mutations.push({ fieldId, field });\n } else if (field.operation === 'subscription') {\n subscriptions.push({ fieldId, field });\n }\n }\n\n // Collect all named types that need to be emitted. Insertion order is\n // preserved (Map), so emitted types follow the order in which the\n // generator first encountered them.\n const namedTypes = new Map<string, string>();\n // Track union metadata for the KEY ENABLER requirement.\n const unionsMetadata = new Map<string, GraphQLUnionMetadata>();\n // Track whether any JSON reference was emitted so we can auto-declare\n // `scalar JSON` when needed.\n const jsonUsage = { used: false };\n\n // Track whether preamble already declares scalar JSON.\n const preambleHasJsonScalar = Boolean(\n config?.preamble && /\\bscalar\\s+JSON\\b/.test(config.preamble),\n );\n\n const rootTypeLines: string[] = [];\n\n // Build Query type\n if (queries.length > 0) {\n rootTypeLines.push('type Query {');\n for (const { fieldId, field } of queries) {\n const fieldLine = buildFieldLine(\n fieldId,\n field,\n namedTypes,\n unionsMetadata,\n jsonUsage,\n includeDescriptions,\n includeDeprecations,\n );\n rootTypeLines.push(fieldLine);\n }\n rootTypeLines.push('}');\n rootTypeLines.push('');\n }\n\n // Build Mutation type\n if (mutations.length > 0) {\n rootTypeLines.push('type Mutation {');\n for (const { fieldId, field } of mutations) {\n const fieldLine = buildFieldLine(\n fieldId,\n field,\n namedTypes,\n unionsMetadata,\n jsonUsage,\n includeDescriptions,\n includeDeprecations,\n );\n rootTypeLines.push(fieldLine);\n }\n rootTypeLines.push('}');\n rootTypeLines.push('');\n }\n\n // Build Subscription type\n if (subscriptions.length > 0) {\n rootTypeLines.push('type Subscription {');\n for (const { fieldId, field } of subscriptions) {\n const fieldLine = buildFieldLine(\n fieldId,\n field,\n namedTypes,\n unionsMetadata,\n jsonUsage,\n includeDescriptions,\n includeDeprecations,\n );\n rootTypeLines.push(fieldLine);\n }\n rootTypeLines.push('}');\n rootTypeLines.push('');\n }\n\n // Also check namedTypes bodies for JSON references (covers nested fields).\n if (!jsonUsage.used) {\n for (const [, body] of namedTypes) {\n if (/\\bJSON\\b/.test(body)) {\n jsonUsage.used = true;\n break;\n }\n }\n }\n\n const lines: string[] = [];\n\n // Preamble (may or may not already contain scalar JSON)\n if (config?.preamble) {\n lines.push(config.preamble);\n lines.push('');\n }\n\n // Auto-declare `scalar JSON` when JSON is used and not already declared.\n // Placed after any caller-supplied preamble so we don't reorder custom\n // scalar declarations, but before the operation types.\n if (jsonUsage.used && !preambleHasJsonScalar) {\n lines.push('scalar JSON');\n lines.push('');\n }\n\n lines.push(...rootTypeLines);\n\n // Emit named types (inputs and outputs)\n for (const [, typeBody] of namedTypes) {\n lines.push(typeBody);\n lines.push('');\n }\n\n const sdl = lines.join('\\n').trimEnd() + '\\n';\n\n // Build the result unions record.\n const unions: Record<string, GraphQLUnionMetadata> = {};\n for (const [typeName, meta] of unionsMetadata) {\n unions[typeName] = meta;\n }\n\n const result: GraphQLSDLResult = {\n sdl,\n unions,\n toString() {\n return this.sdl;\n },\n };\n\n return result;\n}\n\n// ============================================================================\n// Internal Helpers\n// ============================================================================\n\n/**\n * Builds a single field line for a root type (Query/Mutation/Subscription).\n */\nfunction buildFieldLine(\n fieldId: string,\n field: GraphQLFieldDefinition,\n namedTypes: Map<string, string>,\n unionsMetadata: Map<string, GraphQLUnionMetadata>,\n jsonUsage: { used: boolean },\n includeDescriptions: boolean,\n includeDeprecations: boolean,\n): string {\n const parts: string[] = [];\n\n // Description as SDL doc string (escaped to prevent triple-quote injection)\n if (includeDescriptions && field.docs.description) {\n const escaped = field.docs.description.replace(/\"\"\"/g, '\\\\\"\"\"');\n parts.push(` \"\"\"${escaped}\"\"\"`);\n }\n\n // Build args and return type\n const inputTypeName = field.input ? `${capitalize(fieldId)}Input` : undefined;\n const outputRootName = field.output ? `${capitalize(fieldId)}Output` : undefined;\n\n // Register named input type — may return 'JSON' for empty objects\n let resolvedInputType: string | undefined;\n if (field.input && inputTypeName) {\n const jsonSchema = (field.input as SchemaAdapter).toJsonSchema();\n resolvedInputType = registerInputType(inputTypeName, jsonSchema, namedTypes);\n if (resolvedInputType === 'JSON') jsonUsage.used = true;\n }\n\n // Register named output type — `outputTypeName` is what the field's\n // return type signature uses, which differs from `outputRootName` when\n // the schema is a union (the field references the union name; the\n // member object types live alongside it).\n let outputTypeName: string | undefined;\n if (field.output && outputRootName) {\n const jsonSchema = (field.output as SchemaAdapter).toJsonSchema();\n outputTypeName = registerOutputType(outputRootName, jsonSchema, namedTypes, unionsMetadata);\n if (outputTypeName === 'JSON' || (outputTypeName && outputTypeName.includes('JSON'))) {\n jsonUsage.used = true;\n }\n }\n\n // Build field signature\n let signature = ` ${fieldId}`;\n\n if (resolvedInputType) {\n signature += `(input: ${resolvedInputType}!)`;\n }\n\n signature += ': ';\n if (!outputTypeName) {\n // No output schema — fall back to JSON\n jsonUsage.used = true;\n signature += 'JSON';\n } else {\n signature += outputTypeName;\n }\n\n // Deprecation directive\n if (includeDeprecations && field.docs.deprecated) {\n const reason = field.docs.deprecationReason;\n signature += reason ? ` @deprecated(reason: \"${escapeSDLString(reason)}\")` : ' @deprecated';\n }\n\n if (parts.length > 0) {\n return parts.join('\\n') + '\\n' + signature;\n }\n return signature;\n}\n\n/**\n * Builds a GraphQL input type from a JSON schema, recursing into nested\n * objects (which become their own named input types). Returns the\n * registered type name. Falls back to registering a `JSON`-only marker\n * if the schema has no usable structure.\n */\nfunction registerInputType(\n typeName: string,\n jsonSchema: JsonSchema,\n namedTypes: Map<string, string>,\n): string {\n if (namedTypes.has(typeName)) return typeName;\n\n // Reserve the slot upfront so cycles don't recurse forever.\n namedTypes.set(typeName, '');\n\n const properties = (jsonSchema.properties ?? {}) as Record<string, JsonSchema>;\n const required = new Set(\n Array.isArray(jsonSchema.required) ? (jsonSchema.required as string[]) : [],\n );\n\n // Guard: empty object inputs are invalid SDL — fall back to JSON same as outputs.\n if (Object.keys(properties).length === 0) {\n namedTypes.delete(typeName);\n return 'JSON';\n }\n\n const lines: string[] = [`input ${typeName} {`];\n for (const [propName, propSchema] of Object.entries(properties)) {\n const safeName = sanitizeIdentifier(propName);\n const graphqlType = jsonSchemaToGraphQLType(\n propSchema,\n `${typeName}_${capitalize(safeName)}`,\n namedTypes,\n 'input',\n );\n const isRequired = required.has(propName) && !isNullable(propSchema);\n lines.push(` ${safeName}: ${graphqlType}${isRequired ? '!' : ''}`);\n }\n lines.push('}');\n\n namedTypes.set(typeName, lines.join('\\n'));\n return typeName;\n}\n\n/**\n * Builds a GraphQL output type (or union) from a JSON schema. Returns\n * the name to use in the field signature. For object schemas this is\n * `typeName`; for `oneOf` / `anyOf` schemas it's also `typeName` but\n * registered as a `union` whose members are named hierarchically.\n */\nfunction registerOutputType(\n typeName: string,\n jsonSchema: JsonSchema,\n namedTypes: Map<string, string>,\n unionsMetadata: Map<string, GraphQLUnionMetadata>,\n): string {\n if (namedTypes.has(typeName)) return typeName;\n\n // Array-rooted output — emit no named wrapper for the array itself,\n // recurse into the element schema and return `[Element]`. The element\n // type is named after `typeName_Item` so callers writing a list-style\n // query against `meTeamList: [TeamListOutput_Item]` get a meaningful\n // SDL name.\n if (jsonSchema.type === 'array') {\n const items = jsonSchema.items as JsonSchema | undefined;\n if (!items) return '[JSON]';\n const itemTypeName = jsonSchemaToGraphQLType(\n items,\n `${typeName}_Item`,\n namedTypes,\n 'output',\n unionsMetadata,\n );\n return `[${itemTypeName}]`;\n }\n\n // Union (oneOf / anyOf) at the root output position.\n const variants = pickVariants(jsonSchema);\n if (variants) {\n return registerUnionType(typeName, variants, namedTypes, unionsMetadata);\n }\n\n // Reserve before recursing.\n namedTypes.set(typeName, '');\n\n const properties = (jsonSchema.properties ?? {}) as Record<string, JsonSchema>;\n if (Object.keys(properties).length === 0) {\n // Nothing to emit; fall back to a JSON-shaped placeholder so the\n // field stays usable. We undo the reservation by removing the entry\n // so the caller's signature uses `JSON` instead.\n namedTypes.delete(typeName);\n return 'JSON';\n }\n\n const required = new Set(\n Array.isArray(jsonSchema.required) ? (jsonSchema.required as string[]) : [],\n );\n const lines: string[] = [`type ${typeName} {`];\n for (const [propName, propSchema] of Object.entries(properties)) {\n const safeName = sanitizeIdentifier(propName);\n const graphqlType = jsonSchemaToGraphQLType(\n propSchema,\n `${typeName}_${capitalize(safeName)}`,\n namedTypes,\n 'output',\n unionsMetadata,\n );\n const isRequired = required.has(propName) && !isNullable(propSchema);\n lines.push(` ${safeName}: ${graphqlType}${isRequired ? '!' : ''}`);\n }\n lines.push('}');\n\n namedTypes.set(typeName, lines.join('\\n'));\n return typeName;\n}\n\n/**\n * Build a GraphQL union for an `oneOf` / `anyOf` schema. Each branch\n * must be an object schema; non-object branches make the union\n * unrepresentable and we fall back to `JSON`. Member types are named\n * after the discriminator literal when one is present (zod's\n * `discriminatedUnion` produces a `const` literal on the discriminator\n * field), otherwise positional (`Member1`, `Member2`).\n */\nfunction registerUnionType(\n typeName: string,\n variants: JsonSchema[],\n namedTypes: Map<string, string>,\n unionsMetadata: Map<string, GraphQLUnionMetadata>,\n): string {\n if (namedTypes.has(typeName)) return typeName;\n\n // Zero-variant union — e.g. zod's `z.tuple([])` (\"always-empty array\")\n // emits `{ type: 'array', items: { anyOf: [] } }`. GraphQL has no\n // representation for a memberless union, and emitting `union X = `\n // produces unparseable SDL that breaks the whole schema. Fall back to\n // the JSON scalar like the other unrepresentable shapes.\n if (variants.length === 0) return 'JSON';\n\n // Reserve.\n namedTypes.set(typeName, '');\n\n const memberNames: string[] = [];\n const seen = new Set<string>();\n let discriminatorField: string | null = null;\n\n for (let i = 0; i < variants.length; i++) {\n const variant = variants[i];\n if (!variant || !isObjectSchema(variant)) {\n // Mixed-type union — bail. Drop the reservation so the field\n // signature falls back to `JSON`.\n namedTypes.delete(typeName);\n return 'JSON';\n }\n const discriminatorInfo = pickDiscriminatorInfo(variant);\n if (discriminatorInfo && discriminatorField === null) {\n discriminatorField = discriminatorInfo.field;\n }\n const discriminatorLabel = discriminatorInfo?.label ?? null;\n let memberName = discriminatorLabel\n ? `${typeName}_${pascalize(discriminatorLabel)}`\n : `${typeName}_Member${i + 1}`;\n // Ensure uniqueness within this union (two branches with the same\n // discriminator would be a Zod modelling bug, but we keep the\n // generator robust).\n let suffix = 2;\n while (seen.has(memberName)) {\n memberName = `${typeName}_${pascalize(discriminatorLabel ?? 'Member')}${suffix++}`;\n }\n seen.add(memberName);\n\n // Register the member type — it may return 'JSON' for empty objects.\n // In that case we still want to fall back gracefully for the union.\n const registeredName = registerOutputType(memberName, variant, namedTypes, unionsMetadata);\n\n // If the member fell back to JSON (empty object), skip adding it as\n // a named member — GraphQL cannot have JSON as a union member.\n // We fall back the whole union to JSON in this case.\n if (registeredName === 'JSON') {\n namedTypes.delete(typeName);\n return 'JSON';\n }\n\n memberNames.push(memberName);\n }\n\n namedTypes.set(typeName, `union ${typeName} = ${memberNames.join(' | ')}`);\n\n // Record union metadata for the KEY ENABLER.\n unionsMetadata.set(typeName, {\n members: memberNames,\n discriminatorField,\n });\n\n return typeName;\n}\n\n/**\n * Returns the variant list for a `oneOf` / `anyOf` schema, or `null` if\n * the schema doesn't carry one. `allOf` is not treated as a union — it\n * means \"intersect all\" and the JSON schema produced by Zod for plain\n * objects with shared fields would use it; we leave that as a future\n * improvement.\n */\nfunction pickVariants(schema: JsonSchema): JsonSchema[] | null {\n if (Array.isArray(schema.oneOf)) return schema.oneOf as JsonSchema[];\n if (Array.isArray(schema.anyOf)) return schema.anyOf as JsonSchema[];\n return null;\n}\n\n/**\n * Treat a schema as an object if it has `type: 'object'` or carries\n * `properties` (zod sometimes omits the explicit type when the shape is\n * obvious).\n */\nfunction isObjectSchema(schema: JsonSchema): boolean {\n if (!schema || typeof schema !== 'object') return false;\n if (schema.type === 'object') return true;\n if (schema.properties && typeof schema.properties === 'object') return true;\n return false;\n}\n\n/**\n * Returns true when the JSON schema fragment represents a nullable value.\n *\n * Handles two conventions:\n * - OpenAPI 3.0 style: `{ nullable: true, type: 'string' }`\n * - JSON Schema draft style: `{ anyOf: [{type:'string'},{type:'null'}] }`\n * - Array type style: `{ type: ['string', 'null'] }`\n */\nfunction isNullable(schema: JsonSchema): boolean {\n if ((schema as { nullable?: boolean }).nullable === true) return true;\n\n // type: ['string', 'null']\n const type = schema.type;\n if (Array.isArray(type) && (type as string[]).includes('null')) return true;\n\n // anyOf: [{type:'X'},{type:'null'}]\n const anyOf = (schema as { anyOf?: unknown[] }).anyOf;\n if (Array.isArray(anyOf)) {\n return anyOf.some(\n (branch) =>\n branch !== null &&\n typeof branch === 'object' &&\n (branch as { type?: string }).type === 'null',\n );\n }\n\n return false;\n}\n\n/**\n * Find the literal value and field name of a discriminator-style property in\n * an object schema. Zod's `z.discriminatedUnion('kind', [...])` emits each\n * branch with `properties.kind = { type: 'string', const: 'MEMBER' }` (or\n * `enum: ['MEMBER']`). We surface that literal so union member type names can\n * carry domain meaning instead of positional indexes.\n *\n * Returns `{ field, label }` when found, or `null` otherwise.\n */\nfunction pickDiscriminatorInfo(schema: JsonSchema): { field: string; label: string } | null {\n const properties = (schema.properties ?? {}) as Record<string, JsonSchema>;\n for (const [propName, propSchema] of Object.entries(properties)) {\n if (!propSchema || typeof propSchema !== 'object') continue;\n const constValue = (propSchema as { const?: unknown }).const;\n if (typeof constValue === 'string' && constValue.length > 0) {\n return { field: propName, label: constValue };\n }\n const enumValue = (propSchema as { enum?: unknown[] }).enum;\n if (\n Array.isArray(enumValue) &&\n enumValue.length === 1 &&\n typeof enumValue[0] === 'string' &&\n enumValue[0].length > 0\n ) {\n return { field: propName, label: enumValue[0] as string };\n }\n }\n return null;\n}\n\n/**\n * Converts a JSON schema fragment to a GraphQL type expression.\n *\n * For nested object shapes the function registers a new named type via\n * the supplied `namedTypes` map and returns its name. Arrays produce\n * `[ItemType]` and recurse into the element schema. Scalars map to the\n * GraphQL built-ins. Anything we can't represent (e.g. mixed-type\n * unions, schemas with no shape information) falls back to the `JSON`\n * scalar so the field stays queryable as an opaque value.\n *\n * `kind: 'input'` walks down through `registerInputType` so nested\n * structures stay on the input-types side; `kind: 'output'` mirrors\n * that for output types and unions.\n */\nfunction jsonSchemaToGraphQLType(\n schema: JsonSchema,\n parentTypeName: string,\n namedTypes: Map<string, string>,\n kind: 'input' | 'output',\n unionsMetadata?: Map<string, GraphQLUnionMetadata>,\n): string {\n if (!schema || typeof schema !== 'object') return 'JSON';\n\n // Handle enum (single-value enums and full enums alike → String for\n // now; named GraphQL enums are a future improvement).\n if (schema.enum && Array.isArray(schema.enum)) {\n return 'String';\n }\n\n // Union — only meaningful at output-position. Inputs collapse to JSON\n // because the GraphQL spec forbids unions in input types.\n const variants = pickVariants(schema);\n if (variants) {\n if (kind === 'input') return 'JSON';\n return registerUnionType(parentTypeName, variants, namedTypes, unionsMetadata ?? new Map());\n }\n\n const type = schema.type as string | undefined;\n\n switch (type) {\n case 'string':\n return 'String';\n case 'integer':\n return 'Int';\n case 'number':\n return 'Float';\n case 'boolean':\n return 'Boolean';\n case 'array': {\n const items = schema.items as JsonSchema | undefined;\n if (!items) return '[JSON]';\n const itemType = jsonSchemaToGraphQLType(\n items,\n // Use a stable '_Item' suffix appended to the full parent type name.\n // This avoids the previous s-stripping heuristic which caused sibling\n // array properties (e.g. 'item' and 'items') to collide on the same\n // generated type name.\n `${parentTypeName}_Item`,\n namedTypes,\n kind,\n unionsMetadata,\n );\n return `[${itemType}]`;\n }\n case 'object':\n if (kind === 'input') {\n return registerInputType(parentTypeName, schema, namedTypes);\n }\n return registerOutputType(parentTypeName, schema, namedTypes, unionsMetadata ?? new Map());\n default:\n // Object without explicit type but has properties.\n if (isObjectSchema(schema)) {\n if (kind === 'input') {\n return registerInputType(parentTypeName, schema, namedTypes);\n }\n return registerOutputType(parentTypeName, schema, namedTypes, unionsMetadata ?? new Map());\n }\n return 'JSON';\n }\n}\n\n/**\n * Sanitizes a JSON schema property name to a valid GraphQL identifier.\n *\n * GraphQL identifiers must match the pattern [_A-Za-z][_0-9A-Za-z]*. Property\n * names from JSON Schema can contain hyphens, dots, spaces, and other\n * characters that are illegal in GraphQL. We camelCase-encode separators\n * and strip remaining non-identifier characters so the resulting name is\n * always a valid SDL token.\n *\n * Examples:\n * - 'content-type' becomes 'contentType'\n * - 'meta.version' becomes 'metaVersion'\n * - 'first name' becomes 'firstName'\n * - '$id' becomes '_id'\n */\nfunction sanitizeIdentifier(name: string): string {\n if (/^[_A-Za-z][_0-9A-Za-z]*$/.test(name)) {\n // Already a valid identifier — no change needed.\n return name;\n }\n\n // Split on separator characters (hyphens, dots, spaces, underscores used\n // as word-separator when mixed with other separators) and camelCase them.\n const parts = name.split(/[-.\\s]+/);\n const camelCased = parts\n .filter((p) => p.length > 0)\n .map((part, index) => (index === 0 ? part : part.charAt(0).toUpperCase() + part.slice(1)))\n .join('');\n\n // Strip any remaining characters that are not valid identifier chars.\n const stripped = camelCased.replace(/[^_0-9A-Za-z]/g, '');\n\n // If the first char is a digit, prepend underscore.\n const safe = /^[0-9]/.test(stripped) ? `_${stripped}` : stripped;\n\n // Replace leading $ with _.\n return safe.replace(/^\\$/, '_');\n}\n\n/**\n * Capitalizes the first letter of a string.\n */\nfunction capitalize(str: string): string {\n return str.charAt(0).toUpperCase() + str.slice(1);\n}\n\n/**\n * Pascal-case a string by uppercasing each segment between non-alnum\n * runs. Used to turn discriminator literals (which may be `kebab-case`,\n * `snake_case`, or `UPPER_SNAKE`) into safe GraphQL type-name segments.\n */\nfunction pascalize(str: string): string {\n return str\n .split(/[^a-zA-Z0-9]+/g)\n .filter((part) => part.length > 0)\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())\n .join('');\n}\n\n/**\n * Escapes a string for use in SDL quoted string literals.\n * Handles backslashes, quotes, newlines, and triple-quote injection.\n */\nfunction escapeSDLString(str: string): string {\n return str\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/\"/g, '\\\\\"')\n .replace(/\\n/g, '\\\\n')\n .replace(/\\r/g, '\\\\r')\n .replace(/\\t/g, '\\\\t');\n}\n"],"mappings":"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","names":[]}
@@ -991,6 +991,7 @@ function registerOutputType(typeName, jsonSchema, namedTypes, unionsMetadata) {
991
991
  }
992
992
  function registerUnionType(typeName, variants, namedTypes, unionsMetadata) {
993
993
  if (namedTypes.has(typeName)) return typeName;
994
+ if (variants.length === 0) return "JSON";
994
995
  namedTypes.set(typeName, "");
995
996
  const memberNames = [];
996
997
  const seen = /* @__PURE__ */ new Set();