polen 0.11.0-next.16 → 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 (251) 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 +5 -5
  22. package/build/api/examples/diagnostic/missing-versions.d.ts +5 -4
  23. package/build/api/examples/diagnostic/missing-versions.d.ts.map +1 -1
  24. package/build/api/examples/diagnostic/missing-versions.js +3 -2
  25. package/build/api/examples/diagnostic/missing-versions.js.map +1 -1
  26. package/build/api/examples/diagnostic/unknown-version.d.ts +5 -4
  27. package/build/api/examples/diagnostic/unknown-version.d.ts.map +1 -1
  28. package/build/api/examples/diagnostic/unknown-version.js +3 -2
  29. package/build/api/examples/diagnostic/unknown-version.js.map +1 -1
  30. package/build/api/examples/diagnostic/validation-error.d.ts +3 -2
  31. package/build/api/examples/diagnostic/validation-error.d.ts.map +1 -1
  32. package/build/api/examples/diagnostic/validation-error.js +9 -3
  33. package/build/api/examples/diagnostic/validation-error.js.map +1 -1
  34. package/build/api/examples/diagnostic/validator.d.ts.map +1 -1
  35. package/build/api/examples/diagnostic/validator.js +115 -69
  36. package/build/api/examples/diagnostic/validator.js.map +1 -1
  37. package/build/api/examples/filter.d.ts.map +1 -1
  38. package/build/api/examples/filter.js +9 -6
  39. package/build/api/examples/filter.js.map +1 -1
  40. package/build/api/examples/scanner.d.ts.map +1 -1
  41. package/build/api/examples/scanner.js +93 -103
  42. package/build/api/examples/scanner.js.map +1 -1
  43. package/build/api/examples/type-usage-indexer.d.ts.map +1 -1
  44. package/build/api/examples/type-usage-indexer.js +18 -32
  45. package/build/api/examples/type-usage-indexer.js.map +1 -1
  46. package/build/api/iso/schema/routing.d.ts.map +1 -1
  47. package/build/api/iso/schema/routing.js +8 -8
  48. package/build/api/iso/schema/routing.js.map +1 -1
  49. package/build/api/iso/schema/validation.d.ts.map +1 -1
  50. package/build/api/iso/schema/validation.js +3 -2
  51. package/build/api/iso/schema/validation.js.map +1 -1
  52. package/build/api/schema/input-sources/directory.js +2 -2
  53. package/build/api/schema/input-sources/directory.js.map +1 -1
  54. package/build/api/schema/input-sources/versioned-directory.d.ts +3 -3
  55. package/build/api/schema/input-sources/versioned-directory.d.ts.map +1 -1
  56. package/build/api/schema/input-sources/versioned-directory.js +6 -4
  57. package/build/api/schema/input-sources/versioned-directory.js.map +1 -1
  58. package/build/api/schema/load.d.ts.map +1 -1
  59. package/build/api/schema/load.js +2 -2
  60. package/build/api/schema/load.js.map +1 -1
  61. package/build/cli/commands/hero-image.js +1 -1
  62. package/build/cli/commands/hero-image.js.map +1 -1
  63. package/build/lib/catalog/catalog.d.ts +65 -24
  64. package/build/lib/catalog/catalog.d.ts.map +1 -1
  65. package/build/lib/catalog/catalog.js +72 -16
  66. package/build/lib/catalog/catalog.js.map +1 -1
  67. package/build/lib/catalog/versioned.d.ts +46 -26
  68. package/build/lib/catalog/versioned.d.ts.map +1 -1
  69. package/build/lib/catalog/versioned.js +44 -7
  70. package/build/lib/catalog/versioned.js.map +1 -1
  71. package/build/lib/catalog-statistics/analyze-catalog.js +3 -3
  72. package/build/lib/catalog-statistics/analyze-catalog.js.map +1 -1
  73. package/build/lib/document/document.d.ts +55 -5
  74. package/build/lib/document/document.d.ts.map +1 -1
  75. package/build/lib/document/document.js +96 -2
  76. package/build/lib/document/document.js.map +1 -1
  77. package/build/lib/document/versioned.d.ts +2 -2
  78. package/build/lib/document/versioned.d.ts.map +1 -1
  79. package/build/lib/document/versioned.js +7 -7
  80. package/build/lib/document/versioned.js.map +1 -1
  81. package/build/lib/lifecycles/lifecycles.d.ts +5 -4
  82. package/build/lib/lifecycles/lifecycles.d.ts.map +1 -1
  83. package/build/lib/lifecycles/lifecycles.js +15 -13
  84. package/build/lib/lifecycles/lifecycles.js.map +1 -1
  85. package/build/lib/version-coverage/$$.d.ts +2 -0
  86. package/build/lib/version-coverage/$$.d.ts.map +1 -0
  87. package/build/lib/version-coverage/$$.js +2 -0
  88. package/build/lib/version-coverage/$$.js.map +1 -0
  89. package/build/lib/version-coverage/$.d.ts.map +1 -0
  90. package/build/lib/version-coverage/$.js.map +1 -0
  91. package/build/lib/{version-selection/version-selection.d.ts → version-coverage/version-coverage.d.ts} +1 -1
  92. package/build/lib/version-coverage/version-coverage.d.ts.map +1 -0
  93. package/build/lib/{version-selection/version-selection.js → version-coverage/version-coverage.js} +2 -2
  94. package/build/lib/version-coverage/version-coverage.js.map +1 -0
  95. package/build/template/components/Changelog/Changelog.d.ts.map +1 -1
  96. package/build/template/components/Changelog/Changelog.js +7 -7
  97. package/build/template/components/Changelog/Changelog.js.map +1 -1
  98. package/build/template/components/GraphQLDocument.d.ts +1 -1
  99. package/build/template/components/GraphQLDocument.d.ts.map +1 -1
  100. package/build/template/components/GraphQLDocument.js +10 -38
  101. package/build/template/components/GraphQLDocument.js.map +1 -1
  102. package/build/template/components/GraphQLInteractive/lib/parser.d.ts +28 -0
  103. package/build/template/components/GraphQLInteractive/lib/parser.d.ts.map +1 -1
  104. package/build/template/components/GraphQLInteractive/lib/parser.js +60 -27
  105. package/build/template/components/GraphQLInteractive/lib/parser.js.map +1 -1
  106. package/build/template/components/VersionCoveragePicker.d.ts +1 -1
  107. package/build/template/components/VersionCoveragePicker.d.ts.map +1 -1
  108. package/build/template/components/VersionCoveragePicker.js +4 -6
  109. package/build/template/components/VersionCoveragePicker.js.map +1 -1
  110. package/build/template/components/VersionPicker.d.ts.map +1 -1
  111. package/build/template/components/VersionPicker.js +5 -2
  112. package/build/template/components/VersionPicker.js.map +1 -1
  113. package/build/template/components/home/FeaturesGrid.js +1 -1
  114. package/build/template/components/home/FeaturesGrid.js.map +1 -1
  115. package/build/template/components/home/HeroSection.js +1 -1
  116. package/build/template/components/home/HeroSection.js.map +1 -1
  117. package/build/template/components/home/QuickStart.d.ts.map +1 -1
  118. package/build/template/components/home/QuickStart.js +8 -4
  119. package/build/template/components/home/QuickStart.js.map +1 -1
  120. package/build/template/components/home/RecentChanges.d.ts.map +1 -1
  121. package/build/template/components/home/RecentChanges.js +2 -1
  122. package/build/template/components/home/RecentChanges.js.map +1 -1
  123. package/build/template/hooks/use-highlighted.d.ts.map +1 -1
  124. package/build/template/hooks/use-highlighted.js +19 -13
  125. package/build/template/hooks/use-highlighted.js.map +1 -1
  126. package/build/template/lib/fetch-text.d.ts +18 -0
  127. package/build/template/lib/fetch-text.d.ts.map +1 -1
  128. package/build/template/lib/fetch-text.js +32 -4
  129. package/build/template/lib/fetch-text.js.map +1 -1
  130. package/build/template/routes/changelog.d.ts +1 -1
  131. package/build/template/routes/changelog.d.ts.map +1 -1
  132. package/build/template/routes/changelog.js +7 -4
  133. package/build/template/routes/changelog.js.map +1 -1
  134. package/build/template/routes/examples/_index.js +1 -1
  135. package/build/template/routes/examples/_index.js.map +1 -1
  136. package/build/template/routes/examples/name.d.ts.map +1 -1
  137. package/build/template/routes/examples/name.js +4 -2
  138. package/build/template/routes/examples/name.js.map +1 -1
  139. package/build/template/routes/reference.js +6 -6
  140. package/build/template/routes/reference.js.map +1 -1
  141. package/build/template/stores/toast.d.ts.map +1 -1
  142. package/build/template/stores/toast.js +5 -3
  143. package/build/template/stores/toast.js.map +1 -1
  144. package/build/vite/plugins/navbar.js +1 -1
  145. package/build/vite/plugins/navbar.js.map +1 -1
  146. package/build/vite/plugins/routes-manifest.js +1 -1
  147. package/build/vite/plugins/routes-manifest.js.map +1 -1
  148. package/package.json +7 -7
  149. package/src/api/builder/ssg/generate.ts +10 -5
  150. package/src/api/builder/ssg/page-generator.worker.ts +18 -3
  151. package/src/api/config/normalized.ts +12 -3
  152. package/src/api/config-template/template.ts +2 -2
  153. package/src/api/content/sidebar.ts +3 -3
  154. package/src/api/examples/config.test.ts +10 -0
  155. package/src/api/examples/config.ts +33 -4
  156. package/src/api/examples/diagnostic/missing-versions.ts +3 -2
  157. package/src/api/examples/diagnostic/unknown-version.ts +3 -2
  158. package/src/api/examples/diagnostic/validation-error.ts +9 -3
  159. package/src/api/examples/diagnostic/validator.test.ts +100 -55
  160. package/src/api/examples/diagnostic/validator.ts +148 -105
  161. package/src/api/examples/filter.ts +9 -6
  162. package/src/api/examples/scanner.ts +144 -120
  163. package/src/api/examples/type-usage-indexer.test.ts +44 -33
  164. package/src/api/examples/type-usage-indexer.ts +25 -40
  165. package/src/api/iso/schema/routing.ts +10 -10
  166. package/src/api/iso/schema/validation.ts +3 -2
  167. package/src/api/schema/$.test.ts +2 -2
  168. package/src/api/schema/input-sources/directory.ts +2 -2
  169. package/src/api/schema/input-sources/versioned-directory.ts +11 -8
  170. package/src/api/schema/load.ts +2 -2
  171. package/src/cli/commands/hero-image.ts +1 -1
  172. package/src/lib/catalog/catalog.ts +93 -16
  173. package/src/lib/catalog/versioned.ts +57 -7
  174. package/src/lib/catalog-statistics/$.test.ts +22 -12
  175. package/src/lib/catalog-statistics/analyze-catalog.ts +3 -3
  176. package/src/lib/document/document.ts +135 -2
  177. package/src/lib/document/versioned.ts +8 -8
  178. package/src/lib/lifecycles/lifecycles.ts +33 -28
  179. package/src/lib/version-coverage/$$.ts +1 -0
  180. package/src/lib/{version-selection/version-selection.ts → version-coverage/version-coverage.ts} +1 -1
  181. package/src/template/components/Changelog/Changelog.tsx +10 -6
  182. package/src/template/components/GraphQLDocument.tsx +11 -68
  183. package/src/template/components/GraphQLInteractive/lib/parser.ts +81 -29
  184. package/src/template/components/VersionCoveragePicker.tsx +4 -5
  185. package/src/template/components/VersionPicker.tsx +9 -2
  186. package/src/template/components/home/FeaturesGrid.tsx +1 -1
  187. package/src/template/components/home/HeroSection.tsx +1 -1
  188. package/src/template/components/home/QuickStart.tsx +16 -7
  189. package/src/template/components/home/RecentChanges.tsx +3 -1
  190. package/src/template/hooks/use-highlighted.ts +31 -19
  191. package/src/template/lib/fetch-text.ts +45 -4
  192. package/src/template/routes/changelog.tsx +10 -4
  193. package/src/template/routes/examples/_index.tsx +1 -1
  194. package/src/template/routes/examples/name.tsx +4 -2
  195. package/src/template/routes/reference.tsx +6 -6
  196. package/src/template/stores/toast.ts +6 -3
  197. package/src/vite/plugins/navbar.ts +1 -1
  198. package/src/vite/plugins/routes-manifest.ts +1 -1
  199. package/build/lib/graph/$$.d.ts +0 -2
  200. package/build/lib/graph/$$.d.ts.map +0 -1
  201. package/build/lib/graph/$$.js +0 -2
  202. package/build/lib/graph/$$.js.map +0 -1
  203. package/build/lib/graph/$.d.ts +0 -2
  204. package/build/lib/graph/$.d.ts.map +0 -1
  205. package/build/lib/graph/$.js +0 -2
  206. package/build/lib/graph/$.js.map +0 -1
  207. package/build/lib/graph/graph.d.ts +0 -127
  208. package/build/lib/graph/graph.d.ts.map +0 -1
  209. package/build/lib/graph/graph.js +0 -152
  210. package/build/lib/graph/graph.js.map +0 -1
  211. package/build/lib/mask/$$.d.ts +0 -3
  212. package/build/lib/mask/$$.d.ts.map +0 -1
  213. package/build/lib/mask/$$.js +0 -3
  214. package/build/lib/mask/$$.js.map +0 -1
  215. package/build/lib/mask/$.d.ts +0 -2
  216. package/build/lib/mask/$.d.ts.map +0 -1
  217. package/build/lib/mask/$.js +0 -2
  218. package/build/lib/mask/$.js.map +0 -1
  219. package/build/lib/mask/apply.d.ts +0 -86
  220. package/build/lib/mask/apply.d.ts.map +0 -1
  221. package/build/lib/mask/apply.js +0 -86
  222. package/build/lib/mask/apply.js.map +0 -1
  223. package/build/lib/mask/mask.d.ts +0 -124
  224. package/build/lib/mask/mask.d.ts.map +0 -1
  225. package/build/lib/mask/mask.js +0 -137
  226. package/build/lib/mask/mask.js.map +0 -1
  227. package/build/lib/mask/mask.test-d.d.ts +0 -2
  228. package/build/lib/mask/mask.test-d.d.ts.map +0 -1
  229. package/build/lib/mask/mask.test-d.js +0 -102
  230. package/build/lib/mask/mask.test-d.js.map +0 -1
  231. package/build/lib/version-selection/$$.d.ts +0 -2
  232. package/build/lib/version-selection/$$.d.ts.map +0 -1
  233. package/build/lib/version-selection/$$.js +0 -2
  234. package/build/lib/version-selection/$$.js.map +0 -1
  235. package/build/lib/version-selection/$.d.ts.map +0 -1
  236. package/build/lib/version-selection/$.js.map +0 -1
  237. package/build/lib/version-selection/version-selection.d.ts.map +0 -1
  238. package/build/lib/version-selection/version-selection.js.map +0 -1
  239. package/src/lib/graph/$$.ts +0 -1
  240. package/src/lib/graph/$.ts +0 -1
  241. package/src/lib/graph/graph.ts +0 -197
  242. package/src/lib/mask/$$.ts +0 -2
  243. package/src/lib/mask/$.test.ts +0 -226
  244. package/src/lib/mask/$.ts +0 -1
  245. package/src/lib/mask/apply.ts +0 -134
  246. package/src/lib/mask/mask.test-d.ts +0 -156
  247. package/src/lib/mask/mask.ts +0 -244
  248. package/src/lib/version-selection/$$.ts +0 -1
  249. /package/build/lib/{version-selection → version-coverage}/$.d.ts +0 -0
  250. /package/build/lib/{version-selection → version-coverage}/$.js +0 -0
  251. /package/src/lib/{version-selection → version-coverage}/$.ts +0 -0
@@ -3,7 +3,7 @@ import { Change } from '#lib/change/$'
3
3
  import { Revision } from '#lib/revision/$'
4
4
  import { Schema } from '#lib/schema/$'
5
5
  import { Version } from '#lib/version/$'
6
- import { Match } from 'effect'
6
+ import { Match, Option } from 'effect'
7
7
 
8
8
  // ============================================================================
9
9
  // Lifecycles Type - Simple Index
@@ -44,21 +44,21 @@ export type Since =
44
44
  /**
45
45
  * Extract the type name from a change
46
46
  */
47
- const extractTypeNameFromChange = (change: Change.Change): string | null => {
47
+ const extractTypeNameFromChange = (change: Change.Change): Option.Option<string> => {
48
48
  return Match.value(change).pipe(
49
- Match.tag('TYPE_ADDED', (c) => c.name),
50
- Match.tag('TYPE_REMOVED', (c) => c.name),
51
- Match.tag('FIELD_ADDED', (c) => c.typeName),
52
- Match.tag('FIELD_REMOVED', (c) => c.typeName),
53
- Match.tag('INPUT_FIELD_ADDED', (c) => c.inputName),
54
- Match.tag('INPUT_FIELD_REMOVED', (c) => c.inputName),
55
- Match.tag('ENUM_VALUE_ADDED', (c) => c.enumName),
56
- Match.tag('ENUM_VALUE_REMOVED', (c) => c.enumName),
57
- Match.tag('UNION_MEMBER_ADDED', (c) => c.unionName),
58
- Match.tag('UNION_MEMBER_REMOVED', (c) => c.unionName),
59
- Match.tag('OBJECT_TYPE_INTERFACE_ADDED', (c) => c.objectName),
60
- Match.tag('OBJECT_TYPE_INTERFACE_REMOVED', (c) => c.objectName),
61
- Match.orElse(() => null),
49
+ Match.tag('TYPE_ADDED', (c) => Option.some(c.name)),
50
+ Match.tag('TYPE_REMOVED', (c) => Option.some(c.name)),
51
+ Match.tag('FIELD_ADDED', (c) => Option.some(c.typeName)),
52
+ Match.tag('FIELD_REMOVED', (c) => Option.some(c.typeName)),
53
+ Match.tag('INPUT_FIELD_ADDED', (c) => Option.some(c.inputName)),
54
+ Match.tag('INPUT_FIELD_REMOVED', (c) => Option.some(c.inputName)),
55
+ Match.tag('ENUM_VALUE_ADDED', (c) => Option.some(c.enumName)),
56
+ Match.tag('ENUM_VALUE_REMOVED', (c) => Option.some(c.enumName)),
57
+ Match.tag('UNION_MEMBER_ADDED', (c) => Option.some(c.unionName)),
58
+ Match.tag('UNION_MEMBER_REMOVED', (c) => Option.some(c.unionName)),
59
+ Match.tag('OBJECT_TYPE_INTERFACE_ADDED', (c) => Option.some(c.objectName)),
60
+ Match.tag('OBJECT_TYPE_INTERFACE_REMOVED', (c) => Option.some(c.objectName)),
61
+ Match.orElse(() => Option.none()),
62
62
  )
63
63
  }
64
64
 
@@ -72,8 +72,9 @@ export const createFromSchema = (schema: Schema.Schema): Lifecycles => {
72
72
  for (const revision of schema.revisions) {
73
73
  // Process all changes in this revision
74
74
  for (const change of revision.changes) {
75
- const typeName = extractTypeNameFromChange(change)
76
- if (typeName) {
75
+ const typeNameOption = extractTypeNameFromChange(change)
76
+ if (Option.isSome(typeNameOption)) {
77
+ const typeName = typeNameOption.value
77
78
  if (!lifecycles[typeName]) {
78
79
  lifecycles[typeName] = []
79
80
  }
@@ -100,8 +101,9 @@ export const create = (catalog: Catalog.Catalog): Lifecycles => {
100
101
  for (const revision of schema.revisions) {
101
102
  // Process all changes in this revision
102
103
  for (const change of revision.changes) {
103
- const typeName = extractTypeNameFromChange(change)
104
- if (typeName) {
104
+ const typeNameOption = extractTypeNameFromChange(change)
105
+ if (Option.isSome(typeNameOption)) {
106
+ const typeName = typeNameOption.value
105
107
  if (!lifecycles[typeName]) {
106
108
  lifecycles[typeName] = []
107
109
  }
@@ -119,7 +121,7 @@ export const create = (catalog: Catalog.Catalog): Lifecycles => {
119
121
  Catalog.fold(
120
122
  // Versioned catalog - process each versioned schema
121
123
  (versioned) => {
122
- for (const schema of versioned.entries) {
124
+ for (const schema of Catalog.Versioned.getAll(versioned)) {
123
125
  processSchema(schema)
124
126
  }
125
127
  },
@@ -477,9 +479,9 @@ export const getFieldLifecycle = (
477
479
  lifecycles: Lifecycles,
478
480
  typeName: string,
479
481
  fieldName: string,
480
- ): { events: ChangeEntry[] } | undefined => {
482
+ ): Option.Option<{ events: ChangeEntry[] }> => {
481
483
  const entries = lifecycles[typeName]
482
- if (!entries) return undefined
484
+ if (!entries) return Option.none()
483
485
 
484
486
  // Filter entries related to this specific field
485
487
  const fieldEntries = entries.filter(entry => {
@@ -497,25 +499,28 @@ export const getFieldLifecycle = (
497
499
  && entry.change.fieldName === fieldName)
498
500
  })
499
501
 
500
- if (fieldEntries.length === 0) return undefined
502
+ if (fieldEntries.length === 0) return Option.none()
501
503
 
502
504
  // Return in a format compatible with existing code that expects an object with events
503
- return { events: fieldEntries }
505
+ return Option.some({ events: fieldEntries })
504
506
  }
505
507
 
506
508
  /**
507
509
  * Get type lifecycle info (for compatibility with existing UI code)
508
510
  */
509
- export const getTypeLifecycle = (lifecycles: Lifecycles, typeName: string): { events: ChangeEntry[] } | undefined => {
511
+ export const getTypeLifecycle = (
512
+ lifecycles: Lifecycles,
513
+ typeName: string,
514
+ ): Option.Option<{ events: ChangeEntry[] }> => {
510
515
  const entries = lifecycles[typeName]
511
- if (!entries) return undefined
516
+ if (!entries) return Option.none()
512
517
 
513
518
  // Filter entries related to type-level changes
514
519
  const typeEntries = entries.filter(entry => {
515
520
  return entry.change._tag === 'TYPE_ADDED' || entry.change._tag === 'TYPE_REMOVED'
516
521
  })
517
522
 
518
- if (typeEntries.length === 0) return undefined
523
+ if (typeEntries.length === 0) return Option.none()
519
524
 
520
- return { events: typeEntries }
525
+ return Option.some({ events: typeEntries })
521
526
  }
@@ -0,0 +1 @@
1
+ export * from './version-coverage.js'
@@ -58,7 +58,7 @@ export const encodeSync = S.encodeSync(VersionCoverage)
58
58
  export const equivalence = S.equivalence(VersionCoverage)
59
59
 
60
60
  // ============================================================================
61
- // Domain Logic
61
+ // Domain Logic - Basic Operations
62
62
  // ============================================================================
63
63
 
64
64
  /**
@@ -4,6 +4,7 @@ import { DateOnly } from '#lib/date-only/$'
4
4
  import { Revision } from '#lib/revision/$'
5
5
  import { Schema } from '#lib/schema/$'
6
6
  import { Version } from '#lib/version/$'
7
+ import { HashMap, Option } from 'effect'
7
8
  const CRITICALITY_LEVELS = ['BREAKING', 'DANGEROUS', 'NON_BREAKING'] as const
8
9
  import type { CriticalityLevel } from '@graphql-inspector/core'
9
10
  import { Box, Heading } from '@radix-ui/themes'
@@ -35,8 +36,7 @@ export const Changelog: React.FC<{ catalog: Catalog.Catalog }> = ({ catalog }) =
35
36
  useEffect(() => {
36
37
  if (urlVersion) return
37
38
  if (Catalog.Unversioned.is(catalog)) return
38
- const latestSchema = catalog.entries[0]
39
- if (!latestSchema) return
39
+ const latestSchema = Catalog.Versioned.getLatestOrThrow(catalog)
40
40
  const latestVersion = Version.encodeSync(latestSchema.version)
41
41
  navigate(`/changelog/version/${latestVersion}`, { replace: true })
42
42
  }, [catalog, urlVersion, navigate])
@@ -51,10 +51,14 @@ export const Changelog: React.FC<{ catalog: Catalog.Catalog }> = ({ catalog }) =
51
51
  } else {
52
52
  // For versioned catalogs, always show specific version (never all)
53
53
  if (urlVersion) {
54
- const entry = catalog.entries.find(e => Version.encodeSync(e.version) === urlVersion)
55
- return entry
56
- ? { revisions: entry.revisions, schema: entry }
57
- : { revisions: [], schema: null }
54
+ const entryOption = Option.map(
55
+ HashMap.findFirst(catalog.entries, (_, key) => Version.encodeSync(key) === urlVersion),
56
+ ([, value]) => value,
57
+ )
58
+ return Option.match(entryOption, {
59
+ onNone: () => ({ revisions: [], schema: null }),
60
+ onSome: (entry) => ({ revisions: entry.revisions, schema: entry }),
61
+ })
58
62
  }
59
63
  // This shouldn't happen due to redirect above, but return empty as fallback
60
64
  return { revisions: [], schema: null }
@@ -1,12 +1,7 @@
1
1
  import { Catalog } from '#lib/catalog/$'
2
2
  import { Document } from '#lib/document/$'
3
- import { S } from '#lib/kit-temp/$'
4
- import type { Schema } from '#lib/schema/$'
5
- import { VersionCoverage } from '#lib/version-selection/$'
6
- import { VersionCoverageSet } from '#lib/version-selection/version-selection'
7
- import { Version } from '#lib/version/$'
8
- import { Array, HashMap, Match } from 'effect'
9
- import type { GraphQLSchema } from 'graphql'
3
+ import { VersionCoverage } from '#lib/version-coverage'
4
+ import { Either, Option } from 'effect'
10
5
  import * as React from 'react'
11
6
  import { useHighlighted } from '../hooks/use-highlighted.js'
12
7
  import { GraphQLInteractive } from './GraphQLInteractive/GraphQLInteractive.js'
@@ -47,7 +42,7 @@ export const GraphQLDocument: React.FC<GraphQLDocumentProps> = ({
47
42
  /// ━ VERSION MANAGEMENT
48
43
  const isControlled = controlledVersionCoverage !== undefined
49
44
  const [internalVersionCoverage, setInternalVersionCoverage] = React.useState<VersionCoverage.VersionCoverage | null>(
50
- Catalog.getLatestVersionIdentifier(schemaCatalog),
45
+ Option.getOrNull(Catalog.getLatestVersion(schemaCatalog)),
51
46
  )
52
47
  const selectedVersionCoverage = isControlled ? controlledVersionCoverage : internalVersionCoverage
53
48
  const internalOnVersionChange = (version: VersionCoverage.VersionCoverage) => {
@@ -58,11 +53,15 @@ export const GraphQLDocument: React.FC<GraphQLDocumentProps> = ({
58
53
  }
59
54
 
60
55
  /// ━ DATA RESOLUTION
61
- const {
62
- schema,
63
- content,
64
- } = resolveSelectedVerCov(document, selectedVersionCoverage, schemaCatalog)
56
+ const result = Document.resolveDocumentAndSchema(document, schemaCatalog, selectedVersionCoverage)
65
57
 
58
+ // Handle resolution errors gracefully
59
+ if (Either.isLeft(result)) {
60
+ console.error('Failed to resolve document and schema:', result.left.message)
61
+ return null
62
+ }
63
+
64
+ const { schema, content } = result.right
66
65
  const highlightedCode = useHighlighted(content, { interactive: true })
67
66
 
68
67
  if (!highlightedCode) {
@@ -87,59 +86,3 @@ export const GraphQLDocument: React.FC<GraphQLDocumentProps> = ({
87
86
  />
88
87
  )
89
88
  }
90
-
91
- /**
92
- * Matches a document with an optional schema catalog, returning the document content,
93
- * corresponding schema, and available versions for the selected version.
94
- *
95
- * @param document - The document to retrieve content from (required)
96
- * @param selectedVersionCoverage - The version to select (optional, defaults to latest)
97
- * @param schemaCatalog - The schema catalog to match against (optional)
98
- * @returns Object with document content, optional schema, and available versions
99
- * @throws {Error} If versions in catalog don't match versions in document
100
- */
101
- type Result = { content: string; schema?: Schema.Schema }
102
- const resolveSelectedVerCov = (
103
- document: Document.Document,
104
- selectedVersionCoverage?: VersionCoverage.VersionCoverage | null,
105
- schemaCatalog?: Catalog.Catalog,
106
- ): Result => {
107
- if (Document.Unversioned.is(document)) {
108
- return {
109
- content: document.document,
110
- schema: Match.value(schemaCatalog).pipe(
111
- Match.tagsExhaustive({
112
- CatalogUnversioned: (catalog) => catalog.schema,
113
- CatalogVersioned: (catalog) => Catalog.Versioned.getLatest(catalog),
114
- }),
115
- ),
116
- }
117
- }
118
-
119
- return Match.value(selectedVersionCoverage).pipe(
120
- Match.whenOr(null, undefined, _ => {
121
- const content = Document.Versioned.getContentForLatestVersionOrThrow(document)
122
- return { content }
123
- }),
124
- Match.orElse(_ => {
125
- const version = VersionCoverage.getLatest(_)
126
-
127
- const content = Document.Versioned.getContentForVersion(document, version)
128
- if (!content) {
129
- throw new Error(`Version ${Version.encodeSync(version)} not covered by document`)
130
- }
131
-
132
- if (!schemaCatalog) return { version, content }
133
-
134
- if (Catalog.Unversioned.is(schemaCatalog)) {
135
- throw new Error('Cannot use a set of versions with an unversioned catalog')
136
- }
137
- const schema = schemaCatalog.entries.find(e => Version.equivalence(e.version, version))
138
- if (!schema) {
139
- throw new Error(`Version ${Version.encodeSync(version)} not found in catalog`)
140
- }
141
-
142
- return { content, schema: schema }
143
- }),
144
- )
145
- }
@@ -7,6 +7,7 @@
7
7
 
8
8
  import { Api } from '#api/iso'
9
9
  import type { CodeAnnotation } from 'codehike/code'
10
+ import { Either } from 'effect'
10
11
  import {
11
12
  getNamedType,
12
13
  type GraphQLArgument,
@@ -504,41 +505,65 @@ class SemanticContext {
504
505
  }
505
506
 
506
507
  /**
507
- * Parse GraphQL code into interactive tokens with semantic information
508
+ * Error types for GraphQL parsing
509
+ */
510
+ export type ParseError =
511
+ | { readonly _tag: 'InvalidInput'; readonly message: string }
512
+ | { readonly _tag: 'DocumentTooLarge'; readonly maxSize: number; readonly actualSize: number }
513
+ | { readonly _tag: 'TreeSitterError'; readonly message: string }
514
+ | { readonly _tag: 'ParserInitError'; readonly message: string }
515
+
516
+ const makeParseError = {
517
+ invalidInput: (message: string): ParseError => ({ _tag: 'InvalidInput', message }),
518
+ documentTooLarge: (maxSize: number, actualSize: number): ParseError => ({
519
+ _tag: 'DocumentTooLarge',
520
+ maxSize,
521
+ actualSize,
522
+ }),
523
+ treeSitterError: (message: string): ParseError => ({ _tag: 'TreeSitterError', message }),
524
+ parserInitError: (message: string): ParseError => ({ _tag: 'ParserInitError', message }),
525
+ }
526
+
527
+ /**
528
+ * Parse GraphQL code into interactive tokens with semantic information (Either version)
508
529
  *
509
530
  * @param code - The raw GraphQL code to parse
510
531
  * @param annotations - CodeHike annotations that might affect rendering
511
532
  * @param schema - Optional GraphQL schema for semantic analysis
512
- * @returns Array of tokens representing the parsed code
533
+ * @returns Either with array of tokens on right or ParseError on left
513
534
  */
514
- export async function parseGraphQLWithTreeSitter(
535
+ export async function parseGraphQLWithTreeSitterEither(
515
536
  code: string,
516
537
  annotations: CodeAnnotation[] = [],
517
538
  schema?: GraphQLSchema,
518
- ): Promise<GraphQLToken[]> {
539
+ ): Promise<Either.Either<GraphQLToken[], ParseError>> {
519
540
  // Validate input
520
541
  if (!code || typeof code !== 'string') {
521
- throw new Error('Invalid GraphQL code: code must be a non-empty string')
542
+ return Either.left(makeParseError.invalidInput('Invalid GraphQL code: code must be a non-empty string'))
522
543
  }
523
544
 
524
545
  // Prevent parsing extremely large documents that could cause performance issues
525
- if (code.length > 100_000) {
526
- throw new Error('GraphQL document too large: maximum 100,000 characters allowed')
546
+ const maxSize = 100_000
547
+ if (code.length > maxSize) {
548
+ return Either.left(makeParseError.documentTooLarge(maxSize, code.length))
527
549
  }
528
550
 
529
551
  // Step 1: Parse with tree-sitter
530
- const parser = await getParser()
552
+ let parser: WebTreeSitter.Parser
553
+ try {
554
+ parser = await getParser()
555
+ } catch (error) {
556
+ return Either.left(makeParseError.parserInitError(
557
+ error instanceof Error ? error.message : 'Failed to initialize parser',
558
+ ))
559
+ }
560
+
531
561
  const tree = parser.parse(code)
532
562
 
533
563
  if (!tree) {
534
- throw new Error('Tree-sitter failed to parse GraphQL code')
564
+ return Either.left(makeParseError.treeSitterError('Tree-sitter failed to parse GraphQL code'))
535
565
  }
536
566
 
537
- // Check if tree-sitter found syntax errors (disabled for now as it may be too strict)
538
- // if (tree.rootNode.hasError) {
539
- // throw new Error('GraphQL syntax error detected by tree-sitter parser')
540
- // }
541
-
542
567
  try {
543
568
  // Step 2: Walk tree and attach semantics
544
569
  const tokens = collectTokensWithSemantics(tree, code, schema, annotations)
@@ -546,26 +571,53 @@ export async function parseGraphQLWithTreeSitter(
546
571
  // Step 3: Add error hint tokens after invalid fields
547
572
  const tokensWithHints = addErrorHintTokens(tokens, code, annotations)
548
573
 
549
- return tokensWithHints
574
+ return Either.right(tokensWithHints)
575
+ } catch (error) {
576
+ return Either.left(makeParseError.treeSitterError(
577
+ error instanceof Error ? error.message : 'Failed to process tokens',
578
+ ))
550
579
  } finally {
551
- // ## Tree-sitter Resource Lifecycle Management
552
- //
553
- // Tree-sitter creates native WASM objects that must be explicitly freed to prevent memory leaks.
554
- // The tree object holds references to parsed nodes and internal parser state that won't be
555
- // garbage collected automatically by JavaScript.
556
- //
557
- // Critical cleanup points:
558
- // 1. Always call tree.delete() in a finally block to ensure cleanup even on errors
559
- // 2. Do not access tree or any of its nodes after calling delete()
560
- // 3. The parser instance is cached globally and reused across multiple parsing calls
561
- //
562
- // Memory safety: Once tree.delete() is called, all WebTreeSitter.Node references become invalid.
563
- // Our tokens hold references to these nodes, but only use their text and position properties
564
- // which are copied during token creation, so the nodes can be safely deleted.
580
+ // Tree-sitter Resource Lifecycle Management
565
581
  tree.delete()
566
582
  }
567
583
  }
568
584
 
585
+ /**
586
+ * Parse GraphQL code into interactive tokens with semantic information
587
+ *
588
+ * @param code - The raw GraphQL code to parse
589
+ * @param annotations - CodeHike annotations that might affect rendering
590
+ * @param schema - Optional GraphQL schema for semantic analysis
591
+ * @returns Array of tokens representing the parsed code
592
+ * @deprecated Use parseGraphQLWithTreeSitterEither for better error handling
593
+ */
594
+ export async function parseGraphQLWithTreeSitter(
595
+ code: string,
596
+ annotations: CodeAnnotation[] = [],
597
+ schema?: GraphQLSchema,
598
+ ): Promise<GraphQLToken[]> {
599
+ const result = await parseGraphQLWithTreeSitterEither(code, annotations, schema)
600
+
601
+ if (Either.isLeft(result)) {
602
+ const error = result.left
603
+ switch (error._tag) {
604
+ case 'InvalidInput':
605
+ throw new Error(error.message)
606
+ case 'DocumentTooLarge':
607
+ throw new Error(`GraphQL document too large: maximum ${error.maxSize} characters allowed`)
608
+ case 'TreeSitterError':
609
+ case 'ParserInitError':
610
+ throw new Error(error.message)
611
+ default: {
612
+ const exhaustiveCheck: never = error
613
+ throw new Error(`Unhandled parse error: ${JSON.stringify(exhaustiveCheck)}`)
614
+ }
615
+ }
616
+ }
617
+
618
+ return result.right
619
+ }
620
+
569
621
  /**
570
622
  * Get or create the tree-sitter parser instance
571
623
  */
@@ -1,6 +1,6 @@
1
1
  import { Catalog } from '#lib/catalog/$'
2
2
  import { Document } from '#lib/document/$'
3
- import { VersionCoverage } from '#lib/version-selection/$'
3
+ import { VersionCoverage } from '#lib/version-coverage'
4
4
  import { Select } from '@radix-ui/themes'
5
5
  import { HashMap } from 'effect'
6
6
  import type { FC } from 'react'
@@ -30,12 +30,11 @@ export const VersionCoveragePicker: FC<Props> = ({
30
30
  <Select.Root
31
31
  value={VersionCoverage.toLabel(current)}
32
32
  onValueChange={(label) => {
33
- // Find selection by label and pick first version from it
33
+ // Find the full version coverage by label and pass it entirely
34
34
  const selection = options.find(s => VersionCoverage.toLabel(s) === label)
35
35
  if (selection) {
36
- const versions = VersionCoverage.toVersions(selection)
37
- const version = versions[0]
38
- if (version) onChange(version)
36
+ // Pass the entire VersionCoverage, not just the first version
37
+ onChange(selection)
39
38
  }
40
39
  }}
41
40
  >
@@ -1,6 +1,8 @@
1
1
  import { Api } from '#api/iso'
2
2
  import type { React } from '#dep/react/index'
3
+ import { Catalog } from '#lib/catalog/$'
3
4
  import { Version } from '#lib/version/$'
5
+ import { HashMap, Option } from 'effect'
4
6
  import { useNavigate } from 'react-router'
5
7
  import { schemasCatalog } from 'virtual:polen/project/schemas'
6
8
  import { useReferencePath } from '../hooks/useReferencePath.js'
@@ -33,12 +35,17 @@ export const VersionPicker: React.FC<Props> = ({ data, current }) => {
33
35
 
34
36
  // Find the schema for the target version
35
37
  // Note: newVersion is a string that we need to parse
36
- const targetSchema = catalog.entries.find(schema => Version.encodeSync(schema.version) === newVersion)
38
+ const targetSchemaOption = Option.map(
39
+ HashMap.findFirst(catalog.entries, (_, key) => Version.encodeSync(key) === newVersion),
40
+ ([, value]) => value,
41
+ )
37
42
 
38
- if (!targetSchema) {
43
+ if (Option.isNone(targetSchemaOption)) {
39
44
  throw new Error(`Version ${newVersion} not found`)
40
45
  }
41
46
 
47
+ const targetSchema = Option.getOrThrow(targetSchemaOption)
48
+
42
49
  // Find fallback path if needed
43
50
  const fallbackPath = Api.Schema.Validation.findFallbackPath(targetSchema.definition, currentPath)
44
51
  // Get redirect description if path changed
@@ -80,7 +80,7 @@ function getDefaultFeatures(): Feature[] {
80
80
  if (schemasCatalog) {
81
81
  try {
82
82
  // Get the latest schema from the catalog
83
- const latestSchema = Catalog.getLatestSchema(schemasCatalog)
83
+ const latestSchema = Catalog.getLatest(schemasCatalog)
84
84
 
85
85
  if (latestSchema && latestSchema.definition) {
86
86
  // Build stats from the schema definition
@@ -84,7 +84,7 @@ export const Hero: React.FC<HeroProps> = ({
84
84
  let schemaStats = null
85
85
  if (schemasCatalog) {
86
86
  try {
87
- const latestSchema = Catalog.getLatestSchema(schemasCatalog)
87
+ const latestSchema = Catalog.getLatest(schemasCatalog)
88
88
  if (latestSchema && latestSchema.definition) {
89
89
  const versionStats = CatalogStatistics.analyzeSchema(latestSchema.definition, 'current')
90
90
  schemaStats = {
@@ -1,5 +1,6 @@
1
1
  import { Box, Card, Heading, Section, Tabs, Text } from '@radix-ui/themes'
2
2
  import { highlight } from 'codehike/code'
3
+ import { Effect } from 'effect'
3
4
  import * as React from 'react'
4
5
  import { CodeBlock } from '../CodeBlock.js'
5
6
 
@@ -73,14 +74,22 @@ export const QuickStartSection: React.FC<Props> = ({ examples = defaultExamples
73
74
 
74
75
  React.useEffect(() => {
75
76
  const highlightExamples = async () => {
76
- const highlighted = await Promise.all(
77
- examples.map(async (example) => ({
78
- label: example.label,
79
- codeblock: await highlight(
80
- { value: example.code, lang: example.language, meta: '' },
81
- { theme: 'github-light' },
77
+ const highlighted = await Effect.runPromise(
78
+ Effect.all(
79
+ examples.map((example) =>
80
+ Effect.tryPromise({
81
+ try: async () => ({
82
+ label: example.label,
83
+ codeblock: await highlight(
84
+ { value: example.code, lang: example.language, meta: '' },
85
+ { theme: 'github-light' },
86
+ ),
87
+ }),
88
+ catch: (error) => new Error(`Failed to highlight ${example.label}: ${error}`),
89
+ })
82
90
  ),
83
- })),
91
+ { concurrency: 'unbounded' },
92
+ ),
84
93
  )
85
94
  setHighlightedExamples(highlighted)
86
95
  }
@@ -3,6 +3,8 @@ import * as React from 'react'
3
3
  import { Link } from 'react-router'
4
4
  import { schemasCatalog } from 'virtual:polen/project/schemas'
5
5
 
6
+ import { Catalog } from '#lib/catalog'
7
+
6
8
  interface RecentChangesProps {
7
9
  limit?: number
8
10
  showVersions?: boolean
@@ -30,7 +32,7 @@ export const RecentChangesSection: React.FC<RecentChangesProps> = ({
30
32
 
31
33
  // For versioned catalogs, we look at entries
32
34
  if (schemasCatalog._tag === 'CatalogVersioned') {
33
- const entries = schemasCatalog.entries || []
35
+ const entries = Catalog.Versioned.getAll(schemasCatalog)
34
36
  const recentEntries = entries.slice(0, limit)
35
37
 
36
38
  return recentEntries.flatMap(entry =>
@@ -1,5 +1,6 @@
1
1
  import type { HighlightedCode } from 'codehike/code'
2
2
  import { highlight } from 'codehike/code'
3
+ import { Duration, Effect } from 'effect'
3
4
  import * as React from 'react'
4
5
 
5
6
  /**
@@ -29,26 +30,37 @@ export const useHighlighted = (
29
30
  const [highlightedCode, setHighlightedCode] = React.useState<HighlightedCode | null>(null)
30
31
 
31
32
  React.useEffect(() => {
32
- const highlightContent = async () => {
33
- // Add a timeout to detect if highlight is hanging
34
- const timeoutPromise = new Promise((_, reject) => {
35
- setTimeout(() => reject(new Error('Highlight timeout after 5 seconds')), 5000)
36
- })
37
-
38
- const highlightPromise = highlight(
39
- {
40
- value: content,
41
- lang: config.lang,
42
- meta: metaString,
43
- },
44
- {
45
- theme: 'github-light',
46
- },
47
- )
33
+ const HIGHLIGHT_TIMEOUT = Duration.seconds(5)
48
34
 
49
- const highlighted = await Promise.race([highlightPromise, timeoutPromise])
50
-
51
- setHighlightedCode(highlighted as any)
35
+ const highlightContent = async () => {
36
+ try {
37
+ const highlighted = await Effect.runPromise(
38
+ Effect.tryPromise({
39
+ try: () =>
40
+ highlight(
41
+ {
42
+ value: content,
43
+ lang: config.lang,
44
+ meta: metaString,
45
+ },
46
+ {
47
+ theme: 'github-light',
48
+ },
49
+ ),
50
+ catch: (error) => new Error(`Highlight failed: ${error}`),
51
+ }).pipe(
52
+ Effect.timeout(HIGHLIGHT_TIMEOUT),
53
+ Effect.catchTag(
54
+ 'TimeoutException',
55
+ () => Effect.fail(new Error(`Highlight timeout after ${Duration.toSeconds(HIGHLIGHT_TIMEOUT)} seconds`)),
56
+ ),
57
+ ),
58
+ )
59
+ setHighlightedCode(highlighted)
60
+ } catch (error) {
61
+ console.error('Failed to highlight code:', error)
62
+ setHighlightedCode(null)
63
+ }
52
64
  }
53
65
  highlightContent()
54
66
  }, [content, config.lang, config.interactive])