rwsdk 1.0.0-beta.25 → 1.0.0-beta.27
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/dist/runtime/client/client.d.ts +33 -0
- package/dist/runtime/client/client.js +33 -0
- package/dist/runtime/client/navigation.d.ts +41 -0
- package/dist/runtime/client/navigation.js +41 -0
- package/dist/runtime/lib/db/SqliteDurableObject.d.ts +2 -2
- package/dist/runtime/lib/db/SqliteDurableObject.js +2 -2
- package/dist/runtime/lib/db/typeInference/builders/columnDefinition.d.ts +1 -1
- package/dist/runtime/lib/db/typeInference/typetests/createTable.typetest.js +2 -1
- package/dist/runtime/lib/links.d.ts +18 -0
- package/dist/runtime/lib/links.js +18 -0
- package/dist/runtime/lib/router.d.ts +110 -5
- package/dist/runtime/lib/router.js +109 -4
- package/package.json +2 -2
|
@@ -4,6 +4,39 @@ export { ClientOnly } from "./ClientOnly.js";
|
|
|
4
4
|
export { initClientNavigation, navigate } from "./navigation.js";
|
|
5
5
|
import type { HydrationOptions, Transport } from "./types";
|
|
6
6
|
export declare const fetchTransport: Transport;
|
|
7
|
+
/**
|
|
8
|
+
* Initializes the React client and hydrates the RSC payload.
|
|
9
|
+
*
|
|
10
|
+
* This function sets up client-side hydration for React Server Components,
|
|
11
|
+
* making the page interactive. Call this from your client entry point.
|
|
12
|
+
*
|
|
13
|
+
* @param transport - Custom transport for server communication (defaults to fetchTransport)
|
|
14
|
+
* @param hydrateRootOptions - Options passed to React's hydrateRoot
|
|
15
|
+
* @param handleResponse - Custom response handler for navigation errors
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* // Basic usage
|
|
19
|
+
* import { initClient } from "rwsdk/client";
|
|
20
|
+
*
|
|
21
|
+
* initClient();
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* // With client-side navigation
|
|
25
|
+
* import { initClient, initClientNavigation } from "rwsdk/client";
|
|
26
|
+
*
|
|
27
|
+
* const { handleResponse } = initClientNavigation();
|
|
28
|
+
* initClient({ handleResponse });
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* // With custom React hydration options
|
|
32
|
+
* initClient({
|
|
33
|
+
* hydrateRootOptions: {
|
|
34
|
+
* onRecoverableError: (error) => {
|
|
35
|
+
* console.warn("Recoverable error:", error);
|
|
36
|
+
* },
|
|
37
|
+
* },
|
|
38
|
+
* });
|
|
39
|
+
*/
|
|
7
40
|
export declare const initClient: ({ transport, hydrateRootOptions, handleResponse, }?: {
|
|
8
41
|
transport?: Transport;
|
|
9
42
|
hydrateRootOptions?: HydrationOptions;
|
|
@@ -50,6 +50,39 @@ export const fetchTransport = (transportContext) => {
|
|
|
50
50
|
};
|
|
51
51
|
return fetchCallServer;
|
|
52
52
|
};
|
|
53
|
+
/**
|
|
54
|
+
* Initializes the React client and hydrates the RSC payload.
|
|
55
|
+
*
|
|
56
|
+
* This function sets up client-side hydration for React Server Components,
|
|
57
|
+
* making the page interactive. Call this from your client entry point.
|
|
58
|
+
*
|
|
59
|
+
* @param transport - Custom transport for server communication (defaults to fetchTransport)
|
|
60
|
+
* @param hydrateRootOptions - Options passed to React's hydrateRoot
|
|
61
|
+
* @param handleResponse - Custom response handler for navigation errors
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* // Basic usage
|
|
65
|
+
* import { initClient } from "rwsdk/client";
|
|
66
|
+
*
|
|
67
|
+
* initClient();
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* // With client-side navigation
|
|
71
|
+
* import { initClient, initClientNavigation } from "rwsdk/client";
|
|
72
|
+
*
|
|
73
|
+
* const { handleResponse } = initClientNavigation();
|
|
74
|
+
* initClient({ handleResponse });
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* // With custom React hydration options
|
|
78
|
+
* initClient({
|
|
79
|
+
* hydrateRootOptions: {
|
|
80
|
+
* onRecoverableError: (error) => {
|
|
81
|
+
* console.warn("Recoverable error:", error);
|
|
82
|
+
* },
|
|
83
|
+
* },
|
|
84
|
+
* });
|
|
85
|
+
*/
|
|
53
86
|
export const initClient = async ({ transport = fetchTransport, hydrateRootOptions, handleResponse, } = {}) => {
|
|
54
87
|
const transportContext = {
|
|
55
88
|
setRscPayload: () => { },
|
|
@@ -12,6 +12,47 @@ export interface NavigateOptions {
|
|
|
12
12
|
};
|
|
13
13
|
}
|
|
14
14
|
export declare function navigate(href: string, options?: NavigateOptions): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Initializes client-side navigation for Single Page App (SPA) behavior.
|
|
17
|
+
*
|
|
18
|
+
* Intercepts clicks on internal links and fetches page content without full-page reloads.
|
|
19
|
+
* Returns a handleResponse function to pass to initClient.
|
|
20
|
+
*
|
|
21
|
+
* @param opts.scrollToTop - Scroll to top after navigation (default: true)
|
|
22
|
+
* @param opts.scrollBehavior - How to scroll: 'instant', 'smooth', or 'auto' (default: 'instant')
|
|
23
|
+
* @param opts.onNavigate - Callback executed after history push but before RSC fetch
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* // Basic usage
|
|
27
|
+
* import { initClient, initClientNavigation } from "rwsdk/client";
|
|
28
|
+
*
|
|
29
|
+
* const { handleResponse } = initClientNavigation();
|
|
30
|
+
* initClient({ handleResponse });
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* // With custom scroll behavior
|
|
34
|
+
* const { handleResponse } = initClientNavigation({
|
|
35
|
+
* scrollBehavior: "smooth",
|
|
36
|
+
* scrollToTop: true,
|
|
37
|
+
* });
|
|
38
|
+
* initClient({ handleResponse });
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* // Preserve scroll position (e.g., for infinite scroll)
|
|
42
|
+
* const { handleResponse } = initClientNavigation({
|
|
43
|
+
* scrollToTop: false,
|
|
44
|
+
* });
|
|
45
|
+
* initClient({ handleResponse });
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* // With navigation callback
|
|
49
|
+
* const { handleResponse } = initClientNavigation({
|
|
50
|
+
* onNavigate: () => {
|
|
51
|
+
* console.log("Navigating to:", window.location.href);
|
|
52
|
+
* },
|
|
53
|
+
* });
|
|
54
|
+
* initClient({ handleResponse });
|
|
55
|
+
*/
|
|
15
56
|
export declare function initClientNavigation(opts?: ClientNavigationOptions): {
|
|
16
57
|
handleResponse: (response: Response) => boolean;
|
|
17
58
|
};
|
|
@@ -64,6 +64,47 @@ function saveScrollPosition(x, y) {
|
|
|
64
64
|
scrollY: y,
|
|
65
65
|
}, "", window.location.href);
|
|
66
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* Initializes client-side navigation for Single Page App (SPA) behavior.
|
|
69
|
+
*
|
|
70
|
+
* Intercepts clicks on internal links and fetches page content without full-page reloads.
|
|
71
|
+
* Returns a handleResponse function to pass to initClient.
|
|
72
|
+
*
|
|
73
|
+
* @param opts.scrollToTop - Scroll to top after navigation (default: true)
|
|
74
|
+
* @param opts.scrollBehavior - How to scroll: 'instant', 'smooth', or 'auto' (default: 'instant')
|
|
75
|
+
* @param opts.onNavigate - Callback executed after history push but before RSC fetch
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* // Basic usage
|
|
79
|
+
* import { initClient, initClientNavigation } from "rwsdk/client";
|
|
80
|
+
*
|
|
81
|
+
* const { handleResponse } = initClientNavigation();
|
|
82
|
+
* initClient({ handleResponse });
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* // With custom scroll behavior
|
|
86
|
+
* const { handleResponse } = initClientNavigation({
|
|
87
|
+
* scrollBehavior: "smooth",
|
|
88
|
+
* scrollToTop: true,
|
|
89
|
+
* });
|
|
90
|
+
* initClient({ handleResponse });
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* // Preserve scroll position (e.g., for infinite scroll)
|
|
94
|
+
* const { handleResponse } = initClientNavigation({
|
|
95
|
+
* scrollToTop: false,
|
|
96
|
+
* });
|
|
97
|
+
* initClient({ handleResponse });
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* // With navigation callback
|
|
101
|
+
* const { handleResponse } = initClientNavigation({
|
|
102
|
+
* onNavigate: () => {
|
|
103
|
+
* console.log("Navigating to:", window.location.href);
|
|
104
|
+
* },
|
|
105
|
+
* });
|
|
106
|
+
* initClient({ handleResponse });
|
|
107
|
+
*/
|
|
67
108
|
export function initClientNavigation(opts = {}) {
|
|
68
109
|
IS_CLIENT_NAVIGATION = true;
|
|
69
110
|
history.scrollRestoration = "auto";
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { DurableObject } from "cloudflare:workers";
|
|
2
|
-
import { Kysely, QueryResult } from "kysely";
|
|
2
|
+
import { Kysely, KyselyPlugin, QueryResult } from "kysely";
|
|
3
3
|
export declare class SqliteDurableObject<T = any> extends DurableObject {
|
|
4
4
|
migrations: Record<string, any>;
|
|
5
5
|
kysely: Kysely<T>;
|
|
6
6
|
private initialized;
|
|
7
7
|
private migrationTableName;
|
|
8
|
-
constructor(ctx: DurableObjectState, env: any, migrations: Record<string, any>, migrationTableName?: string);
|
|
8
|
+
constructor(ctx: DurableObjectState, env: any, migrations: Record<string, any>, migrationTableName?: string, plugins?: KyselyPlugin[]);
|
|
9
9
|
initialize(): Promise<void>;
|
|
10
10
|
kyselyExecuteQuery<R>(compiledQuery: {
|
|
11
11
|
sql: string;
|
|
@@ -6,14 +6,14 @@ import { createMigrator } from "./index.js";
|
|
|
6
6
|
const log = debug("sdk:do-db");
|
|
7
7
|
// Base class for Durable Objects that need Kysely database access
|
|
8
8
|
export class SqliteDurableObject extends DurableObject {
|
|
9
|
-
constructor(ctx, env, migrations, migrationTableName = "__migrations") {
|
|
9
|
+
constructor(ctx, env, migrations, migrationTableName = "__migrations", plugins = [new ParseJSONResultsPlugin()]) {
|
|
10
10
|
super(ctx, env);
|
|
11
11
|
this.initialized = false;
|
|
12
12
|
this.migrations = migrations;
|
|
13
13
|
this.migrationTableName = migrationTableName;
|
|
14
14
|
this.kysely = new Kysely({
|
|
15
15
|
dialect: new DODialect({ ctx }),
|
|
16
|
-
plugins:
|
|
16
|
+
plugins: plugins,
|
|
17
17
|
});
|
|
18
18
|
}
|
|
19
19
|
async initialize() {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ColumnDefinitionNode, Expression, sql } from "kysely";
|
|
2
|
-
type DefaultValueExpression = string | number | boolean | null | typeof sql
|
|
2
|
+
type DefaultValueExpression = string | number | boolean | null | ReturnType<typeof sql>;
|
|
3
3
|
export interface ColumnDefinitionBuilder<TType> {
|
|
4
4
|
autoIncrement(): ColumnDefinitionBuilder<TType>;
|
|
5
5
|
identity(): ColumnDefinitionBuilder<TType>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { sql } from "kysely";
|
|
1
2
|
(_it = "createTable") => {
|
|
2
3
|
const migrations = {
|
|
3
4
|
"001_init": {
|
|
@@ -23,6 +24,7 @@
|
|
|
23
24
|
.addColumn("username", "text", (col) => col.notNull())
|
|
24
25
|
.addColumn("age", "integer", (col) => col.defaultTo(18))
|
|
25
26
|
.addColumn("active", "boolean", (col) => col.defaultTo(true))
|
|
27
|
+
.addColumn("anotherBoolean", "boolean", (col) => col.defaultTo(sql `true`))
|
|
26
28
|
.execute(),
|
|
27
29
|
];
|
|
28
30
|
},
|
|
@@ -30,4 +32,3 @@
|
|
|
30
32
|
};
|
|
31
33
|
(_test) => { };
|
|
32
34
|
};
|
|
33
|
-
export {};
|
|
@@ -10,5 +10,23 @@ type ParseRoute<T extends string> = T extends `${infer Start}:${infer Param}/${i
|
|
|
10
10
|
type LinkFunction<T extends readonly string[]> = {
|
|
11
11
|
<Path extends T[number]>(path: Path, params?: ParseRoute<Path> extends Record<string, never> ? undefined : ParseRoute<Path>): string;
|
|
12
12
|
};
|
|
13
|
+
/**
|
|
14
|
+
* Creates a type-safe link generation function from route patterns.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* // Define your routes
|
|
18
|
+
* const link = defineLinks([
|
|
19
|
+
* "/",
|
|
20
|
+
* "/about",
|
|
21
|
+
* "/users/:id",
|
|
22
|
+
* "/files/*",
|
|
23
|
+
* ] as const)
|
|
24
|
+
*
|
|
25
|
+
* // Generate links with type checking
|
|
26
|
+
* link("/") // "/"
|
|
27
|
+
* link("/about") // "/about"
|
|
28
|
+
* link("/users/:id", { id: "123" }) // "/users/123"
|
|
29
|
+
* link("/files/*", { $0: "docs/guide.pdf" }) // "/files/docs/guide.pdf"
|
|
30
|
+
*/
|
|
13
31
|
export declare function defineLinks<const T extends readonly string[]>(routes: T): LinkFunction<T>;
|
|
14
32
|
export {};
|
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates a type-safe link generation function from route patterns.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* // Define your routes
|
|
6
|
+
* const link = defineLinks([
|
|
7
|
+
* "/",
|
|
8
|
+
* "/about",
|
|
9
|
+
* "/users/:id",
|
|
10
|
+
* "/files/*",
|
|
11
|
+
* ] as const)
|
|
12
|
+
*
|
|
13
|
+
* // Generate links with type checking
|
|
14
|
+
* link("/") // "/"
|
|
15
|
+
* link("/about") // "/about"
|
|
16
|
+
* link("/users/:id", { id: "123" }) // "/users/123"
|
|
17
|
+
* link("/files/*", { $0: "docs/guide.pdf" }) // "/files/docs/guide.pdf"
|
|
18
|
+
*/
|
|
1
19
|
export function defineLinks(routes) {
|
|
2
20
|
// Validate routes at runtime
|
|
3
21
|
routes.forEach((route) => {
|
|
@@ -5,7 +5,7 @@ export type RouteMiddleware<T extends RequestInfo = RequestInfo> = (requestInfo:
|
|
|
5
5
|
type RouteFunction<T extends RequestInfo = RequestInfo> = (requestInfo: T) => MaybePromise<Response>;
|
|
6
6
|
type MaybePromise<T> = T | Promise<T>;
|
|
7
7
|
type RouteComponent<T extends RequestInfo = RequestInfo> = (requestInfo: T) => MaybePromise<React.JSX.Element | Response | void>;
|
|
8
|
-
type RouteHandler<T extends RequestInfo = RequestInfo> = RouteFunction<T> | RouteComponent<T> | [...RouteMiddleware<T>[], RouteFunction<T> | RouteComponent<T>];
|
|
8
|
+
type RouteHandler<T extends RequestInfo = RequestInfo> = RouteFunction<T> | RouteComponent<T> | readonly [...RouteMiddleware<T>[], RouteFunction<T> | RouteComponent<T>];
|
|
9
9
|
declare const METHOD_VERBS: readonly ["delete", "get", "head", "patch", "post", "put"];
|
|
10
10
|
export type MethodVerb = (typeof METHOD_VERBS)[number];
|
|
11
11
|
export type MethodHandlers<T extends RequestInfo = RequestInfo> = {
|
|
@@ -37,18 +37,123 @@ export declare function defineRoutes<T extends RequestInfo = RequestInfo>(routes
|
|
|
37
37
|
rscActionHandler: (request: Request) => Promise<unknown>;
|
|
38
38
|
}) => Response | Promise<Response>;
|
|
39
39
|
};
|
|
40
|
+
/**
|
|
41
|
+
* Defines a route handler for a path pattern.
|
|
42
|
+
*
|
|
43
|
+
* Supports three types of path patterns:
|
|
44
|
+
* - Static: /about, /contact
|
|
45
|
+
* - Parameters: /users/:id, /posts/:postId/edit
|
|
46
|
+
* - Wildcards: /files/*, /api/*\/download
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* // Static route
|
|
50
|
+
* route("/about", () => <AboutPage />)
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* // Route with parameters
|
|
54
|
+
* route("/users/:id", ({ params }) => {
|
|
55
|
+
* return <UserProfile userId={params.id} />
|
|
56
|
+
* })
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* // Route with wildcards
|
|
60
|
+
* route("/files/*", ({ params }) => {
|
|
61
|
+
* const filePath = params.$0
|
|
62
|
+
* return <FileViewer path={filePath} />
|
|
63
|
+
* })
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* // Method-based routing
|
|
67
|
+
* route("/api/users", {
|
|
68
|
+
* get: () => Response.json(users),
|
|
69
|
+
* post: ({ request }) => Response.json({ status: "created" }, { status: 201 }),
|
|
70
|
+
* delete: () => new Response(null, { status: 204 }),
|
|
71
|
+
* })
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* // Route with middleware array
|
|
75
|
+
* route("/admin", [isAuthenticated, isAdmin, () => <AdminDashboard />])
|
|
76
|
+
*/
|
|
40
77
|
export declare function route<T extends RequestInfo = RequestInfo>(path: string, handler: RouteHandler<T> | MethodHandlers<T>): RouteDefinition<T>;
|
|
78
|
+
/**
|
|
79
|
+
* Defines a route handler for the root path "/".
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* // Homepage
|
|
83
|
+
* index(() => <HomePage />)
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* // With middleware
|
|
87
|
+
* index([logRequest, () => <HomePage />])
|
|
88
|
+
*/
|
|
41
89
|
export declare function index<T extends RequestInfo = RequestInfo>(handler: RouteHandler<T>): RouteDefinition<T>;
|
|
90
|
+
/**
|
|
91
|
+
* Prefixes a group of routes with a path.
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* // Organize blog routes under /blog
|
|
95
|
+
* const blogRoutes = [
|
|
96
|
+
* route("/", () => <BlogIndex />),
|
|
97
|
+
* route("/post/:id", ({ params }) => <BlogPost id={params.id} />),
|
|
98
|
+
* route("/admin", [isAuthenticated, () => <BlogAdmin />]),
|
|
99
|
+
* ]
|
|
100
|
+
*
|
|
101
|
+
* // In worker.tsx
|
|
102
|
+
* defineApp([
|
|
103
|
+
* render(Document, [
|
|
104
|
+
* route("/", () => <HomePage />),
|
|
105
|
+
* prefix("/blog", blogRoutes),
|
|
106
|
+
* ]),
|
|
107
|
+
* ])
|
|
108
|
+
*/
|
|
42
109
|
export declare function prefix<T extends RequestInfo = RequestInfo>(prefixPath: string, routes: Route<T>[]): Route<T>[];
|
|
43
110
|
export declare const wrapHandlerToThrowResponses: <T extends RequestInfo = RequestInfo>(handler: RouteFunction<T> | RouteComponent<T>) => RouteHandler<T>;
|
|
111
|
+
/**
|
|
112
|
+
* Wraps routes with a layout component.
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* // Define a layout component
|
|
116
|
+
* function BlogLayout({ children }: { children: React.ReactNode }) {
|
|
117
|
+
* return (
|
|
118
|
+
* <div>
|
|
119
|
+
* <nav>Blog Navigation</nav>
|
|
120
|
+
* <main>{children}</main>
|
|
121
|
+
* </div>
|
|
122
|
+
* )
|
|
123
|
+
* }
|
|
124
|
+
*
|
|
125
|
+
* // Apply layout to routes
|
|
126
|
+
* const blogRoutes = layout(BlogLayout, [
|
|
127
|
+
* route("/", () => <BlogIndex />),
|
|
128
|
+
* route("/post/:id", ({ params }) => <BlogPost id={params.id} />),
|
|
129
|
+
* ])
|
|
130
|
+
*/
|
|
44
131
|
export declare function layout<T extends RequestInfo = RequestInfo>(LayoutComponent: React.FC<LayoutProps<T>>, routes: Route<T>[]): Route<T>[];
|
|
45
|
-
export declare function render<T extends RequestInfo = RequestInfo>(Document: React.FC<DocumentProps<T>>, routes: Route<T>[],
|
|
46
132
|
/**
|
|
47
|
-
*
|
|
133
|
+
* Wraps routes with a Document component and configures rendering options.
|
|
134
|
+
*
|
|
48
135
|
* @param options.rscPayload - Toggle the RSC payload that's appended to the Document. Disabling this will mean that interactivity no longer works.
|
|
49
|
-
* @param options.ssr - Disable sever side rendering for all these routes. This only allow client side rendering
|
|
136
|
+
* @param options.ssr - Disable sever side rendering for all these routes. This only allow client side rendering, which requires `rscPayload` to be enabled.
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* // Basic usage
|
|
140
|
+
* defineApp([
|
|
141
|
+
* render(Document, [
|
|
142
|
+
* route("/", () => <HomePage />),
|
|
143
|
+
* route("/about", () => <AboutPage />),
|
|
144
|
+
* ]),
|
|
145
|
+
* ])
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* // With custom rendering options
|
|
149
|
+
* render(Document, [
|
|
150
|
+
* route("/", () => <HomePage />),
|
|
151
|
+
* ], {
|
|
152
|
+
* rscPayload: true,
|
|
153
|
+
* ssr: true,
|
|
154
|
+
* })
|
|
50
155
|
*/
|
|
51
|
-
options?: {
|
|
156
|
+
export declare function render<T extends RequestInfo = RequestInfo>(Document: React.FC<DocumentProps<T>>, routes: Route<T>[], options?: {
|
|
52
157
|
rscPayload?: boolean;
|
|
53
158
|
ssr?: boolean;
|
|
54
159
|
}): Route<T>[];
|
|
@@ -221,6 +221,43 @@ export function defineRoutes(routes) {
|
|
|
221
221
|
},
|
|
222
222
|
};
|
|
223
223
|
}
|
|
224
|
+
/**
|
|
225
|
+
* Defines a route handler for a path pattern.
|
|
226
|
+
*
|
|
227
|
+
* Supports three types of path patterns:
|
|
228
|
+
* - Static: /about, /contact
|
|
229
|
+
* - Parameters: /users/:id, /posts/:postId/edit
|
|
230
|
+
* - Wildcards: /files/*, /api/*\/download
|
|
231
|
+
*
|
|
232
|
+
* @example
|
|
233
|
+
* // Static route
|
|
234
|
+
* route("/about", () => <AboutPage />)
|
|
235
|
+
*
|
|
236
|
+
* @example
|
|
237
|
+
* // Route with parameters
|
|
238
|
+
* route("/users/:id", ({ params }) => {
|
|
239
|
+
* return <UserProfile userId={params.id} />
|
|
240
|
+
* })
|
|
241
|
+
*
|
|
242
|
+
* @example
|
|
243
|
+
* // Route with wildcards
|
|
244
|
+
* route("/files/*", ({ params }) => {
|
|
245
|
+
* const filePath = params.$0
|
|
246
|
+
* return <FileViewer path={filePath} />
|
|
247
|
+
* })
|
|
248
|
+
*
|
|
249
|
+
* @example
|
|
250
|
+
* // Method-based routing
|
|
251
|
+
* route("/api/users", {
|
|
252
|
+
* get: () => Response.json(users),
|
|
253
|
+
* post: ({ request }) => Response.json({ status: "created" }, { status: 201 }),
|
|
254
|
+
* delete: () => new Response(null, { status: 204 }),
|
|
255
|
+
* })
|
|
256
|
+
*
|
|
257
|
+
* @example
|
|
258
|
+
* // Route with middleware array
|
|
259
|
+
* route("/admin", [isAuthenticated, isAdmin, () => <AdminDashboard />])
|
|
260
|
+
*/
|
|
224
261
|
export function route(path, handler) {
|
|
225
262
|
if (!path.endsWith("/")) {
|
|
226
263
|
path = path + "/";
|
|
@@ -240,9 +277,39 @@ export function route(path, handler) {
|
|
|
240
277
|
handler,
|
|
241
278
|
};
|
|
242
279
|
}
|
|
280
|
+
/**
|
|
281
|
+
* Defines a route handler for the root path "/".
|
|
282
|
+
*
|
|
283
|
+
* @example
|
|
284
|
+
* // Homepage
|
|
285
|
+
* index(() => <HomePage />)
|
|
286
|
+
*
|
|
287
|
+
* @example
|
|
288
|
+
* // With middleware
|
|
289
|
+
* index([logRequest, () => <HomePage />])
|
|
290
|
+
*/
|
|
243
291
|
export function index(handler) {
|
|
244
292
|
return route("/", handler);
|
|
245
293
|
}
|
|
294
|
+
/**
|
|
295
|
+
* Prefixes a group of routes with a path.
|
|
296
|
+
*
|
|
297
|
+
* @example
|
|
298
|
+
* // Organize blog routes under /blog
|
|
299
|
+
* const blogRoutes = [
|
|
300
|
+
* route("/", () => <BlogIndex />),
|
|
301
|
+
* route("/post/:id", ({ params }) => <BlogPost id={params.id} />),
|
|
302
|
+
* route("/admin", [isAuthenticated, () => <BlogAdmin />]),
|
|
303
|
+
* ]
|
|
304
|
+
*
|
|
305
|
+
* // In worker.tsx
|
|
306
|
+
* defineApp([
|
|
307
|
+
* render(Document, [
|
|
308
|
+
* route("/", () => <HomePage />),
|
|
309
|
+
* prefix("/blog", blogRoutes),
|
|
310
|
+
* ]),
|
|
311
|
+
* ])
|
|
312
|
+
*/
|
|
246
313
|
export function prefix(prefixPath, routes) {
|
|
247
314
|
return routes.map((r) => {
|
|
248
315
|
if (typeof r === "function") {
|
|
@@ -308,6 +375,26 @@ export const wrapHandlerToThrowResponses = (handler) => {
|
|
|
308
375
|
ComponentWrappedToThrowResponses.__rwsdk_route_component = true;
|
|
309
376
|
return ComponentWrappedToThrowResponses;
|
|
310
377
|
};
|
|
378
|
+
/**
|
|
379
|
+
* Wraps routes with a layout component.
|
|
380
|
+
*
|
|
381
|
+
* @example
|
|
382
|
+
* // Define a layout component
|
|
383
|
+
* function BlogLayout({ children }: { children: React.ReactNode }) {
|
|
384
|
+
* return (
|
|
385
|
+
* <div>
|
|
386
|
+
* <nav>Blog Navigation</nav>
|
|
387
|
+
* <main>{children}</main>
|
|
388
|
+
* </div>
|
|
389
|
+
* )
|
|
390
|
+
* }
|
|
391
|
+
*
|
|
392
|
+
* // Apply layout to routes
|
|
393
|
+
* const blogRoutes = layout(BlogLayout, [
|
|
394
|
+
* route("/", () => <BlogIndex />),
|
|
395
|
+
* route("/post/:id", ({ params }) => <BlogPost id={params.id} />),
|
|
396
|
+
* ])
|
|
397
|
+
*/
|
|
311
398
|
export function layout(LayoutComponent, routes) {
|
|
312
399
|
// Attach layouts directly to route definitions
|
|
313
400
|
return routes.map((route) => {
|
|
@@ -326,13 +413,31 @@ export function layout(LayoutComponent, routes) {
|
|
|
326
413
|
};
|
|
327
414
|
});
|
|
328
415
|
}
|
|
329
|
-
export function render(Document, routes,
|
|
330
416
|
/**
|
|
331
|
-
*
|
|
417
|
+
* Wraps routes with a Document component and configures rendering options.
|
|
418
|
+
*
|
|
332
419
|
* @param options.rscPayload - Toggle the RSC payload that's appended to the Document. Disabling this will mean that interactivity no longer works.
|
|
333
|
-
* @param options.ssr - Disable sever side rendering for all these routes. This only allow client side rendering
|
|
420
|
+
* @param options.ssr - Disable sever side rendering for all these routes. This only allow client side rendering, which requires `rscPayload` to be enabled.
|
|
421
|
+
*
|
|
422
|
+
* @example
|
|
423
|
+
* // Basic usage
|
|
424
|
+
* defineApp([
|
|
425
|
+
* render(Document, [
|
|
426
|
+
* route("/", () => <HomePage />),
|
|
427
|
+
* route("/about", () => <AboutPage />),
|
|
428
|
+
* ]),
|
|
429
|
+
* ])
|
|
430
|
+
*
|
|
431
|
+
* @example
|
|
432
|
+
* // With custom rendering options
|
|
433
|
+
* render(Document, [
|
|
434
|
+
* route("/", () => <HomePage />),
|
|
435
|
+
* ], {
|
|
436
|
+
* rscPayload: true,
|
|
437
|
+
* ssr: true,
|
|
438
|
+
* })
|
|
334
439
|
*/
|
|
335
|
-
options = {}) {
|
|
440
|
+
export function render(Document, routes, options = {}) {
|
|
336
441
|
options = {
|
|
337
442
|
rscPayload: true,
|
|
338
443
|
ssr: true,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rwsdk",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.27",
|
|
4
4
|
"description": "Build fast, server-driven webapps on Cloudflare with SSR, RSC, and realtime",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -196,7 +196,7 @@
|
|
|
196
196
|
"semver": "~7.7.1",
|
|
197
197
|
"tsx": "~4.20.0",
|
|
198
198
|
"typescript": "~5.9.0",
|
|
199
|
-
"vite": "~7.
|
|
199
|
+
"vite": "~7.2.0",
|
|
200
200
|
"vitest": "~3.2.0"
|
|
201
201
|
}
|
|
202
202
|
}
|