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
@@ -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,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
  }
@@ -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])
@@ -1,13 +1,54 @@
1
+ import { Either } from 'effect'
2
+
3
+ /**
4
+ * Error type for fetch failures
5
+ */
6
+ export interface FetchError {
7
+ readonly _tag: 'FetchError'
8
+ readonly url: string
9
+ readonly status: number
10
+ readonly statusText: string
11
+ readonly message: string
12
+ }
13
+
14
+ const makeFetchError = (url: string, status: number, statusText: string): FetchError => ({
15
+ _tag: 'FetchError',
16
+ url,
17
+ status,
18
+ statusText,
19
+ message: `Failed to fetch: ${url} (${status} ${statusText})`,
20
+ })
21
+
1
22
  /**
2
23
  * Fetch text content from a URL
3
24
  * @param url - The URL to fetch from
25
+ * @returns Either with text content on right or FetchError on left
26
+ */
27
+ export const fetchTextEither = async (url: string): Promise<Either.Either<string, FetchError>> => {
28
+ try {
29
+ const response = await fetch(url)
30
+ if (!response.ok) {
31
+ return Either.left(makeFetchError(url, response.status, response.statusText))
32
+ }
33
+ const text = await response.text()
34
+ return Either.right(text)
35
+ } catch (error) {
36
+ // Network errors or other exceptions
37
+ return Either.left(makeFetchError(url, 0, 'Network Error'))
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Fetch text content from a URL (legacy throwing version)
43
+ * @param url - The URL to fetch from
4
44
  * @returns Promise that resolves to the text content
5
45
  * @throws Error if the request fails
46
+ * @deprecated Use fetchTextEither for better error handling
6
47
  */
7
48
  export const fetchText = async (url: string): Promise<string> => {
8
- const response = await fetch(url)
9
- if (!response.ok) {
10
- throw new Error(`Failed to fetch: ${url} (${response.status} ${response.statusText})`)
49
+ const result = await fetchTextEither(url)
50
+ if (Either.isLeft(result)) {
51
+ throw new Error(result.left.message)
11
52
  }
12
- return response.text()
53
+ return result.right
13
54
  }
@@ -4,6 +4,7 @@ import { useLoaderData } from '#lib/react-router-effect/use-loader-data'
4
4
  import { Version } from '#lib/version/$'
5
5
  import { Heading } from '@radix-ui/themes'
6
6
  import { Str } from '@wollybeard/kit'
7
+ import { Array, Option } from 'effect'
7
8
  import { useSearchParams } from 'react-router'
8
9
  import { examplesCatalog } from 'virtual:polen/project/examples'
9
10
  import { schemasCatalog } from 'virtual:polen/project/schemas'
@@ -23,10 +24,11 @@ export const nameLoader = async ({ params }: any) => {
23
24
  }
24
25
 
25
26
  // Check if the example exists
26
- const example = examplesCatalog.examples.find((e: any) => e.name === name)
27
- if (!example) {
27
+ const exampleOption = Array.findFirst(examplesCatalog.examples, (e) => e.name === name)
28
+ if (Option.isNone(exampleOption)) {
28
29
  throw new Response('Not Found', { status: 404 })
29
30
  }
31
+ const example = exampleOption.value
30
32
 
31
33
  return example
32
34
  }
@@ -1,4 +1,4 @@
1
- import { Schema as S } from 'effect'
1
+ import { Duration, Schema as S } from 'effect'
2
2
  import { proxy } from 'valtio'
3
3
 
4
4
  // ============================================================================
@@ -68,8 +68,10 @@ export const initialState: State = {
68
68
  toasts: [],
69
69
  }
70
70
 
71
+ const DEFAULT_TOAST_DURATION = Duration.seconds(5)
72
+
71
73
  const toastDefaults = {
72
- duration: 5000,
74
+ duration: Duration.toMillis(DEFAULT_TOAST_DURATION),
73
75
  actions: [],
74
76
  } satisfies Partial<Toast>
75
77
 
@@ -96,7 +98,8 @@ export const store = proxy({
96
98
 
97
99
  // Auto-remove after duration (default 5 seconds). Set duration to 0 to disable auto-dismiss
98
100
  if (toast.duration !== 0) {
99
- setTimeout(() => store.remove(id), toast.duration || toastDefaults.duration)
101
+ const durationMs = toast.duration || toastDefaults.duration
102
+ setTimeout(() => store.remove(id), durationMs)
100
103
  }
101
104
 
102
105
  return id
@@ -1,2 +0,0 @@
1
- export * from './graph.js';
2
- //# sourceMappingURL=$$.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"$$.d.ts","sourceRoot":"","sources":["../../../src/lib/graph/$$.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA"}
@@ -1,2 +0,0 @@
1
- export * from './graph.js';
2
- //# sourceMappingURL=$$.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"$$.js","sourceRoot":"","sources":["../../../src/lib/graph/$$.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA"}
@@ -1,2 +0,0 @@
1
- export * as Graph from './$$.js';
2
- //# sourceMappingURL=$.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"$.d.ts","sourceRoot":"","sources":["../../../src/lib/graph/$.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,SAAS,CAAA"}
@@ -1,2 +0,0 @@
1
- export * as Graph from './$$.js';
2
- //# sourceMappingURL=$.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"$.js","sourceRoot":"","sources":["../../../src/lib/graph/$.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,SAAS,CAAA"}
@@ -1,127 +0,0 @@
1
- import { S } from '#lib/kit-temp/effect';
2
- /**
3
- * Dependency graph for tracking relationships between nodes
4
- * Used to efficiently handle dependencies in various contexts
5
- */
6
- export declare const DependencyGraph: S.Struct<{
7
- /**
8
- * Map from parent ID to array of child IDs (parent depends on children)
9
- */
10
- dependencies: S.Record$<typeof S.String, S.Array$<typeof S.String>>;
11
- /**
12
- * Map from child ID to array of parent IDs (child is depended on by parents)
13
- */
14
- dependents: S.Record$<typeof S.String, S.Array$<typeof S.String>>;
15
- }>;
16
- export type DependencyGraph = typeof DependencyGraph.Type;
17
- export declare const make: (props: {
18
- readonly dependencies: {
19
- readonly [x: string]: readonly string[];
20
- };
21
- readonly dependents: {
22
- readonly [x: string]: readonly string[];
23
- };
24
- }, options?: S.MakeOptions) => {
25
- readonly dependencies: {
26
- readonly [x: string]: readonly string[];
27
- };
28
- readonly dependents: {
29
- readonly [x: string]: readonly string[];
30
- };
31
- };
32
- /**
33
- * Create an empty dependency graph
34
- */
35
- export declare const create: () => DependencyGraph;
36
- /**
37
- * Add a dependency relationship (immutable)
38
- * @param graph - The dependency graph
39
- * @param parent - The parent node ID
40
- * @param child - The child node ID that the parent depends on
41
- * @returns A new graph with the dependency added
42
- */
43
- export declare const addDependency: (graph: DependencyGraph, parent: string, child: string) => DependencyGraph;
44
- /**
45
- * Add a dependency relationship (mutable)
46
- * @param graph - The dependency graph to mutate
47
- * @param parent - The parent node ID
48
- * @param child - The child node ID that the parent depends on
49
- */
50
- export declare const addDependencyMutable: (graph: DependencyGraph, parent: string, child: string) => void;
51
- /**
52
- * Find all nodes that have no dependencies (leaf nodes)
53
- */
54
- export declare const findLeafNodes: (graph: DependencyGraph) => Set<string>;
55
- /**
56
- * Check if all dependencies of a node have been processed
57
- */
58
- export declare const areDependenciesReady: (node: string, graph: DependencyGraph, processed: Set<string>) => boolean;
59
- /**
60
- * Get topological ordering of nodes (children before parents)
61
- * This ensures we process dependencies before the nodes that depend on them
62
- *
63
- * @param graph - The dependency graph
64
- * @returns Array of node IDs in topological order
65
- */
66
- export declare const topologicalSort: (graph: DependencyGraph) => string[];
67
- export declare const decode: (i: {
68
- readonly dependencies: {
69
- readonly [x: string]: readonly string[];
70
- };
71
- readonly dependents: {
72
- readonly [x: string]: readonly string[];
73
- };
74
- }, overrideOptions?: import("effect/SchemaAST").ParseOptions) => import("effect/Effect").Effect<{
75
- readonly dependencies: {
76
- readonly [x: string]: readonly string[];
77
- };
78
- readonly dependents: {
79
- readonly [x: string]: readonly string[];
80
- };
81
- }, import("effect/ParseResult").ParseError, never>;
82
- export declare const decodeSync: (i: {
83
- readonly dependencies: {
84
- readonly [x: string]: readonly string[];
85
- };
86
- readonly dependents: {
87
- readonly [x: string]: readonly string[];
88
- };
89
- }, overrideOptions?: import("effect/SchemaAST").ParseOptions) => {
90
- readonly dependencies: {
91
- readonly [x: string]: readonly string[];
92
- };
93
- readonly dependents: {
94
- readonly [x: string]: readonly string[];
95
- };
96
- };
97
- export declare const encode: (a: {
98
- readonly dependencies: {
99
- readonly [x: string]: readonly string[];
100
- };
101
- readonly dependents: {
102
- readonly [x: string]: readonly string[];
103
- };
104
- }, overrideOptions?: import("effect/SchemaAST").ParseOptions) => import("effect/Effect").Effect<{
105
- readonly dependencies: {
106
- readonly [x: string]: readonly string[];
107
- };
108
- readonly dependents: {
109
- readonly [x: string]: readonly string[];
110
- };
111
- }, import("effect/ParseResult").ParseError, never>;
112
- export declare const encodeSync: (a: {
113
- readonly dependencies: {
114
- readonly [x: string]: readonly string[];
115
- };
116
- readonly dependents: {
117
- readonly [x: string]: readonly string[];
118
- };
119
- }, overrideOptions?: import("effect/SchemaAST").ParseOptions) => {
120
- readonly dependencies: {
121
- readonly [x: string]: readonly string[];
122
- };
123
- readonly dependents: {
124
- readonly [x: string]: readonly string[];
125
- };
126
- };
127
- //# sourceMappingURL=graph.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"graph.d.ts","sourceRoot":"","sources":["../../../src/lib/graph/graph.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,sBAAsB,CAAA;AAIxC;;;GAGG;AACH,eAAO,MAAM,eAAe;IAC1B;;OAEG;;IAGH;;OAEG;;EAKH,CAAA;AAEF,MAAM,MAAM,eAAe,GAAG,OAAO,eAAe,CAAC,IAAI,CAAA;AAIzD,eAAO,MAAM,IAAI;;;;;;;;;;;;;;CAAuB,CAAA;AAExC;;GAEG;AACH,eAAO,MAAM,MAAM,QAAO,eAItB,CAAA;AAIJ;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,GACxB,OAAO,eAAe,EACtB,QAAQ,MAAM,EACd,OAAO,MAAM,KACZ,eAqBF,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,GAC/B,OAAO,eAAe,EACtB,QAAQ,MAAM,EACd,OAAO,MAAM,KACZ,IAsBF,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,aAAa,GAAI,OAAO,eAAe,KAAG,GAAG,CAAC,MAAM,CAYhE,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,oBAAoB,GAC/B,MAAM,MAAM,EACZ,OAAO,eAAe,EACtB,WAAW,GAAG,CAAC,MAAM,CAAC,KACrB,OAKF,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,GAAI,OAAO,eAAe,KAAG,MAAM,EA2C9D,CAAA;AAID,eAAO,MAAM,MAAM;;;;;;;;;;;;;;kDAA4B,CAAA;AAC/C,eAAO,MAAM,UAAU;;;;;;;;;;;;;;CAAgC,CAAA;AACvD,eAAO,MAAM,MAAM;;;;;;;;;;;;;;kDAA4B,CAAA;AAC/C,eAAO,MAAM,UAAU;;;;;;;;;;;;;;CAAgC,CAAA"}