@solidjs/start 2.0.0-rc.4 → 2.0.0-rc.6
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.
- package/CHANGELOG.md +53 -0
- package/dist/config/app-root-alias.d.ts +7 -0
- package/dist/config/app-root-alias.js +58 -0
- package/dist/config/constants.d.ts +2 -0
- package/dist/config/constants.js +2 -0
- package/dist/config/fs-routes/index.js +3 -3
- package/dist/config/fs-routes/tree-shake.d.ts +27 -0
- package/dist/config/fs-routes/tree-shake.js +46 -1
- package/dist/config/index.d.ts +41 -1
- package/dist/config/index.js +14 -6
- package/dist/config/manifest.js +16 -0
- package/dist/fns/client.js +2 -3
- package/dist/fns/error-handler.d.ts +3 -0
- package/dist/fns/error-handler.js +5 -0
- package/dist/fns/handler.d.ts +1 -1
- package/dist/fns/handler.js +39 -7
- package/dist/fns/plugins.d.ts +34 -0
- package/dist/fns/plugins.js +33 -0
- package/dist/fns/serialization.js +14 -4
- package/dist/server/handler.js +9 -1
- package/dist/server/index.d.ts +1 -0
- package/dist/server/spa/StartServer.jsx +1 -1
- package/dist/shared/dev-toolbar/index.jsx +0 -1
- package/package.json +9 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,58 @@
|
|
|
1
1
|
# @solidjs/start
|
|
2
2
|
|
|
3
|
+
## 2.0.0-rc.6
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- bac24b2: Add a `serverFunctions.onError` option naming a module that observes and replaces what a server function threw, before it is serialized into the response
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- 27c2877: Remove leftover debug `console.log` calls from the server functions inspector, which logged on every server function request in dev.
|
|
12
|
+
- f15724b: Declare `@solidjs/router` as an optional peer dependency constrained to `>=0.16.0 <2.0.0-0`. Router v2 is expected to target Solid v2, so installing it alongside `@solidjs/start` v2 now surfaces a peer warning instead of silently producing an incompatible pairing. The peer is marked optional, so apps that do not use the router are unaffected.
|
|
13
|
+
- 5c8612f: Apply cookies set on a returned or thrown response during single flight mutations. `redirect(to, { headers: { "Set-Cookie": ... } })` previously only reached the browser: the single flight re-render of the redirect target still ran with the old request cookies, so queries reading that cookie saw stale values. Those cookies are now merged into the request the re-render sees, matching what a browser round trip would have sent.
|
|
14
|
+
- 83122ed: Reject server function calls when the response is a 5xx without an X-Error header, instead of resolving with the parsed error body
|
|
15
|
+
|
|
16
|
+
## 2.0.0-rc.5
|
|
17
|
+
|
|
18
|
+
### Minor Changes
|
|
19
|
+
|
|
20
|
+
- 4c803e5: Add `serialization.plugins` to configure custom Seroval plugins for server functions.
|
|
21
|
+
|
|
22
|
+
Values Seroval has no built-in support for (Mongo's `ObjectId`, Prisma's `Decimal`, `Temporal`, and other custom classes) previously threw when returned from or passed to a server function. Point the new option at a module whose default export is an array of plugins:
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
// vite.config.ts
|
|
26
|
+
solidStart({
|
|
27
|
+
serialization: {
|
|
28
|
+
plugins: "src/seroval-plugins.ts",
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
// src/seroval-plugins.ts
|
|
35
|
+
import { createPlugin } from "@solidjs/start/serialization";
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The module is bundled into both the client and the server so both ends of a server function agree on the format, so it must not import server-only code. SolidStart's built-in plugins keep precedence. Only server-function and action payloads are affected; the SSR hydration payload is serialized by `solid-js/web`.
|
|
39
|
+
|
|
40
|
+
Also adds a `@solidjs/start/serialization` entrypoint re-exporting Seroval's `createPlugin`, `OpaqueReference`, and plugin types, so plugin authors stay on the same Seroval version SolidStart serializes with.
|
|
41
|
+
|
|
42
|
+
### Patch Changes
|
|
43
|
+
|
|
44
|
+
- e117d91: Route module ids now end in the source extension, so ecosystem plugins apply inside `src/routes`.
|
|
45
|
+
|
|
46
|
+
Route files are imported through an id carrying the picked exports in the query (`routes/api.ts?pick=GET`), which left the id ending in the export name. Plugins whose filter is anchored on the file extension (`/\.[cm]?[jt]sx?$/`, the default for `unplugin-auto-import`, `unplugin-macros` and others) silently skipped every route file. The id now ends with a `lang.<ext>` marker, the same convention Vue SFCs use for `?vue&type=script&lang.ts`. Chunk filenames are unchanged.
|
|
47
|
+
|
|
48
|
+
- d8f1ea8: Apply the configured `nonce` to the two script tags that were still missing it, so a strict `script-src` CSP no longer needs `unsafe-inline`:
|
|
49
|
+
|
|
50
|
+
- The client-side redirect that streaming mode emits after the shell has already flushed (`<script>window.location=...</script>`) now carries the nonce.
|
|
51
|
+
- The SPA entry script tag now carries the nonce, matching the SSR entry script.
|
|
52
|
+
|
|
53
|
+
- 27fca88: Fix actions returning `json()` or `reload()` leaving no-JS form submissions stranded on the `/_server` endpoint. These responses carry a value rather than a destination, so the redirect issued for progressive-enhancement submissions had no `Location` header. It now falls back to the submitting page, and the response value is unwrapped into the flash cookie so `useSubmission().result` matches the JS path.
|
|
54
|
+
- 75debc3: Scope the built-in `~` alias to the app package, so files in other workspace packages can map `~` to their own root through an importer-aware plugin such as `vite-tsconfig-paths`. In stylesheets and asset URLs (CSS `@import`, `url()`, `new URL(..., import.meta.url)`) `~` still always means the app root, since Vite resolves those without running plugins.
|
|
55
|
+
|
|
3
56
|
## 2.0.0-rc.4
|
|
4
57
|
|
|
5
58
|
### Patch Changes
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type Plugin } from "vite";
|
|
2
|
+
/**
|
|
3
|
+
* Provides SolidStart's `~` app-root alias without claiming module imports made
|
|
4
|
+
* by other workspace packages. Those packages may map `~` to their own root
|
|
5
|
+
* through an importer-aware resolver such as vite-tsconfig-paths.
|
|
6
|
+
*/
|
|
7
|
+
export declare function appRootAlias(projectRoot: string, appRoot: string): Plugin;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
3
|
+
import { normalizePath } from "vite";
|
|
4
|
+
/**
|
|
5
|
+
* `~/app.css`, `~/logo.svg`, ... but not `~/lib/api.ts` or `~/components/Counter`.
|
|
6
|
+
*
|
|
7
|
+
* CSS `@import`, `url()`, preprocessor imports and `new URL(..., import.meta.url)`
|
|
8
|
+
* are resolved by internal Vite resolvers that only run the alias plugin, never
|
|
9
|
+
* user plugins, so those ids can never reach `resolveId` below. Keeping non-module
|
|
10
|
+
* ids on a plain alias preserves `~` in stylesheets and asset URLs, where it always
|
|
11
|
+
* means the app root.
|
|
12
|
+
*/
|
|
13
|
+
const NON_MODULE_ID = /^~\/([^?#]*\.(?![cm]?[jt]sx?(?:[?#]|$))[^./?#]+(?:[?#].*)?)$/;
|
|
14
|
+
/**
|
|
15
|
+
* Provides SolidStart's `~` app-root alias without claiming module imports made
|
|
16
|
+
* by other workspace packages. Those packages may map `~` to their own root
|
|
17
|
+
* through an importer-aware resolver such as vite-tsconfig-paths.
|
|
18
|
+
*/
|
|
19
|
+
export function appRootAlias(projectRoot, appRoot) {
|
|
20
|
+
const appDir = normalizePath(resolve(projectRoot, appRoot));
|
|
21
|
+
const packageRoots = new Map();
|
|
22
|
+
/** Nearest directory at or above `directory` that holds a `package.json`. */
|
|
23
|
+
function packageRoot(directory) {
|
|
24
|
+
if (!packageRoots.has(directory)) {
|
|
25
|
+
const parent = dirname(directory);
|
|
26
|
+
packageRoots.set(directory, existsSync(join(directory, "package.json"))
|
|
27
|
+
? directory
|
|
28
|
+
: parent === directory
|
|
29
|
+
? undefined
|
|
30
|
+
: packageRoot(parent));
|
|
31
|
+
}
|
|
32
|
+
return packageRoots.get(directory);
|
|
33
|
+
}
|
|
34
|
+
const appPackage = packageRoot(appDir);
|
|
35
|
+
/** True only when the importer demonstrably belongs to another package. */
|
|
36
|
+
function isForeignImporter(importer) {
|
|
37
|
+
const file = normalizePath(importer.replace(/[?#].*$/s, ""));
|
|
38
|
+
if (!isAbsolute(file))
|
|
39
|
+
return false;
|
|
40
|
+
const owner = packageRoot(dirname(file));
|
|
41
|
+
return owner !== undefined && owner !== appPackage;
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
name: "solid-start:app-root-alias",
|
|
45
|
+
enforce: "pre",
|
|
46
|
+
config() {
|
|
47
|
+
return { resolve: { alias: [{ find: NON_MODULE_ID, replacement: `${appDir}/$1` }] } };
|
|
48
|
+
},
|
|
49
|
+
async resolveId(id, importer, options) {
|
|
50
|
+
if (id !== "~" && !id.startsWith("~/"))
|
|
51
|
+
return null;
|
|
52
|
+
if (importer && isForeignImporter(importer))
|
|
53
|
+
return null;
|
|
54
|
+
const target = join(appDir, id.slice(1));
|
|
55
|
+
return (await this.resolve(target, importer, { ...options, skipSelf: true })) ?? target;
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
@@ -5,7 +5,9 @@ export declare const VIRTUAL_MODULES: {
|
|
|
5
5
|
readonly getClientManifest: "solid-start:get-client-manifest";
|
|
6
6
|
readonly getManifest: "solid-start:get-manifest";
|
|
7
7
|
readonly middleware: "solid-start:middleware";
|
|
8
|
+
readonly serovalPlugins: "solid-start:seroval-plugins";
|
|
8
9
|
readonly serverFnManifest: "solid-start:server-fn-manifest";
|
|
10
|
+
readonly serverFnErrorHandler: "solid-start:server-fn-error-handler";
|
|
9
11
|
readonly clientEntry: "solid-start:client-entry";
|
|
10
12
|
readonly serverEntry: "solid-start:server-entry";
|
|
11
13
|
readonly app: "solid-start:app";
|
package/dist/config/constants.js
CHANGED
|
@@ -5,7 +5,9 @@ export const VIRTUAL_MODULES = {
|
|
|
5
5
|
getClientManifest: "solid-start:get-client-manifest",
|
|
6
6
|
getManifest: "solid-start:get-manifest",
|
|
7
7
|
middleware: "solid-start:middleware",
|
|
8
|
+
serovalPlugins: "solid-start:seroval-plugins",
|
|
8
9
|
serverFnManifest: "solid-start:server-fn-manifest",
|
|
10
|
+
serverFnErrorHandler: "solid-start:server-fn-error-handler",
|
|
9
11
|
clientEntry: "solid-start:client-entry",
|
|
10
12
|
serverEntry: "solid-start:server-entry",
|
|
11
13
|
app: "solid-start:app",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { relative } from "node:path";
|
|
2
2
|
import { fileSystemWatcher } from "./fs-watcher.js";
|
|
3
|
-
import { treeShake } from "./tree-shake.js";
|
|
3
|
+
import { toPickId, treeShake } from "./tree-shake.js";
|
|
4
4
|
export const moduleId = "solid-start:routes";
|
|
5
5
|
export function fsRoutes({ routers }) {
|
|
6
6
|
globalThis.ROUTERS = routers;
|
|
@@ -24,7 +24,7 @@ export function fsRoutes({ routers }) {
|
|
|
24
24
|
if (v === undefined)
|
|
25
25
|
return undefined;
|
|
26
26
|
if (k.startsWith("$$")) {
|
|
27
|
-
const buildId =
|
|
27
|
+
const buildId = toPickId(v.src, v.pick);
|
|
28
28
|
/**
|
|
29
29
|
* @type {{ [key: string]: string }}
|
|
30
30
|
*/
|
|
@@ -40,7 +40,7 @@ export function fsRoutes({ routers }) {
|
|
|
40
40
|
};
|
|
41
41
|
}
|
|
42
42
|
else if (k.startsWith("$")) {
|
|
43
|
-
const buildId =
|
|
43
|
+
const buildId = toPickId(v.src, v.pick);
|
|
44
44
|
return {
|
|
45
45
|
src: relative(root, buildId),
|
|
46
46
|
build: isBuild ? `_$() => import('${buildId}')$_` : undefined,
|
|
@@ -1,2 +1,29 @@
|
|
|
1
1
|
import type { Plugin } from "vite";
|
|
2
|
+
/**
|
|
3
|
+
* Builds the module id used to import a subset of a route file's exports.
|
|
4
|
+
*
|
|
5
|
+
* The `pick` list has to live in the query so that the same file can be
|
|
6
|
+
* instantiated once per export subset (the client picks `default`/`$css`,
|
|
7
|
+
* the server picks its HTTP handlers). That query would otherwise leave the
|
|
8
|
+
* id ending in `?pick=GET`, which silently excludes route files from any
|
|
9
|
+
* plugin whose filter is anchored on the file extension -- a very common
|
|
10
|
+
* default, e.g. unplugin-macros' `/\.[cm]?[jt]sx?$/`.
|
|
11
|
+
*
|
|
12
|
+
* The trailing `lang.<ext>` marker is the same convention Vue SFCs use
|
|
13
|
+
* (`?vue&type=script&lang.ts`) and puts a real extension back at the end of
|
|
14
|
+
* the id, so those filters match again.
|
|
15
|
+
*
|
|
16
|
+
* @see https://github.com/solidjs/solid-start/issues/1918
|
|
17
|
+
*/
|
|
18
|
+
export declare function toPickId(src: string, pick: string[]): string;
|
|
19
|
+
/**
|
|
20
|
+
* Rolldown's default chunk name sanitizer, plus a pass that drops the
|
|
21
|
+
* `?pick=...&lang` tail so route chunks keep the short, stable filenames they
|
|
22
|
+
* had before the `lang` marker was introduced (`index-<hash>.js`, not
|
|
23
|
+
* `index.tsx_pick_default_pick__css_lang-<hash>.js`). Client chunk filenames
|
|
24
|
+
* are public URLs, so this is not purely cosmetic.
|
|
25
|
+
*
|
|
26
|
+
* @see https://github.com/rollup/rollup/blob/master/src/utils/sanitizeFileName.ts
|
|
27
|
+
*/
|
|
28
|
+
export declare function sanitizeChunkFileName(name: string): string;
|
|
2
29
|
export declare function treeShake(): Plugin;
|
|
@@ -1,7 +1,52 @@
|
|
|
1
1
|
// All credit for this work goes to the amazing Next.js team.
|
|
2
2
|
// https://github.com/vercel/next.js/blob/canary/packages/next/build/babel/plugins/next-ssg-transform.ts
|
|
3
3
|
// This is adapted to work with routeData functions. It can be run in two modes, one which preserves the routeData and the Component in the same file, and one which creates a
|
|
4
|
-
import { basename } from "pathe";
|
|
4
|
+
import { basename, extname } from "pathe";
|
|
5
|
+
const PICKABLE_EXTENSIONS = ["js", "jsx", "ts", "tsx"];
|
|
6
|
+
/**
|
|
7
|
+
* Builds the module id used to import a subset of a route file's exports.
|
|
8
|
+
*
|
|
9
|
+
* The `pick` list has to live in the query so that the same file can be
|
|
10
|
+
* instantiated once per export subset (the client picks `default`/`$css`,
|
|
11
|
+
* the server picks its HTTP handlers). That query would otherwise leave the
|
|
12
|
+
* id ending in `?pick=GET`, which silently excludes route files from any
|
|
13
|
+
* plugin whose filter is anchored on the file extension -- a very common
|
|
14
|
+
* default, e.g. unplugin-macros' `/\.[cm]?[jt]sx?$/`.
|
|
15
|
+
*
|
|
16
|
+
* The trailing `lang.<ext>` marker is the same convention Vue SFCs use
|
|
17
|
+
* (`?vue&type=script&lang.ts`) and puts a real extension back at the end of
|
|
18
|
+
* the id, so those filters match again.
|
|
19
|
+
*
|
|
20
|
+
* @see https://github.com/solidjs/solid-start/issues/1918
|
|
21
|
+
*/
|
|
22
|
+
export function toPickId(src, pick) {
|
|
23
|
+
const query = pick.map(p => `pick=${p}`).join("&");
|
|
24
|
+
const ext = extname(src).slice(1);
|
|
25
|
+
return PICKABLE_EXTENSIONS.includes(ext) ? `${src}?${query}&lang.${ext}` : `${src}?${query}`;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Matches the tail that {@link toPickId} appends, as it appears in a chunk
|
|
29
|
+
* name. Rolldown derives a chunk name from the module id by taking the
|
|
30
|
+
* basename minus its extension, so `index.tsx?pick=default&pick=$css&lang.tsx`
|
|
31
|
+
* arrives here as `index.tsx?pick=default&pick=$css&lang`.
|
|
32
|
+
*/
|
|
33
|
+
const PICK_CHUNK_NAME_RE = /\.[cm]?[jt]sx?\?pick=[^?]*&lang$/;
|
|
34
|
+
const INVALID_CHAR_RE = /[\u0000-\u001F"#$&*+,:;<=>?[\]^`{|}\u007F]/g;
|
|
35
|
+
const DRIVE_LETTER_RE = /^[a-z]:/i;
|
|
36
|
+
/**
|
|
37
|
+
* Rolldown's default chunk name sanitizer, plus a pass that drops the
|
|
38
|
+
* `?pick=...&lang` tail so route chunks keep the short, stable filenames they
|
|
39
|
+
* had before the `lang` marker was introduced (`index-<hash>.js`, not
|
|
40
|
+
* `index.tsx_pick_default_pick__css_lang-<hash>.js`). Client chunk filenames
|
|
41
|
+
* are public URLs, so this is not purely cosmetic.
|
|
42
|
+
*
|
|
43
|
+
* @see https://github.com/rollup/rollup/blob/master/src/utils/sanitizeFileName.ts
|
|
44
|
+
*/
|
|
45
|
+
export function sanitizeChunkFileName(name) {
|
|
46
|
+
const stripped = name.replace(PICK_CHUNK_NAME_RE, "");
|
|
47
|
+
const driveLetter = DRIVE_LETTER_RE.exec(stripped)?.[0] ?? "";
|
|
48
|
+
return driveLetter + stripped.slice(driveLetter.length).replace(INVALID_CHAR_RE, "_");
|
|
49
|
+
}
|
|
5
50
|
function treeShakeTransform({ types: t }) {
|
|
6
51
|
function getIdentifier(path) {
|
|
7
52
|
const parentPath = path.parentPath;
|
package/dist/config/index.d.ts
CHANGED
|
@@ -86,6 +86,29 @@ export interface SolidStartOptions {
|
|
|
86
86
|
* @default "json"
|
|
87
87
|
*/
|
|
88
88
|
mode?: "js" | "json";
|
|
89
|
+
/**
|
|
90
|
+
* Path to a module whose default export is an array of custom Seroval
|
|
91
|
+
* plugins, used to serialize values Seroval doesn't understand natively
|
|
92
|
+
* (ORM id types, decimals, `Temporal`, and other custom classes).
|
|
93
|
+
*
|
|
94
|
+
* Build plugins with `createPlugin` from `seroval`. The module is bundled
|
|
95
|
+
* into both the client and the server so that both ends of a server
|
|
96
|
+
* function agree on the format, so it must not import server-only code.
|
|
97
|
+
*
|
|
98
|
+
* SolidStart's built-in plugins take precedence: Seroval uses the first
|
|
99
|
+
* plugin whose `test()` passes, and these are appended after the built-ins.
|
|
100
|
+
*
|
|
101
|
+
* A plugin's `deserialize` rebuilds the value under {@link mode} `"json"`.
|
|
102
|
+
* `serialize` is only used by `mode: "js"`, where the payload is evaluated
|
|
103
|
+
* on the client and may therefore reference globals only, not the plugin
|
|
104
|
+
* module's own imports.
|
|
105
|
+
*
|
|
106
|
+
* Only applies to server-function and action payloads. The SSR hydration
|
|
107
|
+
* payload is serialized by `solid-js/web` and is unaffected.
|
|
108
|
+
*
|
|
109
|
+
* @example "src/seroval-plugins.ts"
|
|
110
|
+
*/
|
|
111
|
+
plugins?: string;
|
|
89
112
|
};
|
|
90
113
|
/**
|
|
91
114
|
* Configures plugin behavior per build environment
|
|
@@ -95,6 +118,23 @@ export interface SolidStartOptions {
|
|
|
95
118
|
* Options controlling which files are processed as server functions
|
|
96
119
|
* (inclusion / exclusion filters for the `"use server"` transform).
|
|
97
120
|
*/
|
|
98
|
-
serverFunctions?: Pick<ServerFunctionsOptions, "filter"
|
|
121
|
+
serverFunctions?: Pick<ServerFunctionsOptions, "filter"> & {
|
|
122
|
+
/**
|
|
123
|
+
* Path to a module whose default export is called with whatever a server
|
|
124
|
+
* function threw, before it is serialized into the response. Return a
|
|
125
|
+
* value to send it in place of what was thrown, or `undefined` to send the
|
|
126
|
+
* original.
|
|
127
|
+
*
|
|
128
|
+
* Naming the module here rather than registering a handler at runtime
|
|
129
|
+
* keeps the app in sole control of it: no dependency can reach into the
|
|
130
|
+
* running server and take over reporting.
|
|
131
|
+
*
|
|
132
|
+
* The module is bundled into the server only, so it may import server-only
|
|
133
|
+
* code such as a monitoring SDK.
|
|
134
|
+
*
|
|
135
|
+
* @example "src/server-fn-error.ts"
|
|
136
|
+
*/
|
|
137
|
+
onError?: string;
|
|
138
|
+
};
|
|
99
139
|
}
|
|
100
140
|
export declare function solidStart(options?: SolidStartOptions): Array<PluginOption>;
|
package/dist/config/index.js
CHANGED
|
@@ -3,12 +3,14 @@ import { globSync } from "node:fs";
|
|
|
3
3
|
import { basename, extname, isAbsolute, join } from "node:path";
|
|
4
4
|
import solid from "vite-plugin-solid";
|
|
5
5
|
import { serverFunctionsPlugin } from "../directives/index.js";
|
|
6
|
+
import { appRootAlias } from "./app-root-alias.js";
|
|
6
7
|
import { boundaryModules } from "./boundary-modules.js";
|
|
7
8
|
import { DEFAULT_EXTENSIONS, VIRTUAL_MODULES, VITE_ENVIRONMENTS } from "./constants.js";
|
|
8
9
|
import { devServer } from "./dev-server.js";
|
|
9
10
|
import { envPlugin } from "./env.js";
|
|
10
11
|
import { SolidStartClientFileRouter, SolidStartServerFileRouter } from "./fs-router.js";
|
|
11
12
|
import { fsRoutes } from "./fs-routes/index.js";
|
|
13
|
+
import { sanitizeChunkFileName, toPickId } from "./fs-routes/tree-shake.js";
|
|
12
14
|
import lazy from "./lazy.js";
|
|
13
15
|
import { manifest } from "./manifest.js";
|
|
14
16
|
import { parseIdQuery } from "./utils.js";
|
|
@@ -71,17 +73,23 @@ export function solidStart(options) {
|
|
|
71
73
|
for (const route of await clientRouter.getRoutes()) {
|
|
72
74
|
for (const [key, value] of Object.entries(route)) {
|
|
73
75
|
if (value && key.startsWith("$") && !key.startsWith("$$")) {
|
|
74
|
-
|
|
75
|
-
return `${route.src}?${route.pick.map((p) => `pick=${p}`).join("&")}`;
|
|
76
|
-
}
|
|
77
|
-
clientInput.push(toRouteId(value));
|
|
76
|
+
clientInput.push(toPickId(value.src, value.pick));
|
|
78
77
|
}
|
|
79
78
|
}
|
|
80
79
|
}
|
|
81
80
|
}
|
|
82
81
|
return {
|
|
83
82
|
appType: "custom",
|
|
84
|
-
build: {
|
|
83
|
+
build: {
|
|
84
|
+
assetsDir: "_build/assets",
|
|
85
|
+
rollupOptions: {
|
|
86
|
+
output: {
|
|
87
|
+
// Keeps route chunks named after their file rather than after
|
|
88
|
+
// the `?pick=...` id that addresses them. See toPickId.
|
|
89
|
+
sanitizeFileName: sanitizeChunkFileName,
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
},
|
|
85
93
|
optimizeDeps: {
|
|
86
94
|
// Suppress TS errors from Vite 7 types when configuring Vite 8's Rolldown
|
|
87
95
|
...{
|
|
@@ -126,7 +134,6 @@ export function solidStart(options) {
|
|
|
126
134
|
resolve: {
|
|
127
135
|
alias: {
|
|
128
136
|
"@solidjs/start/server/entry": handlers.server,
|
|
129
|
-
"~": join(process.cwd(), start.appRoot),
|
|
130
137
|
...(!start.ssr
|
|
131
138
|
? {
|
|
132
139
|
"@solidjs/start/server": "@solidjs/start/server/spa",
|
|
@@ -175,6 +182,7 @@ export function solidStart(options) {
|
|
|
175
182
|
};
|
|
176
183
|
},
|
|
177
184
|
},
|
|
185
|
+
appRootAlias(root, start.appRoot),
|
|
178
186
|
manifest(start),
|
|
179
187
|
fsRoutes({
|
|
180
188
|
routers: {
|
package/dist/config/manifest.js
CHANGED
|
@@ -28,6 +28,18 @@ export function manifest(start) {
|
|
|
28
28
|
return await this.resolve(start.middleware);
|
|
29
29
|
return `\0${VIRTUAL_MODULES.middleware}`;
|
|
30
30
|
}
|
|
31
|
+
if (id === VIRTUAL_MODULES.serovalPlugins) {
|
|
32
|
+
const plugins = start.serialization?.plugins;
|
|
33
|
+
if (plugins)
|
|
34
|
+
return await this.resolve(plugins);
|
|
35
|
+
return `\0${VIRTUAL_MODULES.serovalPlugins}`;
|
|
36
|
+
}
|
|
37
|
+
if (id === VIRTUAL_MODULES.serverFnErrorHandler) {
|
|
38
|
+
const onError = start.serverFunctions?.onError;
|
|
39
|
+
if (onError)
|
|
40
|
+
return await this.resolve(onError);
|
|
41
|
+
return `\0${VIRTUAL_MODULES.serverFnErrorHandler}`;
|
|
42
|
+
}
|
|
31
43
|
},
|
|
32
44
|
async load(id) {
|
|
33
45
|
if (id === `\0${VIRTUAL_MODULES.clientViteManifest}`) {
|
|
@@ -76,6 +88,10 @@ export function manifest(start) {
|
|
|
76
88
|
}
|
|
77
89
|
else if (id === `\0${VIRTUAL_MODULES.middleware}`)
|
|
78
90
|
return "export default {};";
|
|
91
|
+
else if (id === `\0${VIRTUAL_MODULES.serovalPlugins}`)
|
|
92
|
+
return "export default [];";
|
|
93
|
+
else if (id === `\0${VIRTUAL_MODULES.serverFnErrorHandler}`)
|
|
94
|
+
return "export default undefined;";
|
|
79
95
|
else if (id.startsWith("/@manifest")) {
|
|
80
96
|
if (this.environment.mode !== "dev")
|
|
81
97
|
throw new Error("@manifest queries are only allowed in dev");
|
package/dist/fns/client.js
CHANGED
|
@@ -15,7 +15,6 @@ async function createRequest(base, id, instance, options) {
|
|
|
15
15
|
},
|
|
16
16
|
});
|
|
17
17
|
if (import.meta.env.DEV) {
|
|
18
|
-
console.log(pushRequest);
|
|
19
18
|
pushRequest(id, instance, request.clone());
|
|
20
19
|
}
|
|
21
20
|
const response = await fetch(request);
|
|
@@ -73,8 +72,8 @@ async function fetchServerFunction(base, id, options, args) {
|
|
|
73
72
|
return response;
|
|
74
73
|
}
|
|
75
74
|
const result = await extractBody(instance, true, response.clone());
|
|
76
|
-
if (response.headers.has("X-Error")) {
|
|
77
|
-
throw result;
|
|
75
|
+
if (response.headers.has("X-Error") || response.status >= 500) {
|
|
76
|
+
throw result ?? new Error(`Server function call failed with status ${response.status}`);
|
|
78
77
|
}
|
|
79
78
|
return result;
|
|
80
79
|
}
|
package/dist/fns/handler.d.ts
CHANGED
|
@@ -2,4 +2,4 @@ import { type H3Event } from "h3";
|
|
|
2
2
|
import "solid-start:server-fn-manifest";
|
|
3
3
|
import type { FetchEvent } from "../server/types.ts";
|
|
4
4
|
export declare function handleServerFunction(h3Event: H3Event): Promise<unknown>;
|
|
5
|
-
export declare function createSingleFlightHeaders(sourceEvent: FetchEvent): Headers;
|
|
5
|
+
export declare function createSingleFlightHeaders(sourceEvent: FetchEvent, result?: unknown): Headers;
|
package/dist/fns/handler.js
CHANGED
|
@@ -17,6 +17,7 @@ import { deserializeFromJSONString, serializeToJSONStream, serializeToJSStream,
|
|
|
17
17
|
import { BODY_FORMAT_KEY, BodyFormat, extractBody, getHeadersAndBody } from "./shared.js";
|
|
18
18
|
import "solid-start:server-fn-manifest";
|
|
19
19
|
import { getServerFunction, hasServerFunction } from "./registration.js";
|
|
20
|
+
import { applyServerFunctionErrorHandler } from "./error-handler.js";
|
|
20
21
|
import { getExpectedRedirectStatus } from "../server/util.js";
|
|
21
22
|
export async function handleServerFunction(h3Event) {
|
|
22
23
|
const event = getFetchEvent(h3Event);
|
|
@@ -109,7 +110,7 @@ export async function handleServerFunction(h3Event) {
|
|
|
109
110
|
}
|
|
110
111
|
// handle no JS success case
|
|
111
112
|
if (!instance)
|
|
112
|
-
return handleNoJS(result, request, parsed);
|
|
113
|
+
return await handleNoJS(result, request, parsed);
|
|
113
114
|
const body = getHeadersAndBody(result);
|
|
114
115
|
if (body) {
|
|
115
116
|
return new Response(body.body, {
|
|
@@ -124,6 +125,7 @@ export async function handleServerFunction(h3Event) {
|
|
|
124
125
|
return serializeToJSONStream(result);
|
|
125
126
|
}
|
|
126
127
|
catch (x) {
|
|
128
|
+
x = applyServerFunctionErrorHandler(x);
|
|
127
129
|
if (x instanceof Response) {
|
|
128
130
|
if (singleFlight && instance) {
|
|
129
131
|
x = await handleSingleFlight(event, x);
|
|
@@ -146,7 +148,7 @@ export async function handleServerFunction(h3Event) {
|
|
|
146
148
|
h3Event.res.headers.set("X-Error", toHeaderValue(error));
|
|
147
149
|
}
|
|
148
150
|
else {
|
|
149
|
-
x = handleNoJS(x, request, parsed, true);
|
|
151
|
+
x = await handleNoJS(x, request, parsed, true);
|
|
150
152
|
}
|
|
151
153
|
if (instance) {
|
|
152
154
|
const body = getHeadersAndBody(x);
|
|
@@ -181,7 +183,20 @@ function toHeaderValue(value) {
|
|
|
181
183
|
return "true";
|
|
182
184
|
}
|
|
183
185
|
}
|
|
184
|
-
function
|
|
186
|
+
function getRefererLocation(request, url) {
|
|
187
|
+
const referer = request.headers.get("referer");
|
|
188
|
+
try {
|
|
189
|
+
if (referer)
|
|
190
|
+
return new URL(referer).toString();
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
// fall through to the app root below
|
|
194
|
+
}
|
|
195
|
+
// no usable referer (e.g. a no-referrer policy): the app root still beats
|
|
196
|
+
// leaving the browser sitting on the server function endpoint
|
|
197
|
+
return new URL(import.meta.env.BASE_URL, url.origin).toString();
|
|
198
|
+
}
|
|
199
|
+
async function handleNoJS(result, request, parsed, thrown) {
|
|
185
200
|
const url = new URL(request.url);
|
|
186
201
|
const isError = result instanceof Error;
|
|
187
202
|
let statusCode = 302;
|
|
@@ -192,10 +207,19 @@ function handleNoJS(result, request, parsed, thrown) {
|
|
|
192
207
|
headers.set(`Location`, new URL(result.headers.get("Location"), url.origin + import.meta.env.BASE_URL).toString());
|
|
193
208
|
statusCode = getExpectedRedirectStatus(result);
|
|
194
209
|
}
|
|
210
|
+
else {
|
|
211
|
+
// responses that carry a value rather than a destination (json(), reload())
|
|
212
|
+
// still have to send the browser back to the page the form came from
|
|
213
|
+
headers.set("Location", getRefererLocation(request, url));
|
|
214
|
+
}
|
|
215
|
+
// the body is dropped from the redirect, so don't advertise its type
|
|
216
|
+
headers.delete("Content-Type");
|
|
217
|
+
// mirror the JS path: the flash cookie carries the value, not the Response
|
|
218
|
+
result = result.customBody ? await result.customBody() : null;
|
|
195
219
|
}
|
|
196
220
|
else
|
|
197
221
|
headers = new Headers({
|
|
198
|
-
Location:
|
|
222
|
+
Location: getRefererLocation(request, url),
|
|
199
223
|
});
|
|
200
224
|
if (result) {
|
|
201
225
|
headers.append("Set-Cookie", `flash=${encodeURIComponent(JSON.stringify({
|
|
@@ -203,7 +227,9 @@ function handleNoJS(result, request, parsed, thrown) {
|
|
|
203
227
|
result: isError ? result.message : result,
|
|
204
228
|
thrown: thrown,
|
|
205
229
|
error: isError,
|
|
206
|
-
input:
|
|
230
|
+
input: parsed.length
|
|
231
|
+
? [...parsed.slice(0, -1), [...parsed[parsed.length - 1].entries()]]
|
|
232
|
+
: [],
|
|
207
233
|
}))}; Secure; HttpOnly;`);
|
|
208
234
|
}
|
|
209
235
|
return new Response(null, {
|
|
@@ -212,13 +238,19 @@ function handleNoJS(result, request, parsed, thrown) {
|
|
|
212
238
|
});
|
|
213
239
|
}
|
|
214
240
|
let App;
|
|
215
|
-
export function createSingleFlightHeaders(sourceEvent) {
|
|
241
|
+
export function createSingleFlightHeaders(sourceEvent, result) {
|
|
216
242
|
// cookie handling logic is pretty simplistic so this might be imperfect
|
|
217
243
|
// unclear if h3 internals are available on all platforms but we need a way to
|
|
218
244
|
// update request headers on the underlying H3 event.
|
|
219
245
|
const headers = new Headers(sourceEvent.request.headers);
|
|
220
246
|
const cookies = parseCookies(sourceEvent.nativeEvent);
|
|
221
247
|
const SetCookies = sourceEvent.response.headers.getSetCookie();
|
|
248
|
+
// cookies attached to the returned/thrown response (eg. `redirect(to, { headers })`)
|
|
249
|
+
// haven't been merged onto the event response yet, but a browser round trip would
|
|
250
|
+
// have sent them back with the next request, so apply them here too. They come after
|
|
251
|
+
// the ones on the event response, so they win on conflict.
|
|
252
|
+
if (result instanceof Response)
|
|
253
|
+
SetCookies.push(...result.headers.getSetCookie());
|
|
222
254
|
headers.delete("cookie");
|
|
223
255
|
// let useH3Internals = false;
|
|
224
256
|
// if (sourceEvent.nativeEvent.node?.req) {
|
|
@@ -262,7 +294,7 @@ async function handleSingleFlight(sourceEvent, result) {
|
|
|
262
294
|
}
|
|
263
295
|
const event = { ...sourceEvent };
|
|
264
296
|
event.request = new Request(url, {
|
|
265
|
-
headers: createSingleFlightHeaders(sourceEvent),
|
|
297
|
+
headers: createSingleFlightHeaders(sourceEvent, result),
|
|
266
298
|
});
|
|
267
299
|
return await provideRequestEvent(event, async () => {
|
|
268
300
|
await createPageEvent(event);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Re-exports Seroval's plugin authoring API for use with the
|
|
3
|
+
* `serialization.plugins` option in `vite.config.ts`.
|
|
4
|
+
*
|
|
5
|
+
* Importing from here rather than depending on `seroval` directly keeps plugin
|
|
6
|
+
* authors on the exact Seroval version SolidStart serializes with. A mismatched
|
|
7
|
+
* version would not fail the build: it would produce plugin nodes the other end
|
|
8
|
+
* of the wire can't interpret.
|
|
9
|
+
*
|
|
10
|
+
* @see https://github.com/solidjs/solid-start/issues/1474
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* // src/seroval-plugins.ts
|
|
15
|
+
* import { createPlugin } from "@solidjs/start/serialization";
|
|
16
|
+
* import { ObjectId } from "mongodb";
|
|
17
|
+
*
|
|
18
|
+
* const ObjectIdPlugin = createPlugin<ObjectId, { hex: any }>({
|
|
19
|
+
* tag: "app/ObjectId",
|
|
20
|
+
* test: value => value instanceof ObjectId,
|
|
21
|
+
* parse: {
|
|
22
|
+
* sync: (value, ctx) => ({ hex: ctx.parse(value.toHexString()) }),
|
|
23
|
+
* async: async (value, ctx) => ({ hex: await ctx.parse(value.toHexString()) }),
|
|
24
|
+
* stream: (value, ctx) => ({ hex: ctx.parse(value.toHexString()) }),
|
|
25
|
+
* },
|
|
26
|
+
* serialize: (node, ctx) => `globalThis.ObjectId(${ctx.serialize(node.hex)})`,
|
|
27
|
+
* deserialize: (node, ctx) => new ObjectId(ctx.deserialize(node.hex) as string),
|
|
28
|
+
* });
|
|
29
|
+
*
|
|
30
|
+
* export default [ObjectIdPlugin];
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export { createPlugin, OpaqueReference } from "seroval";
|
|
34
|
+
export type { AsyncParsePluginContext, DeserializePluginContext, Plugin, PluginData, PluginInfo, SerializePluginContext, SerovalNode, StreamParsePluginContext, SyncParsePluginContext, } from "seroval";
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Re-exports Seroval's plugin authoring API for use with the
|
|
3
|
+
* `serialization.plugins` option in `vite.config.ts`.
|
|
4
|
+
*
|
|
5
|
+
* Importing from here rather than depending on `seroval` directly keeps plugin
|
|
6
|
+
* authors on the exact Seroval version SolidStart serializes with. A mismatched
|
|
7
|
+
* version would not fail the build: it would produce plugin nodes the other end
|
|
8
|
+
* of the wire can't interpret.
|
|
9
|
+
*
|
|
10
|
+
* @see https://github.com/solidjs/solid-start/issues/1474
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* // src/seroval-plugins.ts
|
|
15
|
+
* import { createPlugin } from "@solidjs/start/serialization";
|
|
16
|
+
* import { ObjectId } from "mongodb";
|
|
17
|
+
*
|
|
18
|
+
* const ObjectIdPlugin = createPlugin<ObjectId, { hex: any }>({
|
|
19
|
+
* tag: "app/ObjectId",
|
|
20
|
+
* test: value => value instanceof ObjectId,
|
|
21
|
+
* parse: {
|
|
22
|
+
* sync: (value, ctx) => ({ hex: ctx.parse(value.toHexString()) }),
|
|
23
|
+
* async: async (value, ctx) => ({ hex: await ctx.parse(value.toHexString()) }),
|
|
24
|
+
* stream: (value, ctx) => ({ hex: ctx.parse(value.toHexString()) }),
|
|
25
|
+
* },
|
|
26
|
+
* serialize: (node, ctx) => `globalThis.ObjectId(${ctx.serialize(node.hex)})`,
|
|
27
|
+
* deserialize: (node, ctx) => new ObjectId(ctx.deserialize(node.hex) as string),
|
|
28
|
+
* });
|
|
29
|
+
*
|
|
30
|
+
* export default [ObjectIdPlugin];
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export { createPlugin, OpaqueReference } from "seroval";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { crossSerializeStream, deserialize, Feature, fromCrossJSON, getCrossReferenceHeader, toCrossJSONStream, } from "seroval";
|
|
2
2
|
import { AbortSignalPlugin, CustomEventPlugin, DOMExceptionPlugin, EventPlugin, FormDataPlugin, HeadersPlugin, ReadableStreamPlugin, RequestPlugin, ResponsePlugin, URLPlugin, URLSearchParamsPlugin, } from "seroval-plugins/web";
|
|
3
|
-
|
|
3
|
+
import userPlugins from "solid-start:seroval-plugins";
|
|
4
4
|
const DEFAULT_PLUGINS = [
|
|
5
5
|
AbortSignalPlugin,
|
|
6
6
|
CustomEventPlugin,
|
|
@@ -14,6 +14,16 @@ const DEFAULT_PLUGINS = [
|
|
|
14
14
|
URLSearchParamsPlugin,
|
|
15
15
|
URLPlugin,
|
|
16
16
|
];
|
|
17
|
+
/**
|
|
18
|
+
* Plugins from `serialization.plugins` are appended rather than prepended:
|
|
19
|
+
* seroval picks the first plugin whose `test()` passes, so the built-ins win
|
|
20
|
+
* and a loose user `test()` can't take over `Request`/`FormData`/`URL`.
|
|
21
|
+
*
|
|
22
|
+
* The same list has to be used on both ends of a server function, which is why
|
|
23
|
+
* this comes from a virtual module bundled into the client and the server
|
|
24
|
+
* rather than a runtime option.
|
|
25
|
+
*/
|
|
26
|
+
const PLUGINS = [...DEFAULT_PLUGINS, ...userPlugins];
|
|
17
27
|
const MAX_SERIALIZATION_DEPTH_LIMIT = 64;
|
|
18
28
|
const DISABLED_FEATURES = Feature.RegExp;
|
|
19
29
|
/**
|
|
@@ -67,7 +77,7 @@ export function serializeToJSStream(id, value) {
|
|
|
67
77
|
crossSerializeStream(value, {
|
|
68
78
|
scopeId: id,
|
|
69
79
|
disabledFeatures: JS_SERIALIZE_DISABLED_FEATURES,
|
|
70
|
-
plugins:
|
|
80
|
+
plugins: PLUGINS,
|
|
71
81
|
onSerialize(data, initial) {
|
|
72
82
|
controller.enqueue(createChunk(initial ? `(${getCrossReferenceHeader(id)},${data})` : data));
|
|
73
83
|
},
|
|
@@ -87,7 +97,7 @@ export function serializeToJSONStream(value) {
|
|
|
87
97
|
toCrossJSONStream(value, {
|
|
88
98
|
disabledFeatures: SERIALIZE_DISABLED_FEATURES,
|
|
89
99
|
depthLimit: MAX_SERIALIZATION_DEPTH_LIMIT,
|
|
90
|
-
plugins:
|
|
100
|
+
plugins: PLUGINS,
|
|
91
101
|
onParse(node) {
|
|
92
102
|
controller.enqueue(createChunk(JSON.stringify(node)));
|
|
93
103
|
},
|
|
@@ -200,7 +210,7 @@ export async function deserializeJSONStream(response) {
|
|
|
200
210
|
refs,
|
|
201
211
|
disabledFeatures: DISABLED_FEATURES,
|
|
202
212
|
depthLimit: MAX_SERIALIZATION_DEPTH_LIMIT,
|
|
203
|
-
plugins:
|
|
213
|
+
plugins: PLUGINS,
|
|
204
214
|
});
|
|
205
215
|
return value;
|
|
206
216
|
}
|
package/dist/server/handler.js
CHANGED
|
@@ -171,9 +171,17 @@ function handleStreamCompleteRedirect(context) {
|
|
|
171
171
|
return ({ write }) => {
|
|
172
172
|
context.complete = true;
|
|
173
173
|
const to = context.response && context.response.headers.get("Location");
|
|
174
|
-
|
|
174
|
+
if (!to)
|
|
175
|
+
return;
|
|
176
|
+
// The shell has already flushed, so the redirect has to happen client side.
|
|
177
|
+
// Carry the nonce so a strict `script-src` CSP doesn't block it.
|
|
178
|
+
const nonce = context.nonce ? ` nonce="${escapeAttribute(context.nonce)}"` : "";
|
|
179
|
+
write(`<script${nonce}>window.location=${JSON.stringify(to).replace(/</g, "\\u003c")}</script>`);
|
|
175
180
|
};
|
|
176
181
|
}
|
|
182
|
+
function escapeAttribute(value) {
|
|
183
|
+
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<");
|
|
184
|
+
}
|
|
177
185
|
function stripBaseUrl(path) {
|
|
178
186
|
const base = import.meta.env.SERVER_BASE_URL || import.meta.env.BASE_URL || "/";
|
|
179
187
|
return stripPathBase(path, base);
|
package/dist/server/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export { getServerFunctionMeta } from "../shared/serverFunction.ts";
|
|
|
3
3
|
export { StartServer } from "./StartServer.tsx";
|
|
4
4
|
export { decorateHandler, decorateMiddleware } from "./fetchEvent.ts";
|
|
5
5
|
export { createHandler } from "./handler.ts";
|
|
6
|
+
export type { ServerFunctionErrorHandler } from "../fns/error-handler.ts";
|
|
6
7
|
export type { APIEvent, APIHandler, Asset, ContextMatches, DocumentComponentProps, FetchEvent, HandlerOptions, PageEvent, ResponseStub, ServerFunctionMeta, StartHandler, } from "./types.ts";
|
|
7
8
|
/**
|
|
8
9
|
* Checks if user has set a redirect status in the response.
|
|
@@ -18,7 +18,7 @@ export function StartServer(props) {
|
|
|
18
18
|
<TopErrorBoundary>
|
|
19
19
|
<props.document scripts={<>
|
|
20
20
|
<PatchVirtualDevStyles nonce={nonce}/>
|
|
21
|
-
<script type="module" src={getSsrManifest("client").path(import.meta.env.START_CLIENT_ENTRY_URL)}/>
|
|
21
|
+
<script type="module" nonce={nonce} src={getSsrManifest("client").path(import.meta.env.START_CLIENT_ENTRY_URL)}/>
|
|
22
22
|
</>}/>
|
|
23
23
|
</TopErrorBoundary>
|
|
24
24
|
</NoHydration>);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solidjs/start",
|
|
3
|
-
"version": "2.0.0-rc.
|
|
3
|
+
"version": "2.0.0-rc.6",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/solidjs/solid-start.git",
|
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
"./http": "./dist/http/index.js",
|
|
25
25
|
"./env": "./env.d.ts",
|
|
26
26
|
"./fns/server": "./dist/fns/server.js",
|
|
27
|
-
"./fns/client": "./dist/fns/client.js"
|
|
27
|
+
"./fns/client": "./dist/fns/client.js",
|
|
28
|
+
"./serialization": "./dist/fns/plugins.js"
|
|
28
29
|
},
|
|
29
30
|
"publishConfig": {
|
|
30
31
|
"access": "public"
|
|
@@ -63,8 +64,14 @@
|
|
|
63
64
|
"vitest": "^4.1.10"
|
|
64
65
|
},
|
|
65
66
|
"peerDependencies": {
|
|
67
|
+
"@solidjs/router": ">=0.16.0 <2.0.0-0",
|
|
66
68
|
"vite": "^8 || ^9"
|
|
67
69
|
},
|
|
70
|
+
"peerDependenciesMeta": {
|
|
71
|
+
"@solidjs/router": {
|
|
72
|
+
"optional": true
|
|
73
|
+
}
|
|
74
|
+
},
|
|
68
75
|
"engines": {
|
|
69
76
|
"node": ">=24"
|
|
70
77
|
},
|