@solidjs/start 1.2.1 → 2.0.0-alpha.1

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 (191) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +2 -2
  3. package/dist/client/StartClient.d.ts +0 -1
  4. package/dist/client/StartClient.jsx +2 -4
  5. package/dist/client/index.d.ts +2 -3
  6. package/dist/client/index.jsx +2 -4
  7. package/dist/client/mount.d.ts +1 -7
  8. package/dist/client/mount.js +1 -91
  9. package/dist/client/spa/index.d.ts +1 -2
  10. package/dist/client/spa/index.jsx +1 -2
  11. package/dist/config/constants.d.ts +16 -0
  12. package/dist/config/constants.js +16 -0
  13. package/dist/config/dev-server.d.ts +2 -0
  14. package/dist/config/dev-server.js +108 -0
  15. package/dist/config/fs-router.d.ts +37 -0
  16. package/dist/config/fs-router.js +141 -0
  17. package/dist/config/fs-routes/fs-watcher.d.ts +3 -0
  18. package/dist/config/fs-routes/fs-watcher.js +49 -0
  19. package/dist/config/fs-routes/index.d.ts +7 -0
  20. package/dist/config/fs-routes/index.js +107 -0
  21. package/dist/config/fs-routes/router.d.ts +37 -0
  22. package/dist/config/fs-routes/router.js +136 -0
  23. package/dist/config/fs-routes/tree-shake.d.ts +2 -0
  24. package/dist/config/fs-routes/tree-shake.js +348 -0
  25. package/dist/config/index.d.ts +10 -0
  26. package/dist/config/index.js +214 -0
  27. package/dist/config/lazy.d.ts +3 -0
  28. package/dist/config/lazy.js +82 -0
  29. package/dist/config/manifest.d.ts +3 -0
  30. package/dist/config/manifest.js +74 -0
  31. package/dist/config/utils.d.ts +4 -0
  32. package/dist/config/utils.js +6 -0
  33. package/dist/config/vite-utils.d.ts +16 -0
  34. package/dist/config/vite-utils.js +81 -0
  35. package/dist/http/index.d.ts +81 -0
  36. package/dist/http/index.js +129 -0
  37. package/dist/index.d.ts +5 -5
  38. package/dist/index.jsx +5 -5
  39. package/dist/middleware/index.d.ts +6 -6
  40. package/dist/middleware/index.js +39 -0
  41. package/dist/{router/FileRoutes.js → router.jsx} +7 -8
  42. package/dist/server/StartServer.d.ts +3 -3
  43. package/dist/server/StartServer.jsx +9 -63
  44. package/dist/server/assets/index.d.ts +2 -0
  45. package/dist/server/assets/index.js +52 -0
  46. package/dist/server/assets/render.d.ts +19 -0
  47. package/dist/server/{renderAsset.jsx → assets/render.jsx} +1 -1
  48. package/dist/server/collect-styles.d.ts +3 -0
  49. package/dist/server/collect-styles.js +69 -0
  50. package/dist/server/fetchEvent.d.ts +4 -3
  51. package/dist/server/fetchEvent.js +12 -74
  52. package/dist/server/fetchEvent.spec.js +18 -197
  53. package/dist/server/handler.d.ts +6 -12
  54. package/dist/server/handler.js +193 -96
  55. package/dist/server/index.d.ts +9 -6
  56. package/dist/server/index.jsx +14 -6
  57. package/dist/server/manifest/client-manifest.d.ts +9 -0
  58. package/dist/server/manifest/client-manifest.js +6 -0
  59. package/dist/server/manifest/dev-client-manifest.d.ts +4 -0
  60. package/dist/server/manifest/dev-client-manifest.js +24 -0
  61. package/dist/server/manifest/dev-ssr-manifest.d.ts +5 -0
  62. package/dist/server/manifest/dev-ssr-manifest.js +23 -0
  63. package/dist/server/manifest/prod-client-manifest.d.ts +5 -0
  64. package/dist/server/manifest/prod-client-manifest.js +26 -0
  65. package/dist/server/manifest/prod-ssr-manifest.d.ts +6 -0
  66. package/dist/server/manifest/prod-ssr-manifest.js +85 -0
  67. package/dist/server/manifest/ssr-manifest.d.ts +4 -0
  68. package/dist/server/manifest/ssr-manifest.js +6 -0
  69. package/dist/{router → server}/routes.d.ts +5 -10
  70. package/dist/{router → server}/routes.js +32 -33
  71. package/dist/{runtime → server}/server-fns-runtime.d.ts +1 -1
  72. package/dist/{runtime → server}/server-fns-runtime.js +8 -7
  73. package/dist/server/server-functions-handler.d.ts +2 -0
  74. package/dist/{runtime/server-handler.js → server/server-functions-handler.js} +57 -97
  75. package/dist/server/server-runtime.d.ts +3 -0
  76. package/dist/{runtime → server}/server-runtime.js +17 -24
  77. package/dist/server/spa/StartServer.d.ts +1 -1
  78. package/dist/server/spa/StartServer.jsx +7 -9
  79. package/dist/server/spa/handler.d.ts +3 -3
  80. package/dist/server/spa/handler.js +9 -11
  81. package/dist/server/spa/index.d.ts +4 -3
  82. package/dist/server/spa/index.jsx +4 -3
  83. package/dist/server/types.d.ts +7 -9
  84. package/dist/server/util.d.ts +2 -0
  85. package/dist/server/util.js +8 -0
  86. package/dist/shared/ErrorBoundary.jsx +2 -2
  87. package/dist/shared/HttpHeader.jsx +4 -2
  88. package/dist/shared/{HttpStatusCode.jsx → HttpStatusCode.js} +3 -1
  89. package/dist/shared/dev-overlay/CodeView.d.ts +1 -1
  90. package/dist/shared/dev-overlay/CodeView.jsx +23 -34
  91. package/dist/shared/dev-overlay/DevOverlayDialog.d.ts +2 -2
  92. package/dist/shared/dev-overlay/DevOverlayDialog.jsx +73 -64
  93. package/dist/shared/dev-overlay/createStackFrame.js +3 -3
  94. package/dist/shared/dev-overlay/get-source-map.d.ts +1 -1
  95. package/dist/shared/dev-overlay/get-source-map.js +5 -5
  96. package/dist/shared/dev-overlay/icons.d.ts +10 -10
  97. package/dist/shared/dev-overlay/icons.jsx +91 -91
  98. package/dist/shared/dev-overlay/index.jsx +6 -9
  99. package/dist/shared/lazy.d.ts +3 -0
  100. package/dist/shared/lazy.js +42 -0
  101. package/dist/shared/serverFunction.d.ts +1 -1
  102. package/env.d.ts +2 -19
  103. package/package.json +44 -65
  104. package/scripts/build.js +15 -0
  105. package/scripts/validate-imports.js +316 -0
  106. package/src/client/StartClient.tsx +34 -0
  107. package/src/client/index.tsx +2 -0
  108. package/src/client/mount.ts +1 -0
  109. package/src/client/spa/index.tsx +9 -0
  110. package/src/config/constants.ts +19 -0
  111. package/src/config/dev-server.ts +145 -0
  112. package/{config/fs-router.js → src/config/fs-router.ts} +49 -29
  113. package/src/config/fs-routes/fs-watcher.ts +77 -0
  114. package/src/config/fs-routes/index.ts +136 -0
  115. package/src/config/fs-routes/router.ts +169 -0
  116. package/src/config/fs-routes/tree-shake.ts +371 -0
  117. package/src/config/index.ts +233 -0
  118. package/src/config/lazy.ts +100 -0
  119. package/src/config/manifest.ts +84 -0
  120. package/src/config/utils.ts +5 -0
  121. package/src/config/vite-utils.ts +109 -0
  122. package/src/env.d.ts +17 -0
  123. package/src/http/index.ts +216 -0
  124. package/src/index.tsx +7 -0
  125. package/src/internal.d.ts +14 -0
  126. package/src/middleware/index.ts +72 -0
  127. package/src/router.tsx +37 -0
  128. package/src/server/StartServer.tsx +56 -0
  129. package/src/server/assets/index.ts +70 -0
  130. package/src/server/assets/render.tsx +46 -0
  131. package/src/server/collect-styles.ts +87 -0
  132. package/src/server/fetchEvent.spec.ts +85 -0
  133. package/src/server/fetchEvent.ts +36 -0
  134. package/src/server/handler.ts +257 -0
  135. package/src/server/index.tsx +29 -0
  136. package/src/server/manifest/client-manifest.ts +8 -0
  137. package/src/server/manifest/dev-client-manifest.ts +21 -0
  138. package/src/server/manifest/dev-ssr-manifest.ts +23 -0
  139. package/src/server/manifest/prod-client-manifest.ts +21 -0
  140. package/src/server/manifest/prod-ssr-manifest.ts +109 -0
  141. package/src/server/manifest/ssr-manifest.ts +10 -0
  142. package/src/server/routes.ts +126 -0
  143. package/src/server/server-fns-runtime.ts +31 -0
  144. package/src/server/server-functions-handler.ts +339 -0
  145. package/src/server/server-runtime.ts +240 -0
  146. package/src/server/spa/StartServer.tsx +40 -0
  147. package/src/server/spa/handler.ts +29 -0
  148. package/src/server/spa/index.tsx +5 -0
  149. package/src/server/types.ts +82 -0
  150. package/src/server/util.ts +12 -0
  151. package/src/shared/ErrorBoundary.tsx +52 -0
  152. package/src/shared/GET.ts +8 -0
  153. package/src/shared/HttpHeader.tsx +42 -0
  154. package/src/shared/HttpStatusCode.ts +27 -0
  155. package/src/shared/clientOnly.ts +44 -0
  156. package/src/shared/dev-overlay/CodeView.tsx +92 -0
  157. package/src/shared/dev-overlay/DevOverlayDialog.tsx +316 -0
  158. package/src/shared/dev-overlay/createStackFrame.ts +74 -0
  159. package/src/shared/dev-overlay/download.ts +6 -0
  160. package/src/shared/dev-overlay/env.d.ts +4 -0
  161. package/src/shared/dev-overlay/get-source-map.ts +33 -0
  162. package/src/shared/dev-overlay/icons.tsx +456 -0
  163. package/src/shared/dev-overlay/index.tsx +63 -0
  164. package/src/shared/dev-overlay/styles.css +292 -0
  165. package/src/shared/lazy.ts +52 -0
  166. package/src/shared/serverFunction.ts +10 -0
  167. package/src/virtual.d.ts +27 -0
  168. package/tsconfig.json +23 -0
  169. package/vitest.config.ts +9 -0
  170. package/config/index.d.ts +0 -40
  171. package/config/index.js +0 -304
  172. package/config/server-components.js +0 -106
  173. package/dist/client/islands.d.ts +0 -7
  174. package/dist/client/islands.jsx +0 -10
  175. package/dist/middleware/index.jsx +0 -35
  176. package/dist/router/index.d.ts +0 -1
  177. package/dist/router/index.jsx +0 -2
  178. package/dist/router/lazyRoute.d.ts +0 -6
  179. package/dist/router/lazyRoute.js +0 -53
  180. package/dist/runtime/server-handler.d.ts +0 -2
  181. package/dist/runtime/server-runtime.d.ts +0 -3
  182. package/dist/server/islands/index.d.ts +0 -19
  183. package/dist/server/islands/index.jsx +0 -62
  184. package/dist/server/islands/utils.d.ts +0 -1
  185. package/dist/server/islands/utils.jsx +0 -85
  186. package/dist/server/pageEvent.d.ts +0 -2
  187. package/dist/server/pageEvent.js +0 -56
  188. package/dist/server/renderAsset.d.ts +0 -2
  189. /package/dist/{router/FileRoutes.d.ts → router.d.ts} +0 -0
  190. /package/dist/shared/{clientOnly.jsx → clientOnly.js} +0 -0
  191. /package/dist/shared/{serverFunction.jsx → serverFunction.js} +0 -0
@@ -1,62 +1,35 @@
1
+ import * as h3 from "h3";
1
2
  import { beforeEach, describe, expect, it, vi } from "vitest";
2
- import { cloneEvent, createFetchEvent, getFetchEvent, mergeResponseHeaders } from "./fetchEvent";
3
- vi.mock("vinxi/http", () => ({
4
- getWebRequest: vi.fn(),
5
- getRequestIP: vi.fn(),
6
- getResponseStatus: vi.fn(),
7
- setResponseStatus: vi.fn(),
8
- getResponseStatusText: vi.fn(),
9
- getResponseHeader: vi.fn(),
10
- getResponseHeaders: vi.fn(),
11
- setResponseHeader: vi.fn(),
12
- appendResponseHeader: vi.fn(),
13
- removeResponseHeader: vi.fn()
14
- }));
15
- import * as vinxiHttp from "vinxi/http";
16
- const mockedVinxiHttp = vi.mocked(vinxiHttp);
17
- const createMockH3Event = () => {
18
- const mockRequest = new Request("http://localhost/test");
19
- const mockStatus = 200;
20
- const mockStatusText = "OK";
3
+ import { createFetchEvent, getFetchEvent, mergeResponseHeaders } from "./fetchEvent.js";
4
+ vi.mock(import("h3"), async (mod) => {
21
5
  return {
22
- node: {
23
- req: {},
24
- res: {
25
- statusCode: mockStatus,
26
- statusMessage: mockStatusText
27
- }
28
- },
29
- context: {},
30
- web: {
31
- request: mockRequest
32
- }
6
+ ...(await mod()),
7
+ getRequestIP: vi.fn(),
33
8
  };
9
+ });
10
+ const mockedH3 = vi.mocked(h3);
11
+ const createMockH3Event = () => {
12
+ const event = new h3.H3Event(new Request("http://localhost/test"));
13
+ event.res.status = 200;
14
+ event.res.statusText = "OK";
15
+ return event;
34
16
  };
35
17
  describe("fetchEvent", () => {
36
18
  let mockH3Event;
37
19
  beforeEach(() => {
38
20
  mockH3Event = createMockH3Event();
39
21
  vi.clearAllMocks();
40
- mockedVinxiHttp.getWebRequest.mockReturnValue(mockH3Event.web?.request);
41
- mockedVinxiHttp.getRequestIP.mockReturnValue("127.0.0.1");
42
- mockedVinxiHttp.getResponseStatus.mockReturnValue(200);
43
- mockedVinxiHttp.setResponseStatus.mockImplementation(() => { });
44
- mockedVinxiHttp.getResponseStatusText.mockReturnValue("OK");
45
- mockedVinxiHttp.getResponseHeader.mockReturnValue(undefined);
46
- mockedVinxiHttp.getResponseHeaders.mockReturnValue({});
47
- mockedVinxiHttp.setResponseHeader.mockImplementation(() => { });
48
- mockedVinxiHttp.appendResponseHeader.mockImplementation(() => { });
49
- mockedVinxiHttp.removeResponseHeader.mockImplementation(() => { });
22
+ mockedH3.getRequestIP.mockReturnValue("127.0.0.1");
50
23
  });
51
24
  describe("createFetchEvent", () => {
52
25
  it("should create a FetchEvent from H3Event", () => {
53
26
  const fetchEvent = createFetchEvent(mockH3Event);
54
27
  expect(fetchEvent).toEqual({
55
- request: mockH3Event.web?.request,
28
+ request: mockH3Event.req,
56
29
  response: expect.any(Object),
57
30
  clientAddress: "127.0.0.1",
58
31
  locals: {},
59
- nativeEvent: mockH3Event
32
+ nativeEvent: mockH3Event,
60
33
  });
61
34
  });
62
35
  it("should create response stub with correct properties", () => {
@@ -66,16 +39,6 @@ describe("fetchEvent", () => {
66
39
  expect(fetchEvent.response).toHaveProperty("headers");
67
40
  });
68
41
  });
69
- describe("cloneEvent", () => {
70
- it("should create a shallow copy of FetchEvent", () => {
71
- const original = createFetchEvent(mockH3Event);
72
- const cloned = cloneEvent(original);
73
- expect(cloned).toEqual(original);
74
- expect(cloned).not.toBe(original);
75
- expect(cloned.request).toBe(original.request);
76
- expect(cloned.response).toBe(original.response);
77
- });
78
- });
79
42
  describe("getFetchEvent", () => {
80
43
  it("should create and cache FetchEvent on first call", () => {
81
44
  const fetchEvent = getFetchEvent(mockH3Event);
@@ -92,153 +55,11 @@ describe("fetchEvent", () => {
92
55
  it("should merge headers from Headers object to H3Event", () => {
93
56
  const headers = new Headers({
94
57
  "content-type": "application/json",
95
- "x-custom": "value"
58
+ "x-custom": "value",
96
59
  });
97
60
  mergeResponseHeaders(mockH3Event, headers);
98
- expect(mockedVinxiHttp.appendResponseHeader).toHaveBeenCalledWith(mockH3Event, "content-type", "application/json");
99
- expect(mockedVinxiHttp.appendResponseHeader).toHaveBeenCalledWith(mockH3Event, "x-custom", "value");
100
- });
101
- });
102
- describe("ResponseStub", () => {
103
- let fetchEvent;
104
- beforeEach(() => {
105
- fetchEvent = createFetchEvent(mockH3Event);
106
- });
107
- describe("status", () => {
108
- it("should get status from H3Event", () => {
109
- expect(fetchEvent.response.status).toBe(200);
110
- });
111
- it("should set status on H3Event", () => {
112
- fetchEvent.response.status = 404;
113
- expect(mockedVinxiHttp.setResponseStatus).toHaveBeenCalledWith(mockH3Event, 404);
114
- });
115
- });
116
- describe("statusText", () => {
117
- it("should get statusText from H3Event", () => {
118
- expect(fetchEvent.response.statusText).toBe("OK");
119
- });
120
- it("should set statusText on H3Event", () => {
121
- fetchEvent.response.statusText = "Not Found";
122
- expect(mockedVinxiHttp.setResponseStatus).toHaveBeenCalledWith(mockH3Event, 200, "Not Found");
123
- });
124
- });
125
- });
126
- describe("HeaderProxy", () => {
127
- let fetchEvent;
128
- beforeEach(() => {
129
- fetchEvent = createFetchEvent(mockH3Event);
130
- });
131
- describe("get", () => {
132
- it("should return null for non-existent header", () => {
133
- expect(fetchEvent.response.headers.get("non-existent")).toBe(null);
134
- });
135
- it("should return string value for single header", () => {
136
- mockedVinxiHttp.getResponseHeader.mockReturnValue("application/json");
137
- expect(fetchEvent.response.headers.get("content-type")).toBe("application/json");
138
- });
139
- it("should join array values with comma", () => {
140
- mockedVinxiHttp.getResponseHeader.mockReturnValue(["text/html", "application/json"]);
141
- expect(fetchEvent.response.headers.get("accept")).toBe("text/html, application/json");
142
- });
143
- });
144
- describe("has", () => {
145
- it("should return false for non-existent header", () => {
146
- mockedVinxiHttp.getResponseHeader.mockReturnValue(undefined);
147
- expect(fetchEvent.response.headers.has("non-existent")).toBe(false);
148
- });
149
- it("should return true for existing header", () => {
150
- mockedVinxiHttp.getResponseHeader.mockReturnValue("application/json");
151
- expect(fetchEvent.response.headers.has("content-type")).toBe(true);
152
- });
153
- });
154
- describe("set", () => {
155
- it("should set header value", () => {
156
- fetchEvent.response.headers.set("content-type", "application/json");
157
- expect(mockedVinxiHttp.setResponseHeader).toHaveBeenCalledWith(mockH3Event, "content-type", "application/json");
158
- });
159
- });
160
- describe("delete", () => {
161
- it("should remove header", () => {
162
- fetchEvent.response.headers.delete("content-type");
163
- expect(mockedVinxiHttp.removeResponseHeader).toHaveBeenCalledWith(mockH3Event, "content-type");
164
- });
165
- });
166
- describe("append", () => {
167
- it("should append header value", () => {
168
- fetchEvent.response.headers.append("x-custom", "value");
169
- expect(mockedVinxiHttp.appendResponseHeader).toHaveBeenCalledWith(mockH3Event, "x-custom", "value");
170
- });
171
- });
172
- describe("getSetCookie", () => {
173
- it("should return array for single cookie", () => {
174
- mockedVinxiHttp.getResponseHeader.mockReturnValue("session=abc123");
175
- expect(fetchEvent.response.headers.getSetCookie()).toEqual(["session=abc123"]);
176
- });
177
- it("should return array for multiple cookies", () => {
178
- mockedVinxiHttp.getResponseHeader.mockReturnValue(["session=abc123", "theme=dark"]);
179
- expect(fetchEvent.response.headers.getSetCookie()).toEqual([
180
- "session=abc123",
181
- "theme=dark"
182
- ]);
183
- });
184
- });
185
- describe("forEach", () => {
186
- it("should iterate over headers", () => {
187
- mockedVinxiHttp.getResponseHeaders.mockReturnValue({
188
- "content-type": "application/json",
189
- "x-custom": ["value1", "value2"]
190
- });
191
- const callback = vi.fn();
192
- fetchEvent.response.headers.forEach(callback);
193
- expect(callback).toHaveBeenCalledWith("application/json", "content-type", expect.any(Object));
194
- expect(callback).toHaveBeenCalledWith("value1, value2", "x-custom", expect.any(Object));
195
- });
196
- });
197
- describe("entries", () => {
198
- it("should return iterator of header entries", () => {
199
- mockedVinxiHttp.getResponseHeaders.mockReturnValue({
200
- "content-type": "application/json",
201
- "x-custom": ["value1", "value2"]
202
- });
203
- const entries = Array.from(fetchEvent.response.headers.entries());
204
- expect(entries).toEqual([
205
- ["content-type", "application/json"],
206
- ["x-custom", "value1, value2"]
207
- ]);
208
- });
209
- });
210
- describe("keys", () => {
211
- it("should return iterator of header keys", () => {
212
- mockedVinxiHttp.getResponseHeaders.mockReturnValue({
213
- "content-type": "application/json",
214
- "x-custom": "value"
215
- });
216
- const keys = Array.from(fetchEvent.response.headers.keys());
217
- expect(keys).toEqual(["content-type", "x-custom"]);
218
- });
219
- });
220
- describe("values", () => {
221
- it("should return iterator of header values", () => {
222
- mockedVinxiHttp.getResponseHeaders.mockReturnValue({
223
- "content-type": "application/json",
224
- "x-custom": ["value1", "value2"]
225
- });
226
- const values = Array.from(fetchEvent.response.headers.values());
227
- expect(values).toEqual(["application/json", "value1, value2"]);
228
- });
229
- });
230
- describe("Symbol.iterator", () => {
231
- it("should be iterable", () => {
232
- mockedVinxiHttp.getResponseHeaders.mockReturnValue({
233
- "content-type": "application/json",
234
- "x-custom": "value"
235
- });
236
- const entries = Array.from(fetchEvent.response.headers);
237
- expect(entries).toEqual([
238
- ["content-type", "application/json"],
239
- ["x-custom", "value"]
240
- ]);
241
- });
61
+ expect(headers.get("content-type")).toBe("application/json");
62
+ expect(headers.get("x-custom")).toBe("value");
242
63
  });
243
64
  });
244
65
  });
@@ -1,12 +1,6 @@
1
- import type { FetchEvent, HandlerOptions, PageEvent, ResponseStub } from "./types";
2
- /**
3
- * Checks if user has set a redirect status in the response.
4
- * If not, falls back to the 302 (temporary redirect)
5
- */
6
- export declare function getExpectedRedirectStatus(response: ResponseStub): number;
7
- export declare function createBaseHandler(fn: (context: PageEvent) => unknown, createPageEvent: (event: FetchEvent) => Promise<PageEvent>, options?: HandlerOptions | ((context: PageEvent) => HandlerOptions | Promise<HandlerOptions>), routerLoad?: (event: FetchEvent) => Promise<void>): import("vinxi/http").EventHandler<import("vinxi/http").EventHandlerRequest, Promise<any>>;
8
- /**
9
- *
10
- * Read more: https://docs.solidjs.com/solid-start/reference/server/create-handler
11
- */
12
- export declare function createHandler(fn: (context: PageEvent) => unknown, options?: HandlerOptions | ((context: PageEvent) => HandlerOptions | Promise<HandlerOptions>), routerLoad?: (event: FetchEvent) => Promise<void>): import("vinxi/http").EventHandler<import("vinxi/http").EventHandlerRequest, Promise<any>>;
1
+ import { H3 } from "h3";
2
+ import type { JSX } from "solid-js";
3
+ import type { FetchEvent, HandlerOptions, PageEvent } from "./types.ts";
4
+ export declare function createBaseHandler(createPageEvent: (e: FetchEvent) => Promise<PageEvent>, fn: (context: PageEvent) => JSX.Element, options?: HandlerOptions | ((context: PageEvent) => HandlerOptions | Promise<HandlerOptions>)): H3;
5
+ export declare function createHandler(fn: (context: PageEvent) => JSX.Element, options?: HandlerOptions | ((context: PageEvent) => HandlerOptions | Promise<HandlerOptions>)): H3;
6
+ export declare function createPageEvent(ctx: FetchEvent): Promise<PageEvent>;
@@ -1,108 +1,175 @@
1
+ import middleware from "solid-start:middleware";
2
+ import { defineHandler, getCookie, H3, redirect, setCookie } from "h3";
1
3
  import { sharedConfig } from "solid-js";
2
- import { renderToStream, renderToString } from "solid-js/web";
3
- import { provideRequestEvent } from "solid-js/web/storage";
4
- import { eventHandler, sendRedirect, setHeader, setResponseStatus } from "vinxi/http";
5
- import { matchAPIRoute } from "../router/routes";
6
- import { getFetchEvent } from "./fetchEvent";
7
- import { createPageEvent } from "./pageEvent";
8
- // according to https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#redirection_messages
9
- const validRedirectStatuses = new Set([301, 302, 303, 307, 308]);
10
- /**
11
- * Checks if user has set a redirect status in the response.
12
- * If not, falls back to the 302 (temporary redirect)
13
- */
14
- export function getExpectedRedirectStatus(response) {
15
- if (response.status && validRedirectStatuses.has(response.status)) {
16
- return response.status;
17
- }
18
- return 302;
19
- }
20
- export function createBaseHandler(fn, createPageEvent, options = {}, routerLoad) {
21
- return eventHandler({
22
- handler: (e) => {
23
- const event = getFetchEvent(e);
24
- return provideRequestEvent(event, async () => {
25
- if (routerLoad) {
26
- await routerLoad(event);
27
- }
28
- // api
29
- const match = matchAPIRoute(new URL(event.request.url).pathname, event.request.method);
30
- if (match) {
31
- const mod = await match.handler.import();
32
- const fn = event.request.method === "HEAD" ? mod["HEAD"] || mod["GET"] : mod[event.request.method];
33
- event.params = match.params || {};
34
- // @ts-ignore
35
- sharedConfig.context = { event };
36
- const res = await fn(event);
37
- if (res !== undefined)
38
- return res;
39
- if (event.request.method !== "GET") {
40
- throw new Error(`API handler for ${event.request.method} "${event.request.url}" did not return a response.`);
41
- }
42
- if (!match.isPage)
43
- return;
44
- }
45
- // render
46
- const context = await createPageEvent(event);
47
- const resolvedOptions = typeof options == "function" ? await options(context) : { ...options };
48
- const mode = resolvedOptions.mode || "stream";
49
- // @ts-ignore
50
- if (resolvedOptions.nonce)
51
- context.nonce = resolvedOptions.nonce;
52
- if (mode === "sync" || !import.meta.env.START_SSR) {
53
- const html = renderToString(() => {
54
- sharedConfig.context.event = context;
55
- return fn(context);
56
- }, resolvedOptions);
57
- context.complete = true;
58
- if (context.response && context.response.headers.get("Location")) {
59
- const status = getExpectedRedirectStatus(context.response);
60
- return sendRedirect(e, context.response.headers.get("Location"), status);
61
- }
62
- return html;
63
- }
64
- if (resolvedOptions.onCompleteAll) {
65
- const og = resolvedOptions.onCompleteAll;
66
- resolvedOptions.onCompleteAll = options => {
67
- handleStreamCompleteRedirect(context)(options);
68
- og(options);
69
- };
4
+ import { getRequestEvent, renderToStream, renderToString } from "solid-js/web";
5
+ import { createRoutes } from "../router.jsx";
6
+ import { decorateHandler, decorateMiddleware } from "./fetchEvent.js";
7
+ import { getSsrManifest } from "./manifest/ssr-manifest.js";
8
+ import { matchAPIRoute } from "./routes.js";
9
+ import { handleServerFunction } from "./server-functions-handler.js";
10
+ import { getExpectedRedirectStatus } from "./util.js";
11
+ const SERVER_FN_BASE = "/_server";
12
+ export function createBaseHandler(createPageEvent, fn, options = {}) {
13
+ const handler = defineHandler({
14
+ middleware: middleware.length ? middleware.map(decorateMiddleware) : undefined,
15
+ handler: decorateHandler(async (e) => {
16
+ const event = getRequestEvent();
17
+ const url = new URL(event.request.url);
18
+ const pathname = stripBaseUrl(url.pathname);
19
+ if (pathname.startsWith(SERVER_FN_BASE)) {
20
+ const serverFnResponse = await handleServerFunction(e);
21
+ if (serverFnResponse instanceof Response)
22
+ return produceResponseWithEventHeaders(serverFnResponse);
23
+ return new Response(serverFnResponse, {
24
+ headers: e.res.headers,
25
+ });
26
+ }
27
+ const match = matchAPIRoute(pathname, event.request.method);
28
+ if (match) {
29
+ const mod = await match.handler.import();
30
+ const fn = event.request.method === "HEAD" ? mod["HEAD"] || mod["GET"] : mod[event.request.method];
31
+ event.params = match.params || {};
32
+ // @ts-expect-error
33
+ sharedConfig.context = { event };
34
+ const res = await fn(event);
35
+ if (res !== undefined) {
36
+ if (res instanceof Response)
37
+ return produceResponseWithEventHeaders(res);
38
+ return res;
70
39
  }
71
- else
72
- resolvedOptions.onCompleteAll = handleStreamCompleteRedirect(context);
73
- if (resolvedOptions.onCompleteShell) {
74
- const og = resolvedOptions.onCompleteShell;
75
- resolvedOptions.onCompleteShell = options => {
76
- handleShellCompleteRedirect(context, e)();
77
- og(options);
78
- };
40
+ if (event.request.method !== "GET") {
41
+ throw new Error(`API handler for ${event.request.method} "${event.request.url}" did not return a response.`);
79
42
  }
80
- else
81
- resolvedOptions.onCompleteShell = handleShellCompleteRedirect(context, e);
82
- const stream = renderToStream(() => {
43
+ if (!match.isPage)
44
+ return;
45
+ }
46
+ const context = await createPageEvent(event);
47
+ const resolvedOptions = typeof options === "function" ? await options(context) : { ...options };
48
+ const mode = resolvedOptions.mode || "stream";
49
+ if (resolvedOptions.nonce)
50
+ context.nonce = resolvedOptions.nonce;
51
+ if (mode === "sync" || !import.meta.env.START_SSR) {
52
+ const html = renderToString(() => {
83
53
  sharedConfig.context.event = context;
84
54
  return fn(context);
85
55
  }, resolvedOptions);
56
+ context.complete = true;
86
57
  if (context.response && context.response.headers.get("Location")) {
87
58
  const status = getExpectedRedirectStatus(context.response);
88
- return sendRedirect(e, context.response.headers.get("Location"), status);
59
+ return redirect(context.response.headers.get("Location"), status);
89
60
  }
90
- if (mode === "async")
91
- return stream;
92
- // fix cloudflare streaming
93
- const { writable, readable } = new TransformStream();
94
- stream.pipeTo(writable);
95
- return readable;
96
- });
97
- }
61
+ return html;
62
+ }
63
+ if (resolvedOptions.onCompleteAll) {
64
+ const og = resolvedOptions.onCompleteAll;
65
+ resolvedOptions.onCompleteAll = options => {
66
+ handleStreamCompleteRedirect(context)(options);
67
+ og(options);
68
+ };
69
+ }
70
+ else
71
+ resolvedOptions.onCompleteAll = handleStreamCompleteRedirect(context);
72
+ if (resolvedOptions.onCompleteShell) {
73
+ const og = resolvedOptions.onCompleteShell;
74
+ resolvedOptions.onCompleteShell = options => {
75
+ handleShellCompleteRedirect(context, e)();
76
+ og(options);
77
+ };
78
+ }
79
+ else
80
+ resolvedOptions.onCompleteShell = handleShellCompleteRedirect(context, e);
81
+ const _stream = renderToStream(() => {
82
+ sharedConfig.context.event = context;
83
+ return fn(context);
84
+ }, resolvedOptions);
85
+ const stream = _stream; // stream has a hidden 'then' method
86
+ if (context.response && context.response.headers.get("Location")) {
87
+ const status = getExpectedRedirectStatus(context.response);
88
+ return redirect(context.response.headers.get("Location"), status);
89
+ }
90
+ if (mode === "async")
91
+ return await stream;
92
+ delete stream.then;
93
+ // using TransformStream in dev can cause solid-start-dev-server to crash
94
+ // when stream is cancelled
95
+ if (globalThis.USING_SOLID_START_DEV_SERVER)
96
+ return stream;
97
+ // returning stream directly breaks cloudflare workers
98
+ const { writable, readable } = new TransformStream();
99
+ stream.pipeTo(writable);
100
+ return readable;
101
+ }),
98
102
  });
103
+ const app = new H3();
104
+ app.use(handler);
105
+ return app;
106
+ }
107
+ export function createHandler(fn, options = {}) {
108
+ return createBaseHandler(createPageEvent, fn, options);
109
+ }
110
+ export async function createPageEvent(ctx) {
111
+ ctx.response.headers.set("Content-Type", "text/html");
112
+ // const prevPath = ctx.request.headers.get("x-solid-referrer");
113
+ // const mutation = ctx.request.headers.get("x-solid-mutation") === "true";
114
+ const manifest = getSsrManifest(import.meta.env.SSR && import.meta.env.DEV ? "ssr" : "client");
115
+ // Handle Vite build.cssCodeSplit
116
+ // When build.cssCodeSplit is false, a single CSS file is generated with the key style.css
117
+ const mergedCSS = import.meta.env.PROD ? await manifest.getAssets("style.css") : [];
118
+ const assets = [
119
+ ...mergedCSS,
120
+ ...(await manifest.getAssets(import.meta.env.START_CLIENT_ENTRY)),
121
+ ...(await manifest.getAssets(import.meta.env.START_APP_ENTRY)),
122
+ // ...(import.meta.env.START_ISLANDS
123
+ // ? (await serverManifest.inputs[serverManifest.handler]!.assets()).filter(
124
+ // s => (s as any).attrs.rel !== "modulepreload"
125
+ // )
126
+ // : [])
127
+ ];
128
+ const pageEvent = Object.assign(ctx, {
129
+ assets,
130
+ router: {
131
+ submission: initFromFlash(ctx),
132
+ },
133
+ routes: createRoutes(),
134
+ // prevUrl: prevPath || "",
135
+ // mutation: mutation,
136
+ // $type: FETCH_EVENT,
137
+ complete: false,
138
+ $islands: new Set(),
139
+ });
140
+ return pageEvent;
141
+ }
142
+ function initFromFlash(ctx) {
143
+ const flash = getCookie(ctx.nativeEvent, "flash");
144
+ if (!flash)
145
+ return;
146
+ try {
147
+ const param = JSON.parse(flash);
148
+ if (!param || !param.result)
149
+ return;
150
+ const input = [...param.input.slice(0, -1), new Map(param.input[param.input.length - 1])];
151
+ const result = param.error ? new Error(param.result) : param.result;
152
+ return {
153
+ input,
154
+ url: param.url,
155
+ pending: false,
156
+ result: param.thrown ? undefined : result,
157
+ error: param.thrown ? result : undefined,
158
+ };
159
+ }
160
+ catch (e) {
161
+ console.error(e);
162
+ }
163
+ finally {
164
+ setCookie(ctx.nativeEvent, "flash", "", { maxAge: 0 });
165
+ }
99
166
  }
100
167
  function handleShellCompleteRedirect(context, e) {
101
168
  return () => {
102
169
  if (context.response && context.response.headers.get("Location")) {
103
170
  const status = getExpectedRedirectStatus(context.response);
104
- setResponseStatus(e, status);
105
- setHeader(e, "Location", context.response.headers.get("Location"));
171
+ e.res.status = status;
172
+ e.res.headers.set("Location", context.response.headers.get("Location"));
106
173
  }
107
174
  };
108
175
  }
@@ -113,10 +180,40 @@ function handleStreamCompleteRedirect(context) {
113
180
  to && write(`<script>window.location="${to}"</script>`);
114
181
  };
115
182
  }
116
- /**
117
- *
118
- * Read more: https://docs.solidjs.com/solid-start/reference/server/create-handler
119
- */
120
- export function createHandler(fn, options, routerLoad) {
121
- return createBaseHandler(fn, createPageEvent, options, routerLoad);
183
+ function produceResponseWithEventHeaders(res) {
184
+ const event = getRequestEvent();
185
+ let ret = res;
186
+ // Response.redirect returns an immutable value, so we clone on any redirect just in case
187
+ if (300 <= res.status && res.status < 400) {
188
+ const cookies = res.headers.getSetCookie?.() ?? [];
189
+ const headers = new Headers();
190
+ res.headers.forEach((value, key) => {
191
+ if (key.toLowerCase() !== "set-cookie") {
192
+ headers.set(key, value);
193
+ }
194
+ });
195
+ for (const cookie of cookies) {
196
+ headers.append("Set-Cookie", cookie);
197
+ }
198
+ ret = new Response(res.body, {
199
+ status: res.status,
200
+ statusText: res.statusText,
201
+ headers,
202
+ });
203
+ }
204
+ const eventCookies = event.response.headers.getSetCookie?.() ?? [];
205
+ for (const cookie of eventCookies) {
206
+ ret.headers.append("Set-Cookie", cookie);
207
+ }
208
+ for (const [name, value] of event.response.headers) {
209
+ if (name.toLowerCase() !== "set-cookie") {
210
+ ret.headers.set(name, value);
211
+ }
212
+ }
213
+ return ret;
214
+ }
215
+ function stripBaseUrl(path) {
216
+ if (import.meta.env.BASE_URL === "/" || import.meta.env.BASE_URL === "")
217
+ return path;
218
+ return path.slice(import.meta.env.BASE_URL.length);
122
219
  }
@@ -1,6 +1,9 @@
1
- export { createHandler } from "./handler";
2
- export { StartServer } from "./StartServer";
3
- export type { APIEvent, APIHandler, Asset, ContextMatches, DocumentComponentProps, FetchEvent, HandlerOptions, PageEvent, ResponseStub, ServerFunctionMeta } from "./types";
4
- import { getServerFunctionMeta as getServerFunctionMeta_ } from "../shared/serverFunction";
5
- /** @deprecated */
6
- export declare const getServerFunctionMeta: typeof getServerFunctionMeta_;
1
+ export { default as lazy } from "../shared/lazy.ts";
2
+ export { getServerFunctionMeta } from "../shared/serverFunction.ts";
3
+ export { StartServer } from "./StartServer.tsx";
4
+ export { createHandler } from "./handler.ts";
5
+ export type { APIEvent, APIHandler, Asset, ContextMatches, DocumentComponentProps, FetchEvent, HandlerOptions, PageEvent, ResponseStub, ServerFunctionMeta, } from "./types.ts";
6
+ /**
7
+ * Checks if user has set a redirect status in the response.
8
+ * If not, falls back to the 302 (temporary redirect)
9
+ */
@@ -1,6 +1,14 @@
1
- // @refresh skip
2
- export { createHandler } from "./handler";
3
- export { StartServer } from "./StartServer";
4
- import { getServerFunctionMeta as getServerFunctionMeta_ } from "../shared/serverFunction";
5
- /** @deprecated */
6
- export const getServerFunctionMeta = getServerFunctionMeta_;
1
+ export { default as lazy } from "../shared/lazy.js";
2
+ export { getServerFunctionMeta } from "../shared/serverFunction.js";
3
+ export { StartServer } from "./StartServer.jsx";
4
+ export { createHandler } from "./handler.js";
5
+ /**
6
+ * Checks if user has set a redirect status in the response.
7
+ * If not, falls back to the 302 (temporary redirect)
8
+ */
9
+ // export function getExpectedRedirectStatus(response: ResponseStub): number {
10
+ // if (response.status && validRedirectStatuses.has(response.status)) {
11
+ // return response.status;
12
+ // }
13
+ // return 302;
14
+ // }
@@ -0,0 +1,9 @@
1
+ export declare function getClientManifest(): {
2
+ import(id: string): Promise<any>;
3
+ getAssets(id: string): Promise<any[]>;
4
+ } | {
5
+ import(id: string): Promise<any>;
6
+ getAssets(id: string): Promise<any>;
7
+ json(): Promise<any>;
8
+ };
9
+ export { getClientManifest as getManifest };
@@ -0,0 +1,6 @@
1
+ import { getClientDevManifest } from "./dev-client-manifest.js";
2
+ import { getClientProdManifest } from "./prod-client-manifest.js";
3
+ export function getClientManifest() {
4
+ return import.meta.env.DEV ? getClientDevManifest() : getClientProdManifest();
5
+ }
6
+ export { getClientManifest as getManifest };
@@ -0,0 +1,4 @@
1
+ export declare function getClientDevManifest(): {
2
+ import(id: string): Promise<any>;
3
+ getAssets(id: string): Promise<any[]>;
4
+ };