polen 0.11.0-next.1 → 0.11.0-next.10

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 (123) hide show
  1. package/build/api/config/configurator.d.ts +35 -23
  2. package/build/api/config/configurator.d.ts.map +1 -1
  3. package/build/api/config/configurator.js +0 -6
  4. package/build/api/config/configurator.js.map +1 -1
  5. package/build/api/config/merge.d.ts.map +1 -1
  6. package/build/api/config/merge.js +0 -11
  7. package/build/api/config/merge.js.map +1 -1
  8. package/build/api/content/metadata.d.ts +8 -2
  9. package/build/api/content/metadata.d.ts.map +1 -1
  10. package/build/api/content/metadata.js +1 -1
  11. package/build/api/content/metadata.js.map +1 -1
  12. package/build/api/schema/data-sources/data-sources.d.ts +1 -0
  13. package/build/api/schema/data-sources/data-sources.d.ts.map +1 -1
  14. package/build/api/schema/data-sources/data-sources.js +1 -0
  15. package/build/api/schema/data-sources/data-sources.js.map +1 -1
  16. package/build/api/schema/data-sources/introspection/introspection.d.ts +83 -0
  17. package/build/api/schema/data-sources/introspection/introspection.d.ts.map +1 -0
  18. package/build/api/schema/data-sources/introspection/introspection.js +110 -0
  19. package/build/api/schema/data-sources/introspection/introspection.js.map +1 -0
  20. package/build/api/schema/metadata.d.ts +11 -3
  21. package/build/api/schema/metadata.d.ts.map +1 -1
  22. package/build/api/schema/metadata.js +1 -1
  23. package/build/api/schema/metadata.js.map +1 -1
  24. package/build/api/schema/read.d.ts +83 -9
  25. package/build/api/schema/read.d.ts.map +1 -1
  26. package/build/api/schema/read.js +15 -6
  27. package/build/api/schema/read.js.map +1 -1
  28. package/build/api/schema-source/schema-source.d.ts +1 -1
  29. package/build/api/schema-source/schema-source.d.ts.map +1 -1
  30. package/build/api/schema-source/schema-source.js.map +1 -1
  31. package/build/api/static/manifest.d.ts +1 -2
  32. package/build/api/static/manifest.d.ts.map +1 -1
  33. package/build/api/static/manifest.js +1 -1
  34. package/build/api/static/manifest.js.map +1 -1
  35. package/build/api/vite/plugins/build.d.ts.map +1 -1
  36. package/build/api/vite/plugins/build.js +3 -0
  37. package/build/api/vite/plugins/build.js.map +1 -1
  38. package/build/api/vite/plugins/core.js +8 -8
  39. package/build/api/vite/plugins/core.js.map +1 -1
  40. package/build/api/vite/plugins/main.d.ts.map +1 -1
  41. package/build/api/vite/plugins/main.js +1 -8
  42. package/build/api/vite/plugins/main.js.map +1 -1
  43. package/build/api/vite/plugins/schema-assets.d.ts.map +1 -1
  44. package/build/api/vite/plugins/schema-assets.js +52 -11
  45. package/build/api/vite/plugins/schema-assets.js.map +1 -1
  46. package/build/cli/commands/open.js +1 -1
  47. package/build/cli/commands/open.js.map +1 -1
  48. package/build/lib/grafaid/schema/schema.d.ts +1 -1
  49. package/build/lib/grafaid/schema/schema.d.ts.map +1 -1
  50. package/build/lib/grafaid/schema/schema.js +1 -1
  51. package/build/lib/grafaid/schema/schema.js.map +1 -1
  52. package/build/lib/kit-temp.js +2 -2
  53. package/build/lib/kit-temp.js.map +1 -1
  54. package/build/lib/react-router-aid/react-router-aid.d.ts +5 -3
  55. package/build/lib/react-router-aid/react-router-aid.d.ts.map +1 -1
  56. package/build/lib/react-router-aid/react-router-aid.js +7 -4
  57. package/build/lib/react-router-aid/react-router-aid.js.map +1 -1
  58. package/build/template/routes/changelog.js +2 -2
  59. package/build/template/routes/changelog.js.map +1 -1
  60. package/build/template/routes/index.js +2 -2
  61. package/build/template/routes/index.js.map +1 -1
  62. package/build/template/routes/pages.js +2 -2
  63. package/build/template/routes/pages.js.map +1 -1
  64. package/build/template/routes/reference.d.ts +27 -4
  65. package/build/template/routes/reference.d.ts.map +1 -1
  66. package/build/template/routes/reference.js +81 -20
  67. package/build/template/routes/reference.js.map +1 -1
  68. package/build/template/routes/root.js +3 -3
  69. package/build/template/routes/root.js.map +1 -1
  70. package/build/template/server/ssg/get-route-paths.d.ts.map +1 -1
  71. package/build/template/server/ssg/get-route-paths.js +42 -3
  72. package/build/template/server/ssg/get-route-paths.js.map +1 -1
  73. package/build/template/sources/schema-source.d.ts +1 -1
  74. package/build/template/sources/schema-source.d.ts.map +1 -1
  75. package/build/template/sources/schema-source.js +2 -1
  76. package/build/template/sources/schema-source.js.map +1 -1
  77. package/package.json +16 -17
  78. package/src/api/config/configurator.ts +35 -30
  79. package/src/api/config/merge.ts +0 -16
  80. package/src/api/content/metadata.ts +1 -1
  81. package/src/api/schema/data-sources/data-sources.ts +1 -0
  82. package/src/api/schema/data-sources/introspection/introspection.ts +213 -0
  83. package/src/api/schema/metadata.ts +1 -1
  84. package/src/api/schema/read.ts +107 -16
  85. package/src/api/schema-source/schema-source.ts +3 -3
  86. package/src/api/static/manifest.ts +1 -1
  87. package/src/api/vite/plugins/build.ts +3 -0
  88. package/src/api/vite/plugins/core.ts +8 -8
  89. package/src/api/vite/plugins/main.ts +1 -9
  90. package/src/api/vite/plugins/schema-assets.ts +59 -12
  91. package/src/cli/commands/open.ts +1 -1
  92. package/src/lib/grafaid/schema/schema.ts +1 -0
  93. package/src/lib/kit-temp.ts +2 -2
  94. package/src/lib/mask/$.test.ts +3 -3
  95. package/src/lib/react-router-aid/react-router-aid.ts +12 -6
  96. package/src/template/routes/changelog.tsx +2 -2
  97. package/src/template/routes/index.tsx +2 -2
  98. package/src/template/routes/pages.tsx +2 -2
  99. package/src/template/routes/reference.tsx +88 -20
  100. package/src/template/routes/root.tsx +3 -3
  101. package/src/template/server/ssg/get-route-paths.test.ts +132 -0
  102. package/src/template/server/ssg/get-route-paths.ts +41 -3
  103. package/src/template/sources/schema-source.ts +2 -1
  104. package/build/template/routes/reference.$type.$field.d.ts +0 -5
  105. package/build/template/routes/reference.$type.$field.d.ts.map +0 -1
  106. package/build/template/routes/reference.$type.$field.js +0 -31
  107. package/build/template/routes/reference.$type.$field.js.map +0 -1
  108. package/build/template/routes/reference.$type.d.ts +0 -9
  109. package/build/template/routes/reference.$type.d.ts.map +0 -1
  110. package/build/template/routes/reference.$type.js +0 -25
  111. package/build/template/routes/reference.$type.js.map +0 -1
  112. package/build/template/routes/reference.version.$version.$type.$field.d.ts +0 -6
  113. package/build/template/routes/reference.version.$version.$type.$field.d.ts.map +0 -1
  114. package/build/template/routes/reference.version.$version.$type.$field.js +0 -32
  115. package/build/template/routes/reference.version.$version.$type.$field.js.map +0 -1
  116. package/build/template/routes/reference.version.$version.$type.d.ts +0 -11
  117. package/build/template/routes/reference.version.$version.$type.d.ts.map +0 -1
  118. package/build/template/routes/reference.version.$version.$type.js +0 -26
  119. package/build/template/routes/reference.version.$version.$type.js.map +0 -1
  120. package/src/template/routes/reference.$type.$field.tsx +0 -34
  121. package/src/template/routes/reference.$type.tsx +0 -29
  122. package/src/template/routes/reference.version.$version.$type.$field.tsx +0 -35
  123. package/src/template/routes/reference.version.$version.$type.tsx +0 -30
@@ -5,7 +5,7 @@ export const PolenBuildManifestSchema = z.object({
5
5
  type: z.enum([`ssg`, `ssr`]),
6
6
  version: z.string(),
7
7
  basePath: z.string(),
8
- }).loose()
8
+ })
9
9
 
10
10
  export type PolenBuildManifest = z.infer<typeof PolenBuildManifestSchema>
11
11
 
@@ -75,6 +75,9 @@ export const Build = (config: Config.Config): Vite.Plugin[] => {
75
75
  // @see https://github.com/vitejs/vite/issues/20098
76
76
  ssr: {
77
77
  noExternal: true,
78
+ // Exclude lightningcss from bundling due to native bindings
79
+ // @see https://github.com/parcel-bundler/lightningcss/issues/701
80
+ external: ['lightningcss'],
78
81
  },
79
82
  environments: {
80
83
  ssr: {
@@ -35,15 +35,15 @@ export const Core = (config: Config.Config): Vite.PluginOption[] => {
35
35
 
36
36
  const readSchema = async () => {
37
37
  if (schemaCache === null) {
38
- const schema = await Schema.readOrThrow({
38
+ const schemaResult = await Schema.readOrThrow({
39
39
  ...config.schema,
40
40
  projectRoot: config.paths.project.rootDir,
41
41
  })
42
42
  // todo: augmentations scoped to a version
43
- schema?.forEach(version => {
43
+ schemaResult.data?.forEach(version => {
44
44
  SchemaAugmentation.apply(version.after, config.schemaAugmentations)
45
45
  })
46
- schemaCache = schema
46
+ schemaCache = schemaResult
47
47
  }
48
48
  return schemaCache
49
49
  }
@@ -166,8 +166,8 @@ export const Core = (config: Config.Config): Vite.PluginOption[] => {
166
166
  const debug = debugPolen.sub(`module-project-schema`)
167
167
  debug(`load`, { id: viProjectSchema.id })
168
168
 
169
- const schema = await readSchema()
170
- return superjson.stringify(schema)
169
+ const schemaResult = await readSchema()
170
+ return superjson.stringify(schemaResult.data)
171
171
  },
172
172
  },
173
173
  {
@@ -191,18 +191,18 @@ export const Core = (config: Config.Config): Vite.PluginOption[] => {
191
191
 
192
192
  debug(`load`, { id: viProjectData.id })
193
193
 
194
- const schema = await readSchema()
194
+ const schemaResult = await readSchema()
195
195
 
196
196
  const navbar = []
197
197
 
198
198
  // ━ Schema presence causes adding some navbar items
199
- if (schema) {
199
+ if (schemaResult.data) {
200
200
  // IMPORTANT: Always ensure paths start with '/' for React Router compatibility.
201
201
  // Without the leading slash, React Router treats paths as relative, which causes
202
202
  // hydration mismatches between SSR (where base path is prepended) and client
203
203
  // (where basename is configured). This ensures consistent behavior.
204
204
  navbar.push({ pathExp: `/reference`, title: `Reference` })
205
- if (schema.length > 1) {
205
+ if (schemaResult.data.length > 1) {
206
206
  navbar.push({ pathExp: `/changelog`, title: `Changelog` })
207
207
  }
208
208
  }
@@ -2,9 +2,8 @@ import type { Config } from '#api/config/index'
2
2
  import type { Vite } from '#dep/vite/index'
3
3
  import { vitePluginSsrCss } from '@hiogawa/vite-plugin-ssr-css'
4
4
  import ViteReact from '@vitejs/plugin-react-oxc'
5
- import { Arr, Path } from '@wollybeard/kit'
5
+ import { Path } from '@wollybeard/kit'
6
6
  import Inspect from 'vite-plugin-inspect'
7
- import Restart from 'vite-plugin-restart'
8
7
  import { Branding } from './branding.js'
9
8
  import { Build } from './build.js'
10
9
  import { Core } from './core.js'
@@ -26,13 +25,6 @@ export const Main = (
26
25
  plugins.push(plugin)
27
26
  }
28
27
 
29
- if (Arr.isntEmpty(config.watch.also)) {
30
- const plugin = Restart({
31
- restart: config.watch.also,
32
- })
33
- plugins.push(plugin)
34
- }
35
-
36
28
  plugins.push(
37
29
  ViteReact(),
38
30
  vitePluginSsrCss({
@@ -9,6 +9,7 @@ import { debugPolen } from '#singletons/debug'
9
9
  import { Cache } from '@wollybeard/kit'
10
10
  import * as NodeFs from 'node:fs/promises'
11
11
  import * as NodePath from 'node:path'
12
+ import type { NonEmptyChangeSets } from '../../schema/schema.js'
12
13
  import { polenVirtual } from '../vi.js'
13
14
 
14
15
  export const viProjectSchemaMetadata = polenVirtual([`project`, `schema-metadata`])
@@ -35,27 +36,28 @@ export const SchemaAssets = (config: Config.Config): Vite.Plugin => {
35
36
 
36
37
  // Helper to load and process schema data
37
38
  const loadAndProcessSchemaData = Cache.memoize(async () => {
38
- const schemaData = await Schema.readOrThrow({
39
+ const schemaResult = await Schema.readOrThrow({
39
40
  ...config.schema,
40
41
  projectRoot: config.paths.project.rootDir,
41
42
  })
42
43
 
43
- if (!schemaData) {
44
+ if (!schemaResult.data) {
44
45
  const metadata: Schema.SchemaMetadata = { hasSchema: false, versions: [] }
45
46
  return {
46
47
  schemaData: null,
47
48
  metadata,
49
+ source: schemaResult.source,
48
50
  }
49
51
  }
50
52
 
51
53
  // Apply augmentations
52
- schemaData.forEach(version => {
54
+ schemaResult.data.forEach(version => {
53
55
  SchemaAugmentation.apply(version.after, config.schemaAugmentations)
54
56
  })
55
57
 
56
58
  // Build metadata
57
59
  const versionStrings: string[] = []
58
- for (const [index, version] of schemaData.entries()) {
60
+ for (const [index, version] of schemaResult.data.entries()) {
59
61
  const versionName = index === 0 ? Schema.VERSION_LATEST : Schema.dateToVersionString(version.date)
60
62
  versionStrings.push(versionName)
61
63
  }
@@ -65,11 +67,12 @@ export const SchemaAssets = (config: Config.Config): Vite.Plugin => {
65
67
  versions: versionStrings,
66
68
  }
67
69
 
68
- debug(`schemaDataLoaded`, { versionCount: schemaData.length })
70
+ debug(`schemaDataLoaded`, { versionCount: schemaResult.data.length })
69
71
 
70
72
  return {
71
- schemaData,
73
+ schemaData: schemaResult.data,
72
74
  metadata,
75
+ source: schemaResult.source,
73
76
  }
74
77
  })
75
78
 
@@ -102,10 +105,10 @@ export const SchemaAssets = (config: Config.Config): Vite.Plugin => {
102
105
 
103
106
  // Helper to write assets using schema-source API
104
107
  const writeDevAssets = async (
105
- schemaData: Awaited<ReturnType<typeof Schema.readOrThrow>>,
108
+ schemaData: NonEmptyChangeSets,
106
109
  metadata: Schema.SchemaMetadata,
107
110
  ) => {
108
- if (!schemaData) return
111
+ // schemaData is now guaranteed to be non-null NonEmptyChangeSets
109
112
 
110
113
  const devAssetsDir = config.paths.framework.devAssets.schemas
111
114
  await NodeFs.mkdir(devAssetsDir, { recursive: true })
@@ -149,6 +152,13 @@ export const SchemaAssets = (config: Config.Config): Vite.Plugin => {
149
152
  debug(`watchingSchemaFile`, { path: config.schema.dataSources.file.path })
150
153
  }
151
154
 
155
+ if (config.schema?.dataSources?.introspection?.url) {
156
+ // Watch the introspection file if introspection is configured
157
+ const introspectionFilePath = NodePath.join(config.paths.project.rootDir, `schema.introspection.json`)
158
+ server.watcher.add(introspectionFilePath)
159
+ debug(`watchingIntrospectionFile`, { path: introspectionFilePath })
160
+ }
161
+
152
162
  // Handle file removal
153
163
  server.watcher.on('unlink', async (file) => {
154
164
  const isSchemaFile = config.schema && (() => {
@@ -172,6 +182,15 @@ export const SchemaAssets = (config: Config.Config): Vite.Plugin => {
172
182
  if (absoluteFile.startsWith(absoluteSchemaDir + NodePath.sep)) return true
173
183
  }
174
184
 
185
+ // Check if file is the introspection file
186
+ if (config.schema.dataSources?.introspection?.url) {
187
+ const absoluteIntrospectionFile = NodePath.resolve(
188
+ config.paths.project.rootDir,
189
+ `schema.introspection.json`,
190
+ )
191
+ if (absoluteFile === absoluteIntrospectionFile) return true
192
+ }
193
+
175
194
  return false
176
195
  })()
177
196
 
@@ -181,14 +200,33 @@ export const SchemaAssets = (config: Config.Config): Vite.Plugin => {
181
200
  try {
182
201
  // Clear cache and regenerate
183
202
  loadAndProcessSchemaData.clear()
184
- const { schemaData, metadata } = await loadAndProcessSchemaData()
185
-
186
- if (schemaData) {
203
+ const { schemaData, metadata, source } = await loadAndProcessSchemaData()
204
+
205
+ // If file was deleted but can be recreated, attempt recreation
206
+ if (!schemaData && source.reCreate) {
207
+ debug(`attemptingSchemaRecreation`, { sourceType: source.type })
208
+ try {
209
+ const recreatedData = await source.reCreate()
210
+ if (recreatedData) {
211
+ // Clear cache again and reload after recreation
212
+ loadAndProcessSchemaData.clear()
213
+ const reloadResult = await loadAndProcessSchemaData()
214
+ if (reloadResult.schemaData) {
215
+ await writeDevAssets(reloadResult.schemaData, reloadResult.metadata)
216
+ debug(`hmr:schemaRecreatedAndWritten`, { versionCount: reloadResult.schemaData.length })
217
+ }
218
+ } else {
219
+ debug(`hmr:schemaRecreationFailed`, { reason: 'reCreate returned null' })
220
+ }
221
+ } catch (recreationError) {
222
+ debug(`hmr:schemaRecreationFailed`, { error: recreationError })
223
+ }
224
+ } else if (schemaData) {
187
225
  // Write new assets without the removed file
188
226
  await writeDevAssets(schemaData, metadata)
189
227
  debug(`hmr:schemaAssetsUpdatedAfterRemoval`, { versionCount: schemaData.length })
190
228
  } else {
191
- // No schema data - clear all assets
229
+ // No schema data and cannot recreate - clear all assets
192
230
  const schemaSource = createDevSchemaSource({ hasSchema: false, versions: [] })
193
231
  await schemaSource.clearAllAssets()
194
232
  debug(`hmr:allAssetsCleared`, {})
@@ -274,6 +312,15 @@ export const SchemaAssets = (config: Config.Config): Vite.Plugin => {
274
312
  if (absoluteFile.startsWith(absoluteSchemaDir + NodePath.sep)) return true
275
313
  }
276
314
 
315
+ // Check if file is the introspection file
316
+ if (config.schema.dataSources?.introspection?.url) {
317
+ const absoluteIntrospectionFile = NodePath.resolve(
318
+ config.paths.project.rootDir,
319
+ `schema.introspection.json`,
320
+ )
321
+ if (absoluteFile === absoluteIntrospectionFile) return true
322
+ }
323
+
277
324
  return false
278
325
  })()
279
326
  if (isSchemaFile) {
@@ -132,7 +132,7 @@ const wrapCache = <fn extends Fn.AnyAnyAsync>(fn: fn): fn => {
132
132
 
133
133
  // todo: use a proper validation, e.g. zod, better yet: allow to specify the validation in molt itself
134
134
  const parseHeaders = (headersJsonEncoded: string): Record<string, string> => {
135
- const headersJson = Json.codec.deserialize(headersJsonEncoded)
135
+ const headersJson = Json.codec.decode(headersJsonEncoded)
136
136
  if (!Rec.is(headersJson)) {
137
137
  console.log(`--introspection-headers must be a JSON object.`)
138
138
  process.exit(1)
@@ -4,6 +4,7 @@ export {
4
4
  buildASTSchema as fromAST,
5
5
  buildClientSchema as fromIntrospectionQuery,
6
6
  GraphQLSchema as Schema,
7
+ introspectionFromSchema as toIntrospectionQuery,
7
8
  printSchema as print,
8
9
  } from 'graphql'
9
10
 
@@ -105,9 +105,9 @@ export const objPolicyFilter = <
105
105
  const result: any = mode === `deny` ? { ...obj } : {}
106
106
 
107
107
  if (mode === `allow`) {
108
- // For allow mode, only add specified keys
108
+ // For allow mode, only add specified keys that are own properties
109
109
  for (const key of keys) {
110
- if (key in obj) {
110
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
111
111
  // @ts-expect-error
112
112
  result[key] = obj[key]
113
113
  }
@@ -113,9 +113,9 @@ describe('property-based tests', () => {
113
113
  // Result contains only keys that were in both mask and object
114
114
  expect(resultKeys.every(key => keys.includes(key))).toBe(true)
115
115
 
116
- // All requested keys that exist in obj are in result
116
+ // All requested keys that are own properties of obj are in result
117
117
  keys.forEach(key => {
118
- if (key in obj) {
118
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
119
119
  expect(result).toHaveProperty(key, (obj as any)[key])
120
120
  }
121
121
  })
@@ -169,7 +169,7 @@ describe('property-based tests', () => {
169
169
  const result = Mask.apply(obj as any, Mask.create(keys))
170
170
 
171
171
  keys.forEach(key => {
172
- if (key in obj) {
172
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
173
173
  expect(result).toHaveProperty(key)
174
174
  expect((result as any)[key]).toBe((obj as any)[key])
175
175
  }
@@ -1,4 +1,5 @@
1
1
  import type { ReactRouter } from '#dep/react-router/index'
2
+ import type { React } from '#dep/react/index'
2
3
  import type { Http } from '@wollybeard/kit'
3
4
 
4
5
  export * from './get-paths-patterns.js'
@@ -18,23 +19,28 @@ export interface RouteHandle {
18
19
  statusCode?: Http.Status.Code.All
19
20
  }
20
21
 
21
- export const createRoute = <routeObject extends RouteObject>(
22
+ export const route = <routeObject extends RouteObject>(
22
23
  routeObject: routeObject,
23
24
  ): routeObject => {
24
25
  return {
25
- id: routeObject.path,
26
26
  ...routeObject,
27
27
  }
28
28
  }
29
29
 
30
- export type RouteObjectIndexInput = Omit<RouteObjectIndex, `index` | `children`>
30
+ export type RouteObjectIndexInput = RouteObjectIndexInputConfig | React.ComponentType
31
+ export type RouteObjectIndexInputConfig = Omit<RouteObjectIndex, `index` | `children`>
31
32
 
32
- export const createRouteIndex = (
33
- indexRouteObjectInput: RouteObjectIndexInput,
33
+ export const routeIndex = (
34
+ input: RouteObjectIndexInput,
34
35
  ): RouteObjectIndex => {
36
+ const routeConfig: RouteObjectIndexInputConfig = isComponentType(input) ? { Component: input } : input
35
37
  return {
36
- ...indexRouteObjectInput,
38
+ ...routeConfig,
37
39
  index: true,
38
40
  children: undefined,
39
41
  }
40
42
  }
43
+
44
+ const isComponentType = (value: unknown): value is React.ComponentType => {
45
+ return typeof value === 'function'
46
+ }
@@ -1,5 +1,5 @@
1
1
  import type { GraphqlChangeset } from '#lib/graphql-changeset/index'
2
- import { createRoute } from '#lib/react-router-aid/react-router-aid'
2
+ import { route } from '#lib/react-router-aid/react-router-aid'
3
3
  import { createLoader, useLoaderData } from '#lib/react-router-loader/react-router-loader'
4
4
  import { Changelog } from '../components/Changelog.js'
5
5
  import { ChangelogLayout } from '../layouts/index.js'
@@ -33,7 +33,7 @@ const Component = () => {
33
33
  )
34
34
  }
35
35
 
36
- export const changelog = createRoute({
36
+ export const changelog = route({
37
37
  path: `changelog`,
38
38
  loader,
39
39
  Component,
@@ -1,10 +1,10 @@
1
- import { createRouteIndex } from '#lib/react-router-aid/react-router-aid'
1
+ import { routeIndex } from '#lib/react-router-aid/react-router-aid'
2
2
  import { Box } from '@radix-ui/themes'
3
3
 
4
4
  const Component = () => {
5
5
  return <Box>home todo</Box>
6
6
  }
7
7
 
8
- export const index = createRouteIndex({
8
+ export const index = routeIndex({
9
9
  Component,
10
10
  })
@@ -1,4 +1,4 @@
1
- import { createRoute } from '#lib/react-router-aid/react-router-aid'
1
+ import { route } from '#lib/react-router-aid/react-router-aid'
2
2
  import { createLoader, useLoaderData } from '#lib/react-router-loader/react-router-loader'
3
3
  import { SidebarLayout } from '#template/layouts/index'
4
4
  import { MDXProvider } from '@mdx-js/react'
@@ -101,7 +101,7 @@ const Component = () => {
101
101
  )
102
102
  }
103
103
 
104
- export const pages = createRoute({
104
+ export const pages = route({
105
105
  // Pathless layout route - doesn't affect URL paths
106
106
  loader,
107
107
  Component,
@@ -1,19 +1,19 @@
1
1
  import type { Content } from '#api/content/$'
2
2
  import { GrafaidOld } from '#lib/grafaid-old/index'
3
- import { createRoute } from '#lib/react-router-aid/react-router-aid'
3
+ import { Grafaid } from '#lib/grafaid/index'
4
+ import { route, routeIndex } from '#lib/react-router-aid/react-router-aid'
4
5
  import { createLoader, useLoaderData } from '#lib/react-router-loader/react-router-loader'
5
6
  import { Box } from '@radix-ui/themes'
6
- import { Outlet } from 'react-router'
7
+ import { Outlet, useParams } from 'react-router'
8
+ import { Field } from '../components/Field.js'
7
9
  import { MissingSchema } from '../components/MissingSchema.js'
10
+ import { NamedType } from '../components/NamedType.js'
8
11
  import { VersionSelector } from '../components/VersionSelector.js'
9
- import { useVersionPath } from '../hooks/useVersionPath.js'
10
12
  import { SidebarLayout } from '../layouts/index.js'
11
13
  import { VERSION_LATEST } from '../lib/schema-utils/constants.js'
12
14
  import { schemaSource } from '../sources/schema-source.js'
13
- import { reference$type } from './reference.$type.js'
14
- import { referenceVersion$version$type } from './reference.version.$version.$type.js'
15
15
 
16
- const loader = createLoader(async ({ params }) => {
16
+ export const loader = createLoader(async ({ params }) => {
17
17
  // Handle both versioned and unversioned routes:
18
18
  // - Versioned: /reference/version/:version/:type → params.version exists
19
19
  // - Unversioned: /reference/:type → params.version is undefined, defaults to latest
@@ -29,7 +29,7 @@ const loader = createLoader(async ({ params }) => {
29
29
  }
30
30
  })
31
31
 
32
- const Component = () => {
32
+ const RouteReferenceComponent = () => {
33
33
  const data = useLoaderData<typeof loader>()
34
34
 
35
35
  if (!data.schema) {
@@ -42,8 +42,11 @@ const Component = () => {
42
42
  const sidebarItems: Content.Item[] = []
43
43
  const kindEntries = Object.entries(kindMap.list).filter(([_, types]) => types.length > 0)
44
44
 
45
- // Build path prefix based on current version using new route structure
46
- const versionPath = useVersionPath()
45
+ // Build path prefix based on current version from loader data
46
+ // This ensures sidebar links always match the current version being viewed
47
+ const versionPath = data.currentVersion === VERSION_LATEST
48
+ ? ``
49
+ : `version/${data.currentVersion}/`
47
50
 
48
51
  for (const [title, types] of kindEntries) {
49
52
  sidebarItems.push({
@@ -72,18 +75,83 @@ const Component = () => {
72
75
  )
73
76
  }
74
77
 
75
- // Create the versioned reference route with explicit version prefix
76
- const referenceVersioned = createRoute({
77
- path: `version/:version`,
78
- loader,
79
- Component,
80
- children: [referenceVersion$version$type],
81
- })
78
+ // Shared hooks for schema data validation and retrieval
79
+ const useReferenceSchema = () => {
80
+ const data = useLoaderData<typeof loader>('reference')
81
+ if (!data?.schema) {
82
+ throw new Error('Schema not found')
83
+ }
84
+ return data
85
+ }
86
+
87
+ const useSchemaType = (typeName: string) => {
88
+ const { schema } = useReferenceSchema()
89
+ const type = schema.getType(typeName)
90
+ if (!type) {
91
+ throw new Error(`Could not find type ${typeName}`)
92
+ }
93
+ return type
94
+ }
95
+
96
+ const useSchemaField = (typeName: string, fieldName: string) => {
97
+ const type = useSchemaType(typeName)
98
+ if (!Grafaid.Schema.TypesLike.isFielded(type)) {
99
+ throw new Error(`Type ${typeName} does not have fields`)
100
+ }
101
+
102
+ const fields = type.getFields()
103
+ const field = fields[fieldName]
104
+ if (!field) {
105
+ throw new Error(`Could not find field ${fieldName} on type ${typeName}`)
106
+ }
107
+ return field
108
+ }
109
+
110
+ const RouteComponentIndex = () => {
111
+ return <div>Select a type from the sidebar to view its documentation.</div>
112
+ }
113
+
114
+ const RouteComponentType = () => {
115
+ const params = useParams() as { type: string }
116
+ const type = useSchemaType(params.type)
117
+ return <NamedType data={type} />
118
+ }
119
+
120
+ const RouteComponentTypeField = () => {
121
+ const params = useParams() as { type: string; field: string }
122
+ const field = useSchemaField(params.type, params.field)
123
+ return <Field data={field} />
124
+ }
125
+
126
+ const typeAndFieldRoutes = [
127
+ routeIndex(RouteComponentIndex),
128
+ route({
129
+ path: `:type`,
130
+ Component: RouteComponentType,
131
+ errorElement: <MissingSchema />,
132
+ children: [
133
+ route({
134
+ path: `:field`,
135
+ Component: RouteComponentTypeField,
136
+ errorElement: <MissingSchema />,
137
+ }),
138
+ ],
139
+ }),
140
+ ]
82
141
 
83
- // Create the main reference route with explicit version path and fallback type path
84
- export const reference = createRoute({
142
+ /**
143
+ * Reference documentation with proper nested structure - all routes in one file
144
+ */
145
+ export const reference = route({
146
+ id: 'reference',
85
147
  path: `reference`,
86
148
  loader,
87
- Component,
88
- children: [referenceVersioned, reference$type],
149
+ Component: RouteReferenceComponent,
150
+ children: [
151
+ ...typeAndFieldRoutes,
152
+ route({
153
+ path: `version/:version`,
154
+ children: typeAndFieldRoutes,
155
+ }),
156
+ ],
89
157
  })
@@ -1,5 +1,5 @@
1
1
  import type { ReactRouter } from '#dep/react-router/index'
2
- import { createRoute } from '#lib/react-router-aid/react-router-aid'
2
+ import { route } from '#lib/react-router-aid/react-router-aid'
3
3
  import { Box, Flex, Theme } from '@radix-ui/themes'
4
4
  import { Link as LinkReactRouter } from 'react-router'
5
5
  import { Outlet, ScrollRestoration } from 'react-router'
@@ -105,7 +105,7 @@ if (PROJECT_SCHEMA) {
105
105
  //
106
106
  //
107
107
 
108
- const notFoundRoute = createRoute({
108
+ const notFoundRoute = route({
109
109
  id: `*_not_found`,
110
110
  path: `*`,
111
111
  Component: NotFound,
@@ -122,7 +122,7 @@ children.push(notFoundRoute)
122
122
  //
123
123
  //
124
124
 
125
- export const root = createRoute({
125
+ export const root = route({
126
126
  path: `/`,
127
127
  Component,
128
128
  children,