Skip to content

Fionabronwen/graphql alloy foundation#75

Draft
FionaBronwen wants to merge 3 commits intofeature/graphqlfrom
fionabronwen/graphql-alloy-foundation
Draft

Fionabronwen/graphql alloy foundation#75
FionaBronwen wants to merge 3 commits intofeature/graphqlfrom
fionabronwen/graphql-alloy-foundation

Conversation

@FionaBronwen
Copy link
Copy Markdown

Summary

  • Add Alloy JSX build toolchain (@alloy-js/graphql, @alloy-js/cli, rollup plugin, TSX/JSX
    compiler config)
  • Introduce GraphQL schema context system (GraphQLSchemaContext, GraphQLTypeResolutionContext)
    for sharing classified types and input/output mode across the component tree
  • Add field-level components: Field, OperationField, and GraphQLTypeExpression — the core type
    resolution component that handles scalars, models, enums, unions, arrays, nullability, and
    input/output type splitting
  • Fix nullable tracking: track nullability on the container (ModelProperty, Operation) instead
    of the shared type singleton to prevent poisoning all uses of a scalar. Add comprehensive
    module documentation in nullable.ts explaining the architecture.
  • Add getGraphQLBuiltinName() for identity-based scalar → GraphQL built-in resolution
  • Add operation return type nullable tracking in GraphQLOperationMutation

Details

This is the foundation layer for the Alloy-based GraphQL emitter rewrite. It provides:

Build infrastructure: Alloy CLI for builds, rollup plugin for vitest JSX transform, TSX-aware
TypeScript config.

Context system: Two React-style contexts — GraphQLSchemaContext provides classified types
(models, enums, scalars, unions, operations) and model variant lookups for input/output name
resolution. GraphQLTypeResolutionContext tracks whether we're resolving types in input or
output mode, which determines naming (e.g., Foo vs FooInput) and non-null semantics.

Nullable tracking: The mutation engine strips null variants from unions before components
render. Since the structural evidence of nullability is gone by render time, state maps bridge
the gap. Three tracking sites:

  • Property-level: GraphQLModelPropertyMutation marks ModelProperty for inline T | null
  • Operation-level: GraphQLOperationMutation marks Operation for op foo(): T | null
  • Type-level: GraphQLUnionMutation marks the new union object for union Pet { Cat, Dog, null }

See nullable.ts module doc for the full architectural explanation.

Field components: Field renders model properties as gql.Field/gql.InputField. OperationField
renders operations as fields with arguments. Both delegate type resolution to
GraphQLTypeExpression, which uses typekit predicates for type narrowing and render-prop
children for structured output.

Foundation for the component-based GraphQL emitter:

Build infrastructure:
- Add @alloy-js/graphql dependency, switch to alloy build
- Configure JSX in tsconfig for Alloy components
- Add Alloy Rollup plugin for vitest

Context system:
- GraphQLSchemaContext: schema-wide state (classified types, model
  variants, scalar specifications)
- TypeResolutionContext: input/output mode tracking for type resolution

Field components:
- GraphQLTypeExpression: resolves TypeSpec types to GraphQL type info
  using Typekit predicates and context hooks. Handles nullability from
  two sources (property-level and type-level state maps), inline union
  unwrapping, array element recursion, scalar mapping, and model
  Input suffix determination.
- Field: renders a model property as gql.Field or gql.InputField
- OperationField: renders an operation as a field with input arguments

Nullable tracking fix:
- Track nullability on ModelProperty (unique per use-site) instead of
  the type (shared scalar singletons get poisoned)
- Add nullableElements state for Array<T | null> element tracking
- Add getGraphQLBuiltinName() identity-based scalar check
Replace raw indexer checks and `as Union`/`as Model` casts with proper
type guards (isArray(), $.union.is() narrowing). Add comprehensive
module-level documentation to nullable.ts explaining why state maps are
needed and how property-level vs type-level tracking works. Fix missing
nullable propagation for operation parameters in OperationField.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant