polen 0.11.0-next.23 → 0.11.0-next.25

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 (192) hide show
  1. package/build/api/config/input.d.ts +76 -77
  2. package/build/api/config/input.d.ts.map +1 -1
  3. package/build/api/config/normalized.d.ts +136 -143
  4. package/build/api/config/normalized.d.ts.map +1 -1
  5. package/build/api/config-template/template.d.ts +110 -121
  6. package/build/api/config-template/template.d.ts.map +1 -1
  7. package/build/api/examples/diagnostic/diagnostic.d.ts +0 -23
  8. package/build/api/examples/diagnostic/diagnostic.d.ts.map +1 -1
  9. package/build/api/examples/diagnostic/diagnostic.js +1 -3
  10. package/build/api/examples/diagnostic/diagnostic.js.map +1 -1
  11. package/build/api/examples/scanner.d.ts +27 -1
  12. package/build/api/examples/scanner.d.ts.map +1 -1
  13. package/build/api/examples/scanner.js +19 -17
  14. package/build/api/examples/scanner.js.map +1 -1
  15. package/build/api/examples/schemas/catalog.d.ts +20 -12
  16. package/build/api/examples/schemas/catalog.d.ts.map +1 -1
  17. package/build/api/examples/schemas/example/example.d.ts +17 -11
  18. package/build/api/examples/schemas/example/example.d.ts.map +1 -1
  19. package/build/api/schema/augmentations/$$.d.ts +7 -0
  20. package/build/api/schema/augmentations/$$.d.ts.map +1 -0
  21. package/build/api/schema/augmentations/$$.js +7 -0
  22. package/build/api/schema/augmentations/$$.js.map +1 -0
  23. package/build/api/schema/augmentations/$.d.ts +1 -1
  24. package/build/api/schema/augmentations/$.d.ts.map +1 -1
  25. package/build/api/schema/augmentations/$.js +1 -1
  26. package/build/api/schema/augmentations/$.js.map +1 -1
  27. package/build/api/schema/augmentations/apply.d.ts +29 -0
  28. package/build/api/schema/augmentations/apply.d.ts.map +1 -0
  29. package/build/api/schema/augmentations/apply.js +181 -0
  30. package/build/api/schema/augmentations/apply.js.map +1 -0
  31. package/build/api/schema/augmentations/augmentation.d.ts +31 -0
  32. package/build/api/schema/augmentations/augmentation.d.ts.map +1 -0
  33. package/build/api/schema/augmentations/augmentation.js +22 -0
  34. package/build/api/schema/augmentations/augmentation.js.map +1 -0
  35. package/build/api/schema/augmentations/config.d.ts +24 -0
  36. package/build/api/schema/augmentations/config.d.ts.map +1 -0
  37. package/build/api/schema/augmentations/config.js +9 -0
  38. package/build/api/schema/augmentations/config.js.map +1 -0
  39. package/build/api/schema/augmentations/diagnostics/diagnostic.d.ts +67 -0
  40. package/build/api/schema/augmentations/diagnostics/diagnostic.d.ts.map +1 -0
  41. package/build/api/schema/augmentations/diagnostics/diagnostic.js +13 -0
  42. package/build/api/schema/augmentations/diagnostics/diagnostic.js.map +1 -0
  43. package/build/api/schema/augmentations/diagnostics/duplicate-version.d.ts +45 -0
  44. package/build/api/schema/augmentations/diagnostics/duplicate-version.d.ts.map +1 -0
  45. package/build/api/schema/augmentations/diagnostics/duplicate-version.js +17 -0
  46. package/build/api/schema/augmentations/diagnostics/duplicate-version.js.map +1 -0
  47. package/build/api/schema/augmentations/diagnostics/invalid-path.d.ts +46 -0
  48. package/build/api/schema/augmentations/diagnostics/invalid-path.d.ts.map +1 -0
  49. package/build/api/schema/augmentations/diagnostics/invalid-path.js +18 -0
  50. package/build/api/schema/augmentations/diagnostics/invalid-path.js.map +1 -0
  51. package/build/api/schema/augmentations/diagnostics/version-mismatch.d.ts +46 -0
  52. package/build/api/schema/augmentations/diagnostics/version-mismatch.d.ts.map +1 -0
  53. package/build/api/schema/augmentations/diagnostics/version-mismatch.js +18 -0
  54. package/build/api/schema/augmentations/diagnostics/version-mismatch.js.map +1 -0
  55. package/build/api/schema/augmentations/input.d.ts +145 -0
  56. package/build/api/schema/augmentations/input.d.ts.map +1 -0
  57. package/build/api/schema/augmentations/input.js +191 -0
  58. package/build/api/schema/augmentations/input.js.map +1 -0
  59. package/build/api/schema/augmentations/placement.d.ts +8 -0
  60. package/build/api/schema/augmentations/placement.d.ts.map +1 -0
  61. package/build/api/schema/augmentations/placement.js +7 -0
  62. package/build/api/schema/augmentations/placement.js.map +1 -0
  63. package/build/api/schema/config-schema.d.ts +66 -66
  64. package/build/api/schema/config-schema.d.ts.map +1 -1
  65. package/build/api/schema/config-schema.js +2 -2
  66. package/build/api/schema/config-schema.js.map +1 -1
  67. package/build/api/schema/input-source/load.d.ts +2 -0
  68. package/build/api/schema/input-source/load.d.ts.map +1 -1
  69. package/build/api/schema/input-source/load.js.map +1 -1
  70. package/build/api/schema/input-sources/directory.d.ts +39 -39
  71. package/build/api/schema/input-sources/file.d.ts +39 -39
  72. package/build/api/schema/input-sources/introspection-file.d.ts +39 -39
  73. package/build/api/schema/input-sources/introspection.d.ts +39 -39
  74. package/build/api/schema/input-sources/memory.d.ts +39 -39
  75. package/build/api/schema/input-sources/versioned-directory.d.ts +79 -79
  76. package/build/api/schema/load.d.ts.map +1 -1
  77. package/build/api/schema/load.js +9 -2
  78. package/build/api/schema/load.js.map +1 -1
  79. package/build/lib/catalog/catalog.d.ts +1181 -1181
  80. package/build/lib/catalog/unversioned.d.ts +312 -312
  81. package/build/lib/catalog/versioned.d.ts +634 -634
  82. package/build/lib/change/change.d.ts +238 -238
  83. package/build/lib/document/document.d.ts +14 -8
  84. package/build/lib/document/document.d.ts.map +1 -1
  85. package/build/lib/document/versioned.d.ts +17 -10
  86. package/build/lib/document/versioned.d.ts.map +1 -1
  87. package/build/lib/grafaid/schema/KindMap/_.d.ts +1 -1
  88. package/build/lib/graphql-path/$$.d.ts +7 -13
  89. package/build/lib/graphql-path/$$.d.ts.map +1 -1
  90. package/build/lib/graphql-path/$$.js +7 -13
  91. package/build/lib/graphql-path/$$.js.map +1 -1
  92. package/build/lib/graphql-path/definition.d.ts +104 -94
  93. package/build/lib/graphql-path/definition.d.ts.map +1 -1
  94. package/build/lib/graphql-path/definition.js +126 -125
  95. package/build/lib/graphql-path/definition.js.map +1 -1
  96. package/build/lib/graphql-path/query.d.ts +25 -57
  97. package/build/lib/graphql-path/query.d.ts.map +1 -1
  98. package/build/lib/graphql-path/query.js +15 -93
  99. package/build/lib/graphql-path/query.js.map +1 -1
  100. package/build/lib/graphql-path/schema.d.ts +49 -0
  101. package/build/lib/graphql-path/schema.d.ts.map +1 -0
  102. package/build/lib/graphql-path/schema.js +89 -0
  103. package/build/lib/graphql-path/schema.js.map +1 -0
  104. package/build/lib/graphql-path/types.d.ts +76 -28
  105. package/build/lib/graphql-path/types.d.ts.map +1 -1
  106. package/build/lib/graphql-path/types.js +101 -2
  107. package/build/lib/graphql-path/types.js.map +1 -1
  108. package/build/lib/revision/revision.d.ts +1170 -1170
  109. package/build/lib/schema/schema.d.ts +708 -708
  110. package/build/lib/schema/unversioned.d.ts +1092 -1092
  111. package/build/lib/schema/versioned.d.ts +634 -634
  112. package/build/lib/semver/official-release.d.ts +10 -10
  113. package/build/lib/semver/pre-release.d.ts +10 -10
  114. package/build/lib/semver/semver.d.ts +50 -50
  115. package/build/lib/version-coverage/version-coverage.d.ts +43 -11
  116. package/build/lib/version-coverage/version-coverage.d.ts.map +1 -1
  117. package/build/lib/version-coverage/version-coverage.js +40 -6
  118. package/build/lib/version-coverage/version-coverage.js.map +1 -1
  119. package/build/template/hooks/use-examples.d.ts +1 -1
  120. package/build/template/routes/changelog/ChangelogSidebar.d.ts +2 -2
  121. package/build/template/routes/changelog/ChangelogSidebar.d.ts.map +1 -1
  122. package/build/template/routes/changelog/ChangelogSidebar.js +4 -4
  123. package/build/template/routes/changelog/ChangelogSidebar.js.map +1 -1
  124. package/build/template/routes/examples/_.d.ts +3 -3
  125. package/build/template/routes/examples/_index.d.ts +2 -2
  126. package/build/template/routes/examples/name.d.ts +3 -3
  127. package/build/template/routes/pages.d.ts.map +1 -1
  128. package/build/template/routes/pages.js +5 -1
  129. package/build/template/routes/pages.js.map +1 -1
  130. package/build/template/stores/changelog.d.ts +39 -39
  131. package/build/vite/plugins/examples.d.ts.map +1 -1
  132. package/build/vite/plugins/examples.js +0 -2
  133. package/build/vite/plugins/examples.js.map +1 -1
  134. package/build/vite/plugins/schemas.d.ts.map +1 -1
  135. package/build/vite/plugins/schemas.js +38 -2
  136. package/build/vite/plugins/schemas.js.map +1 -1
  137. package/package.json +1 -1
  138. package/src/api/examples/diagnostic/diagnostic.ts +0 -3
  139. package/src/api/examples/scanner.test.ts +83 -0
  140. package/src/api/examples/scanner.ts +17 -21
  141. package/src/api/schema/augmentations/$$.ts +6 -0
  142. package/src/api/schema/augmentations/$.ts +1 -1
  143. package/src/api/schema/augmentations/apply.test.ts +89 -0
  144. package/src/api/schema/augmentations/apply.ts +277 -0
  145. package/src/api/schema/augmentations/augmentation.ts +24 -0
  146. package/src/api/schema/augmentations/config.ts +11 -0
  147. package/src/api/schema/augmentations/diagnostics/diagnostic.ts +20 -0
  148. package/src/api/schema/augmentations/diagnostics/duplicate-version.ts +20 -0
  149. package/src/api/schema/augmentations/diagnostics/invalid-path.ts +21 -0
  150. package/src/api/schema/augmentations/diagnostics/version-mismatch.ts +21 -0
  151. package/src/api/schema/augmentations/input.test.ts +144 -0
  152. package/src/api/schema/augmentations/input.ts +215 -0
  153. package/src/api/schema/augmentations/placement.ts +11 -0
  154. package/src/api/schema/config-schema.ts +2 -2
  155. package/src/api/schema/input-source/load.ts +2 -0
  156. package/src/api/schema/load.ts +19 -2
  157. package/src/lib/graphql-path/$$.ts +7 -13
  158. package/src/lib/graphql-path/$.test.ts +175 -0
  159. package/src/lib/graphql-path/definition.ts +162 -162
  160. package/src/lib/graphql-path/query.ts +15 -98
  161. package/src/lib/graphql-path/schema.ts +136 -0
  162. package/src/lib/graphql-path/types.ts +108 -28
  163. package/src/lib/version-coverage/version-coverage.ts +48 -6
  164. package/src/template/routes/changelog/ChangelogSidebar.tsx +4 -4
  165. package/src/template/routes/pages.tsx +6 -1
  166. package/src/vite/plugins/examples.ts +0 -2
  167. package/src/vite/plugins/schemas.ts +51 -2
  168. package/build/api/examples/diagnostic/unused-default.d.ts +0 -49
  169. package/build/api/examples/diagnostic/unused-default.d.ts.map +0 -1
  170. package/build/api/examples/diagnostic/unused-default.js +0 -19
  171. package/build/api/examples/diagnostic/unused-default.js.map +0 -1
  172. package/build/api/schema/augmentations/augmentations/description.d.ts +0 -26
  173. package/build/api/schema/augmentations/augmentations/description.d.ts.map +0 -1
  174. package/build/api/schema/augmentations/augmentations/description.js +0 -55
  175. package/build/api/schema/augmentations/augmentations/description.js.map +0 -1
  176. package/build/api/schema/augmentations/schema-augmentation.d.ts +0 -20
  177. package/build/api/schema/augmentations/schema-augmentation.d.ts.map +0 -1
  178. package/build/api/schema/augmentations/schema-augmentation.js +0 -22
  179. package/build/api/schema/augmentations/schema-augmentation.js.map +0 -1
  180. package/build/api/schema/augmentations/target.d.ts +0 -25
  181. package/build/api/schema/augmentations/target.d.ts.map +0 -1
  182. package/build/api/schema/augmentations/target.js +0 -39
  183. package/build/api/schema/augmentations/target.js.map +0 -1
  184. package/build/lib/graphql-path/constructors.d.ts +0 -57
  185. package/build/lib/graphql-path/constructors.d.ts.map +0 -1
  186. package/build/lib/graphql-path/constructors.js +0 -73
  187. package/build/lib/graphql-path/constructors.js.map +0 -1
  188. package/src/api/examples/diagnostic/unused-default.ts +0 -22
  189. package/src/api/schema/augmentations/augmentations/description.ts +0 -69
  190. package/src/api/schema/augmentations/schema-augmentation.ts +0 -32
  191. package/src/api/schema/augmentations/target.ts +0 -61
  192. package/src/lib/graphql-path/constructors.ts +0 -81
@@ -0,0 +1,215 @@
1
+ import type { Augmentation } from '#api/schema/augmentations/augmentation'
2
+ import type { AugmentationConfig } from '#api/schema/augmentations/config'
3
+ import { Placement } from '#api/schema/augmentations/placement'
4
+ import { GraphQLPath } from '#lib/graphql-path'
5
+ import { S } from '#lib/kit-temp/effect'
6
+ import { VersionCoverage } from '#lib/version-coverage'
7
+ import { Version } from '#lib/version/$'
8
+ import { HashMap } from 'effect'
9
+
10
+ /**
11
+ * Configuration for augmenting GraphQL schema descriptions.
12
+ *
13
+ * This schema supports two usage patterns:
14
+ *
15
+ * 1. **Unversioned augmentations** - Apply to all schema versions
16
+ * 2. **Version-specific augmentations** - Apply to specific schema versions with optional defaults
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * // Unversioned - applies to all versions
21
+ * {
22
+ * on: 'Pokemon',
23
+ * placement: 'after',
24
+ * content: '**Note:** This type includes battle stats.'
25
+ * }
26
+ *
27
+ * // Version-specific with defaults
28
+ * {
29
+ * on: 'Pokemon', // Default for all versions
30
+ * placement: 'after', // Default for all versions
31
+ * content: 'Base Pokemon info.', // Default content
32
+ * versions: {
33
+ * '2': {
34
+ * content: 'Enhanced Pokemon with battle capabilities.' // Override for v2
35
+ * },
36
+ * '3': {
37
+ * on: 'BattlePokemon', // Different type in v3
38
+ * content: 'Battle-ready Pokemon with full stats.'
39
+ * }
40
+ * }
41
+ * }
42
+ * ```
43
+ *
44
+ * @remarks
45
+ * When using version-specific augmentations:
46
+ * - Top-level fields serve as defaults for all versions
47
+ * - Version-specific fields override defaults
48
+ * - A version entry must have complete configuration (after merging with defaults) to be valid
49
+ * - Invalid paths or missing types will generate build diagnostics rather than crashing
50
+ */
51
+ export const AugmentationInput = S.Struct({
52
+ /**
53
+ * GraphQL path to the type or field to augment.
54
+ *
55
+ * @example
56
+ * - Type path: `'Pokemon'`, `'Query'`, `'User'`
57
+ * - Field path: `'Pokemon.name'`, `'Query.users'`, `'Mutation.createUser'`
58
+ *
59
+ * @remarks
60
+ * If the specified path doesn't exist in the schema, a diagnostic error will be generated
61
+ * during the build process rather than causing a crash.
62
+ */
63
+ on: S.optional(S.String),
64
+
65
+ /**
66
+ * How to apply the augmentation content relative to existing description.
67
+ *
68
+ * - `'over'` - Replace the existing description entirely
69
+ * - `'before'` - Prepend content to the existing description
70
+ * - `'after'` - Append content to the existing description
71
+ *
72
+ * @default 'after'
73
+ */
74
+ placement: S.optional(Placement),
75
+
76
+ /**
77
+ * The content to add to the description.
78
+ *
79
+ * Supports full Markdown syntax including:
80
+ * - Bold/italic text
81
+ * - Code blocks and inline code
82
+ * - Links
83
+ * - Lists
84
+ * - Tables
85
+ *
86
+ * @example
87
+ * ```ts
88
+ * content: `
89
+ * **Important:** This field requires authentication.
90
+ *
91
+ * Example query:
92
+ * \`\`\`graphql
93
+ * query {
94
+ * users(limit: 10) {
95
+ * id
96
+ * name
97
+ * }
98
+ * }
99
+ * \`\`\`
100
+ * `
101
+ * ```
102
+ */
103
+ content: S.optional(S.String),
104
+
105
+ /**
106
+ * Version-specific augmentation overrides.
107
+ *
108
+ * Keys are version identifiers (e.g., `'1'`, `'2'`, `'3'`).
109
+ * Values are partial augmentation configs that override top-level defaults.
110
+ *
111
+ * @example
112
+ * ```ts
113
+ * versions: {
114
+ * '1': { content: 'Legacy API - see migration guide.' },
115
+ * '2': { content: 'Current stable API.' },
116
+ * '3': {
117
+ * on: 'NewTypeName', // Type renamed in v3
118
+ * content: 'Beta API - subject to change.'
119
+ * }
120
+ * }
121
+ * ```
122
+ *
123
+ * @remarks
124
+ * - Version keys must be valid numeric strings
125
+ * - Each version inherits top-level defaults
126
+ * - Invalid version identifiers will generate build diagnostics
127
+ * - Duplicate version keys will generate build diagnostics
128
+ */
129
+ versions: S.optional(
130
+ S.Record({
131
+ key: S.String,
132
+ value: S.Struct({
133
+ on: S.optional(S.String),
134
+ placement: S.optional(Placement),
135
+ content: S.optional(S.String),
136
+ }),
137
+ }),
138
+ ),
139
+ }).annotations({
140
+ identifier: 'AugmentationInput',
141
+ description: 'Configuration for augmenting GraphQL schema descriptions with version support',
142
+ })
143
+
144
+ export type AugmentationInput = S.Schema.Type<typeof AugmentationInput>
145
+
146
+ /**
147
+ * Transform user-facing input to normalized internal representation.
148
+ *
149
+ * Rules:
150
+ * 1. If no versions field → create single unversioned entry
151
+ * 2. If versions field exists:
152
+ * - Create version-specific entries for each version
153
+ * - If top-level defaults exist, they are used as fallbacks for version entries
154
+ * - If a version entry would be incomplete without defaults, skip it
155
+ */
156
+ export const normalizeAugmentationInput = (input: AugmentationInput): Augmentation | null => {
157
+ const map = HashMap.empty<VersionCoverage.VersionCoverage, AugmentationConfig>()
158
+
159
+ // Case 1: No versions field - create unversioned entry
160
+ if (!input.versions || Object.keys(input.versions).length === 0) {
161
+ // Must have complete top-level configuration
162
+ if (!input.on || !input.placement || !input.content) {
163
+ return null // Invalid: incomplete unversioned augmentation
164
+ }
165
+
166
+ const unversionedConfig: AugmentationConfig = {
167
+ on: GraphQLPath.Definition.decodeSync(input.on),
168
+ placement: input.placement,
169
+ content: input.content,
170
+ }
171
+
172
+ return {
173
+ versionAugmentations: HashMap.set(
174
+ map,
175
+ VersionCoverage.unversioned(),
176
+ unversionedConfig,
177
+ ),
178
+ }
179
+ }
180
+
181
+ // Case 2: Has versions field - create version-specific entries
182
+ let resultMap = map
183
+
184
+ // Process each version
185
+ for (const [versionStr, versionOverrides] of Object.entries(input.versions)) {
186
+ // Merge with top-level defaults
187
+ const onString = versionOverrides.on ?? input.on
188
+ const placement = versionOverrides.placement ?? input.placement
189
+ const content = versionOverrides.content ?? input.content
190
+
191
+ // Skip if incomplete after merging with defaults
192
+ if (!onString || !placement || !content) {
193
+ continue
194
+ }
195
+
196
+ const config: AugmentationConfig = {
197
+ on: GraphQLPath.Definition.decodeSync(onString),
198
+ placement,
199
+ content,
200
+ }
201
+ const version = Version.decodeSync(versionStr)
202
+ const coverage = VersionCoverage.single(version)
203
+
204
+ resultMap = HashMap.set(resultMap, coverage, config)
205
+ }
206
+
207
+ // If no valid version configurations were created, return null
208
+ if (HashMap.size(resultMap) === 0) {
209
+ return null
210
+ }
211
+
212
+ return {
213
+ versionAugmentations: resultMap,
214
+ }
215
+ }
@@ -0,0 +1,11 @@
1
+ import { S } from '#lib/kit-temp/effect'
2
+
3
+ export const Placement = S.Enums(
4
+ {
5
+ before: 'before',
6
+ after: 'after',
7
+ over: 'over',
8
+ } as const,
9
+ )
10
+
11
+ export type Placement = S.Schema.Type<typeof Placement>
@@ -1,4 +1,4 @@
1
- import { AugmentationSchema } from '#api/schema/augmentations/schema-augmentation'
1
+ import { Augmentations } from '#api/schema/augmentations/$'
2
2
  import { S } from '#lib/kit-temp/effect'
3
3
 
4
4
  // ============================================================================
@@ -364,7 +364,7 @@ export const ConfigSchema = S.Struct({
364
364
  * ]
365
365
  * ```
366
366
  */
367
- augmentations: S.optional(S.Array(AugmentationSchema)),
367
+ augmentations: S.optional(S.Array(Augmentations.AugmentationInput)),
368
368
  /**
369
369
  * Which data sources to use for loading schemas.
370
370
  *
@@ -1,4 +1,5 @@
1
1
  import { Config as PolenConfig } from '#api/config/$'
2
+ import type { Diagnostic } from '#api/schema/augmentations/diagnostics/diagnostic'
2
3
  import type { Config } from '#api/schema/config'
3
4
  import type { InputSource } from '#api/schema/input-source/input-source'
4
5
  import type { Catalog } from '#lib/catalog/$'
@@ -9,6 +10,7 @@ import type { Catalog } from '#lib/catalog/$'
9
10
  export interface LoadedCatalog {
10
11
  data: Catalog.Catalog | null
11
12
  source: InputSource
13
+ diagnostics?: Diagnostic[]
12
14
  // {
13
15
  // type: DataSourceType
14
16
  // /**
@@ -141,16 +141,33 @@ export const loadOrNull = (
141
141
  if (loadedSchema.data && config.schema?.augmentations) {
142
142
  const augmentations = config.schema.augmentations
143
143
  const catalog = loadedSchema.data as Catalog.Catalog
144
+ const allDiagnostics: Augmentations.Diagnostic[] = []
145
+
144
146
  Catalog.fold(
145
147
  (versioned) => {
146
148
  for (const schema of Catalog.Versioned.getAll(versioned)) {
147
- Augmentations.apply(schema.definition, augmentations)
149
+ const { diagnostics } = Augmentations.applyAll(
150
+ schema.definition,
151
+ augmentations,
152
+ schema.version,
153
+ )
154
+ allDiagnostics.push(...diagnostics)
148
155
  }
149
156
  },
150
157
  (unversioned) => {
151
- Augmentations.apply(unversioned.schema.definition, augmentations)
158
+ const { diagnostics } = Augmentations.applyAll(
159
+ unversioned.schema.definition,
160
+ augmentations,
161
+ null,
162
+ )
163
+ allDiagnostics.push(...diagnostics)
152
164
  },
153
165
  )(catalog)
166
+
167
+ // Add diagnostics to loaded schema if any were generated
168
+ if (allDiagnostics.length > 0) {
169
+ loadedSchema.diagnostics = allDiagnostics
170
+ }
154
171
  }
155
172
 
156
173
  return loadedSchema
@@ -22,21 +22,15 @@
22
22
  * const encoded = GraphQLPath.Definition.encode(userNameFieldPath) // "User.name"
23
23
  * const decoded = GraphQLPath.Definition.decode("User.name") // field definition path
24
24
  *
25
- * // Create a query path
26
- * const queryPath = GraphQLPath.Query.builder()
27
- * .type('User')
28
- * .field('posts')
29
- * .type('Post')
30
- * .field('author')
31
- * .type('User')
32
- * .field('name')
33
- * .build()
34
- *
35
- * // Encode/decode query paths
36
- * const encodedQuery = GraphQLPath.Query.encode(queryPath) // "User.posts.Post.author.User.name"
37
- * const decodedQuery = GraphQLPath.Query.decode("User.posts.Post.author") // query path
25
+ * // Decode a query path from a string
26
+ * const queryPath = GraphQLPath.Query.decode("User.posts.Post.author.User.name")
27
+ * // Returns array of segments: [TypeSegment, FieldSegment, TypeSegment, FieldSegment, ...]
28
+ *
29
+ * // Encode query path segments to a string
30
+ * const encoded = GraphQLPath.Query.encode(queryPath) // "User.posts.Post.author.User.name"
38
31
  * ```
39
32
  */
40
33
 
41
34
  export * as Definition from './definition.js'
42
35
  export * as Query from './query.js'
36
+ export * as Schema from './schema.js'
@@ -0,0 +1,175 @@
1
+ import { S } from '#lib/kit-temp/effect'
2
+ import { describe, expect, test } from 'vitest'
3
+ import { GraphQLPath } from './$.js'
4
+
5
+ describe('decode', () => {
6
+ test.for([
7
+ ['User', [{ _tag: 'TypeSegment', type: 'User' }]],
8
+ ['User.name', [
9
+ { _tag: 'TypeSegment', type: 'User' },
10
+ { _tag: 'FieldSegment', field: 'name' },
11
+ ]],
12
+ ['User.posts(limit)', [
13
+ { _tag: 'TypeSegment', type: 'User' },
14
+ { _tag: 'FieldSegment', field: 'posts' },
15
+ { _tag: 'ArgumentSegment', argument: 'limit' },
16
+ ]],
17
+ ])('decodes "%s"', ([input, expected]) => {
18
+ expect(GraphQLPath.Definition.decodeSync(input)).toEqual(expected)
19
+ })
20
+
21
+ test.for([
22
+ '',
23
+ 'Type.field.extra',
24
+ 'Type(arg)',
25
+ '.field',
26
+ 'Type.',
27
+ ])('throws on invalid format "%s"', (input) => {
28
+ expect(() => GraphQLPath.Definition.decodeSync(input)).toThrow()
29
+ })
30
+ })
31
+
32
+ describe('encode', () => {
33
+ test.for(
34
+ [
35
+ [GraphQLPath.Definition.type('User'), 'User'],
36
+ [GraphQLPath.Definition.field('User', 'name'), 'User.name'],
37
+ [GraphQLPath.Definition.argument('User', 'posts', 'limit'), 'User.posts(limit)'],
38
+ ] as const,
39
+ )('encodes path to "%s"', ([path, expected]) => {
40
+ expect(GraphQLPath.Definition.encodeSync(path as any)).toBe(expected)
41
+ })
42
+ })
43
+
44
+ describe('round-trip', () => {
45
+ test.for([
46
+ 'Query',
47
+ 'Mutation',
48
+ 'User',
49
+ 'User.id',
50
+ 'User.name',
51
+ 'Query.users',
52
+ 'User.posts(limit)',
53
+ 'Query.search(query)',
54
+ 'Mutation.createUser(input)',
55
+ ])('round-trips "%s"', (pathString) => {
56
+ const decoded = GraphQLPath.Definition.decodeSync(pathString)
57
+ const encoded = GraphQLPath.Definition.encodeSync(decoded)
58
+ expect(encoded).toBe(pathString)
59
+ })
60
+ })
61
+
62
+ describe('type guards', () => {
63
+ const typePath = GraphQLPath.Definition.decodeSync('User')
64
+ const fieldPath = GraphQLPath.Definition.decodeSync('User.name')
65
+ const argPath = GraphQLPath.Definition.decodeSync('User.posts(limit)')
66
+
67
+ test('isTypeDefinitionPath', () => {
68
+ expect(GraphQLPath.Definition.isTypeDefinitionPath(typePath)).toBe(true)
69
+ expect(GraphQLPath.Definition.isTypeDefinitionPath(fieldPath)).toBe(false)
70
+ expect(GraphQLPath.Definition.isTypeDefinitionPath(argPath)).toBe(false)
71
+ })
72
+
73
+ test('isFieldDefinitionPath', () => {
74
+ expect(GraphQLPath.Definition.isFieldDefinitionPath(typePath)).toBe(false)
75
+ expect(GraphQLPath.Definition.isFieldDefinitionPath(fieldPath)).toBe(true)
76
+ expect(GraphQLPath.Definition.isFieldDefinitionPath(argPath)).toBe(false)
77
+ })
78
+
79
+ test('isArgumentDefinitionPath', () => {
80
+ expect(GraphQLPath.Definition.isArgumentDefinitionPath(typePath)).toBe(false)
81
+ expect(GraphQLPath.Definition.isArgumentDefinitionPath(fieldPath)).toBe(false)
82
+ expect(GraphQLPath.Definition.isArgumentDefinitionPath(argPath)).toBe(true)
83
+ })
84
+
85
+ test('Effect Schema derived segment type guards', async () => {
86
+ // Import from the types module directly since constructors re-exports them
87
+ const { isTypeSegment, isFieldSegment, isArgumentSegment } = await import('./types.js')
88
+
89
+ const typeSegment = { _tag: 'TypeSegment', type: 'User' } as const
90
+ const fieldSegment = { _tag: 'FieldSegment', field: 'name' } as const
91
+ const argSegment = { _tag: 'ArgumentSegment', argument: 'limit' } as const
92
+
93
+ // Test that Effect Schema derived guards work correctly
94
+ expect(isTypeSegment(typeSegment)).toBe(true)
95
+ expect(isTypeSegment(fieldSegment)).toBe(false)
96
+ expect(isFieldSegment(fieldSegment)).toBe(true)
97
+ expect(isFieldSegment(typeSegment)).toBe(false)
98
+ expect(isArgumentSegment(argSegment)).toBe(true)
99
+ expect(isArgumentSegment(typeSegment)).toBe(false)
100
+ })
101
+ })
102
+
103
+ describe('helper functions', () => {
104
+ test('getType extracts type from all path types', () => {
105
+ expect(GraphQLPath.Definition.getType(GraphQLPath.Definition.decodeSync('User'))).toBe('User')
106
+ expect(GraphQLPath.Definition.getType(GraphQLPath.Definition.decodeSync('User.name'))).toBe('User')
107
+ expect(GraphQLPath.Definition.getType(GraphQLPath.Definition.decodeSync('User.posts(limit)'))).toBe('User')
108
+ })
109
+
110
+ test('getField extracts field name', () => {
111
+ const fieldPath = GraphQLPath.Definition.decodeSync('User.name')
112
+ const argPath = GraphQLPath.Definition.decodeSync('User.posts(limit)')
113
+
114
+ if (GraphQLPath.Definition.isFieldDefinitionPath(fieldPath)) {
115
+ expect(GraphQLPath.Definition.getField(fieldPath)).toBe('name')
116
+ }
117
+ if (GraphQLPath.Definition.isArgumentDefinitionPath(argPath)) {
118
+ expect(GraphQLPath.Definition.getField(argPath)).toBe('posts')
119
+ }
120
+ })
121
+
122
+ test('getArgument extracts argument name', () => {
123
+ const argPath = GraphQLPath.Definition.decodeSync('User.posts(limit)')
124
+ if (GraphQLPath.Definition.isArgumentDefinitionPath(argPath)) {
125
+ expect(GraphQLPath.Definition.getArgument(argPath)).toBe('limit')
126
+ }
127
+ })
128
+ })
129
+
130
+ describe('constructors', () => {
131
+ test.for([
132
+ [GraphQLPath.Definition.type('User'), [{ _tag: 'TypeSegment', type: 'User' }]],
133
+ [GraphQLPath.Definition.field('User', 'name'), [
134
+ { _tag: 'TypeSegment', type: 'User' },
135
+ { _tag: 'FieldSegment', field: 'name' },
136
+ ]],
137
+ [GraphQLPath.Definition.argument('User', 'posts', 'limit'), [
138
+ { _tag: 'TypeSegment', type: 'User' },
139
+ { _tag: 'FieldSegment', field: 'posts' },
140
+ { _tag: 'ArgumentSegment', argument: 'limit' },
141
+ ]],
142
+ ])('constructor creates correct structure', ([result, expected]) => {
143
+ expect(result).toEqual(expected)
144
+ })
145
+ })
146
+
147
+ describe('Schema validation', () => {
148
+ test.for(
149
+ [
150
+ [GraphQLPath.Definition.TypeDefinitionPath, 'User', [{ _tag: 'TypeSegment', type: 'User' }]],
151
+ [GraphQLPath.Definition.FieldDefinitionPath, 'User.name', [
152
+ { _tag: 'TypeSegment', type: 'User' },
153
+ { _tag: 'FieldSegment', field: 'name' },
154
+ ]],
155
+ [GraphQLPath.Definition.ArgumentDefinitionPath, 'User.posts(limit)', [
156
+ { _tag: 'TypeSegment', type: 'User' },
157
+ { _tag: 'FieldSegment', field: 'posts' },
158
+ { _tag: 'ArgumentSegment', argument: 'limit' },
159
+ ]],
160
+ ] as const,
161
+ )('validates through Effect Schema', ([schema, input, expected]) => {
162
+ const result = S.decodeUnknownSync(schema as any)(input)
163
+ expect(result).toEqual(expected)
164
+ })
165
+
166
+ test('union automatically selects correct type', () => {
167
+ const typePath = S.decodeUnknownSync(GraphQLPath.Definition.DefinitionPath)('User')
168
+ const fieldPath = S.decodeUnknownSync(GraphQLPath.Definition.DefinitionPath)('User.name')
169
+ const argPath = S.decodeUnknownSync(GraphQLPath.Definition.DefinitionPath)('User.posts(limit)')
170
+
171
+ expect(typePath.length).toBe(1)
172
+ expect(fieldPath.length).toBe(2)
173
+ expect(argPath.length).toBe(3)
174
+ })
175
+ })