one 1.25.6 → 1.25.8

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 (104) hide show
  1. package/dist/cjs/cli/prebuild.cjs +2 -0
  2. package/dist/cjs/cli/prebuild.native.js +2 -0
  3. package/dist/cjs/cli/prebuild.native.js.map +1 -1
  4. package/dist/cjs/cli/prebuild.test.cjs +45 -0
  5. package/dist/cjs/cli/prebuild.test.native.js +54 -0
  6. package/dist/cjs/cli/prebuild.test.native.js.map +1 -0
  7. package/dist/cjs/createHandleRequest.cjs +5 -1
  8. package/dist/cjs/createHandleRequest.native.js +5 -1
  9. package/dist/cjs/createHandleRequest.native.js.map +1 -1
  10. package/dist/cjs/createHandleRequest.test.cjs +59 -1
  11. package/dist/cjs/createHandleRequest.test.native.js +70 -0
  12. package/dist/cjs/createHandleRequest.test.native.js.map +1 -1
  13. package/dist/cjs/router/diagnoseRouteLoadFailure.cjs +95 -0
  14. package/dist/cjs/router/diagnoseRouteLoadFailure.native.js +202 -0
  15. package/dist/cjs/router/diagnoseRouteLoadFailure.native.js.map +1 -0
  16. package/dist/cjs/router/useViteRoutes.cjs +6 -0
  17. package/dist/cjs/router/useViteRoutes.native.js +6 -0
  18. package/dist/cjs/router/useViteRoutes.native.js.map +1 -1
  19. package/dist/cjs/server/oneServe.cjs +0 -2
  20. package/dist/cjs/server/oneServe.native.js +0 -2
  21. package/dist/cjs/server/oneServe.native.js.map +1 -1
  22. package/dist/cjs/server/workerHandler.cjs +1 -1
  23. package/dist/cjs/server/workerHandler.native.js +1 -1
  24. package/dist/cjs/server/workerHandler.native.js.map +1 -1
  25. package/dist/cjs/server/workerHandler.test.cjs +117 -0
  26. package/dist/cjs/server/workerHandler.test.native.js +137 -0
  27. package/dist/cjs/server/workerHandler.test.native.js.map +1 -0
  28. package/dist/cjs/views/ScrollBehavior.cjs +10 -6
  29. package/dist/cjs/views/ScrollBehavior.native.js +10 -6
  30. package/dist/cjs/views/ScrollBehavior.native.js.map +1 -1
  31. package/dist/cjs/views/ScrollBehavior.test.cjs +132 -0
  32. package/dist/cjs/views/ScrollBehavior.test.native.js +184 -0
  33. package/dist/cjs/views/ScrollBehavior.test.native.js.map +1 -0
  34. package/dist/esm/cli/prebuild.mjs +2 -0
  35. package/dist/esm/cli/prebuild.mjs.map +1 -1
  36. package/dist/esm/cli/prebuild.native.js +2 -0
  37. package/dist/esm/cli/prebuild.native.js.map +1 -1
  38. package/dist/esm/cli/prebuild.test.mjs +46 -0
  39. package/dist/esm/cli/prebuild.test.mjs.map +1 -0
  40. package/dist/esm/cli/prebuild.test.native.js +52 -0
  41. package/dist/esm/cli/prebuild.test.native.js.map +1 -0
  42. package/dist/esm/createHandleRequest.mjs +5 -2
  43. package/dist/esm/createHandleRequest.mjs.map +1 -1
  44. package/dist/esm/createHandleRequest.native.js +5 -2
  45. package/dist/esm/createHandleRequest.native.js.map +1 -1
  46. package/dist/esm/createHandleRequest.test.mjs +59 -1
  47. package/dist/esm/createHandleRequest.test.mjs.map +1 -1
  48. package/dist/esm/createHandleRequest.test.native.js +70 -0
  49. package/dist/esm/createHandleRequest.test.native.js.map +1 -1
  50. package/dist/esm/router/diagnoseRouteLoadFailure.mjs +69 -0
  51. package/dist/esm/router/diagnoseRouteLoadFailure.mjs.map +1 -0
  52. package/dist/esm/router/diagnoseRouteLoadFailure.native.js +173 -0
  53. package/dist/esm/router/diagnoseRouteLoadFailure.native.js.map +1 -0
  54. package/dist/esm/router/useViteRoutes.mjs +6 -0
  55. package/dist/esm/router/useViteRoutes.mjs.map +1 -1
  56. package/dist/esm/router/useViteRoutes.native.js +6 -0
  57. package/dist/esm/router/useViteRoutes.native.js.map +1 -1
  58. package/dist/esm/server/oneServe.mjs +0 -2
  59. package/dist/esm/server/oneServe.mjs.map +1 -1
  60. package/dist/esm/server/oneServe.native.js +0 -2
  61. package/dist/esm/server/oneServe.native.js.map +1 -1
  62. package/dist/esm/server/workerHandler.mjs +2 -2
  63. package/dist/esm/server/workerHandler.mjs.map +1 -1
  64. package/dist/esm/server/workerHandler.native.js +2 -2
  65. package/dist/esm/server/workerHandler.native.js.map +1 -1
  66. package/dist/esm/server/workerHandler.test.mjs +118 -0
  67. package/dist/esm/server/workerHandler.test.mjs.map +1 -0
  68. package/dist/esm/server/workerHandler.test.native.js +135 -0
  69. package/dist/esm/server/workerHandler.test.native.js.map +1 -0
  70. package/dist/esm/views/ScrollBehavior.mjs +10 -6
  71. package/dist/esm/views/ScrollBehavior.mjs.map +1 -1
  72. package/dist/esm/views/ScrollBehavior.native.js +10 -6
  73. package/dist/esm/views/ScrollBehavior.native.js.map +1 -1
  74. package/dist/esm/views/ScrollBehavior.test.mjs +109 -0
  75. package/dist/esm/views/ScrollBehavior.test.mjs.map +1 -0
  76. package/dist/esm/views/ScrollBehavior.test.native.js +158 -0
  77. package/dist/esm/views/ScrollBehavior.test.native.js.map +1 -0
  78. package/package.json +9 -9
  79. package/src/cli/prebuild.test.ts +48 -0
  80. package/src/cli/prebuild.ts +3 -0
  81. package/src/createHandleRequest.test.ts +55 -1
  82. package/src/createHandleRequest.ts +8 -1
  83. package/src/router/diagnoseRouteLoadFailure.ts +108 -0
  84. package/src/router/useViteRoutes.tsx +10 -0
  85. package/src/server/oneServe.ts +0 -2
  86. package/src/server/workerHandler.test.ts +125 -0
  87. package/src/server/workerHandler.ts +3 -2
  88. package/src/views/ScrollBehavior.test.tsx +137 -0
  89. package/src/views/ScrollBehavior.tsx +18 -8
  90. package/types/cli/prebuild.d.ts.map +1 -1
  91. package/types/cli/prebuild.test.d.ts +2 -0
  92. package/types/cli/prebuild.test.d.ts.map +1 -0
  93. package/types/createHandleRequest.d.ts +1 -0
  94. package/types/createHandleRequest.d.ts.map +1 -1
  95. package/types/router/diagnoseRouteLoadFailure.d.ts +14 -0
  96. package/types/router/diagnoseRouteLoadFailure.d.ts.map +1 -0
  97. package/types/router/useViteRoutes.d.ts.map +1 -1
  98. package/types/server/oneServe.d.ts.map +1 -1
  99. package/types/server/workerHandler.d.ts.map +1 -1
  100. package/types/server/workerHandler.test.d.ts +2 -0
  101. package/types/server/workerHandler.test.d.ts.map +1 -0
  102. package/types/views/ScrollBehavior.d.ts.map +1 -1
  103. package/types/views/ScrollBehavior.test.d.ts +2 -0
  104. package/types/views/ScrollBehavior.test.d.ts.map +1 -0
@@ -0,0 +1,158 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import React from "react";
3
+ import TestRenderer, { act } from "react-test-renderer";
4
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
5
+ import { registerScrollGroup, ScrollBehavior } from "./ScrollBehavior.native.js";
6
+ var subscriptions = vi.hoisted(function () {
7
+ return {
8
+ loading: void 0,
9
+ root: void 0
10
+ };
11
+ });
12
+ vi.mock("../router/lastAction", function () {
13
+ return {
14
+ setLastAction: vi.fn()
15
+ };
16
+ });
17
+ vi.mock("../router/router", function () {
18
+ return {
19
+ subscribeToLoadingState: function (subscriber) {
20
+ subscriptions.loading = subscriber;
21
+ return function () {
22
+ subscriptions.loading = void 0;
23
+ };
24
+ },
25
+ subscribeToRootState: function (subscriber) {
26
+ subscriptions.root = subscriber;
27
+ return function () {
28
+ subscriptions.root = void 0;
29
+ };
30
+ }
31
+ };
32
+ });
33
+ globalThis.IS_REACT_ACT_ENVIRONMENT = true;
34
+ var renderer;
35
+ var location;
36
+ var scrollTo;
37
+ beforeEach(function () {
38
+ process.env.VITE_ENVIRONMENT = "client";
39
+ location = {
40
+ pathname: "/",
41
+ search: "",
42
+ hash: ""
43
+ };
44
+ scrollTo = vi.fn();
45
+ var windowMock = Object.assign(new EventTarget(), {
46
+ location,
47
+ scrollTo,
48
+ scrollY: 500
49
+ });
50
+ var storage = /* @__PURE__ */new Map();
51
+ vi.stubGlobal("window", windowMock);
52
+ vi.stubGlobal("document", {
53
+ getElementById: vi.fn()
54
+ });
55
+ vi.stubGlobal("sessionStorage", {
56
+ getItem: function (key) {
57
+ var _storage_get;
58
+ return (_storage_get = storage.get(key)) !== null && _storage_get !== void 0 ? _storage_get : null;
59
+ },
60
+ setItem: function (key, value) {
61
+ return storage.set(key, value);
62
+ },
63
+ removeItem: function (key) {
64
+ return storage.delete(key);
65
+ }
66
+ });
67
+ act(function () {
68
+ renderer = TestRenderer.create(/* @__PURE__ */_jsx(ScrollBehavior, {}));
69
+ });
70
+ });
71
+ afterEach(function () {
72
+ act(function () {
73
+ return renderer === null || renderer === void 0 ? void 0 : renderer.unmount();
74
+ });
75
+ renderer = void 0;
76
+ vi.useRealTimers();
77
+ vi.unstubAllGlobals();
78
+ delete process.env.VITE_ENVIRONMENT;
79
+ });
80
+ describe("ScrollBehavior initial navigation", function () {
81
+ it("does not scroll when the navigator publishes its initial state after mount", function () {
82
+ act(function () {
83
+ var _subscriptions_root;
84
+ return (_subscriptions_root = subscriptions.root) === null || _subscriptions_root === void 0 ? void 0 : _subscriptions_root.call(subscriptions, {});
85
+ });
86
+ expect(scrollTo).not.toHaveBeenCalled();
87
+ });
88
+ it("scrolls to the top when the first state notification is a real navigation", function () {
89
+ location.pathname = "/compat/react-native-gesture-handler";
90
+ act(function () {
91
+ var _subscriptions_root;
92
+ return (_subscriptions_root = subscriptions.root) === null || _subscriptions_root === void 0 ? void 0 : _subscriptions_root.call(subscriptions, {});
93
+ });
94
+ expect(scrollTo).toHaveBeenCalledWith(0, 0);
95
+ });
96
+ it("handles a query change as a first navigation", function () {
97
+ location.search = "?platform=ios";
98
+ act(function () {
99
+ var _subscriptions_root;
100
+ return (_subscriptions_root = subscriptions.root) === null || _subscriptions_root === void 0 ? void 0 : _subscriptions_root.call(subscriptions, {});
101
+ });
102
+ expect(scrollTo).toHaveBeenCalledWith(0, 0);
103
+ });
104
+ });
105
+ describe("ScrollBehavior groups", function () {
106
+ it("preserves position between routes in the same group", function () {
107
+ vi.useFakeTimers();
108
+ var unregister = registerScrollGroup("/compat/react-native-gesture-handler");
109
+ act(function () {
110
+ var _subscriptions_root;
111
+ return (_subscriptions_root = subscriptions.root) === null || _subscriptions_root === void 0 ? void 0 : _subscriptions_root.call(subscriptions, {});
112
+ });
113
+ location.pathname = "/compat/react-native-gesture-handler";
114
+ act(function () {
115
+ var _subscriptions_root;
116
+ return (_subscriptions_root = subscriptions.root) === null || _subscriptions_root === void 0 ? void 0 : _subscriptions_root.call(subscriptions, {});
117
+ });
118
+ scrollTo.mockClear();
119
+ act(function () {
120
+ var _subscriptions_loading;
121
+ return (_subscriptions_loading = subscriptions.loading) === null || _subscriptions_loading === void 0 ? void 0 : _subscriptions_loading.call(subscriptions, "loading");
122
+ });
123
+ location.pathname = "/compat/react-native-gesture-handler/pan-gesture";
124
+ act(function () {
125
+ var _subscriptions_root;
126
+ return (_subscriptions_root = subscriptions.root) === null || _subscriptions_root === void 0 ? void 0 : _subscriptions_root.call(subscriptions, {});
127
+ });
128
+ vi.runAllTimers();
129
+ expect(scrollTo).toHaveBeenCalledWith(0, 500);
130
+ unregister();
131
+ vi.useRealTimers();
132
+ });
133
+ it("does not match a different route whose slug has the same prefix", function () {
134
+ var unregister = registerScrollGroup("/compat/react-native");
135
+ act(function () {
136
+ var _subscriptions_root;
137
+ return (_subscriptions_root = subscriptions.root) === null || _subscriptions_root === void 0 ? void 0 : _subscriptions_root.call(subscriptions, {});
138
+ });
139
+ location.pathname = "/compat/react-native/core";
140
+ act(function () {
141
+ var _subscriptions_root;
142
+ return (_subscriptions_root = subscriptions.root) === null || _subscriptions_root === void 0 ? void 0 : _subscriptions_root.call(subscriptions, {});
143
+ });
144
+ scrollTo.mockClear();
145
+ act(function () {
146
+ var _subscriptions_loading;
147
+ return (_subscriptions_loading = subscriptions.loading) === null || _subscriptions_loading === void 0 ? void 0 : _subscriptions_loading.call(subscriptions, "loading");
148
+ });
149
+ location.pathname = "/compat/react-native-gesture-handler";
150
+ act(function () {
151
+ var _subscriptions_root;
152
+ return (_subscriptions_root = subscriptions.root) === null || _subscriptions_root === void 0 ? void 0 : _subscriptions_root.call(subscriptions, {});
153
+ });
154
+ expect(scrollTo).toHaveBeenCalledWith(0, 0);
155
+ unregister();
156
+ });
157
+ });
158
+ //# sourceMappingURL=ScrollBehavior.test.native.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["jsx","_jsx","React","TestRenderer","act","afterEach","beforeEach","describe","expect","it","vi","registerScrollGroup","ScrollBehavior","subscriptions","hoisted","loading","root","mock","setLastAction","fn","subscribeToLoadingState","subscriber","subscribeToRootState","globalThis","IS_REACT_ACT_ENVIRONMENT","renderer","location","scrollTo","process","env","VITE_ENVIRONMENT","pathname","search","hash","windowMock","Object","assign","EventTarget","scrollY","storage","Map","stubGlobal","getElementById","getItem","key","_storage_get","get","setItem","value","set","removeItem","delete","create","unmount","useRealTimers","unstubAllGlobals","_subscriptions_root","call","not","toHaveBeenCalled","toHaveBeenCalledWith"],"sources":["../../../src/views/ScrollBehavior.test.tsx"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "one",
3
- "version": "1.25.6",
3
+ "version": "1.25.8",
4
4
  "license": "BSD-3-Clause",
5
5
  "sideEffects": [
6
6
  "setup.mjs",
@@ -193,18 +193,18 @@
193
193
  "@cloudflare/vite-plugin": "^1.33.1",
194
194
  "@swc/core": "^1.14.0",
195
195
  "@ungap/structured-clone": "^1.2.0",
196
- "@vxrn/color-scheme": "1.25.6",
197
- "@vxrn/compiler": "1.25.6",
198
- "@vxrn/resolve": "1.25.6",
199
- "@vxrn/tslib-lite": "1.25.6",
200
- "@vxrn/use-isomorphic-layout-effect": "1.25.6",
201
- "@vxrn/vite-plugin-metro": "1.25.6",
196
+ "@vxrn/color-scheme": "1.25.8",
197
+ "@vxrn/compiler": "1.25.8",
198
+ "@vxrn/resolve": "1.25.8",
199
+ "@vxrn/tslib-lite": "1.25.8",
200
+ "@vxrn/use-isomorphic-layout-effect": "1.25.8",
201
+ "@vxrn/vite-plugin-metro": "1.25.8",
202
202
  "babel-dead-code-elimination": "1.0.10",
203
203
  "babel-plugin-module-resolver": "^5.0.2",
204
204
  "babel-preset-expo": "~57.0.5",
205
205
  "citty": "^0.1.6",
206
206
  "core-js": "^3.38.1",
207
- "create-vxrn": "1.25.6",
207
+ "create-vxrn": "1.25.8",
208
208
  "escape-string-regexp": "^5.0.0",
209
209
  "expo-linking": "~57.0.5",
210
210
  "expo-modules-core": "~57.0.10",
@@ -224,7 +224,7 @@
224
224
  "tsconfig-paths": "^4",
225
225
  "use-latest-callback": "^0.2.3",
226
226
  "vite": "^8.2.2",
227
- "vxrn": "1.25.6",
227
+ "vxrn": "1.25.8",
228
228
  "web-streams-polyfill": "^4.3.0",
229
229
  "ws": "^8.21.0",
230
230
  "xxhashjs": "^0.2.2"
@@ -0,0 +1,48 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest'
2
+ import { run } from './prebuild'
3
+
4
+ const { loadUserOneOptionsMock, prebuildMock } = vi.hoisted(() => ({
5
+ loadUserOneOptionsMock: vi.fn(),
6
+ prebuildMock: vi.fn(),
7
+ }))
8
+
9
+ vi.mock('../vite/loadConfig', () => ({
10
+ loadUserOneOptions: loadUserOneOptionsMock,
11
+ }))
12
+
13
+ vi.mock('vxrn', () => ({
14
+ prebuild: prebuildMock,
15
+ }))
16
+
17
+ describe('one prebuild', () => {
18
+ beforeEach(() => {
19
+ loadUserOneOptionsMock.mockReset()
20
+ prebuildMock.mockReset()
21
+ })
22
+
23
+ it('loads the app bundler configuration before native prebuild', async () => {
24
+ loadUserOneOptionsMock.mockResolvedValueOnce({})
25
+
26
+ await run({ platform: 'ios', expo: true, 'no-install': true })
27
+
28
+ expect(loadUserOneOptionsMock).toHaveBeenCalledWith('build', true)
29
+ expect(loadUserOneOptionsMock.mock.invocationCallOrder[0]).toBeLessThan(
30
+ prebuildMock.mock.invocationCallOrder[0]
31
+ )
32
+ expect(prebuildMock).toHaveBeenCalledWith({
33
+ root: process.cwd(),
34
+ platform: 'ios',
35
+ expo: true,
36
+ 'no-install': true,
37
+ })
38
+ })
39
+
40
+ it('does not prebuild when the app configuration fails to load', async () => {
41
+ loadUserOneOptionsMock.mockRejectedValueOnce(new Error('invalid vite config'))
42
+
43
+ await expect(run({ platform: 'ios', expo: true })).rejects.toThrow(
44
+ 'invalid vite config'
45
+ )
46
+ expect(prebuildMock).not.toHaveBeenCalled()
47
+ })
48
+ })
@@ -1,8 +1,11 @@
1
+ import { loadUserOneOptions } from '../vite/loadConfig'
2
+
1
3
  export async function run(args: {
2
4
  platform?: string
3
5
  expo: boolean
4
6
  'no-install'?: boolean
5
7
  }) {
8
+ await loadUserOneOptions('build', true)
6
9
  const { prebuild } = await import('vxrn')
7
10
 
8
11
  await prebuild({
@@ -62,8 +62,9 @@ vi.mock('./vite/getManifest', () => ({
62
62
  }),
63
63
  }))
64
64
 
65
- function createRequest(path: string) {
65
+ function createRequest(path: string, method = 'GET') {
66
66
  return new Request(`http://localhost:3000${path}`, {
67
+ method,
67
68
  headers: {
68
69
  host: 'localhost:3000',
69
70
  },
@@ -262,6 +263,59 @@ describe('createHandleRequest', () => {
262
263
  })
263
264
  })
264
265
 
266
+ describe('request method dispatch', () => {
267
+ it('answers HEAD page requests exactly like GET', async () => {
268
+ const handlePage = vi.fn(
269
+ async () =>
270
+ new Response('<html></html>', { headers: { 'content-type': 'text/html' } })
271
+ )
272
+ const { handler } = createHandleRequest({ handlePage }, { routerRoot: '/app' })
273
+
274
+ const get = await handler(createRequest('/my-page', 'GET'))
275
+ const head = await handler(createRequest('/my-page', 'HEAD'))
276
+
277
+ if (!(get instanceof Response) || !(head instanceof Response)) {
278
+ throw new Error('expected both GET and HEAD to return a Response')
279
+ }
280
+ // the transport drops the body for HEAD, so the handler layer must agree
281
+ // with GET on everything else: status and content type.
282
+ expect(head.status).toBe(get.status)
283
+ expect(head.headers.get('content-type')).toBe(get.headers.get('content-type'))
284
+ expect(handlePage).toHaveBeenCalledTimes(2)
285
+ })
286
+
287
+ it('continues routing HEAD requests to API handlers', async () => {
288
+ const mockHandlersWithAPI = {
289
+ handlePage: vi.fn().mockResolvedValue('<html></html>'),
290
+ handleAPI: vi.fn().mockResolvedValue({
291
+ HEAD: () => new Response(null, { status: 204 }),
292
+ }),
293
+ }
294
+ const { handler } = createHandleRequest(mockHandlersWithAPI, {
295
+ routerRoot: '/app',
296
+ })
297
+
298
+ const result = await handler(createRequest('/api/github/agent/run.sh', 'HEAD'))
299
+
300
+ expect(result).toBeInstanceOf(Response)
301
+ if (!(result instanceof Response)) {
302
+ throw new Error('expected HEAD API request to return a Response')
303
+ }
304
+ expect(result.status).toBe(204)
305
+ expect(mockHandlersWithAPI.handleAPI).toHaveBeenCalledOnce()
306
+ expect(mockHandlersWithAPI.handlePage).not.toHaveBeenCalled()
307
+ })
308
+
309
+ it('does not route POST requests to page handlers', async () => {
310
+ const { handler } = createHandleRequest(mockHandlers, { routerRoot: '/app' })
311
+
312
+ const result = await handler(createRequest('/my-page', 'POST'))
313
+
314
+ expect(result).toBeNull()
315
+ expect(mockHandlers.handlePage).not.toHaveBeenCalled()
316
+ })
317
+ })
318
+
265
319
  describe('special paths', () => {
266
320
  it('should skip __vxrnhmr path', async () => {
267
321
  const { handler } = createHandleRequest(mockHandlers, { routerRoot: '/app' })
@@ -35,6 +35,13 @@ function isStaticAssetRequestPath(pathname: string): boolean {
35
35
  )
36
36
  }
37
37
 
38
+ // page (document) routes answer HEAD exactly like GET. the transport drops the
39
+ // body: node's ServerResponse suppresses it for HEAD and workerd does the same.
40
+ // without this a HEAD document request falls through to a bare 404 while GET is 200.
41
+ export function isPageRequestMethod(method: string) {
42
+ return method === 'GET' || method === 'HEAD'
43
+ }
44
+
38
45
  // ensure handler results are always a proper Response so middleware
39
46
  // can safely use response.body / response.headers / new Response(response.body, ...)
40
47
  function ensureResponse(value: any): Response {
@@ -425,7 +432,7 @@ export function createHandleRequest(
425
432
  }
426
433
  }
427
434
 
428
- if (request.method !== 'GET') {
435
+ if (!isPageRequestMethod(request.method)) {
429
436
  return null
430
437
  }
431
438
 
@@ -0,0 +1,108 @@
1
+ // a browser content blocker (safari's built-in blockers, ublock, adguard,
2
+ // brave shields) matches filter rules against the request url. in dev the
3
+ // vite module url mirrors the source path, so an ordinary app file can match
4
+ // a tracking rule purely by where it lives — easylist, easyprivacy and
5
+ // adguard all ship the plain substring rule `/analytics/pageview`, which is
6
+ // enough to refuse `src/features/site/analytics/PageviewTracker.tsx`.
7
+ //
8
+ // one blocked module fails the whole dynamic import, and the browser reports
9
+ // only `TypeError: Importing a module script failed` pointing at one's own
10
+ // route loader. nothing in that names the file, the app, or the blocker, so
11
+ // it reads as a framework or vite bug and sends people restarting dev servers
12
+ // and clearing caches.
13
+ //
14
+ // a blocked request also fails `fetch()`, so walking the route's module graph
15
+ // from the browser finds the exact module the blocker refused. dev only, and
16
+ // only after a route has already failed to load.
17
+
18
+ const MAX_MODULES = 500
19
+ const TIMEOUT_MS = 5000
20
+
21
+ // vite rewrites bare specifiers to absolute paths, so most of these are
22
+ // already root-relative; the rest resolve against the importing module.
23
+ function collectImports(source: string, base: string): string[] {
24
+ const out: string[] = []
25
+ const add = (spec: string) => {
26
+ if (!/^[./]|^https?:/.test(spec)) return
27
+ try {
28
+ out.push(new URL(spec, base).href)
29
+ } catch {}
30
+ }
31
+ for (const m of source.matchAll(/(?:^|[\s;}])(?:import|export)[^'"]{0,300}?from\s*["']([^"']+)["']/g))
32
+ add(m[1])
33
+ for (const m of source.matchAll(/\bimport\s*\(\s*["']([^"']+)["']\s*\)/g)) add(m[1])
34
+ for (const m of source.matchAll(/(?:^|[\s;}])import\s*["']([^"']+)["']/g)) add(m[1])
35
+ return out
36
+ }
37
+
38
+ /**
39
+ * Walks the module graph under `entryUrl` looking for a module the browser
40
+ * refuses to fetch. Returns the blocked module's url, or null when every
41
+ * module in the graph is reachable (which means the import failed for some
42
+ * other reason, such as a syntax error inside one of them).
43
+ */
44
+ export async function findBlockedModule(entryUrl: string): Promise<string | null> {
45
+ const deadline = Date.now() + TIMEOUT_MS
46
+ const seen = new Set<string>()
47
+ let frontier = [new URL(entryUrl, window.location.href).href]
48
+
49
+ while (frontier.length && seen.size < MAX_MODULES && Date.now() < deadline) {
50
+ const batch = frontier.filter((url) => !seen.has(url)).slice(0, 32)
51
+ if (!batch.length) break
52
+ frontier = frontier.slice(batch.length)
53
+
54
+ const results = await Promise.all(
55
+ batch.map(async (url) => {
56
+ seen.add(url)
57
+ let res: Response
58
+ try {
59
+ res = await fetch(url)
60
+ } catch {
61
+ // the request never reached the server. in dev the server is
62
+ // demonstrably up (the page and one's own runtime loaded from it),
63
+ // so this is the browser refusing to make the request.
64
+ return { url, blocked: true, imports: [] as string[] }
65
+ }
66
+ if (!res.ok) return { url, blocked: false, imports: [] }
67
+ const type = res.headers.get('content-type') ?? ''
68
+ if (!type.includes('javascript')) return { url, blocked: false, imports: [] }
69
+ return { url, blocked: false, imports: collectImports(await res.text(), url) }
70
+ })
71
+ )
72
+
73
+ for (const result of results) {
74
+ if (result.blocked) return result.url
75
+ for (const next of result.imports) {
76
+ if (!seen.has(next)) frontier.push(next)
77
+ }
78
+ }
79
+ }
80
+
81
+ return null
82
+ }
83
+
84
+ /**
85
+ * Turns a failed route import into a message that names the responsible file.
86
+ * Returns null when the graph is fully reachable, leaving the original error
87
+ * as the only report.
88
+ */
89
+ export async function diagnoseRouteLoadFailure(
90
+ routeId: string,
91
+ routeUrl: string
92
+ ): Promise<string | null> {
93
+ const blocked = await findBlockedModule(routeUrl)
94
+ if (!blocked) return null
95
+
96
+ const path = blocked.replace(window.location.origin, '')
97
+ return [
98
+ `Route "${routeId}" failed to load because this browser refused to fetch ${path}`,
99
+ ``,
100
+ `That request never reached the dev server, so a content blocker or privacy`,
101
+ `extension is blocking it. In dev the module URL is the source path, and a`,
102
+ `path containing a word like "analytics", "pageview" or "track" matches the`,
103
+ `tracking rules those blockers ship.`,
104
+ ``,
105
+ `Rename the file or the directory so its path no longer matches, or allow`,
106
+ `${window.location.origin} in the blocker.`,
107
+ ].join('\n')
108
+ }
@@ -1,5 +1,6 @@
1
1
  import type { GlobbedRouteImports } from '../types'
2
2
  import type { One } from '../vite/types'
3
+ import { diagnoseRouteLoadFailure } from './diagnoseRouteLoadFailure'
3
4
  import { hmrImport } from './hmrImport'
4
5
 
5
6
  // essentially a development helper
@@ -303,6 +304,15 @@ export function globbedRoutesToRouteContext(
303
304
  message: err instanceof Error ? err.message : String(err),
304
305
  stack: err instanceof Error ? err.stack : undefined,
305
306
  })
307
+
308
+ // `Importing a module script failed` names neither the module nor
309
+ // the reason. when a browser content blocker refused one of the
310
+ // route's imports, walking the graph finds the exact file.
311
+ if (process.env.NODE_ENV === 'development' && routePaths[id]) {
312
+ diagnoseRouteLoadFailure(id, routePaths[id]).then((message) => {
313
+ if (message) console.error(message)
314
+ })
315
+ }
306
316
  }
307
317
  loadedRoutes[id] = {
308
318
  default: () => null,
@@ -924,7 +924,6 @@ url: ${url}`)
924
924
 
925
925
  for (const route of compiledManifest.apiRoutes) {
926
926
  app.get(route.urlPath, createHonoHandler(route))
927
- app.on('HEAD', route.urlPath, createHonoHandler(route))
928
927
  app.put(route.urlPath, createHonoHandler(route))
929
928
  app.post(route.urlPath, createHonoHandler(route))
930
929
  app.delete(route.urlPath, createHonoHandler(route))
@@ -933,7 +932,6 @@ url: ${url}`)
933
932
 
934
933
  if (route.urlPath !== route.urlCleanPath) {
935
934
  app.get(route.urlCleanPath, createHonoHandler(route))
936
- app.on('HEAD', route.urlCleanPath, createHonoHandler(route))
937
935
  app.put(route.urlCleanPath, createHonoHandler(route))
938
936
  app.post(route.urlCleanPath, createHonoHandler(route))
939
937
  app.delete(route.urlCleanPath, createHonoHandler(route))
@@ -0,0 +1,125 @@
1
+ import { afterEach, describe, expect, it, vi } from 'vitest'
2
+ import type { One } from '../vite/types'
3
+ import { createWorkerHandler, type LazyRoutes } from './workerHandler'
4
+
5
+ const pageRoute = {
6
+ file: 'app/some-page.tsx',
7
+ page: '/some-page',
8
+ namedRegex: '^/some-page(?:/)?$',
9
+ urlPath: '/some-page',
10
+ urlCleanPath: '/some-page',
11
+ routeKeys: {},
12
+ type: 'ssr',
13
+ middlewares: [],
14
+ } satisfies One.BuildInfo['manifest']['pageRoutes'][number]
15
+
16
+ const apiRoute = {
17
+ file: 'app/api/status+api.ts',
18
+ page: '/api/status',
19
+ namedRegex: '^/api/status(?:/)?$',
20
+ urlPath: '/api/status',
21
+ urlCleanPath: '/api/status',
22
+ routeKeys: {},
23
+ type: 'api',
24
+ middlewares: [],
25
+ } satisfies One.BuildInfo['manifest']['apiRoutes'][number]
26
+
27
+ const buildInfo = {
28
+ constants: { CACHE_KEY: 'test' },
29
+ routeToBuildInfo: {
30
+ [pageRoute.file]: {
31
+ type: 'ssr',
32
+ path: pageRoute.page,
33
+ routeFile: pageRoute.file,
34
+ middlewares: [],
35
+ preloadPath: '',
36
+ cssPreloadPath: '',
37
+ loaderPath: '',
38
+ cleanPath: pageRoute.page,
39
+ htmlPath: '',
40
+ clientJsPath: '',
41
+ serverJsPath: '',
42
+ params: {},
43
+ preloads: [],
44
+ css: [],
45
+ layoutCSS: [],
46
+ },
47
+ },
48
+ pathToRoute: {},
49
+ routeMap: {},
50
+ manifest: {
51
+ pageRoutes: [pageRoute],
52
+ apiRoutes: [apiRoute],
53
+ allRoutes: [pageRoute, apiRoute],
54
+ },
55
+ preloads: {},
56
+ cssPreloads: {},
57
+ loaders: {},
58
+ } satisfies One.BuildInfo
59
+
60
+ const lazyRoutes = {
61
+ serverEntry: async () => ({
62
+ default: {
63
+ render: () => '<html></html>',
64
+ },
65
+ }),
66
+ pages: {
67
+ [pageRoute.file]: async () => ({}),
68
+ },
69
+ api: {
70
+ [apiRoute.page]: async () => ({
71
+ HEAD: () => new Response(null, { status: 204 }),
72
+ }),
73
+ },
74
+ middlewares: {},
75
+ } satisfies LazyRoutes
76
+
77
+ function createHandler() {
78
+ vi.stubEnv('ONE_BUFFERED_SSR', '1')
79
+ return createWorkerHandler({
80
+ oneOptions: { web: { defaultRenderMode: 'ssr' } },
81
+ buildInfo,
82
+ lazyRoutes,
83
+ }).handleRequest
84
+ }
85
+
86
+ afterEach(() => {
87
+ vi.unstubAllEnvs()
88
+ })
89
+
90
+ describe('createWorkerHandler', () => {
91
+ it('answers HEAD page requests exactly like GET', async () => {
92
+ const handleRequest = createHandler()
93
+ const get = await handleRequest(new Request('https://example.com/some-page'))
94
+ const head = await handleRequest(
95
+ new Request('https://example.com/some-page', { method: 'HEAD' })
96
+ )
97
+
98
+ if (!(get instanceof Response) || !(head instanceof Response)) {
99
+ throw new Error('expected both GET and HEAD to return a Response')
100
+ }
101
+ // workerd drops the body for HEAD, so the handler must agree with GET on
102
+ // status and content type. asserting an empty body here would only be
103
+ // asserting the transport's job.
104
+ expect(get.status).toBe(200)
105
+ expect(head.status).toBe(get.status)
106
+ expect(head.headers.get('content-type')).toBe(get.headers.get('content-type'))
107
+ })
108
+
109
+ it('does not route POST requests to page handlers', async () => {
110
+ const response = await createHandler()(
111
+ new Request('https://example.com/some-page', { method: 'POST' })
112
+ )
113
+
114
+ expect(response).toBeNull()
115
+ })
116
+
117
+ it('continues routing HEAD requests to API handlers', async () => {
118
+ const response = await createHandler()(
119
+ new Request('https://example.com/api/status', { method: 'HEAD' })
120
+ )
121
+
122
+ expect(response).toBeInstanceOf(Response)
123
+ expect(response?.status).toBe(204)
124
+ })
125
+ })
@@ -7,6 +7,7 @@ import {
7
7
  compileManifest,
8
8
  getSubdomain,
9
9
  getURLfromRequestURL,
10
+ isPageRequestMethod,
10
11
  type RequestHandlers,
11
12
  resolveAPIRoute,
12
13
  resolveLoaderRoute,
@@ -706,8 +707,8 @@ export function createWorkerHandler(options: WorkerHandlerOptions) {
706
707
  }
707
708
  }
708
709
 
709
- // 6. page routes (GET only)
710
- if (method === 'GET') {
710
+ // 6. page routes
711
+ if (isPageRequestMethod(method)) {
711
712
  for (const route of compiledManifest.pageRoutes) {
712
713
  if (!route.compiledRegex.test(pathname)) continue
713
714