@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
@@ -0,0 +1,240 @@
1
+ // @ts-ignore - seroval exports issue with NodeNext
2
+ import { join } from "pathe";
3
+ import { deserialize, toJSONAsync } from "seroval";
4
+ import {
5
+ CustomEventPlugin,
6
+ DOMExceptionPlugin,
7
+ EventPlugin,
8
+ FormDataPlugin,
9
+ HeadersPlugin,
10
+ ReadableStreamPlugin,
11
+ RequestPlugin,
12
+ ResponsePlugin,
13
+ URLPlugin,
14
+ URLSearchParamsPlugin,
15
+ } from "seroval-plugins/web";
16
+ import { type Component } from "solid-js";
17
+
18
+ class SerovalChunkReader {
19
+ reader: ReadableStreamDefaultReader<Uint8Array>;
20
+ buffer: Uint8Array;
21
+ done: boolean;
22
+ constructor(stream: ReadableStream<Uint8Array>) {
23
+ this.reader = stream.getReader();
24
+ this.buffer = new Uint8Array(0);
25
+ this.done = false;
26
+ }
27
+
28
+ async readChunk() {
29
+ // if there's no chunk, read again
30
+ const chunk = await this.reader.read();
31
+ if (!chunk.done) {
32
+ // repopulate the buffer
33
+ let newBuffer = new Uint8Array(this.buffer.length + chunk.value.length);
34
+ newBuffer.set(this.buffer);
35
+ newBuffer.set(chunk.value, this.buffer.length);
36
+ this.buffer = newBuffer;
37
+ } else {
38
+ this.done = true;
39
+ }
40
+ }
41
+
42
+ async next(): Promise<any> {
43
+ // Check if the buffer is empty
44
+ if (this.buffer.length === 0) {
45
+ // if we are already done...
46
+ if (this.done) {
47
+ return {
48
+ done: true,
49
+ value: undefined,
50
+ };
51
+ }
52
+ // Otherwise, read a new chunk
53
+ await this.readChunk();
54
+ return await this.next();
55
+ }
56
+ // Read the "byte header"
57
+ // The byte header tells us how big the expected data is
58
+ // so we know how much data we should wait before we
59
+ // deserialize the data
60
+ const head = new TextDecoder().decode(this.buffer.subarray(1, 11));
61
+ const bytes = Number.parseInt(head, 16); // ;0x00000000;
62
+ // Check if the buffer has enough bytes to be parsed
63
+ while (bytes > this.buffer.length - 12) {
64
+ // If it's not enough, and the reader is done
65
+ // then the chunk is invalid.
66
+ if (this.done) {
67
+ throw new Error("Malformed server function stream.");
68
+ }
69
+ // Otherwise, we read more chunks
70
+ await this.readChunk();
71
+ }
72
+ // Extract the exact chunk as defined by the byte header
73
+ const partial = new TextDecoder().decode(this.buffer.subarray(12, 12 + bytes));
74
+ // The rest goes to the buffer
75
+ this.buffer = this.buffer.subarray(12 + bytes);
76
+
77
+ // Deserialize the chunk
78
+ return {
79
+ done: false,
80
+ value: deserialize(partial),
81
+ };
82
+ }
83
+
84
+ async drain() {
85
+ while (true) {
86
+ const result = await this.next();
87
+ if (result.done) {
88
+ break;
89
+ }
90
+ }
91
+ }
92
+ }
93
+
94
+ async function deserializeStream(id: string, response: Response) {
95
+ if (!response.body) {
96
+ throw new Error("missing body");
97
+ }
98
+ const reader = new SerovalChunkReader(response.body);
99
+
100
+ const result = await reader.next();
101
+
102
+ if (!result.done) {
103
+ reader.drain().then(
104
+ () => {
105
+ // @ts-ignore
106
+ delete $R[id];
107
+ },
108
+ () => {
109
+ // no-op
110
+ },
111
+ );
112
+ }
113
+
114
+ return result.value;
115
+ }
116
+
117
+ let INSTANCE = 0;
118
+
119
+ function createRequest(base: string, id: string, instance: string, options: RequestInit) {
120
+ return fetch(base, {
121
+ method: "POST",
122
+ ...options,
123
+ headers: {
124
+ ...options.headers,
125
+ "X-Server-Id": id,
126
+ "X-Server-Instance": instance,
127
+ },
128
+ });
129
+ }
130
+
131
+ const plugins = [
132
+ CustomEventPlugin,
133
+ DOMExceptionPlugin,
134
+ EventPlugin,
135
+ FormDataPlugin,
136
+ HeadersPlugin,
137
+ ReadableStreamPlugin,
138
+ RequestPlugin,
139
+ ResponsePlugin,
140
+ URLSearchParamsPlugin,
141
+ URLPlugin,
142
+ ];
143
+
144
+ async function fetchServerFunction(
145
+ base: string,
146
+ id: string,
147
+ options: Omit<RequestInit, "body">,
148
+ args: any[],
149
+ ) {
150
+ const instance = `server-fn:${INSTANCE++}`;
151
+ const response = await (args.length === 0
152
+ ? createRequest(base, id, instance, options)
153
+ : args.length === 1 && args[0] instanceof FormData
154
+ ? createRequest(base, id, instance, { ...options, body: args[0] })
155
+ : args.length === 1 && args[0] instanceof URLSearchParams
156
+ ? createRequest(base, id, instance, {
157
+ ...options,
158
+ body: args[0],
159
+ headers: { ...options.headers, "Content-Type": "application/x-www-form-urlencoded" },
160
+ })
161
+ : createRequest(base, id, instance, {
162
+ ...options,
163
+ body: JSON.stringify(await Promise.resolve(toJSONAsync(args, { plugins }))),
164
+ headers: { ...options.headers, "Content-Type": "application/json" },
165
+ }));
166
+
167
+ if (
168
+ response.headers.has("Location") ||
169
+ response.headers.has("X-Revalidate") ||
170
+ response.headers.has("X-Single-Flight")
171
+ ) {
172
+ if (response.body) {
173
+ /* @ts-ignore-next-line */
174
+ response.customBody = () => {
175
+ return deserializeStream(instance, response);
176
+ };
177
+ }
178
+ return response;
179
+ }
180
+
181
+ const contentType = response.headers.get("Content-Type");
182
+ let result;
183
+ if (contentType && contentType.startsWith("text/plain")) {
184
+ result = await response.text();
185
+ } else if (contentType && contentType.startsWith("application/json")) {
186
+ result = await response.json();
187
+ } else {
188
+ result = await deserializeStream(instance, response);
189
+ }
190
+ if (response.headers.has("X-Error")) {
191
+ throw result;
192
+ }
193
+ return result;
194
+ }
195
+
196
+ export function createServerReference(id: string) {
197
+ let baseURL = import.meta.env.BASE_URL ?? "/";
198
+ if (!baseURL.endsWith("/")) baseURL += "/";
199
+
200
+ const fn = (...args: any[]) => fetchServerFunction(`${baseURL}_server`, id, {}, args);
201
+
202
+ return new Proxy(fn, {
203
+ get(target, prop, receiver) {
204
+ if (prop === "url") {
205
+ return `${baseURL}_server?id=${encodeURIComponent(id)}`;
206
+ }
207
+ if (prop === "GET") {
208
+ return receiver.withOptions({ method: "GET" });
209
+ }
210
+ if (prop === "withOptions") {
211
+ const url = `${baseURL}_server?id=${encodeURIComponent(id)}`;
212
+ return (options: RequestInit) => {
213
+ const fn = async (...args: any[]) => {
214
+ const encodeArgs = options.method && options.method.toUpperCase() === "GET";
215
+ return fetchServerFunction(
216
+ encodeArgs
217
+ ? url +
218
+ (args.length
219
+ ? `&args=${encodeURIComponent(
220
+ JSON.stringify(await Promise.resolve(toJSONAsync(args, { plugins }))),
221
+ )}`
222
+ : "")
223
+ : `${baseURL}_server`,
224
+ id,
225
+ options,
226
+ encodeArgs ? [] : args,
227
+ );
228
+ };
229
+ fn.url = url;
230
+ return fn;
231
+ };
232
+ }
233
+ return (target as any)[prop];
234
+ },
235
+ });
236
+ }
237
+
238
+ export function createClientReference(Component: Component<any>, id: string) {
239
+ return Component;
240
+ }
@@ -0,0 +1,40 @@
1
+ // @refresh skip
2
+ // @ts-ignore
3
+ import type { Component } from "solid-js";
4
+ import { NoHydration, getRequestEvent, ssr } from "solid-js/web";
5
+ import { getSsrManifest } from "../manifest/ssr-manifest.ts";
6
+
7
+ import { TopErrorBoundary } from "../../shared/ErrorBoundary.tsx";
8
+ import { useAssets } from "../assets/index.ts";
9
+ import type { DocumentComponentProps, PageEvent } from "../types.ts";
10
+
11
+ const docType = ssr("<!DOCTYPE html>");
12
+
13
+ /**
14
+ *
15
+ * Read more: https://docs.solidjs.com/solid-start/reference/server/start-server
16
+ */
17
+ export function StartServer(props: { document: Component<DocumentComponentProps> }) {
18
+ const context = getRequestEvent() as PageEvent;
19
+ // @ts-ignore
20
+ const nonce = context.nonce;
21
+ useAssets(context.assets, nonce);
22
+
23
+ return (
24
+ <NoHydration>
25
+ {docType as unknown as any}
26
+ <TopErrorBoundary>
27
+ <props.document
28
+ scripts={
29
+ <>
30
+ <script
31
+ type="module"
32
+ src={getSsrManifest("client").path(import.meta.env.START_CLIENT_ENTRY)}
33
+ />
34
+ </>
35
+ }
36
+ />
37
+ </TopErrorBoundary>
38
+ </NoHydration>
39
+ );
40
+ }
@@ -0,0 +1,29 @@
1
+ import type { JSX } from "solid-js";
2
+
3
+ import { createBaseHandler } from "../handler.ts";
4
+ import { getSsrManifest } from "../manifest/ssr-manifest.ts";
5
+ import type { FetchEvent, HandlerOptions, PageEvent } from "../types.ts";
6
+
7
+ /**
8
+ *
9
+ * Read more: https://docs.solidjs.com/solid-start/reference/server/create-handler
10
+ */
11
+ export function createHandler(
12
+ fn: (context: PageEvent) => JSX.Element,
13
+ options?: HandlerOptions | ((context: PageEvent) => HandlerOptions),
14
+ ) {
15
+ return createBaseHandler(createPageEvent, fn, options);
16
+ }
17
+
18
+ async function createPageEvent(ctx: FetchEvent) {
19
+ const manifest = getSsrManifest("ssr");
20
+ const pageEvent: PageEvent = Object.assign(ctx, {
21
+ manifest: "json" in manifest ? await manifest.json() : {},
22
+ assets: await manifest.getAssets(import.meta.env.START_CLIENT_ENTRY),
23
+ routes: [],
24
+ complete: false,
25
+ $islands: new Set<string>(),
26
+ });
27
+
28
+ return pageEvent;
29
+ }
@@ -0,0 +1,5 @@
1
+ // @refresh skip
2
+ export { default as lazy } from "../../shared/lazy.ts";
3
+ export * from "../types.ts";
4
+ export { StartServer } from "./StartServer.tsx";
5
+ export { createHandler } from "./handler.ts";
@@ -0,0 +1,82 @@
1
+ import type { H3Event } from "h3";
2
+ import type { JSX } from "solid-js";
3
+ import type { RequestEvent } from "solid-js/web";
4
+
5
+ // export const FETCH_EVENT = "$FETCH";
6
+
7
+ export type DocumentComponentProps = {
8
+ assets?: JSX.Element;
9
+ scripts: JSX.Element;
10
+ children?: JSX.Element;
11
+ };
12
+
13
+ export type Asset =
14
+ | {
15
+ tag: "style";
16
+ attrs: JSX.StyleHTMLAttributes<HTMLStyleElement> & { key?: string };
17
+ children?: JSX.Element;
18
+ }
19
+ | {
20
+ tag: "script";
21
+ attrs: JSX.ScriptHTMLAttributes<HTMLScriptElement> & { key?: string };
22
+ }
23
+ | {
24
+ tag: "link";
25
+ attrs: JSX.LinkHTMLAttributes<HTMLLinkElement> & { key?: string };
26
+ };
27
+
28
+ export type HandlerOptions = {
29
+ mode?: "sync" | "async" | "stream";
30
+ nonce?: string;
31
+ renderId?: string;
32
+ onCompleteAll?: (options: { write: (v: any) => void }) => void;
33
+ onCompleteShell?: (options: { write: (v: any) => void }) => void;
34
+ };
35
+
36
+ export type ContextMatches = {
37
+ originalPath: string;
38
+ pattern: string;
39
+ path: string;
40
+ params: unknown;
41
+ };
42
+
43
+ export interface ResponseStub {
44
+ status?: number;
45
+ statusText?: string;
46
+ headers: Headers;
47
+ }
48
+
49
+ export interface FetchEvent {
50
+ request: Request;
51
+ response: ResponseStub;
52
+ clientAddress?: string;
53
+ locals: App.RequestEventLocals;
54
+ nativeEvent: H3Event;
55
+ }
56
+
57
+ export interface PageEvent extends RequestEvent {
58
+ assets: any[];
59
+ routes: any[];
60
+ // prevUrl: string | null;
61
+ // $type: typeof FETCH_EVENT;
62
+ $islands: Set<string>;
63
+ complete: boolean;
64
+ nonce?: string;
65
+ // mutation: boolean;
66
+ }
67
+
68
+ export interface APIEvent extends FetchEvent {
69
+ params: { [key: string]: string };
70
+ }
71
+
72
+ export type APIHandler = (event: APIEvent) => Promise<any>;
73
+
74
+ export interface ServerFunctionMeta {
75
+ id: string;
76
+ }
77
+
78
+ declare module "solid-js/web" {
79
+ interface RequestEvent extends FetchEvent {
80
+ serverOnly?: boolean;
81
+ }
82
+ }
@@ -0,0 +1,12 @@
1
+ import type { ResponseStub } from "./types.ts";
2
+
3
+ // according to https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#redirection_messages
4
+ const validRedirectStatuses = new Set([301, 302, 303, 307, 308]);
5
+
6
+ export function getExpectedRedirectStatus(response: ResponseStub): number {
7
+ if (response.status && validRedirectStatuses.has(response.status)) {
8
+ return response.status;
9
+ }
10
+
11
+ return 302;
12
+ }
@@ -0,0 +1,52 @@
1
+ // @refresh skip
2
+ import { ErrorBoundary as DefaultErrorBoundary, catchError, type ParentProps } from "solid-js";
3
+ import { isServer } from "solid-js/web";
4
+ import { HttpStatusCode } from "./HttpStatusCode.ts";
5
+ import { DevOverlay } from "./dev-overlay/index.tsx";
6
+
7
+ export const ErrorBoundary =
8
+ import.meta.env.DEV && import.meta.env.START_DEV_OVERLAY
9
+ ? (props: ParentProps) => <DevOverlay>{props.children}</DevOverlay>
10
+ : (props: ParentProps) => {
11
+ const message = isServer
12
+ ? "500 | Internal Server Error"
13
+ : "Error | Uncaught Client Exception";
14
+ return (
15
+ <DefaultErrorBoundary
16
+ fallback={error => {
17
+ console.error(error);
18
+ return (
19
+ <>
20
+ <span style="font-size:1.5em;text-align:center;position:fixed;left:0px;bottom:55%;width:100%;">
21
+ {message}
22
+ </span>
23
+ <HttpStatusCode code={500} />
24
+ </>
25
+ );
26
+ }}
27
+ >
28
+ {props.children}
29
+ </DefaultErrorBoundary>
30
+ );
31
+ };
32
+
33
+ export const TopErrorBoundary = (props: ParentProps) => {
34
+ let isError = false;
35
+ const res = catchError(
36
+ () => props.children,
37
+ err => {
38
+ console.error(err);
39
+ isError = !!err;
40
+ },
41
+ );
42
+ return isError ? (
43
+ <>
44
+ <span style="font-size:1.5em;text-align:center;position:fixed;left:0px;bottom:55%;width:100%;">
45
+ 500 | Internal Server Error
46
+ </span>
47
+ <HttpStatusCode code={500} />
48
+ </>
49
+ ) : (
50
+ res
51
+ );
52
+ };
@@ -0,0 +1,8 @@
1
+ // @refresh skip
2
+ /**
3
+ *
4
+ * Read more: https://docs.solidjs.com/solid-start/reference/server/get
5
+ */
6
+ export function GET<T extends (...args: any[]) => any>(fn: T) {
7
+ return (fn as any).GET as (...args: Parameters<T>) => ReturnType<T>;
8
+ }
@@ -0,0 +1,42 @@
1
+ // @refresh skip
2
+ import { onCleanup } from "solid-js";
3
+ import { getRequestEvent, isServer } from "solid-js/web";
4
+
5
+ import type { PageEvent } from "../server/types.ts";
6
+ import { appendHeader, setHeader } from "../http/index.ts";
7
+
8
+ export interface HttpHeaderProps {
9
+ name: string;
10
+ value: string;
11
+ append?: boolean;
12
+ }
13
+
14
+ /**
15
+ *
16
+ * Read more: https://docs.solidjs.com/solid-start/reference/server/http-header
17
+ */
18
+ export const HttpHeader = isServer
19
+ ? (props: HttpHeaderProps) => {
20
+ const event = getRequestEvent() as PageEvent;
21
+
22
+ if (props.append) appendHeader(props.name, props.value);
23
+ else setHeader(props.name, props.value);
24
+
25
+ onCleanup(() => {
26
+ // @ts-expect-error
27
+ if (event.nativeEvent.handled || event.complete) return;
28
+ const value = event.response.headers.get(props.name);
29
+ if (!value) return;
30
+ if (!value.includes(", ")) {
31
+ if (value === props.value) event.response.headers.delete(props.name);
32
+ return;
33
+ }
34
+ const values = value.split(", ");
35
+ const index = values.indexOf(props.value);
36
+ index !== -1 && values.splice(index, 1);
37
+ if (values.length) event.response.headers.set(props.name, values.join(","));
38
+ else event.response.headers.delete(props.name);
39
+ });
40
+ return null;
41
+ }
42
+ : (_props: HttpHeaderProps) => null;
@@ -0,0 +1,27 @@
1
+ // @refresh skip
2
+ import { onCleanup } from "solid-js";
3
+ import { getRequestEvent, isServer } from "solid-js/web";
4
+ import type { PageEvent } from "../server/types.ts";
5
+
6
+ export interface HttpStatusCodeProps {
7
+ code: number;
8
+ text?: string;
9
+ }
10
+
11
+ /**
12
+ *
13
+ * Read more: https://docs.solidjs.com/solid-start/reference/server/http-status-code
14
+ */
15
+ export const HttpStatusCode = isServer
16
+ ? (props: HttpStatusCodeProps) => {
17
+ const event = getRequestEvent() as PageEvent;
18
+ event.response.status = props.code;
19
+ event.response.statusText = props.text;
20
+ onCleanup(
21
+ () =>
22
+ // !event.nativeEvent.handled &&
23
+ !event.complete && (event.response.status = 200),
24
+ );
25
+ return null;
26
+ }
27
+ : (_props: HttpStatusCodeProps) => null;
@@ -0,0 +1,44 @@
1
+ // @refresh skip
2
+ import type { Component, ComponentProps, JSX, Setter } from "solid-js";
3
+ import { createMemo, createSignal, onMount, sharedConfig, splitProps, untrack } from "solid-js";
4
+ import { isServer } from "solid-js/web";
5
+
6
+ /**
7
+ *
8
+ * Read more: https://docs.solidjs.com/solid-start/reference/client/client-only
9
+ */
10
+ // not using Suspense
11
+ export default function clientOnly<T extends Component<any>>(
12
+ fn: () => Promise<{
13
+ default: T;
14
+ }>,
15
+ options: { lazy?: boolean } = {},
16
+ ) {
17
+ if (isServer) return (props: ComponentProps<T> & { fallback?: JSX.Element }) => props.fallback;
18
+
19
+ const [comp, setComp] = createSignal<T>();
20
+ !options.lazy && load(fn, setComp);
21
+ return (props: ComponentProps<T>) => {
22
+ let Comp: T | undefined;
23
+ let m: boolean;
24
+ const [, rest] = splitProps(props, ["fallback"]);
25
+ options.lazy && load(fn, setComp);
26
+ if ((Comp = comp()) && !sharedConfig.context) return Comp(rest);
27
+ const [mounted, setMounted] = createSignal(!sharedConfig.context);
28
+ onMount(() => setMounted(true));
29
+ return createMemo(
30
+ () => (
31
+ (Comp = comp()), (m = mounted()), untrack(() => (Comp && m ? Comp(rest) : props.fallback))
32
+ ),
33
+ );
34
+ };
35
+ }
36
+
37
+ function load<T>(
38
+ fn: () => Promise<{
39
+ default: T;
40
+ }>,
41
+ setComp: Setter<T>,
42
+ ) {
43
+ fn().then(m => setComp(() => m.default));
44
+ }
@@ -0,0 +1,92 @@
1
+ // @refresh skip
2
+ import { getSingletonHighlighter, type BuiltinLanguage, type Highlighter } from "shiki";
3
+ import { loadWasm } from "shiki/engine/oniguruma";
4
+ import { createEffect, createResource, type JSX } from "solid-js";
5
+
6
+ import url from "shiki/onig.wasm?url";
7
+
8
+ import langJS from "shiki/langs/javascript.mjs";
9
+ import langJSX from "shiki/langs/jsx.mjs";
10
+ import langTSX from "shiki/langs/tsx.mjs";
11
+ import langTS from "shiki/langs/typescript.mjs";
12
+ import darkPlus from "shiki/themes/dark-plus.mjs";
13
+
14
+ let HIGHLIGHTER: Highlighter;
15
+
16
+ async function loadHighlighter() {
17
+ if (!HIGHLIGHTER) {
18
+ await loadWasm(await fetch(url));
19
+ HIGHLIGHTER = await getSingletonHighlighter({
20
+ themes: [darkPlus],
21
+ langs: [langJS, langJSX, langTS, langTSX],
22
+ });
23
+ }
24
+ return HIGHLIGHTER;
25
+ }
26
+
27
+ export interface CodeViewProps {
28
+ fileName: string;
29
+ content: string;
30
+ line: number;
31
+ }
32
+
33
+ const RANGE = 8;
34
+
35
+ export function CodeView(props: CodeViewProps): JSX.Element | null {
36
+ const lines = () =>
37
+ props.content.split("\n").map((item, index) => ({
38
+ index: index + 1,
39
+ line: item,
40
+ }));
41
+
42
+ const minLine = () => Math.max(props.line - (1 + RANGE), 0);
43
+ const maxLine = () => Math.min(props.line + RANGE, lines().length - 1);
44
+
45
+ let ref: HTMLDivElement | undefined;
46
+
47
+ const [data] = createResource(
48
+ () =>
49
+ lines()
50
+ .slice(minLine(), maxLine())
51
+ .map(item => item.line)
52
+ .join("\n"),
53
+ async value => {
54
+ const highlighter = await loadHighlighter();
55
+ const fileExtension = props.fileName.split(/[#?]/)[0]!.split(".").pop()?.trim();
56
+ let lang = fileExtension as BuiltinLanguage;
57
+ if (fileExtension === "mjs" || fileExtension === "cjs") {
58
+ lang = "js";
59
+ }
60
+ return highlighter.codeToHtml(value, {
61
+ theme: "dark-plus",
62
+ lang,
63
+ });
64
+ },
65
+ );
66
+
67
+ createEffect(() => {
68
+ const result = data();
69
+ if (ref && result) {
70
+ ref.innerHTML = result;
71
+
72
+ const lines = ref.querySelectorAll('span[class="line"]');
73
+
74
+ for (let i = 0, len = lines.length; i < len; i++) {
75
+ const el = lines[i] as HTMLElement;
76
+ if (props.line - minLine() - 1 === i) {
77
+ el.classList.add("dev-overlay-error-line");
78
+ }
79
+ }
80
+ }
81
+ });
82
+
83
+ return (
84
+ <div
85
+ ref={ref}
86
+ class="dev-overlay-code-view"
87
+ style={{
88
+ "--dev-overlay-code-view-start": minLine() + 1,
89
+ }}
90
+ />
91
+ );
92
+ }