polen 0.11.0-next.17 → 0.11.0-next.18

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 (196) hide show
  1. package/build/api/builder/ssg/generate.d.ts.map +1 -1
  2. package/build/api/builder/ssg/generate.js +5 -5
  3. package/build/api/builder/ssg/generate.js.map +1 -1
  4. package/build/api/builder/ssg/page-generator.worker.js +13 -3
  5. package/build/api/builder/ssg/page-generator.worker.js.map +1 -1
  6. package/build/api/config/input.d.ts +88 -3
  7. package/build/api/config/input.d.ts.map +1 -1
  8. package/build/api/config/normalized.d.ts +92 -7
  9. package/build/api/config/normalized.d.ts.map +1 -1
  10. package/build/api/config/normalized.js +11 -3
  11. package/build/api/config/normalized.js.map +1 -1
  12. package/build/api/config-template/template.js +2 -2
  13. package/build/api/config-template/template.js.map +1 -1
  14. package/build/api/content/sidebar.d.ts.map +1 -1
  15. package/build/api/content/sidebar.js +2 -1
  16. package/build/api/content/sidebar.js.map +1 -1
  17. package/build/api/examples/config.d.ts +366 -3
  18. package/build/api/examples/config.d.ts.map +1 -1
  19. package/build/api/examples/config.js +25 -3
  20. package/build/api/examples/config.js.map +1 -1
  21. package/build/api/examples/diagnostic/diagnostic.d.ts +1 -1
  22. package/build/api/examples/diagnostic/validation-error.d.ts +3 -2
  23. package/build/api/examples/diagnostic/validation-error.d.ts.map +1 -1
  24. package/build/api/examples/diagnostic/validation-error.js +9 -3
  25. package/build/api/examples/diagnostic/validation-error.js.map +1 -1
  26. package/build/api/examples/diagnostic/validator.d.ts.map +1 -1
  27. package/build/api/examples/diagnostic/validator.js +115 -68
  28. package/build/api/examples/diagnostic/validator.js.map +1 -1
  29. package/build/api/examples/filter.d.ts.map +1 -1
  30. package/build/api/examples/filter.js +9 -6
  31. package/build/api/examples/filter.js.map +1 -1
  32. package/build/api/examples/scanner.d.ts.map +1 -1
  33. package/build/api/examples/scanner.js +89 -103
  34. package/build/api/examples/scanner.js.map +1 -1
  35. package/build/api/examples/type-usage-indexer.d.ts.map +1 -1
  36. package/build/api/examples/type-usage-indexer.js +17 -30
  37. package/build/api/examples/type-usage-indexer.js.map +1 -1
  38. package/build/api/iso/schema/routing.d.ts.map +1 -1
  39. package/build/api/iso/schema/routing.js +8 -8
  40. package/build/api/iso/schema/routing.js.map +1 -1
  41. package/build/api/iso/schema/validation.d.ts.map +1 -1
  42. package/build/api/iso/schema/validation.js +3 -2
  43. package/build/api/iso/schema/validation.js.map +1 -1
  44. package/build/api/schema/input-sources/directory.js +2 -2
  45. package/build/api/schema/input-sources/directory.js.map +1 -1
  46. package/build/api/schema/input-sources/versioned-directory.d.ts.map +1 -1
  47. package/build/api/schema/input-sources/versioned-directory.js +3 -3
  48. package/build/api/schema/input-sources/versioned-directory.js.map +1 -1
  49. package/build/api/schema/load.d.ts.map +1 -1
  50. package/build/api/schema/load.js +1 -1
  51. package/build/api/schema/load.js.map +1 -1
  52. package/build/lib/catalog/catalog.d.ts +43 -3
  53. package/build/lib/catalog/catalog.d.ts.map +1 -1
  54. package/build/lib/catalog/catalog.js +67 -5
  55. package/build/lib/catalog/catalog.js.map +1 -1
  56. package/build/lib/catalog/versioned.d.ts +11 -1
  57. package/build/lib/catalog/versioned.d.ts.map +1 -1
  58. package/build/lib/catalog/versioned.js +23 -5
  59. package/build/lib/catalog/versioned.js.map +1 -1
  60. package/build/lib/document/document.d.ts +55 -5
  61. package/build/lib/document/document.d.ts.map +1 -1
  62. package/build/lib/document/document.js +96 -2
  63. package/build/lib/document/document.js.map +1 -1
  64. package/build/lib/document/versioned.d.ts +2 -2
  65. package/build/lib/document/versioned.d.ts.map +1 -1
  66. package/build/lib/document/versioned.js +7 -7
  67. package/build/lib/document/versioned.js.map +1 -1
  68. package/build/lib/lifecycles/lifecycles.d.ts +5 -4
  69. package/build/lib/lifecycles/lifecycles.d.ts.map +1 -1
  70. package/build/lib/lifecycles/lifecycles.js +14 -12
  71. package/build/lib/lifecycles/lifecycles.js.map +1 -1
  72. package/build/lib/version-coverage/$$.d.ts +2 -0
  73. package/build/lib/version-coverage/$$.d.ts.map +1 -0
  74. package/build/lib/version-coverage/$$.js +2 -0
  75. package/build/lib/version-coverage/$$.js.map +1 -0
  76. package/build/lib/version-coverage/$.d.ts.map +1 -0
  77. package/build/lib/version-coverage/$.js.map +1 -0
  78. package/build/lib/{version-selection/version-selection.d.ts → version-coverage/version-coverage.d.ts} +1 -1
  79. package/build/lib/version-coverage/version-coverage.d.ts.map +1 -0
  80. package/build/lib/{version-selection/version-selection.js → version-coverage/version-coverage.js} +2 -2
  81. package/build/lib/version-coverage/version-coverage.js.map +1 -0
  82. package/build/template/components/GraphQLDocument.d.ts +1 -1
  83. package/build/template/components/GraphQLDocument.d.ts.map +1 -1
  84. package/build/template/components/GraphQLDocument.js +10 -39
  85. package/build/template/components/GraphQLDocument.js.map +1 -1
  86. package/build/template/components/GraphQLInteractive/lib/parser.d.ts +28 -0
  87. package/build/template/components/GraphQLInteractive/lib/parser.d.ts.map +1 -1
  88. package/build/template/components/GraphQLInteractive/lib/parser.js +60 -27
  89. package/build/template/components/GraphQLInteractive/lib/parser.js.map +1 -1
  90. package/build/template/components/VersionCoveragePicker.d.ts +1 -1
  91. package/build/template/components/VersionCoveragePicker.d.ts.map +1 -1
  92. package/build/template/components/VersionCoveragePicker.js +4 -6
  93. package/build/template/components/VersionCoveragePicker.js.map +1 -1
  94. package/build/template/components/home/QuickStart.d.ts.map +1 -1
  95. package/build/template/components/home/QuickStart.js +8 -4
  96. package/build/template/components/home/QuickStart.js.map +1 -1
  97. package/build/template/hooks/use-highlighted.d.ts.map +1 -1
  98. package/build/template/hooks/use-highlighted.js +19 -13
  99. package/build/template/hooks/use-highlighted.js.map +1 -1
  100. package/build/template/lib/fetch-text.d.ts +18 -0
  101. package/build/template/lib/fetch-text.d.ts.map +1 -1
  102. package/build/template/lib/fetch-text.js +32 -4
  103. package/build/template/lib/fetch-text.js.map +1 -1
  104. package/build/template/routes/examples/name.d.ts.map +1 -1
  105. package/build/template/routes/examples/name.js +4 -2
  106. package/build/template/routes/examples/name.js.map +1 -1
  107. package/build/template/stores/toast.d.ts.map +1 -1
  108. package/build/template/stores/toast.js +5 -3
  109. package/build/template/stores/toast.js.map +1 -1
  110. package/package.json +7 -7
  111. package/src/api/builder/ssg/generate.ts +10 -5
  112. package/src/api/builder/ssg/page-generator.worker.ts +18 -3
  113. package/src/api/config/normalized.ts +12 -3
  114. package/src/api/config-template/template.ts +2 -2
  115. package/src/api/content/sidebar.ts +3 -3
  116. package/src/api/examples/config.test.ts +10 -0
  117. package/src/api/examples/config.ts +33 -4
  118. package/src/api/examples/diagnostic/validation-error.ts +9 -3
  119. package/src/api/examples/diagnostic/validator.test.ts +30 -0
  120. package/src/api/examples/diagnostic/validator.ts +148 -103
  121. package/src/api/examples/filter.ts +9 -6
  122. package/src/api/examples/scanner.ts +136 -117
  123. package/src/api/examples/type-usage-indexer.ts +24 -36
  124. package/src/api/iso/schema/routing.ts +10 -10
  125. package/src/api/iso/schema/validation.ts +3 -2
  126. package/src/api/schema/input-sources/directory.ts +2 -2
  127. package/src/api/schema/input-sources/versioned-directory.ts +5 -7
  128. package/src/api/schema/load.ts +1 -1
  129. package/src/lib/catalog/catalog.ts +89 -6
  130. package/src/lib/catalog/versioned.ts +26 -5
  131. package/src/lib/document/document.ts +135 -2
  132. package/src/lib/document/versioned.ts +8 -8
  133. package/src/lib/lifecycles/lifecycles.ts +32 -27
  134. package/src/lib/version-coverage/$$.ts +1 -0
  135. package/src/lib/{version-selection/version-selection.ts → version-coverage/version-coverage.ts} +1 -1
  136. package/src/template/components/GraphQLDocument.tsx +11 -69
  137. package/src/template/components/GraphQLInteractive/lib/parser.ts +81 -29
  138. package/src/template/components/VersionCoveragePicker.tsx +4 -5
  139. package/src/template/components/home/QuickStart.tsx +16 -7
  140. package/src/template/hooks/use-highlighted.ts +31 -19
  141. package/src/template/lib/fetch-text.ts +45 -4
  142. package/src/template/routes/examples/name.tsx +4 -2
  143. package/src/template/stores/toast.ts +6 -3
  144. package/build/lib/graph/$$.d.ts +0 -2
  145. package/build/lib/graph/$$.d.ts.map +0 -1
  146. package/build/lib/graph/$$.js +0 -2
  147. package/build/lib/graph/$$.js.map +0 -1
  148. package/build/lib/graph/$.d.ts +0 -2
  149. package/build/lib/graph/$.d.ts.map +0 -1
  150. package/build/lib/graph/$.js +0 -2
  151. package/build/lib/graph/$.js.map +0 -1
  152. package/build/lib/graph/graph.d.ts +0 -127
  153. package/build/lib/graph/graph.d.ts.map +0 -1
  154. package/build/lib/graph/graph.js +0 -152
  155. package/build/lib/graph/graph.js.map +0 -1
  156. package/build/lib/mask/$$.d.ts +0 -3
  157. package/build/lib/mask/$$.d.ts.map +0 -1
  158. package/build/lib/mask/$$.js +0 -3
  159. package/build/lib/mask/$$.js.map +0 -1
  160. package/build/lib/mask/$.d.ts +0 -2
  161. package/build/lib/mask/$.d.ts.map +0 -1
  162. package/build/lib/mask/$.js +0 -2
  163. package/build/lib/mask/$.js.map +0 -1
  164. package/build/lib/mask/apply.d.ts +0 -86
  165. package/build/lib/mask/apply.d.ts.map +0 -1
  166. package/build/lib/mask/apply.js +0 -86
  167. package/build/lib/mask/apply.js.map +0 -1
  168. package/build/lib/mask/mask.d.ts +0 -124
  169. package/build/lib/mask/mask.d.ts.map +0 -1
  170. package/build/lib/mask/mask.js +0 -137
  171. package/build/lib/mask/mask.js.map +0 -1
  172. package/build/lib/mask/mask.test-d.d.ts +0 -2
  173. package/build/lib/mask/mask.test-d.d.ts.map +0 -1
  174. package/build/lib/mask/mask.test-d.js +0 -102
  175. package/build/lib/mask/mask.test-d.js.map +0 -1
  176. package/build/lib/version-selection/$$.d.ts +0 -2
  177. package/build/lib/version-selection/$$.d.ts.map +0 -1
  178. package/build/lib/version-selection/$$.js +0 -2
  179. package/build/lib/version-selection/$$.js.map +0 -1
  180. package/build/lib/version-selection/$.d.ts.map +0 -1
  181. package/build/lib/version-selection/$.js.map +0 -1
  182. package/build/lib/version-selection/version-selection.d.ts.map +0 -1
  183. package/build/lib/version-selection/version-selection.js.map +0 -1
  184. package/src/lib/graph/$$.ts +0 -1
  185. package/src/lib/graph/$.ts +0 -1
  186. package/src/lib/graph/graph.ts +0 -197
  187. package/src/lib/mask/$$.ts +0 -2
  188. package/src/lib/mask/$.test.ts +0 -226
  189. package/src/lib/mask/$.ts +0 -1
  190. package/src/lib/mask/apply.ts +0 -134
  191. package/src/lib/mask/mask.test-d.ts +0 -156
  192. package/src/lib/mask/mask.ts +0 -244
  193. package/src/lib/version-selection/$$.ts +0 -1
  194. /package/build/lib/{version-selection → version-coverage}/$.d.ts +0 -0
  195. /package/build/lib/{version-selection → version-coverage}/$.js +0 -0
  196. /package/src/lib/{version-selection → version-coverage}/$.ts +0 -0
@@ -1,18 +1,17 @@
1
1
  import { Catalog as SchemaCatalog } from '#lib/catalog/$'
2
2
  import { Document } from '#lib/document/$'
3
3
  import { EffectGlob } from '#lib/effect-glob/$'
4
- import { VersionCoverage } from '#lib/version-selection/$'
4
+ import { VersionCoverage } from '#lib/version-coverage'
5
5
  import { Version } from '#lib/version/$'
6
6
  import { FileSystem } from '@effect/platform'
7
7
  import { Str } from '@wollybeard/kit'
8
- import { Effect, HashMap, HashSet, Match } from 'effect'
8
+ import { Array, Effect, HashMap, HashSet, Match } from 'effect'
9
9
  import * as Path from 'node:path'
10
10
  import type { Diagnostic } from './diagnostic/diagnostic.js'
11
11
  import {
12
12
  makeDiagnosticDuplicateContent,
13
13
  makeDiagnosticMissingVersions,
14
14
  makeDiagnosticUnknownVersion,
15
- makeDiagnosticUnusedDefault,
16
15
  } from './diagnostic/diagnostic.js'
17
16
  import { validateExamples } from './diagnostic/validator.js'
18
17
  import { Catalog } from './schemas/catalog.js'
@@ -44,11 +43,26 @@ const VERSIONED_FILE_PATTERN = Str.pattern<{ groups: ['name', 'version'] }>(
44
43
  /^(?<name>.+?)\.(?<version>.+)$/,
45
44
  )
46
45
 
46
+ // ============================================================================
47
+ // File Parsing Types
48
+ // ============================================================================
49
+
50
+ type ParsedExampleFile =
51
+ | { type: 'unversioned'; name: string; file: string }
52
+ | { type: 'versioned'; name: string; version: Version.Version; file: string }
53
+ | { type: 'default'; name: string; file: string }
54
+
55
+ type GroupedExampleFiles = Map<string, {
56
+ unversioned?: string
57
+ versioned: Map<Version.Version, string>
58
+ default?: string
59
+ }>
60
+
47
61
  // ============================================================================
48
62
  // Helpers
49
63
  // ============================================================================
50
64
 
51
- const parseExampleFilename = (filename: string): { name: string; version: string | null } => {
65
+ const parseExampleFile = (filename: string): ParsedExampleFile => {
52
66
  const parsed = Path.parse(filename)
53
67
  const base = parsed.name
54
68
 
@@ -60,32 +74,96 @@ const parseExampleFilename = (filename: string): { name: string; version: string
60
74
 
61
75
  // Handle special 'default' keyword
62
76
  if (versionStr === 'default') {
63
- return { name, version: 'default' }
77
+ return { type: 'default', name, file: filename }
64
78
  }
65
- const decoded = Version.decodeSync(versionStr)
66
- // Return canonical version string
67
- return { name, version: Version.encodeSync(decoded) }
79
+
80
+ const version = Version.decodeSync(versionStr)
81
+ return { type: 'versioned', name, version, file: filename }
68
82
  }
69
83
 
70
84
  // No version found - this is an unversioned example
71
- return { name: base, version: null }
85
+ return { type: 'unversioned', name: base, file: filename }
72
86
  }
73
87
 
74
- const groupExampleFiles = (files: string[]): Map<string, Map<string | null, string>> => {
75
- const grouped = new Map<string, Map<string | null, string>>()
88
+ const groupExampleFiles = (files: string[]): GroupedExampleFiles => {
89
+ const grouped: GroupedExampleFiles = new Map()
76
90
 
77
91
  for (const file of files) {
78
- const { name, version } = parseExampleFilename(file)
92
+ const parsed = parseExampleFile(file)
79
93
 
80
- if (!grouped.has(name)) {
81
- grouped.set(name, new Map())
94
+ if (!grouped.has(parsed.name)) {
95
+ grouped.set(parsed.name, {
96
+ versioned: new Map(),
97
+ })
98
+ }
99
+
100
+ const group = grouped.get(parsed.name)!
101
+
102
+ switch (parsed.type) {
103
+ case 'unversioned':
104
+ group.unversioned = parsed.file
105
+ break
106
+ case 'versioned':
107
+ group.versioned.set(parsed.version, parsed.file)
108
+ break
109
+ case 'default':
110
+ group.default = parsed.file
111
+ break
82
112
  }
83
- grouped.get(name)!.set(version, file)
84
113
  }
85
114
 
86
115
  return grouped
87
116
  }
88
117
 
118
+ /**
119
+ * Resolve .default files into proper version coverage.
120
+ * This erases the .default convention and converts it to semantic version sets.
121
+ */
122
+ const resolveDefaultFiles = (
123
+ grouped: GroupedExampleFiles,
124
+ schemaVersions: Version.Version[],
125
+ ): Map<string, {
126
+ versionDocuments: HashMap.HashMap<VersionCoverage.VersionCoverage, string>
127
+ unversioned?: string
128
+ }> => {
129
+ const resolved = new Map<string, {
130
+ versionDocuments: HashMap.HashMap<VersionCoverage.VersionCoverage, string>
131
+ unversioned?: string
132
+ }>()
133
+
134
+ for (const [name, group] of grouped) {
135
+ let versionDocuments = HashMap.empty<VersionCoverage.VersionCoverage, string>()
136
+
137
+ // Add explicit versions
138
+ for (const [version, file] of group.versioned) {
139
+ versionDocuments = HashMap.set(versionDocuments, version, file)
140
+ }
141
+
142
+ // Handle default file if present
143
+ if (group.default) {
144
+ // Determine which versions the default covers
145
+ const explicitVersions = HashSet.fromIterable(group.versioned.keys())
146
+ const defaultVersions = schemaVersions.filter(v => !HashSet.has(explicitVersions, v))
147
+
148
+ if (defaultVersions.length > 0) {
149
+ // Create version coverage for default
150
+ const defaultCoverage = defaultVersions.length === 1
151
+ ? defaultVersions[0]! // Single version
152
+ : HashSet.fromIterable(defaultVersions) // Version set
153
+
154
+ versionDocuments = HashMap.set(versionDocuments, defaultCoverage, group.default)
155
+ }
156
+ }
157
+
158
+ resolved.set(name, {
159
+ versionDocuments,
160
+ ...(group.unversioned ? { unversioned: group.unversioned } : {}),
161
+ })
162
+ }
163
+
164
+ return resolved
165
+ }
166
+
89
167
  const lintFileLayout = (
90
168
  example: Example.Example,
91
169
  schemaCatalog?: SchemaCatalog.Catalog,
@@ -105,7 +183,10 @@ const lintFileLayout = (
105
183
  DocumentVersioned: (doc) => {
106
184
  // Get all versions covered by this document
107
185
  const coveredVersions = Document.Versioned.getAllVersions(doc)
108
- const missingVersions = schemaVersions.filter(sv => !coveredVersions.some(cv => Version.equivalence(sv, cv)))
186
+ const missingVersions = Array.filter(
187
+ schemaVersions,
188
+ sv => !Array.some(coveredVersions, cv => Version.equivalence(sv, cv)),
189
+ )
109
190
 
110
191
  if (missingVersions.length > 0) {
111
192
  diagnostics.push(makeDiagnosticMissingVersions({
@@ -117,7 +198,7 @@ const lintFileLayout = (
117
198
  }
118
199
 
119
200
  // Check for duplicate content between selections
120
- const entries = Array.from(HashMap.entries(doc.versionDocuments))
201
+ const entries = [...HashMap.entries(doc.versionDocuments)]
121
202
  const duplicates: Array<{ version1: string; version2: string }> = []
122
203
  for (let i = 0; i < entries.length; i++) {
123
204
  for (let j = i + 1; j < entries.length; j++) {
@@ -161,32 +242,41 @@ export const scan = (
161
242
  const pattern = `**/*.{${extensions.join(',')}}`
162
243
  const files = options.files ?? (yield* EffectGlob.glob(pattern, { cwd: options.dir }))
163
244
 
245
+ // Get schema versions upfront for default file resolution
246
+ const schemaVersions: Version.Version[] = options.schemaCatalog
247
+ ? SchemaCatalog.fold(
248
+ (versioned) => SchemaCatalog.Versioned.getVersions(versioned),
249
+ () => [], // Unversioned schemas don't have version-specific examples
250
+ )(options.schemaCatalog)
251
+ : []
252
+
164
253
  // Group files by example
165
254
  const groupedFiles = groupExampleFiles(files)
166
255
 
256
+ // Resolve .default files into proper version coverage immediately
257
+ const resolvedFiles = resolveDefaultFiles(groupedFiles, schemaVersions)
258
+
167
259
  // Process each example group
168
260
  const examples: Example.Example[] = []
169
261
  const diagnostics: Diagnostic[] = []
170
262
 
171
- for (const [name, versions] of groupedFiles) {
172
- // Check if this is a versioned or unversioned example
173
- const hasMultipleVersions = versions.size > 1
174
- const hasUnversionedFile = versions.has(null)
175
- const hasDefaultFile = versions.has('default')
176
- const hasOnlyDefaultFile = hasDefaultFile && versions.size === 1
177
-
263
+ for (const [name, resolved] of resolvedFiles) {
178
264
  // Determine the base path for this example
179
- const firstFilePath = Array.from(versions.values())[0]!
180
- const basePath = versions.size === 1
181
- ? firstFilePath
182
- : Path.dirname(firstFilePath)
265
+ const firstFile = resolved.unversioned
266
+ || (HashMap.size(resolved.versionDocuments) > 0
267
+ ? HashMap.values(resolved.versionDocuments).next().value
268
+ : undefined)
269
+ if (!firstFile) continue // No files for this example
270
+
271
+ const basePath = HashMap.size(resolved.versionDocuments) > 1 || resolved.unversioned
272
+ ? Path.dirname(firstFile)
273
+ : firstFile
183
274
 
184
- let example: Example.Example
275
+ let example: Example.Example | undefined
185
276
 
186
- if (hasUnversionedFile && versions.size === 1) {
277
+ if (resolved.unversioned) {
187
278
  // Unversioned example - single file with no version
188
- const filePath = versions.get(null)!
189
- const fullPath = Path.join(options.dir, filePath)
279
+ const fullPath = Path.join(options.dir, resolved.unversioned)
190
280
  const document = yield* fs.readFileString(fullPath)
191
281
 
192
282
  example = Example.make({
@@ -196,104 +286,36 @@ export const scan = (
196
286
  document,
197
287
  }),
198
288
  })
199
- } else if (hasOnlyDefaultFile) {
200
- // Only default file - create versioned document with all schema versions as a set
201
- const filePath = versions.get('default')!
202
- const fullPath = Path.join(options.dir, filePath)
203
- const documentContent = yield* fs.readFileString(fullPath)
204
-
205
- // Get all schema versions to map to this default document
206
- const schemaVersions: Version.Version[] = options.schemaCatalog
207
- ? SchemaCatalog.fold(
208
- (versioned) => SchemaCatalog.Versioned.getVersions(versioned),
209
- () => [], // Unversioned schemas don't have version-specific examples
210
- )(options.schemaCatalog)
211
- : []
212
-
213
- if (schemaVersions.length > 0) {
214
- // Create a version set for all schema versions
215
- const versionSet = HashSet.fromIterable(schemaVersions)
216
- let versionDocuments = HashMap.empty<VersionCoverage.VersionCoverage, string>()
217
- versionDocuments = HashMap.set(versionDocuments, versionSet, documentContent)
218
-
219
- example = Example.make({
220
- name,
221
- path: basePath,
222
- document: Document.Versioned.make({
223
- versionDocuments,
224
- }),
225
- })
226
- } else {
227
- // No schema versions, treat as unversioned
228
- example = Example.make({
229
- name,
230
- path: basePath,
231
- document: Document.Unversioned.make({
232
- document: documentContent,
233
- }),
234
- })
235
- }
236
- } else {
237
- // Versioned example - multiple files or versioned files
289
+ } else if (HashMap.size(resolved.versionDocuments) > 0) {
290
+ // Versioned example - read all version documents
238
291
  let versionDocuments = HashMap.empty<VersionCoverage.VersionCoverage, string>()
239
- let defaultDocument: string | undefined
240
- let explicitVersions = HashSet.empty<Version.Version>() // Track which versions have explicit files
241
292
  const unknownVersions: Version.Version[] = []
242
-
243
- // Get available schema versions if catalog is provided
244
- const schemaVersions: Version.Version[] = options.schemaCatalog
245
- ? SchemaCatalog.fold(
246
- (versioned) => SchemaCatalog.Versioned.getVersions(versioned),
247
- () => [], // Unversioned schemas don't have version-specific examples
248
- )(options.schemaCatalog)
249
- : []
250
-
251
- // Create HashSet for O(1) lookups
252
293
  const schemaVersionsSet = HashSet.fromIterable(schemaVersions)
253
294
 
254
- // Read content for each version
255
- for (const [version, filePath] of versions) {
256
- const fullPath = Path.join(options.dir, filePath)
257
- const fileContent = yield* fs.readFileString(fullPath)
258
-
259
- if (version === 'default') {
260
- defaultDocument = fileContent
261
- } else if (version !== null) {
262
- // Parse the version string
263
- const parsedVersion = Version.decodeSync(version)
264
- // Check if this version exists in the schema
265
- const versionExists = HashSet.has(schemaVersionsSet, parsedVersion)
295
+ for (const [versionCoverage, filePath] of HashMap.entries(resolved.versionDocuments)) {
296
+ // Check if version is known (only for single versions, not sets)
297
+ if (Version.is(versionCoverage)) {
298
+ const versionExists = HashSet.has(schemaVersionsSet, versionCoverage)
266
299
  if (options.schemaCatalog && schemaVersions.length > 0 && !versionExists) {
267
- unknownVersions.push(parsedVersion)
300
+ unknownVersions.push(versionCoverage)
268
301
  // Create diagnostic for unknown version
269
302
  diagnostics.push(makeDiagnosticUnknownVersion({
270
- message: `Example "${name}" specifies version "${version}" which does not exist in the schema`,
303
+ message: `Example "${name}" specifies version "${
304
+ Version.encodeSync(versionCoverage)
305
+ }" which does not exist in the schema`,
271
306
  example: { name, path: basePath },
272
- version: parsedVersion,
307
+ version: versionCoverage,
273
308
  availableVersions: schemaVersions,
274
309
  }))
275
310
  // Skip this version - don't include it in the example
276
311
  continue
277
312
  }
278
-
279
- // We already have parsedVersion from above
280
- versionDocuments = HashMap.set(versionDocuments, parsedVersion, fileContent)
281
- explicitVersions = HashSet.add(explicitVersions, parsedVersion)
282
313
  }
283
- }
284
-
285
- if (defaultDocument) {
286
- // If we have a default, determine which versions it applies to
287
- const defaultVersions = schemaVersions.filter(v => !HashSet.has(explicitVersions, v))
288
314
 
289
- if (defaultVersions.length > 0) {
290
- // Create a version set for the default document
291
- const defaultVersionSet = defaultVersions.length === 1
292
- ? defaultVersions[0]! // Single version
293
- : HashSet.fromIterable(defaultVersions) // Version set
315
+ const fullPath = Path.join(options.dir, filePath)
316
+ const fileContent = yield* fs.readFileString(fullPath)
294
317
 
295
- versionDocuments = HashMap.set(versionDocuments, defaultVersionSet, defaultDocument)
296
- }
318
+ versionDocuments = HashMap.set(versionDocuments, versionCoverage, fileContent)
297
319
  }
298
320
 
299
321
  if (HashMap.size(versionDocuments) > 0) {
@@ -308,9 +330,6 @@ export const scan = (
308
330
  } else if (unknownVersions.length > 0) {
309
331
  // All versions were unknown, skip this example entirely
310
332
  continue
311
- } else {
312
- // No versions at all - shouldn't happen
313
- continue
314
333
  }
315
334
  }
316
335
 
@@ -1,6 +1,5 @@
1
1
  import { Catalog } from '#lib/catalog/$'
2
2
  import { Document } from '#lib/document/$'
3
- import { Schema } from '#lib/schema/$'
4
3
  import { Version } from '#lib/version/$'
5
4
  import { HashMap, HashSet, Option } from 'effect'
6
5
  import { Schema as S } from 'effect'
@@ -76,19 +75,19 @@ const extractTypesFromQuery = (
76
75
  const resolveFieldType = (
77
76
  fieldName: string,
78
77
  parentTypeName: string | null,
79
- ): string | null => {
80
- if (!parentTypeName) return null
78
+ ): Option.Option<string> => {
79
+ if (!parentTypeName) return Option.none()
81
80
 
82
81
  const parentType = schema.getType(parentTypeName)
83
82
  if (!parentType || !isObjectType(parentType) && !isInterfaceType(parentType)) {
84
- return null
83
+ return Option.none()
85
84
  }
86
85
 
87
86
  const field = parentType.getFields()[fieldName]
88
- if (!field) return null
87
+ if (!field) return Option.none()
89
88
 
90
89
  const namedType = getNamedType(field.type)
91
- return namedType.name
90
+ return Option.some(namedType.name)
92
91
  }
93
92
 
94
93
  // Track the current type context as we traverse
@@ -120,8 +119,9 @@ const extractTypesFromQuery = (
120
119
  // Special handling for __typename
121
120
  if (node.name.value === '__typename') return
122
121
 
123
- const fieldType = resolveFieldType(node.name.value, currentType)
124
- if (fieldType) {
122
+ const fieldTypeOption = resolveFieldType(node.name.value, currentType)
123
+ if (Option.isSome(fieldTypeOption)) {
124
+ const fieldType = fieldTypeOption.value
125
125
  addType(fieldType)
126
126
  // Update context for nested selections
127
127
  if (node.selectionSet) {
@@ -160,29 +160,6 @@ const extractTypesFromQuery = (
160
160
  return types
161
161
  }
162
162
 
163
- // ============================================================================
164
- // Helper Functions
165
- // ============================================================================
166
-
167
- /**
168
- * Get a schema by version from the catalog.
169
- */
170
- const getSchemaByVersion = (
171
- catalog: Catalog.Catalog,
172
- version: Version.Version,
173
- ): Schema.Schema | undefined => {
174
- if (Catalog.Versioned.is(catalog)) {
175
- // Use HashMap.get for O(1) lookup
176
- return HashMap.get(catalog.entries, version).pipe(Option.getOrElse(() => undefined))
177
- }
178
- // For unversioned catalog, return the single schema if version matches
179
- if (Catalog.Unversioned.is(catalog)) {
180
- // Unversioned catalogs don't have versions, so we can't match
181
- return undefined
182
- }
183
- return undefined
184
- }
185
-
186
163
  // ============================================================================
187
164
  // Index Creation
188
165
  // ============================================================================
@@ -208,7 +185,10 @@ export const createTypeUsageIndex = (
208
185
 
209
186
  for (const typeName of types) {
210
187
  // For unversioned, use the latest version from the catalog
211
- const latestVersion = Catalog.getLatestVersion(schemasCatalog) ?? Version.fromString('1.0.0')
188
+ const latestVersion = Option.getOrElse(
189
+ Catalog.getLatestVersion(schemasCatalog),
190
+ () => Version.fromString('1.0.0'),
191
+ )
212
192
  index = addExampleToIndex(index, UNVERSIONED_KEY, typeName, example, latestVersion)
213
193
  }
214
194
  } else if (Document.Versioned.is(example.document)) {
@@ -216,11 +196,19 @@ export const createTypeUsageIndex = (
216
196
  const allVersions = Document.Versioned.getAllVersions(example.document)
217
197
 
218
198
  for (const version of allVersions) {
219
- const schema = getSchemaByVersion(schemasCatalog, version)
220
- if (!schema) continue
199
+ // Use centralized resolution to get schema for version
200
+ const schemaOption = Option.liftThrowable(
201
+ () => Catalog.resolveCatalogSchema(schemasCatalog, version),
202
+ )()
203
+ if (Option.isNone(schemaOption)) {
204
+ // Skip if version not found in catalog
205
+ continue
206
+ }
207
+ const schema = schemaOption.value
221
208
 
222
- const documentString = Document.Versioned.getContentForVersion(example.document, version)
223
- if (!documentString) continue
209
+ const documentStringOption = Document.Versioned.getContentForVersion(example.document, version)
210
+ if (Option.isNone(documentStringOption)) continue
211
+ const documentString = documentStringOption.value
224
212
 
225
213
  const types = extractTypesFromQuery(documentString, schema.definition)
226
214
 
@@ -1,6 +1,7 @@
1
1
  import { Grafaid } from '#lib/grafaid'
2
2
  import { Schema } from '#lib/schema/$'
3
3
  import { Version } from '#lib/version/$'
4
+ import { Array, Option, Predicate } from 'effect'
4
5
 
5
6
  export interface ReferencePathParts {
6
7
  version?: Version.Version
@@ -46,16 +47,15 @@ export const createReferenceVersionPath = (version?: Version.Version): string =>
46
47
  export const joinSegmentsAndPaths = (
47
48
  ...segmentsOrPaths: (string | undefined | null | (string | null | undefined)[])[]
48
49
  ): string => {
49
- const path = '/' + segmentsOrPaths
50
- .flat()
51
- .filter((_): _ is string => _ !== undefined && _ !== null)
52
- .map(chunkUnformatted =>
53
- chunkUnformatted
54
- .replace(/^\//, '')
55
- .replace(/\/$/, '')
56
- )
57
- .filter(Boolean)
58
- .join('/')
50
+ const segments = Array.filterMap(
51
+ segmentsOrPaths.flat(),
52
+ (segment) => {
53
+ if (!Predicate.isNotNullable(segment)) return Option.none()
54
+ const cleaned = segment.replace(/^\//, '').replace(/\/$/, '')
55
+ return cleaned ? Option.some(cleaned) : Option.none()
56
+ },
57
+ )
58
+ const path = '/' + segments.join('/')
59
59
 
60
60
  return path
61
61
  }
@@ -1,4 +1,5 @@
1
1
  import { Grafaid } from '#lib/grafaid'
2
+ import { Array, Option } from 'effect'
2
3
  import type { GraphQLFieldMap, GraphQLSchema } from 'graphql'
3
4
 
4
5
  export interface PathValidation {
@@ -28,8 +29,8 @@ export const doesPathExist = (schema: GraphQLSchema, path: PathValidation): bool
28
29
  if (!path.argument) return true
29
30
 
30
31
  // Check if argument exists
31
- const arg = field.args.find(a => a.name === path.argument)
32
- return !!arg
32
+ const arg = Array.findFirst(field.args, a => a.name === path.argument)
33
+ return Option.isSome(arg)
33
34
  }
34
35
 
35
36
  /**
@@ -98,8 +98,8 @@ export const loader = InputSource.createEffect({
98
98
  // Check if we have either:
99
99
  // 1. A single schema.graphql file (non-versioned mode)
100
100
  // 2. Any .graphql files with valid date names (versioned mode)
101
- const hasSchemaFile = files.some(file => file === 'schema.graphql')
102
- const hasVersionedFiles = files.some(file => {
101
+ const hasSchemaFile = Array.some(files, file => file === 'schema.graphql')
102
+ const hasVersionedFiles = Array.some(files, file => {
103
103
  if (!file.endsWith('.graphql')) return false
104
104
  const name = Path.basename(file, '.graphql')
105
105
  return /^\d{4}-\d{2}-\d{2}$/.test(name)
@@ -10,7 +10,7 @@ import { debugPolen } from '#singletons/debug'
10
10
  import { PlatformError } from '@effect/platform/Error'
11
11
  import { FileSystem } from '@effect/platform/FileSystem'
12
12
  import { Arr, Path } from '@wollybeard/kit'
13
- import { Effect, HashMap } from 'effect'
13
+ import { Array, Effect, HashMap } from 'effect'
14
14
  import type { GraphQLSchema } from 'graphql'
15
15
 
16
16
  const debug = debugPolen.sub(`schema:data-source-versioned-schema-directory`)
@@ -452,9 +452,8 @@ export const loader = InputSource.createEffect({
452
452
  // Check for revision files or schema.graphql
453
453
  const dirFiles = yield* Effect.either(fs.readDirectory(versionPath))
454
454
  if (dirFiles._tag === 'Right') {
455
- const hasRevisions = dirFiles.right.some(file =>
456
- /^\d{4}-\d{2}-\d{2}\.graphql$/.test(file) || file === 'schema.graphql'
457
- )
455
+ const hasRevisions = Array.some(dirFiles.right, file =>
456
+ /^\d{4}-\d{2}-\d{2}\.graphql$/.test(file) || file === 'schema.graphql')
458
457
  if (hasRevisions) {
459
458
  return true
460
459
  }
@@ -520,9 +519,8 @@ export const loader = InputSource.createEffect({
520
519
  // Check for schema files
521
520
  const dirFiles = yield* Effect.either(fs.readDirectory(versionPath))
522
521
  if (dirFiles._tag === 'Right') {
523
- const hasSchemaFiles = dirFiles.right.some(file =>
524
- /^\d{4}-\d{2}-\d{2}\.graphql$/.test(file) || file === 'schema.graphql'
525
- )
522
+ const hasSchemaFiles = Array.some(dirFiles.right, file =>
523
+ /^\d{4}-\d{2}-\d{2}\.graphql$/.test(file) || file === 'schema.graphql')
526
524
 
527
525
  if (hasSchemaFiles) {
528
526
  debug('found valid schema files, proceeding with full read')
@@ -7,7 +7,7 @@ import { Catalog } from '#lib/catalog/$'
7
7
  import type { PlatformError } from '@effect/platform/Error'
8
8
  import type { FileSystem } from '@effect/platform/FileSystem'
9
9
  import { Arr } from '@wollybeard/kit'
10
- import { Effect } from 'effect'
10
+ import { Array, Effect, Option } from 'effect'
11
11
 
12
12
  // For now, we'll need a type that accepts both promise and effect sources
13
13
  type AnyInputSource = InputSource | EffectInputSource