one 1.10.1 → 1.10.2

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 (177) hide show
  1. package/devtools/source-inspector.mjs +59 -15
  2. package/dist/cjs/cli/build.cjs +3 -5
  3. package/dist/cjs/cli/build.js +3 -5
  4. package/dist/cjs/cli/build.js.map +1 -1
  5. package/dist/cjs/cli/build.native.js +3 -5
  6. package/dist/cjs/cli/build.native.js.map +1 -1
  7. package/dist/cjs/cli/buildPage.cjs +8 -8
  8. package/dist/cjs/cli/buildPage.js +5 -6
  9. package/dist/cjs/cli/buildPage.js.map +1 -1
  10. package/dist/cjs/cli/buildPage.native.js +6 -8
  11. package/dist/cjs/cli/buildPage.native.js.map +1 -1
  12. package/dist/cjs/cli/daemon.cjs +2 -4
  13. package/dist/cjs/cli/daemon.js +2 -2
  14. package/dist/cjs/cli/daemon.js.map +1 -1
  15. package/dist/cjs/cli/daemon.native.js +2 -4
  16. package/dist/cjs/cli/daemon.native.js.map +1 -1
  17. package/dist/cjs/createHandleRequest.cjs +2 -2
  18. package/dist/cjs/createHandleRequest.js +2 -2
  19. package/dist/cjs/createHandleRequest.js.map +1 -1
  20. package/dist/cjs/createHandleRequest.native.js +2 -2
  21. package/dist/cjs/createHandleRequest.native.js.map +1 -1
  22. package/dist/cjs/daemon/server.cjs +8 -14
  23. package/dist/cjs/daemon/server.js +8 -14
  24. package/dist/cjs/daemon/server.js.map +1 -1
  25. package/dist/cjs/daemon/server.native.js +10 -16
  26. package/dist/cjs/daemon/server.native.js.map +1 -1
  27. package/dist/cjs/daemon/tui.cjs +7 -10
  28. package/dist/cjs/daemon/tui.js +12 -24
  29. package/dist/cjs/daemon/tui.js.map +1 -1
  30. package/dist/cjs/daemon/tui.native.js +5 -7
  31. package/dist/cjs/daemon/tui.native.js.map +1 -1
  32. package/dist/cjs/router/getRoutes.cjs +2 -2
  33. package/dist/cjs/router/getRoutes.js +2 -2
  34. package/dist/cjs/router/getRoutes.js.map +1 -1
  35. package/dist/cjs/router/getRoutes.native.js +2 -2
  36. package/dist/cjs/router/getRoutes.native.js.map +1 -1
  37. package/dist/cjs/router/interceptRoutes.cjs +5 -8
  38. package/dist/cjs/router/interceptRoutes.js +5 -9
  39. package/dist/cjs/router/interceptRoutes.js.map +1 -1
  40. package/dist/cjs/router/interceptRoutes.native.js +5 -8
  41. package/dist/cjs/router/interceptRoutes.native.js.map +1 -1
  42. package/dist/cjs/router/router.js.map +1 -1
  43. package/dist/cjs/router/router.native.js.map +1 -1
  44. package/dist/cjs/server/oneServe.cjs +41 -57
  45. package/dist/cjs/server/oneServe.js +41 -45
  46. package/dist/cjs/server/oneServe.js.map +1 -1
  47. package/dist/cjs/server/oneServe.native.js +54 -73
  48. package/dist/cjs/server/oneServe.native.js.map +1 -1
  49. package/dist/cjs/useLoader.cjs +5 -10
  50. package/dist/cjs/useLoader.js +10 -16
  51. package/dist/cjs/useLoader.js.map +1 -1
  52. package/dist/cjs/useLoader.native.js +5 -10
  53. package/dist/cjs/useLoader.native.js.map +1 -1
  54. package/dist/cjs/views/Navigator.cjs +1 -1
  55. package/dist/cjs/views/Navigator.js +1 -1
  56. package/dist/cjs/views/Navigator.js.map +1 -1
  57. package/dist/cjs/views/Navigator.native.js +1 -3
  58. package/dist/cjs/views/Navigator.native.js.map +1 -1
  59. package/dist/cjs/vite/one.cjs +1 -19
  60. package/dist/cjs/vite/one.js +1 -20
  61. package/dist/cjs/vite/one.js.map +1 -1
  62. package/dist/cjs/vite/one.native.js +1 -17
  63. package/dist/cjs/vite/one.native.js.map +1 -1
  64. package/dist/cjs/vite/plugins/fileSystemRouterPlugin.cjs +2 -7
  65. package/dist/cjs/vite/plugins/fileSystemRouterPlugin.js +3 -7
  66. package/dist/cjs/vite/plugins/fileSystemRouterPlugin.js.map +1 -1
  67. package/dist/cjs/vite/plugins/fileSystemRouterPlugin.native.js +2 -44
  68. package/dist/cjs/vite/plugins/fileSystemRouterPlugin.native.js.map +1 -1
  69. package/dist/esm/cli/build.js +3 -5
  70. package/dist/esm/cli/build.js.map +1 -1
  71. package/dist/esm/cli/build.mjs +3 -5
  72. package/dist/esm/cli/build.mjs.map +1 -1
  73. package/dist/esm/cli/build.native.js +3 -5
  74. package/dist/esm/cli/build.native.js.map +1 -1
  75. package/dist/esm/cli/buildPage.js +5 -6
  76. package/dist/esm/cli/buildPage.js.map +1 -1
  77. package/dist/esm/cli/buildPage.mjs +8 -8
  78. package/dist/esm/cli/buildPage.mjs.map +1 -1
  79. package/dist/esm/cli/buildPage.native.js +6 -8
  80. package/dist/esm/cli/buildPage.native.js.map +1 -1
  81. package/dist/esm/cli/daemon.js +2 -2
  82. package/dist/esm/cli/daemon.js.map +1 -1
  83. package/dist/esm/cli/daemon.mjs +2 -4
  84. package/dist/esm/cli/daemon.mjs.map +1 -1
  85. package/dist/esm/cli/daemon.native.js +2 -4
  86. package/dist/esm/cli/daemon.native.js.map +1 -1
  87. package/dist/esm/createHandleRequest.js +2 -2
  88. package/dist/esm/createHandleRequest.js.map +1 -1
  89. package/dist/esm/createHandleRequest.mjs +2 -2
  90. package/dist/esm/createHandleRequest.mjs.map +1 -1
  91. package/dist/esm/createHandleRequest.native.js +2 -2
  92. package/dist/esm/createHandleRequest.native.js.map +1 -1
  93. package/dist/esm/daemon/server.js +8 -14
  94. package/dist/esm/daemon/server.js.map +1 -1
  95. package/dist/esm/daemon/server.mjs +8 -14
  96. package/dist/esm/daemon/server.mjs.map +1 -1
  97. package/dist/esm/daemon/server.native.js +10 -16
  98. package/dist/esm/daemon/server.native.js.map +1 -1
  99. package/dist/esm/daemon/tui.js +12 -24
  100. package/dist/esm/daemon/tui.js.map +1 -1
  101. package/dist/esm/daemon/tui.mjs +7 -10
  102. package/dist/esm/daemon/tui.mjs.map +1 -1
  103. package/dist/esm/daemon/tui.native.js +6 -8
  104. package/dist/esm/daemon/tui.native.js.map +1 -1
  105. package/dist/esm/router/getRoutes.js +2 -2
  106. package/dist/esm/router/getRoutes.js.map +1 -1
  107. package/dist/esm/router/getRoutes.mjs +2 -2
  108. package/dist/esm/router/getRoutes.mjs.map +1 -1
  109. package/dist/esm/router/getRoutes.native.js +2 -2
  110. package/dist/esm/router/getRoutes.native.js.map +1 -1
  111. package/dist/esm/router/interceptRoutes.js +5 -9
  112. package/dist/esm/router/interceptRoutes.js.map +1 -1
  113. package/dist/esm/router/interceptRoutes.mjs +6 -8
  114. package/dist/esm/router/interceptRoutes.mjs.map +1 -1
  115. package/dist/esm/router/interceptRoutes.native.js +6 -8
  116. package/dist/esm/router/interceptRoutes.native.js.map +1 -1
  117. package/dist/esm/router/router.js.map +1 -1
  118. package/dist/esm/router/router.mjs.map +1 -1
  119. package/dist/esm/router/router.native.js.map +1 -1
  120. package/dist/esm/server/oneServe.js +41 -45
  121. package/dist/esm/server/oneServe.js.map +1 -1
  122. package/dist/esm/server/oneServe.mjs +41 -57
  123. package/dist/esm/server/oneServe.mjs.map +1 -1
  124. package/dist/esm/server/oneServe.native.js +54 -73
  125. package/dist/esm/server/oneServe.native.js.map +1 -1
  126. package/dist/esm/useLoader.js +10 -16
  127. package/dist/esm/useLoader.js.map +1 -1
  128. package/dist/esm/useLoader.mjs +5 -10
  129. package/dist/esm/useLoader.mjs.map +1 -1
  130. package/dist/esm/useLoader.native.js +5 -10
  131. package/dist/esm/useLoader.native.js.map +1 -1
  132. package/dist/esm/views/Navigator.js +1 -1
  133. package/dist/esm/views/Navigator.js.map +1 -1
  134. package/dist/esm/views/Navigator.mjs +1 -1
  135. package/dist/esm/views/Navigator.mjs.map +1 -1
  136. package/dist/esm/views/Navigator.native.js +1 -3
  137. package/dist/esm/views/Navigator.native.js.map +1 -1
  138. package/dist/esm/vite/one.js +1 -20
  139. package/dist/esm/vite/one.js.map +1 -1
  140. package/dist/esm/vite/one.mjs +1 -19
  141. package/dist/esm/vite/one.mjs.map +1 -1
  142. package/dist/esm/vite/one.native.js +1 -17
  143. package/dist/esm/vite/one.native.js.map +1 -1
  144. package/dist/esm/vite/plugins/fileSystemRouterPlugin.js +3 -7
  145. package/dist/esm/vite/plugins/fileSystemRouterPlugin.js.map +1 -1
  146. package/dist/esm/vite/plugins/fileSystemRouterPlugin.mjs +2 -7
  147. package/dist/esm/vite/plugins/fileSystemRouterPlugin.mjs.map +1 -1
  148. package/dist/esm/vite/plugins/fileSystemRouterPlugin.native.js +2 -44
  149. package/dist/esm/vite/plugins/fileSystemRouterPlugin.native.js.map +1 -1
  150. package/package.json +9 -9
  151. package/src/cli/build.ts +3 -20
  152. package/src/cli/buildPage.ts +5 -7
  153. package/src/cli/daemon.ts +2 -2
  154. package/src/createHandleRequest.ts +2 -1
  155. package/src/daemon/server.ts +11 -23
  156. package/src/daemon/tui.ts +11 -29
  157. package/src/router/getRoutes.ts +3 -4
  158. package/src/router/interceptRoutes.ts +5 -30
  159. package/src/router/router.ts +0 -3
  160. package/src/server/oneServe.ts +67 -108
  161. package/src/useLoader.ts +17 -27
  162. package/src/views/Navigator.tsx +1 -3
  163. package/src/vite/one.ts +2 -100
  164. package/src/vite/plugins/fileSystemRouterPlugin.tsx +3 -50
  165. package/types/cli/build.d.ts.map +1 -1
  166. package/types/createHandleRequest.d.ts.map +1 -1
  167. package/types/daemon/server.d.ts.map +1 -1
  168. package/types/daemon/tui.d.ts.map +1 -1
  169. package/types/router/getRoutes.d.ts.map +1 -1
  170. package/types/router/interceptRoutes.d.ts +0 -1
  171. package/types/router/interceptRoutes.d.ts.map +1 -1
  172. package/types/router/router.d.ts.map +1 -1
  173. package/types/server/oneServe.d.ts.map +1 -1
  174. package/types/useLoader.d.ts.map +1 -1
  175. package/types/views/Navigator.d.ts.map +1 -1
  176. package/types/vite/one.d.ts.map +1 -1
  177. package/types/vite/plugins/fileSystemRouterPlugin.d.ts.map +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "one",
3
- "version": "1.10.1",
3
+ "version": "1.10.2",
4
4
  "license": "BSD-3-Clause",
5
5
  "sideEffects": [
6
6
  "setup.mjs",
@@ -154,17 +154,17 @@
154
154
  "@react-navigation/routers": "~7.5.1",
155
155
  "@swc/core": "^1.14.0",
156
156
  "@ungap/structured-clone": "^1.2.0",
157
- "@vxrn/color-scheme": "1.10.1",
158
- "@vxrn/compiler": "1.10.1",
159
- "@vxrn/resolve": "1.10.1",
160
- "@vxrn/tslib-lite": "1.10.1",
161
- "@vxrn/use-isomorphic-layout-effect": "1.10.1",
162
- "@vxrn/vite-plugin-metro": "1.10.1",
157
+ "@vxrn/color-scheme": "1.10.2",
158
+ "@vxrn/compiler": "1.10.2",
159
+ "@vxrn/resolve": "1.10.2",
160
+ "@vxrn/tslib-lite": "1.10.2",
161
+ "@vxrn/use-isomorphic-layout-effect": "1.10.2",
162
+ "@vxrn/vite-plugin-metro": "1.10.2",
163
163
  "babel-dead-code-elimination": "1.0.10",
164
164
  "babel-plugin-module-resolver": "^5.0.2",
165
165
  "citty": "^0.1.6",
166
166
  "core-js": "^3.38.1",
167
- "create-vxrn": "1.10.1",
167
+ "create-vxrn": "1.10.2",
168
168
  "escape-string-regexp": "^5.0.0",
169
169
  "expo-linking": "~8.0.8",
170
170
  "expo-modules-core": "~3.0.24",
@@ -189,7 +189,7 @@
189
189
  "use-latest-callback": "^0.2.3",
190
190
  "vite": "^7.1.12",
191
191
  "vite-tsconfig-paths": "^6.0.5",
192
- "vxrn": "1.10.1",
192
+ "vxrn": "1.10.2",
193
193
  "ws": "^8.18.0",
194
194
  "xxhashjs": "^0.2.2"
195
195
  },
package/src/cli/build.ts CHANGED
@@ -9,7 +9,6 @@ import {
9
9
  type ClientManifestEntry,
10
10
  fillOptions,
11
11
  getOptimizeDeps,
12
- loadEnv,
13
12
  rollupRemoveUnusedImportsPlugin,
14
13
  build as vxrnBuild,
15
14
  } from 'vxrn'
@@ -150,9 +149,7 @@ export async function build(args: {
150
149
  // }) as any,
151
150
  // ],
152
151
 
153
- define: {
154
- ...vxrnOutput!.processEnvDefines,
155
- },
152
+ define: vxrnOutput!.processEnvDefines,
156
153
 
157
154
  ssr: {
158
155
  noExternal: true,
@@ -183,7 +180,7 @@ export async function build(args: {
183
180
  // prevents it from shaking out the exports
184
181
  preserveEntrySignatures: 'strict',
185
182
  input: input,
186
- external: (id) => false,
183
+ external: () => false,
187
184
  output: {
188
185
  entryFileNames: '[name]',
189
186
  exports: 'auto',
@@ -960,7 +957,7 @@ export async function build(args: {
960
957
 
961
958
  // Generate lazy imports for middlewares
962
959
  // The key must match the contextKey used to look up the middleware (e.g., "dist/middlewares/_middleware.js")
963
- for (const [middlewareFile, builtPath] of Object.entries(builtMiddlewares)) {
960
+ for (const [, builtPath] of Object.entries(builtMiddlewares)) {
964
961
  const importPath = './' + builtPath.replace(/^dist\//, '')
965
962
  middlewareRouteMap.push(` '${builtPath}': () => import('${importPath}')`)
966
963
  }
@@ -1160,17 +1157,3 @@ async function moveAllFiles(src: string, dest: string) {
1160
1157
  console.error('Error moving files:', err)
1161
1158
  }
1162
1159
  }
1163
-
1164
- function escapeRegex(string: string) {
1165
- return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') // $& means the whole matched string
1166
- }
1167
-
1168
- function buildRegexExcludingDeps(deps: string[]) {
1169
- // Sanitize each dependency
1170
- const sanitizedDeps = deps.map((dep) => escapeRegex(dep))
1171
- // Join them with the OR operator |
1172
- const exclusionPattern = sanitizedDeps.join('|')
1173
- // Build the final regex pattern
1174
- const regexPattern = `node_modules/(?!(${exclusionPattern})).*`
1175
- return new RegExp(regexPattern)
1176
- }
@@ -1,7 +1,7 @@
1
1
  import { join } from 'node:path'
2
2
  import FSExtra from 'fs-extra'
3
3
  import * as constants from '../constants'
4
- import type { LoaderProps, RenderApp } from '../types'
4
+ import type { LoaderProps } from '../types'
5
5
  import { getLoaderPath, getPreloadCSSPath, getPreloadPath } from '../utils/cleanUrl'
6
6
  import { isResponse } from '../utils/isResponse'
7
7
  import { toAbsolute } from '../utils/toAbsolute'
@@ -28,8 +28,8 @@ export function printBuildTimings() {
28
28
  if (!buildTiming) return
29
29
  console.info('\n📊 Build timing breakdown:')
30
30
  for (const [label, times] of Object.entries(timings)) {
31
- const avg = times.reduce((a, b) => a + b, 0) / times.length
32
31
  const total = times.reduce((a, b) => a + b, 0)
32
+ const avg = total / times.length
33
33
  console.info(
34
34
  ` ${label}: ${avg.toFixed(1)}ms avg, ${total.toFixed(0)}ms total (${times.length} calls)`
35
35
  )
@@ -451,12 +451,10 @@ params:\n\n${JSON.stringify(params || null, null, 2)}`
451
451
  }
452
452
 
453
453
  async function getRender(serverEntry: string) {
454
- let render: RenderApp | null = null
455
-
456
454
  try {
457
455
  const serverImport = await import(serverEntry)
458
456
 
459
- render =
457
+ const render =
460
458
  serverImport.default.render ||
461
459
  // for an unknown reason this is necessary
462
460
  serverImport.default.default?.render
@@ -464,6 +462,8 @@ async function getRender(serverEntry: string) {
464
462
  if (typeof render !== 'function') {
465
463
  throw new Error(`didn't find render function in entry: ${serverEntry}`)
466
464
  }
465
+
466
+ return render
467
467
  } catch (err) {
468
468
  console.error(`❌ Error importing the root entry:`)
469
469
  console.error(` This error happened in the built file: ${serverEntry}`)
@@ -471,8 +471,6 @@ async function getRender(serverEntry: string) {
471
471
  console.error(err['stack'])
472
472
  throw err
473
473
  }
474
-
475
- return render
476
474
  }
477
475
 
478
476
  function removeTrailingSlash(path: string) {
package/src/cli/daemon.ts CHANGED
@@ -67,7 +67,7 @@ async function daemonStart(args: { port?: string; host?: string; tui?: boolean }
67
67
  }
68
68
 
69
69
  async function daemonStop() {
70
- const { isDaemonRunning, getSocketPath, cleanupSocket } = await import('../daemon/ipc')
70
+ const { isDaemonRunning, getSocketPath } = await import('../daemon/ipc')
71
71
 
72
72
  if (!(await isDaemonRunning())) {
73
73
  console.log(colors.yellow('Daemon is not running'))
@@ -182,7 +182,7 @@ export async function openPlatform(platform: 'ios' | 'android') {
182
182
  }
183
183
 
184
184
  async function daemonRoute(args: { app?: string; slot?: string; project?: string }) {
185
- const { isDaemonRunning, getDaemonStatus, setDaemonRoute, clearDaemonRoute } =
185
+ const { isDaemonRunning, getDaemonStatus, setDaemonRoute } =
186
186
  await import('../daemon/ipc')
187
187
 
188
188
  if (!(await isDaemonRunning())) {
@@ -305,6 +305,7 @@ export function createHandleRequest(
305
305
  pathname.startsWith('/@vite/') ||
306
306
  pathname.startsWith('/@fs/') ||
307
307
  pathname.startsWith('/@id/') ||
308
+ pathname.startsWith('/node_modules/') ||
308
309
  pathname.startsWith('/debugger-frontend') ||
309
310
  pathname.startsWith('/inspector')
310
311
  ) {
@@ -403,7 +404,7 @@ function getLoaderParams(
403
404
  config: { compiledRegex: RegExp; routeKeys: Record<string, string> }
404
405
  ) {
405
406
  const params: Record<string, string> = {}
406
- const match = new RegExp(config.compiledRegex).exec(url.pathname)
407
+ const match = config.compiledRegex.exec(url.pathname)
407
408
  if (match?.groups) {
408
409
  for (const [key, value] of Object.entries(match.groups)) {
409
410
  const namedKey = config.routeKeys[key]
@@ -31,29 +31,17 @@ async function getAppNameForServer(root: string): Promise<string | null> {
31
31
  }
32
32
  }
33
33
 
34
- // try app.config.ts - execute it to get the config
35
- const appConfigTsPath = path.join(root, 'app.config.ts')
36
- if (fs.existsSync(appConfigTsPath)) {
37
- // read the file and look for name pattern
38
- const content = fs.readFileSync(appConfigTsPath, 'utf-8')
39
- // simple regex to find name: "..." or name: '...'
40
- const nameMatch = content.match(/name:\s*['"]([^'"]+)['"]/)
41
- if (nameMatch) {
42
- const name = nameMatch[1]
43
- serverAppNames.set(root, name)
44
- return name
45
- }
46
- }
47
-
48
- // try app.config.js
49
- const appConfigJsPath = path.join(root, 'app.config.js')
50
- if (fs.existsSync(appConfigJsPath)) {
51
- const content = fs.readFileSync(appConfigJsPath, 'utf-8')
52
- const nameMatch = content.match(/name:\s*['"]([^'"]+)['"]/)
53
- if (nameMatch) {
54
- const name = nameMatch[1]
55
- serverAppNames.set(root, name)
56
- return name
34
+ // try app.config.ts / app.config.js - read and regex for name pattern
35
+ for (const ext of ['ts', 'js']) {
36
+ const configPath = path.join(root, `app.config.${ext}`)
37
+ if (fs.existsSync(configPath)) {
38
+ const content = fs.readFileSync(configPath, 'utf-8')
39
+ const nameMatch = content.match(/name:\s*['"]([^'"]+)['"]/)
40
+ if (nameMatch) {
41
+ const name = nameMatch[1]
42
+ serverAppNames.set(root, name)
43
+ return name
44
+ }
57
45
  }
58
46
  }
59
47
 
package/src/daemon/tui.ts CHANGED
@@ -52,6 +52,14 @@ interface TUIState {
52
52
  popup: { message: string; timeout: NodeJS.Timeout } | null
53
53
  }
54
54
 
55
+ const CABLE_COLORS = [
56
+ colors.green,
57
+ colors.cyan,
58
+ colors.magenta,
59
+ colors.blue,
60
+ colors.yellow,
61
+ ]
62
+
55
63
  const ESC = '\x1b'
56
64
  const CSI = `${ESC}[`
57
65
 
@@ -447,8 +455,6 @@ async function refreshData(): Promise<void> {
447
455
  tuiState.simulators = newSims
448
456
  tuiState.servers = newServers
449
457
 
450
- const isDragging = tuiState.draggingSimIndex !== null
451
-
452
458
  // get actual simulator -> server mappings from routing state
453
459
  const simMappings = getSimulatorMappings()
454
460
 
@@ -558,15 +564,7 @@ function render(): void {
558
564
  const isSelected = tuiState.selectedCol === 0 && tuiState.selectedRow === row
559
565
  const cable = tuiState.cables.get(row)
560
566
  const hasConnection = cable?.serverIndex !== null
561
- // unique color per cable
562
- const cableColors = [
563
- colors.green,
564
- colors.cyan,
565
- colors.magenta,
566
- colors.blue,
567
- colors.yellow,
568
- ]
569
- const cableColor = cableColors[row % cableColors.length]
567
+ const cableColor = CABLE_COLORS[row % CABLE_COLORS.length]
570
568
  const plug = hasConnection ? cableColor('●') : colors.dim('○')
571
569
  const name = truncate(sim.name, simEndX - 5)
572
570
  simText = `${name} ${plug}`
@@ -591,18 +589,10 @@ function render(): void {
591
589
  let srvRight = ''
592
590
  if (server) {
593
591
  const isSelected = tuiState.selectedCol === 1 && tuiState.selectedRow === row
594
- // check which cables are connected to this server and get their colors
595
- const cableColors = [
596
- colors.green,
597
- colors.cyan,
598
- colors.magenta,
599
- colors.blue,
600
- colors.yellow,
601
- ]
602
592
  let connectedColor: ((s: string) => string) | null = null
603
593
  for (const [simIndex, cable] of tuiState.cables) {
604
594
  if (cable.serverIndex === row) {
605
- connectedColor = cableColors[simIndex % cableColors.length]
595
+ connectedColor = CABLE_COLORS[simIndex % CABLE_COLORS.length]
606
596
  break
607
597
  }
608
598
  }
@@ -695,15 +685,7 @@ function getCableCharAt(x: number, y: number): string | null {
695
685
 
696
686
  if (px === x && py === y) {
697
687
  const connected = cable.serverIndex !== null
698
- // unique color per cable based on sim index
699
- const cableColors = [
700
- colors.green,
701
- colors.cyan,
702
- colors.magenta,
703
- colors.blue,
704
- colors.yellow,
705
- ]
706
- const baseColor = cableColors[simIndex % cableColors.length]
688
+ const baseColor = CABLE_COLORS[simIndex % CABLE_COLORS.length]
707
689
  const color = connected ? baseColor : colors.dim
708
690
 
709
691
  // determine character based on curve direction
@@ -536,7 +536,7 @@ function getFileMeta(
536
536
  const isLayout = filenameWithoutExtensions.startsWith('_layout')
537
537
  const isMiddleware = filenameWithoutExtensions.startsWith('_middleware')
538
538
 
539
- const [_fullname, renderModeFound] =
539
+ const [, renderModeFound] =
540
540
  filename.match(/\+(api|ssg|ssr|spa)\.(\w+\.)?[jt]sx?$/) || []
541
541
  const fileRenderMode = renderModeFound as 'api' | One.RouteRenderMode | undefined
542
542
 
@@ -656,6 +656,7 @@ function getFileMeta(
656
656
  }
657
657
 
658
658
  function getMostSpecific(routes: RouteNode[]) {
659
+ // holey array: .length includes holes, so last element is most specific
659
660
  const route = routes[routes.length - 1]
660
661
 
661
662
  if (!routes[0]) {
@@ -664,9 +665,7 @@ function getMostSpecific(routes: RouteNode[]) {
664
665
  )
665
666
  }
666
667
 
667
- // This works even tho routes is holey array (e.g it might have index 0 and 2 but not 1)
668
- // `.length` includes the holes in its count
669
- return routes[routes.length - 1]
668
+ return route
670
669
  }
671
670
 
672
671
  export function getIgnoreList(options?: Options) {
@@ -27,10 +27,6 @@ export function isHardNavigation(): boolean {
27
27
  return navigationMode === 'hard'
28
28
  }
29
29
 
30
- export function isSoftNavigation(): boolean {
31
- return navigationMode === 'soft'
32
- }
33
-
34
30
  // ============================================
35
31
  // Intercept Route Matching
36
32
  // ============================================
@@ -241,8 +237,7 @@ function findMatchingInterceptInSlot(
241
237
  const resolvedTargetPath = resolveInterceptTargetPath(
242
238
  interceptTargetPath,
243
239
  levels,
244
- layoutNode,
245
- currentPath
240
+ layoutNode
246
241
  )
247
242
 
248
243
  // Try to match the target path against the resolved intercept path
@@ -273,24 +268,11 @@ function findMatchingInterceptInSlot(
273
268
  function resolveInterceptTargetPath(
274
269
  interceptTargetPath: string,
275
270
  levels: number,
276
- layoutNode: RouteNode,
277
- currentPath: string
271
+ layoutNode: RouteNode
278
272
  ): string {
279
- // Get the base path for resolution
280
- // contextKey is like './app/_layout.tsx' or './_layout.tsx'
281
- // We need to extract just the route path portion
282
- let layoutPath = layoutNode.contextKey
283
- .replace(/^\.\//, '') // Remove leading ./
284
- .replace(/\/?_layout.*$/, '') // Remove _layout and extension (with optional leading /)
285
- .replace(/^app\/?/, '') // Remove 'app/' prefix since routes are relative to app dir
286
-
287
- // Strip route groups like (app), (tabs) since they don't appear in URLs
288
- layoutPath = stripGroupSegmentsFromPath(layoutPath)
289
-
290
- // Normalize: empty string or '/' means root
291
- if (!layoutPath || layoutPath === '/') {
292
- layoutPath = ''
293
- }
273
+ // reuse getLayoutPath and strip leading slash for internal resolution
274
+ const fullLayoutPath = getLayoutPath(layoutNode)
275
+ let layoutPath = fullLayoutPath === '/' ? '' : fullLayoutPath.slice(1)
294
276
 
295
277
  if (levels === Infinity) {
296
278
  // (...) matches from root - target path is absolute
@@ -468,13 +450,6 @@ export function isReturningFromIntercept(): boolean {
468
450
  return returningFromIntercept
469
451
  }
470
452
 
471
- // Store intercept state info for forward navigation restoration
472
- interface StoredInterceptState {
473
- slotName: string
474
- routeContextKey: string
475
- params: Record<string, string>
476
- }
477
-
478
453
  // Callback to set slot state - set from Navigator.tsx to avoid circular deps
479
454
  let setSlotStateCallback:
480
455
  | ((
@@ -475,7 +475,6 @@ export function subscribeToStore(subscriber: () => void) {
475
475
  }
476
476
  }
477
477
 
478
- // Subscription functions
479
478
  export function subscribeToLoadingState(subscriber: OneRouter.LoadingStateListener) {
480
479
  loadingStateSubscribers.add(subscriber)
481
480
  return () => {
@@ -1163,8 +1162,6 @@ export async function linkTo(
1163
1162
  }, 1000)
1164
1163
  }
1165
1164
  })
1166
-
1167
- return
1168
1165
  }
1169
1166
 
1170
1167
  const hashes: Record<string, string> = {}
@@ -22,6 +22,19 @@ import { getFetchStaticHtml } from './staticHtmlFetcher'
22
22
 
23
23
  const debugRouter = process.env.ONE_DEBUG_ROUTER
24
24
 
25
+ async function readStaticHtml(htmlPath: string): Promise<string | null> {
26
+ const fetchStaticHtml = getFetchStaticHtml()
27
+ if (fetchStaticHtml) {
28
+ const html = await fetchStaticHtml(htmlPath)
29
+ if (html) return html
30
+ }
31
+ try {
32
+ return await readFile(join('dist/client', htmlPath), 'utf-8')
33
+ } catch {
34
+ return null
35
+ }
36
+ }
37
+
25
38
  /**
26
39
  * Lazy import functions for route modules.
27
40
  * Modules are loaded on-demand when a route is matched, not all upfront.
@@ -102,6 +115,43 @@ export async function oneServe(
102
115
 
103
116
  const apiCJS = oneOptions.build?.api?.outputFormat === 'cjs'
104
117
 
118
+ // shared helper to import a route module and run its loader
119
+ async function importAndRunLoader(
120
+ routeId: string,
121
+ serverPath: string | undefined,
122
+ lazyKey: string | undefined,
123
+ loaderProps: any
124
+ ): Promise<{ loaderData: unknown; routeId: string; isEnoent?: boolean }> {
125
+ if (!serverPath && !lazyKey) {
126
+ return { loaderData: undefined, routeId }
127
+ }
128
+
129
+ try {
130
+ const pathToResolve = serverPath || lazyKey || ''
131
+ const resolvedPath = pathToResolve.includes('dist/server')
132
+ ? pathToResolve
133
+ : join('./', 'dist/server', pathToResolve)
134
+
135
+ const routeExported = lazyKey
136
+ ? options?.lazyRoutes?.pages?.[lazyKey]
137
+ ? await options.lazyRoutes.pages[lazyKey]()
138
+ : await import(toAbsolute(resolvedPath))
139
+ : await import(toAbsolute(serverPath!))
140
+
141
+ const loaderData = await routeExported?.loader?.(loaderProps)
142
+ return { loaderData, routeId }
143
+ } catch (err) {
144
+ if (isResponse(err)) {
145
+ throw err
146
+ }
147
+ if ((err as any)?.code === 'ENOENT') {
148
+ return { loaderData: undefined, routeId, isEnoent: true }
149
+ }
150
+ console.error(`[one] Error running loader for ${routeId}:`, err)
151
+ return { loaderData: undefined, routeId }
152
+ }
153
+ }
154
+
105
155
  // Lazy load server entry only when needed for SSR
106
156
  let render: ((props: RenderAppProps) => any) | null = null
107
157
  async function getRender() {
@@ -211,57 +261,24 @@ export async function oneServe(
211
261
  }
212
262
 
213
263
  try {
214
- // helper to import and run a single loader
215
- async function runLoader(
216
- routeId: string,
217
- serverPath: string | undefined,
218
- lazyKey?: string
219
- ): Promise<{ loaderData: unknown; routeId: string; isEnoent?: boolean }> {
220
- if (!serverPath && !lazyKey) {
221
- return { loaderData: undefined, routeId }
222
- }
223
-
224
- try {
225
- // serverPath may already include dist/server if it came from buildInfo.serverJsPath
226
- const pathToResolve = serverPath || lazyKey || ''
227
- const resolvedPath = pathToResolve.includes('dist/server')
228
- ? pathToResolve
229
- : join('./', 'dist/server', pathToResolve)
230
-
231
- const routeExported = lazyKey
232
- ? options?.lazyRoutes?.pages?.[lazyKey]
233
- ? await options.lazyRoutes.pages[lazyKey]()
234
- : await import(toAbsolute(resolvedPath))
235
- : await import(toAbsolute(serverPath!))
236
-
237
- const loaderData = await routeExported?.loader?.(loaderProps)
238
- return { loaderData, routeId }
239
- } catch (err) {
240
- // if a loader throws a Response (redirect), re-throw it
241
- if (isResponse(err)) {
242
- throw err
243
- }
244
- if ((err as any)?.code === 'ENOENT') {
245
- return { loaderData: undefined, routeId, isEnoent: true }
246
- }
247
- console.error(`[one] Error running loader for ${routeId}:`, err)
248
- return { loaderData: undefined, routeId }
249
- }
250
- }
251
-
252
264
  // collect layout loaders to run in parallel
253
265
  const layoutRoutes = route.layouts || []
254
266
  const layoutLoaderPromises = layoutRoutes.map((layout: any) => {
255
- // layouts may have loaderServerPath set from build, or we can try contextKey
256
267
  const serverPath = layout.loaderServerPath || layout.contextKey
257
- return runLoader(layout.contextKey, serverPath, layout.contextKey)
268
+ return importAndRunLoader(
269
+ layout.contextKey,
270
+ serverPath,
271
+ layout.contextKey,
272
+ loaderProps
273
+ )
258
274
  })
259
275
 
260
276
  // run page loader
261
- const pageLoaderPromise = runLoader(
277
+ const pageLoaderPromise = importAndRunLoader(
262
278
  route.file,
263
279
  buildInfo.serverJsPath,
264
- route.file
280
+ route.file,
281
+ loaderProps
265
282
  )
266
283
 
267
284
  // wait for all loaders in parallel
@@ -375,43 +392,16 @@ url: ${url}`)
375
392
 
376
393
  if (needsSpaShell) {
377
394
  try {
378
- // helper to import and run a single loader
379
- async function runShellLoader(
380
- routeId: string,
381
- serverPath: string | undefined,
382
- lazyKey?: string
383
- ): Promise<{ loaderData: unknown; routeId: string }> {
384
- if (!serverPath && !lazyKey) {
385
- return { loaderData: undefined, routeId }
386
- }
387
- try {
388
- const pathToResolve = serverPath || lazyKey || ''
389
- const resolvedPath = pathToResolve.includes('dist/server')
390
- ? pathToResolve
391
- : join('./', 'dist/server', pathToResolve)
392
-
393
- const routeExported = lazyKey
394
- ? options?.lazyRoutes?.pages?.[lazyKey]
395
- ? await options.lazyRoutes.pages[lazyKey]()
396
- : await import(toAbsolute(resolvedPath))
397
- : await import(toAbsolute(serverPath!))
398
-
399
- const loaderData = await routeExported?.loader?.(loaderProps)
400
- return { loaderData, routeId }
401
- } catch (err) {
402
- if (isResponse(err)) {
403
- throw err
404
- }
405
- console.error(`[one] Error running shell loader for ${routeId}:`, err)
406
- return { loaderData: undefined, routeId }
407
- }
408
- }
409
-
410
395
  // run layout loaders only (page content is client-rendered)
411
396
  const layoutResults = await Promise.all(
412
397
  layoutRoutes.map((layout: any) => {
413
398
  const serverPath = layout.loaderServerPath || layout.contextKey
414
- return runShellLoader(layout.contextKey, serverPath, layout.contextKey)
399
+ return importAndRunLoader(
400
+ layout.contextKey,
401
+ serverPath,
402
+ layout.contextKey,
403
+ loaderProps
404
+ )
415
405
  })
416
406
  )
417
407
 
@@ -477,22 +467,7 @@ url: ${url}`)
477
467
  : routeMap[url.pathname] || routeMap[buildInfo?.cleanPath]
478
468
 
479
469
  if (htmlPath) {
480
- // Try Worker ASSETS binding first (for Cloudflare Workers), fall back to filesystem
481
- const fetchStaticHtml = getFetchStaticHtml()
482
- let html: string | null = null
483
-
484
- if (fetchStaticHtml) {
485
- html = await fetchStaticHtml(htmlPath)
486
- }
487
-
488
- if (!html) {
489
- // Fall back to filesystem (Node.js)
490
- try {
491
- html = await readFile(join('dist/client', htmlPath), 'utf-8')
492
- } catch {
493
- // File not found
494
- }
495
- }
470
+ const html = await readStaticHtml(htmlPath)
496
471
 
497
472
  if (html) {
498
473
  const headers = new Headers()
@@ -511,23 +486,7 @@ url: ${url}`)
511
486
  const notFoundHtmlPath = routeMap[notFoundRoute]
512
487
 
513
488
  if (notFoundHtmlPath) {
514
- const fetchStaticHtml = getFetchStaticHtml()
515
- let notFoundHtml: string | null = null
516
-
517
- if (fetchStaticHtml) {
518
- notFoundHtml = await fetchStaticHtml(notFoundHtmlPath)
519
- }
520
-
521
- if (!notFoundHtml) {
522
- try {
523
- notFoundHtml = await readFile(
524
- join('dist/client', notFoundHtmlPath),
525
- 'utf-8'
526
- )
527
- } catch {
528
- // File not found
529
- }
530
- }
489
+ const notFoundHtml = await readStaticHtml(notFoundHtmlPath)
531
490
 
532
491
  if (notFoundHtml) {
533
492
  // inject 404 marker so client knows this is a 404 response