@effect-gql/core 0.1.0 → 1.1.0

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.
Files changed (164) hide show
  1. package/README.md +100 -0
  2. package/builder/index.cjs +1446 -0
  3. package/builder/index.cjs.map +1 -0
  4. package/builder/index.d.cts +260 -0
  5. package/{dist/builder/pipe-api.d.ts → builder/index.d.ts} +50 -21
  6. package/builder/index.js +1405 -0
  7. package/builder/index.js.map +1 -0
  8. package/index.cjs +3469 -0
  9. package/index.cjs.map +1 -0
  10. package/index.d.cts +529 -0
  11. package/index.d.ts +529 -0
  12. package/index.js +3292 -0
  13. package/index.js.map +1 -0
  14. package/package.json +19 -28
  15. package/schema-builder-DKvkzU_M.d.cts +965 -0
  16. package/schema-builder-DKvkzU_M.d.ts +965 -0
  17. package/server/index.cjs +1579 -0
  18. package/server/index.cjs.map +1 -0
  19. package/server/index.d.cts +682 -0
  20. package/server/index.d.ts +682 -0
  21. package/server/index.js +1548 -0
  22. package/server/index.js.map +1 -0
  23. package/dist/analyzer-extension.d.ts +0 -105
  24. package/dist/analyzer-extension.d.ts.map +0 -1
  25. package/dist/analyzer-extension.js +0 -137
  26. package/dist/analyzer-extension.js.map +0 -1
  27. package/dist/builder/execute.d.ts +0 -26
  28. package/dist/builder/execute.d.ts.map +0 -1
  29. package/dist/builder/execute.js +0 -104
  30. package/dist/builder/execute.js.map +0 -1
  31. package/dist/builder/field-builders.d.ts +0 -30
  32. package/dist/builder/field-builders.d.ts.map +0 -1
  33. package/dist/builder/field-builders.js +0 -200
  34. package/dist/builder/field-builders.js.map +0 -1
  35. package/dist/builder/index.d.ts +0 -7
  36. package/dist/builder/index.d.ts.map +0 -1
  37. package/dist/builder/index.js +0 -31
  38. package/dist/builder/index.js.map +0 -1
  39. package/dist/builder/pipe-api.d.ts.map +0 -1
  40. package/dist/builder/pipe-api.js +0 -151
  41. package/dist/builder/pipe-api.js.map +0 -1
  42. package/dist/builder/schema-builder.d.ts +0 -301
  43. package/dist/builder/schema-builder.d.ts.map +0 -1
  44. package/dist/builder/schema-builder.js +0 -566
  45. package/dist/builder/schema-builder.js.map +0 -1
  46. package/dist/builder/type-registry.d.ts +0 -80
  47. package/dist/builder/type-registry.d.ts.map +0 -1
  48. package/dist/builder/type-registry.js +0 -505
  49. package/dist/builder/type-registry.js.map +0 -1
  50. package/dist/builder/types.d.ts +0 -283
  51. package/dist/builder/types.d.ts.map +0 -1
  52. package/dist/builder/types.js +0 -3
  53. package/dist/builder/types.js.map +0 -1
  54. package/dist/cli/generate-schema.d.ts +0 -29
  55. package/dist/cli/generate-schema.d.ts.map +0 -1
  56. package/dist/cli/generate-schema.js +0 -233
  57. package/dist/cli/generate-schema.js.map +0 -1
  58. package/dist/cli/index.d.ts +0 -19
  59. package/dist/cli/index.d.ts.map +0 -1
  60. package/dist/cli/index.js +0 -24
  61. package/dist/cli/index.js.map +0 -1
  62. package/dist/context.d.ts +0 -18
  63. package/dist/context.d.ts.map +0 -1
  64. package/dist/context.js +0 -11
  65. package/dist/context.js.map +0 -1
  66. package/dist/error.d.ts +0 -45
  67. package/dist/error.d.ts.map +0 -1
  68. package/dist/error.js +0 -29
  69. package/dist/error.js.map +0 -1
  70. package/dist/extensions.d.ts +0 -130
  71. package/dist/extensions.d.ts.map +0 -1
  72. package/dist/extensions.js +0 -78
  73. package/dist/extensions.js.map +0 -1
  74. package/dist/index.d.ts +0 -12
  75. package/dist/index.d.ts.map +0 -1
  76. package/dist/index.js +0 -47
  77. package/dist/index.js.map +0 -1
  78. package/dist/loader.d.ts +0 -169
  79. package/dist/loader.d.ts.map +0 -1
  80. package/dist/loader.js +0 -237
  81. package/dist/loader.js.map +0 -1
  82. package/dist/resolver-context.d.ts +0 -154
  83. package/dist/resolver-context.d.ts.map +0 -1
  84. package/dist/resolver-context.js +0 -184
  85. package/dist/resolver-context.js.map +0 -1
  86. package/dist/schema-mapping.d.ts +0 -30
  87. package/dist/schema-mapping.d.ts.map +0 -1
  88. package/dist/schema-mapping.js +0 -280
  89. package/dist/schema-mapping.js.map +0 -1
  90. package/dist/server/cache-control.d.ts +0 -96
  91. package/dist/server/cache-control.d.ts.map +0 -1
  92. package/dist/server/cache-control.js +0 -308
  93. package/dist/server/cache-control.js.map +0 -1
  94. package/dist/server/complexity.d.ts +0 -165
  95. package/dist/server/complexity.d.ts.map +0 -1
  96. package/dist/server/complexity.js +0 -433
  97. package/dist/server/complexity.js.map +0 -1
  98. package/dist/server/config.d.ts +0 -66
  99. package/dist/server/config.d.ts.map +0 -1
  100. package/dist/server/config.js +0 -104
  101. package/dist/server/config.js.map +0 -1
  102. package/dist/server/graphiql.d.ts +0 -5
  103. package/dist/server/graphiql.d.ts.map +0 -1
  104. package/dist/server/graphiql.js +0 -43
  105. package/dist/server/graphiql.js.map +0 -1
  106. package/dist/server/index.d.ts +0 -18
  107. package/dist/server/index.d.ts.map +0 -1
  108. package/dist/server/index.js +0 -48
  109. package/dist/server/index.js.map +0 -1
  110. package/dist/server/router.d.ts +0 -79
  111. package/dist/server/router.d.ts.map +0 -1
  112. package/dist/server/router.js +0 -232
  113. package/dist/server/router.js.map +0 -1
  114. package/dist/server/schema-builder-extensions.d.ts +0 -42
  115. package/dist/server/schema-builder-extensions.d.ts.map +0 -1
  116. package/dist/server/schema-builder-extensions.js +0 -48
  117. package/dist/server/schema-builder-extensions.js.map +0 -1
  118. package/dist/server/sse-adapter.d.ts +0 -64
  119. package/dist/server/sse-adapter.d.ts.map +0 -1
  120. package/dist/server/sse-adapter.js +0 -227
  121. package/dist/server/sse-adapter.js.map +0 -1
  122. package/dist/server/sse-types.d.ts +0 -192
  123. package/dist/server/sse-types.d.ts.map +0 -1
  124. package/dist/server/sse-types.js +0 -63
  125. package/dist/server/sse-types.js.map +0 -1
  126. package/dist/server/ws-adapter.d.ts +0 -39
  127. package/dist/server/ws-adapter.d.ts.map +0 -1
  128. package/dist/server/ws-adapter.js +0 -247
  129. package/dist/server/ws-adapter.js.map +0 -1
  130. package/dist/server/ws-types.d.ts +0 -169
  131. package/dist/server/ws-types.d.ts.map +0 -1
  132. package/dist/server/ws-types.js +0 -11
  133. package/dist/server/ws-types.js.map +0 -1
  134. package/dist/server/ws-utils.d.ts +0 -42
  135. package/dist/server/ws-utils.d.ts.map +0 -1
  136. package/dist/server/ws-utils.js +0 -99
  137. package/dist/server/ws-utils.js.map +0 -1
  138. package/src/analyzer-extension.ts +0 -254
  139. package/src/builder/execute.ts +0 -153
  140. package/src/builder/field-builders.ts +0 -322
  141. package/src/builder/index.ts +0 -48
  142. package/src/builder/pipe-api.ts +0 -312
  143. package/src/builder/schema-builder.ts +0 -970
  144. package/src/builder/type-registry.ts +0 -670
  145. package/src/builder/types.ts +0 -305
  146. package/src/context.ts +0 -23
  147. package/src/error.ts +0 -32
  148. package/src/extensions.ts +0 -240
  149. package/src/index.ts +0 -32
  150. package/src/loader.ts +0 -363
  151. package/src/resolver-context.ts +0 -253
  152. package/src/schema-mapping.ts +0 -307
  153. package/src/server/cache-control.ts +0 -590
  154. package/src/server/complexity.ts +0 -774
  155. package/src/server/config.ts +0 -174
  156. package/src/server/graphiql.ts +0 -38
  157. package/src/server/index.ts +0 -96
  158. package/src/server/router.ts +0 -432
  159. package/src/server/schema-builder-extensions.ts +0 -51
  160. package/src/server/sse-adapter.ts +0 -327
  161. package/src/server/sse-types.ts +0 -234
  162. package/src/server/ws-adapter.ts +0 -355
  163. package/src/server/ws-types.ts +0 -192
  164. package/src/server/ws-utils.ts +0 -136
@@ -1,670 +0,0 @@
1
- import * as S from "effect/Schema"
2
- import * as AST from "effect/SchemaAST"
3
- import {
4
- GraphQLObjectType,
5
- GraphQLInterfaceType,
6
- GraphQLEnumType,
7
- GraphQLUnionType,
8
- GraphQLInputObjectType,
9
- GraphQLList,
10
- GraphQLNonNull,
11
- GraphQLFieldConfigMap,
12
- GraphQLInputFieldConfigMap,
13
- } from "graphql"
14
- import { toGraphQLType, toGraphQLArgs, toGraphQLInputType } from "../schema-mapping"
15
- import type {
16
- TypeRegistration,
17
- InterfaceRegistration,
18
- EnumRegistration,
19
- UnionRegistration,
20
- InputTypeRegistration,
21
- } from "./types"
22
-
23
- /**
24
- * Extract type name from a schema if it has one.
25
- * Supports:
26
- * - S.TaggedStruct("Name", {...}) - extracts from _tag literal
27
- * - S.TaggedClass()("Name", {...}) - extracts from identifier annotation
28
- * - S.Class<T>("Name")({...}) - extracts from identifier annotation
29
- */
30
- export function getSchemaName(schema: S.Schema<any, any, any>): string | undefined {
31
- const ast = schema.ast
32
-
33
- // Handle Transformation (Schema.Class, TaggedClass)
34
- if (ast._tag === "Transformation") {
35
- const identifier = AST.getIdentifierAnnotation((ast as any).to)
36
- if (identifier._tag === "Some") {
37
- return identifier.value
38
- }
39
- }
40
-
41
- // Handle TypeLiteral (TaggedStruct)
42
- if (ast._tag === "TypeLiteral") {
43
- const tagProp = (ast as any).propertySignatures.find((p: any) => String(p.name) === "_tag")
44
- if (tagProp && tagProp.type._tag === "Literal" && typeof tagProp.type.literal === "string") {
45
- return tagProp.type.literal
46
- }
47
- }
48
-
49
- return undefined
50
- }
51
-
52
- /**
53
- * Context needed for type conversion operations
54
- */
55
- export interface TypeConversionContext {
56
- types: Map<string, TypeRegistration>
57
- interfaces: Map<string, InterfaceRegistration>
58
- enums: Map<string, EnumRegistration>
59
- unions: Map<string, UnionRegistration>
60
- inputs: Map<string, InputTypeRegistration>
61
- typeRegistry: Map<string, GraphQLObjectType>
62
- interfaceRegistry: Map<string, GraphQLInterfaceType>
63
- enumRegistry: Map<string, GraphQLEnumType>
64
- unionRegistry: Map<string, GraphQLUnionType>
65
- inputRegistry: Map<string, GraphQLInputObjectType>
66
- // Reverse lookup caches for O(1) type resolution
67
- schemaToTypeName?: Map<S.Schema<any, any, any>, string>
68
- astToTypeName?: Map<AST.AST, string>
69
- schemaToInterfaceName?: Map<S.Schema<any, any, any>, string>
70
- astToInterfaceName?: Map<AST.AST, string>
71
- schemaToInputName?: Map<S.Schema<any, any, any>, string>
72
- astToInputName?: Map<AST.AST, string>
73
- // Cached sorted values for enum/union matching
74
- enumSortedValues?: Map<string, readonly string[]>
75
- unionSortedTypes?: Map<string, readonly string[]>
76
- // Reverse lookup: literal value -> enum name (for single literal O(1) lookup)
77
- literalToEnumName?: Map<string, string>
78
- }
79
-
80
- /**
81
- * Build reverse lookup maps from registration maps for O(1) type resolution
82
- */
83
- export function buildReverseLookups(ctx: TypeConversionContext): void {
84
- // Build schema/AST -> type name lookups
85
- if (!ctx.schemaToTypeName) {
86
- ctx.schemaToTypeName = new Map()
87
- ctx.astToTypeName = new Map()
88
- for (const [typeName, typeReg] of ctx.types) {
89
- ctx.schemaToTypeName.set(typeReg.schema, typeName)
90
- ctx.astToTypeName.set(typeReg.schema.ast, typeName)
91
- }
92
- }
93
-
94
- // Build schema/AST -> interface name lookups
95
- if (!ctx.schemaToInterfaceName) {
96
- ctx.schemaToInterfaceName = new Map()
97
- ctx.astToInterfaceName = new Map()
98
- for (const [interfaceName, interfaceReg] of ctx.interfaces) {
99
- ctx.schemaToInterfaceName.set(interfaceReg.schema, interfaceName)
100
- ctx.astToInterfaceName.set(interfaceReg.schema.ast, interfaceName)
101
- }
102
- }
103
-
104
- // Build schema/AST -> input name lookups
105
- if (!ctx.schemaToInputName) {
106
- ctx.schemaToInputName = new Map()
107
- ctx.astToInputName = new Map()
108
- for (const [inputName, inputReg] of ctx.inputs) {
109
- ctx.schemaToInputName.set(inputReg.schema, inputName)
110
- ctx.astToInputName.set(inputReg.schema.ast, inputName)
111
- }
112
- }
113
-
114
- // Build cached sorted enum values and literal -> enum lookup
115
- if (!ctx.enumSortedValues) {
116
- ctx.enumSortedValues = new Map()
117
- ctx.literalToEnumName = new Map()
118
- for (const [enumName, enumReg] of ctx.enums) {
119
- ctx.enumSortedValues.set(enumName, [...enumReg.values].sort())
120
- // Build literal -> enum reverse lookup for O(1) single literal lookups
121
- for (const value of enumReg.values) {
122
- ctx.literalToEnumName.set(value, enumName)
123
- }
124
- }
125
- }
126
-
127
- // Build cached sorted union types
128
- if (!ctx.unionSortedTypes) {
129
- ctx.unionSortedTypes = new Map()
130
- for (const [unionName, unionReg] of ctx.unions) {
131
- ctx.unionSortedTypes.set(unionName, [...unionReg.types].sort())
132
- }
133
- }
134
- }
135
-
136
- // GraphQLNonNull wrapper cache for memoization
137
- const nonNullCache = new WeakMap<any, GraphQLNonNull<any>>()
138
-
139
- /**
140
- * Get or create a GraphQLNonNull wrapper (memoized)
141
- */
142
- export function getNonNull<T extends import("graphql").GraphQLNullableType>(
143
- type: T
144
- ): GraphQLNonNull<T> {
145
- let cached = nonNullCache.get(type)
146
- if (!cached) {
147
- cached = new GraphQLNonNull(type)
148
- nonNullCache.set(type, cached)
149
- }
150
- return cached
151
- }
152
-
153
- /**
154
- * Convert schema to GraphQL type, checking registry first for registered types
155
- */
156
- export function toGraphQLTypeWithRegistry(
157
- schema: S.Schema<any, any, any>,
158
- ctx: TypeConversionContext
159
- ): any {
160
- // Ensure reverse lookup maps are built
161
- buildReverseLookups(ctx)
162
-
163
- const ast = schema.ast
164
-
165
- // Check registered object types first
166
- const registeredType = findRegisteredType(schema, ast, ctx)
167
- if (registeredType) return registeredType
168
-
169
- // Check registered interfaces
170
- const registeredInterface = findRegisteredInterface(schema, ast, ctx)
171
- if (registeredInterface) return registeredInterface
172
-
173
- // Handle transformations (like S.Array, S.optional, etc)
174
- if (ast._tag === "Transformation") {
175
- return handleTransformationAST(ast, ctx)
176
- }
177
-
178
- // Handle unions (enum literals or object type unions)
179
- if (ast._tag === "Union") {
180
- return handleUnionAST(ast, ctx)
181
- }
182
-
183
- // Check single literal for enum match
184
- if (ast._tag === "Literal") {
185
- const enumType = findEnumForLiteral(ast, ctx)
186
- if (enumType) return enumType
187
- }
188
-
189
- // Handle tuple types (readonly arrays)
190
- if (ast._tag === "TupleType") {
191
- return handleTupleTypeAST(ast, ctx)
192
- }
193
-
194
- // Handle Suspend (recursive/self-referential schemas)
195
- if (ast._tag === "Suspend") {
196
- const innerAst = (ast as any).f()
197
- return toGraphQLTypeWithRegistry(S.make(innerAst), ctx)
198
- }
199
-
200
- // Fall back to default conversion
201
- return toGraphQLType(schema)
202
- }
203
-
204
- /**
205
- * Find a registered object type matching this schema (O(1) with reverse lookup)
206
- */
207
- function findRegisteredType(
208
- schema: S.Schema<any, any, any>,
209
- ast: AST.AST,
210
- ctx: TypeConversionContext
211
- ): GraphQLObjectType | undefined {
212
- // Use reverse lookup maps for O(1) lookup
213
- const typeName = ctx.schemaToTypeName?.get(schema) ?? ctx.astToTypeName?.get(ast)
214
- if (typeName) {
215
- return ctx.typeRegistry.get(typeName)
216
- }
217
- return undefined
218
- }
219
-
220
- /**
221
- * Find a registered interface matching this schema (O(1) with reverse lookup)
222
- */
223
- function findRegisteredInterface(
224
- schema: S.Schema<any, any, any>,
225
- ast: AST.AST,
226
- ctx: TypeConversionContext
227
- ): GraphQLInterfaceType | undefined {
228
- // Use reverse lookup maps for O(1) lookup
229
- const interfaceName = ctx.schemaToInterfaceName?.get(schema) ?? ctx.astToInterfaceName?.get(ast)
230
- if (interfaceName) {
231
- return ctx.interfaceRegistry.get(interfaceName)
232
- }
233
- return undefined
234
- }
235
-
236
- /**
237
- * Handle Transformation AST nodes (arrays, optional, Schema.Class, etc.)
238
- */
239
- function handleTransformationAST(ast: any, ctx: TypeConversionContext): any {
240
- const toAst = ast.to
241
-
242
- // Check if it's an array (readonly array on the to side)
243
- if (toAst._tag === "TupleType") {
244
- if (toAst.rest && toAst.rest.length > 0) {
245
- const elementSchema = S.make(toAst.rest[0].type)
246
- const elementType = toGraphQLTypeWithRegistry(elementSchema, ctx)
247
- return new GraphQLList(elementType)
248
- } else if (toAst.elements.length > 0) {
249
- const elementSchema = S.make(toAst.elements[0].type)
250
- const elementType = toGraphQLTypeWithRegistry(elementSchema, ctx)
251
- return new GraphQLList(elementType)
252
- }
253
- }
254
-
255
- // Other transformations - recurse on the "to" side
256
- return toGraphQLTypeWithRegistry(S.make(ast.to), ctx)
257
- }
258
-
259
- /**
260
- * Handle Union AST nodes (literal enums or object type unions)
261
- */
262
- function handleUnionAST(ast: any, ctx: TypeConversionContext): any {
263
- const allLiterals = ast.types.every((t: any) => t._tag === "Literal")
264
-
265
- if (allLiterals) {
266
- // This might be an enum
267
- const enumType = findEnumForLiteralUnion(ast.types, ctx)
268
- if (enumType) return enumType
269
- } else {
270
- // This is a Union of object types - check if it matches a registered union
271
- const unionType = findRegisteredUnion(ast.types, ctx)
272
- if (unionType) return unionType
273
- }
274
-
275
- // Fallback: use first type
276
- if (ast.types.length > 0) {
277
- return toGraphQLTypeWithRegistry(S.make(ast.types[0]), ctx)
278
- }
279
-
280
- return toGraphQLType(S.make(ast))
281
- }
282
-
283
- /**
284
- * Find a registered enum matching a union of literals (uses cached sorted values)
285
- */
286
- function findEnumForLiteralUnion(
287
- types: any[],
288
- ctx: TypeConversionContext
289
- ): GraphQLEnumType | undefined {
290
- const literalValues = types.map((t: any) => String(t.literal)).sort()
291
-
292
- for (const [enumName] of ctx.enums) {
293
- // Use cached sorted values instead of sorting on every comparison
294
- const enumValues = ctx.enumSortedValues?.get(enumName)
295
- if (
296
- enumValues &&
297
- literalValues.length === enumValues.length &&
298
- literalValues.every((v: string, i: number) => v === enumValues[i])
299
- ) {
300
- return ctx.enumRegistry.get(enumName)
301
- }
302
- }
303
- return undefined
304
- }
305
-
306
- /**
307
- * Find a registered union matching an object type union (uses cached sorted types)
308
- */
309
- function findRegisteredUnion(
310
- types: any[],
311
- ctx: TypeConversionContext
312
- ): GraphQLUnionType | undefined {
313
- // Collect _tag values from each union member
314
- const memberTags: string[] = []
315
- for (const memberAst of types) {
316
- if (memberAst._tag === "TypeLiteral") {
317
- const tagProp = memberAst.propertySignatures.find((p: any) => String(p.name) === "_tag")
318
- if (tagProp && tagProp.type._tag === "Literal") {
319
- memberTags.push(String(tagProp.type.literal))
320
- }
321
- }
322
- }
323
-
324
- // Check if any registered union has matching types
325
- if (memberTags.length === types.length) {
326
- const sortedTags = memberTags.sort()
327
- for (const [unionName] of ctx.unions) {
328
- // Use cached sorted types instead of sorting on every comparison
329
- const unionTypes = ctx.unionSortedTypes?.get(unionName)
330
- if (
331
- unionTypes &&
332
- sortedTags.length === unionTypes.length &&
333
- sortedTags.every((tag, i) => tag === unionTypes[i])
334
- ) {
335
- return ctx.unionRegistry.get(unionName)
336
- }
337
- }
338
- }
339
- return undefined
340
- }
341
-
342
- /**
343
- * Find a registered enum containing a single literal value (O(1) with reverse lookup)
344
- */
345
- function findEnumForLiteral(ast: any, ctx: TypeConversionContext): GraphQLEnumType | undefined {
346
- const literalValue = String(ast.literal)
347
- // Use reverse lookup map for O(1) lookup instead of O(N×M) iteration
348
- const enumName = ctx.literalToEnumName?.get(literalValue)
349
- if (enumName) {
350
- return ctx.enumRegistry.get(enumName)
351
- }
352
- return undefined
353
- }
354
-
355
- /**
356
- * Handle TupleType AST nodes (arrays)
357
- */
358
- function handleTupleTypeAST(ast: any, ctx: TypeConversionContext): any {
359
- if (ast.rest && ast.rest.length > 0) {
360
- const elementSchema = S.make(ast.rest[0].type)
361
- const elementType = toGraphQLTypeWithRegistry(elementSchema, ctx)
362
- return new GraphQLList(elementType)
363
- } else if (ast.elements && ast.elements.length > 0) {
364
- const elementSchema = S.make(ast.elements[0].type)
365
- const elementType = toGraphQLTypeWithRegistry(elementSchema, ctx)
366
- return new GraphQLList(elementType)
367
- }
368
- return toGraphQLType(S.make(ast))
369
- }
370
-
371
- /**
372
- * Convert a schema to GraphQL fields
373
- */
374
- export function schemaToFields(
375
- schema: S.Schema<any, any, any>,
376
- ctx: TypeConversionContext
377
- ): GraphQLFieldConfigMap<any, any> {
378
- let ast = schema.ast
379
-
380
- // Handle Transformation (Schema.Class, TaggedClass)
381
- if (ast._tag === "Transformation") {
382
- ast = (ast as any).to
383
- }
384
-
385
- // Handle Declaration (Schema.Class wraps TypeLiteral in Declaration)
386
- if (ast._tag === "Declaration") {
387
- const typeParams = (ast as any).typeParameters
388
- if (typeParams && typeParams.length > 0 && typeParams[0]._tag === "TypeLiteral") {
389
- ast = typeParams[0]
390
- }
391
- }
392
-
393
- if (ast._tag === "TypeLiteral") {
394
- const fields: GraphQLFieldConfigMap<any, any> = {}
395
-
396
- for (const field of (ast as any).propertySignatures) {
397
- const fieldName = String(field.name)
398
- const fieldSchema = S.make(field.type)
399
- let fieldType = toGraphQLTypeWithRegistry(fieldSchema, ctx)
400
-
401
- // Make non-optional fields non-null (memoized)
402
- if (!field.isOptional) {
403
- fieldType = getNonNull(fieldType)
404
- }
405
-
406
- fields[fieldName] = { type: fieldType }
407
- }
408
-
409
- return fields
410
- }
411
-
412
- return {}
413
- }
414
-
415
- /**
416
- * Convert a schema to GraphQL input fields
417
- */
418
- export function schemaToInputFields(
419
- schema: S.Schema<any, any, any>,
420
- enumRegistry: Map<string, GraphQLEnumType>,
421
- inputRegistry: Map<string, GraphQLInputObjectType>,
422
- inputs: Map<string, InputTypeRegistration>,
423
- enums: Map<string, EnumRegistration>,
424
- cache?: InputTypeLookupCache
425
- ): GraphQLInputFieldConfigMap {
426
- const ast = schema.ast
427
-
428
- if (ast._tag === "TypeLiteral") {
429
- const fields: GraphQLInputFieldConfigMap = {}
430
-
431
- for (const field of ast.propertySignatures) {
432
- const fieldName = String(field.name)
433
- const fieldSchema = S.make(field.type)
434
- let fieldType = toGraphQLInputTypeWithRegistry(
435
- fieldSchema,
436
- enumRegistry,
437
- inputRegistry,
438
- inputs,
439
- enums,
440
- cache
441
- )
442
-
443
- // Make non-optional fields non-null (memoized)
444
- if (!field.isOptional) {
445
- fieldType = getNonNull(fieldType)
446
- }
447
-
448
- fields[fieldName] = { type: fieldType }
449
- }
450
-
451
- return fields
452
- }
453
-
454
- return {}
455
- }
456
-
457
- /**
458
- * Optional cache for input type lookups to enable O(1) resolution
459
- */
460
- export interface InputTypeLookupCache {
461
- schemaToInputName?: Map<S.Schema<any, any, any>, string>
462
- astToInputName?: Map<AST.AST, string>
463
- literalToEnumName?: Map<string, string>
464
- enumSortedValues?: Map<string, readonly string[]>
465
- }
466
-
467
- /**
468
- * Build lookup caches for input type resolution
469
- */
470
- export function buildInputTypeLookupCache(
471
- inputs: Map<string, InputTypeRegistration>,
472
- enums: Map<string, EnumRegistration>
473
- ): InputTypeLookupCache {
474
- const cache: InputTypeLookupCache = {
475
- schemaToInputName: new Map(),
476
- astToInputName: new Map(),
477
- literalToEnumName: new Map(),
478
- enumSortedValues: new Map(),
479
- }
480
-
481
- // Build input type reverse lookups
482
- for (const [inputName, inputReg] of inputs) {
483
- cache.schemaToInputName!.set(inputReg.schema, inputName)
484
- cache.astToInputName!.set(inputReg.schema.ast, inputName)
485
- }
486
-
487
- // Build enum lookups
488
- for (const [enumName, enumReg] of enums) {
489
- cache.enumSortedValues!.set(enumName, [...enumReg.values].sort())
490
- for (const value of enumReg.values) {
491
- cache.literalToEnumName!.set(value, enumName)
492
- }
493
- }
494
-
495
- return cache
496
- }
497
-
498
- /**
499
- * Convert a schema to GraphQL input type, checking enum and input registries.
500
- * Uses O(1) reverse lookups when cache is provided.
501
- */
502
- export function toGraphQLInputTypeWithRegistry(
503
- schema: S.Schema<any, any, any>,
504
- enumRegistry: Map<string, GraphQLEnumType>,
505
- inputRegistry: Map<string, GraphQLInputObjectType>,
506
- inputs: Map<string, InputTypeRegistration>,
507
- enums: Map<string, EnumRegistration>,
508
- cache?: InputTypeLookupCache
509
- ): any {
510
- const ast = schema.ast
511
-
512
- // Handle transformations (like S.optional wrapping)
513
- if (ast._tag === "Transformation") {
514
- const toAst = (ast as any).to
515
- return toGraphQLInputTypeWithRegistry(
516
- S.make(toAst),
517
- enumRegistry,
518
- inputRegistry,
519
- inputs,
520
- enums,
521
- cache
522
- )
523
- }
524
-
525
- // Check if this schema matches a registered input type (O(1) with cache)
526
- if (cache?.schemaToInputName || cache?.astToInputName) {
527
- const inputName = cache.schemaToInputName?.get(schema) ?? cache.astToInputName?.get(ast)
528
- if (inputName) {
529
- const result = inputRegistry.get(inputName)
530
- if (result) return result
531
- }
532
- } else {
533
- // Fallback to linear scan if no cache
534
- for (const [inputName, inputReg] of inputs) {
535
- if (inputReg.schema.ast === ast || inputReg.schema === schema) {
536
- const result = inputRegistry.get(inputName)
537
- if (result) return result
538
- }
539
- }
540
- }
541
-
542
- // Check if this schema matches a registered enum
543
- if (ast._tag === "Union") {
544
- const unionAst = ast as any
545
-
546
- // Handle S.optional which creates Union(LiteralUnion, UndefinedKeyword)
547
- const nonUndefinedTypes = unionAst.types.filter((t: any) => t._tag !== "UndefinedKeyword")
548
- if (nonUndefinedTypes.length === 1 && nonUndefinedTypes[0]._tag === "Union") {
549
- return toGraphQLInputTypeWithRegistry(
550
- S.make(nonUndefinedTypes[0]),
551
- enumRegistry,
552
- inputRegistry,
553
- inputs,
554
- enums,
555
- cache
556
- )
557
- }
558
-
559
- // Check for nested input type inside optional
560
- if (nonUndefinedTypes.length === 1 && nonUndefinedTypes[0]._tag === "TypeLiteral") {
561
- return toGraphQLInputTypeWithRegistry(
562
- S.make(nonUndefinedTypes[0]),
563
- enumRegistry,
564
- inputRegistry,
565
- inputs,
566
- enums,
567
- cache
568
- )
569
- }
570
-
571
- const allLiterals = unionAst.types.every((t: any) => t._tag === "Literal")
572
-
573
- if (allLiterals) {
574
- const literalValues = unionAst.types.map((t: any) => String(t.literal)).sort()
575
-
576
- // Use cached sorted values if available
577
- for (const [enumName] of enums) {
578
- const enumValues =
579
- cache?.enumSortedValues?.get(enumName) ?? [...enums.get(enumName)!.values].sort()
580
- if (
581
- literalValues.length === enumValues.length &&
582
- literalValues.every((v: string, i: number) => v === enumValues[i])
583
- ) {
584
- const result = enumRegistry.get(enumName)
585
- if (result) return result
586
- }
587
- }
588
- }
589
- }
590
-
591
- // Check single literal (O(1) with cache)
592
- if (ast._tag === "Literal") {
593
- const literalValue = String((ast as any).literal)
594
- if (cache?.literalToEnumName) {
595
- const enumName = cache.literalToEnumName.get(literalValue)
596
- if (enumName) {
597
- const result = enumRegistry.get(enumName)
598
- if (result) return result
599
- }
600
- } else {
601
- // Fallback to linear scan if no cache
602
- for (const [enumName, enumReg] of enums) {
603
- if (enumReg.values.includes(literalValue)) {
604
- const result = enumRegistry.get(enumName)
605
- if (result) return result
606
- }
607
- }
608
- }
609
- }
610
-
611
- // Handle Suspend (recursive/self-referential schemas)
612
- if (ast._tag === "Suspend") {
613
- const innerAst = (ast as any).f()
614
- return toGraphQLInputTypeWithRegistry(
615
- S.make(innerAst),
616
- enumRegistry,
617
- inputRegistry,
618
- inputs,
619
- enums,
620
- cache
621
- )
622
- }
623
-
624
- // Fall back to default toGraphQLInputType
625
- return toGraphQLInputType(schema)
626
- }
627
-
628
- /**
629
- * Convert a schema to GraphQL arguments with registry support.
630
- * Uses O(1) reverse lookups when cache is provided.
631
- */
632
- export function toGraphQLArgsWithRegistry(
633
- schema: S.Schema<any, any, any>,
634
- enumRegistry: Map<string, GraphQLEnumType>,
635
- inputRegistry: Map<string, GraphQLInputObjectType>,
636
- inputs: Map<string, InputTypeRegistration>,
637
- enums: Map<string, EnumRegistration>,
638
- cache?: InputTypeLookupCache
639
- ): any {
640
- const ast = schema.ast
641
-
642
- if (ast._tag === "TypeLiteral") {
643
- const args: Record<string, { type: any }> = {}
644
-
645
- for (const field of (ast as any).propertySignatures) {
646
- const fieldName = String(field.name)
647
- const fieldSchema = S.make(field.type)
648
- let fieldType = toGraphQLInputTypeWithRegistry(
649
- fieldSchema,
650
- enumRegistry,
651
- inputRegistry,
652
- inputs,
653
- enums,
654
- cache
655
- )
656
-
657
- // Make non-optional fields non-null (memoized)
658
- if (!field.isOptional) {
659
- fieldType = getNonNull(fieldType)
660
- }
661
-
662
- args[fieldName] = { type: fieldType }
663
- }
664
-
665
- return args
666
- }
667
-
668
- // Fall back to default toGraphQLArgs
669
- return toGraphQLArgs(schema)
670
- }