@oselvar/sveltekit-add-worker-exports 0.3.3 → 0.3.4

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 (2) hide show
  1. package/README.md +104 -80
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,10 +1,20 @@
1
1
  # @oselvar/sveltekit-add-worker-exports
2
2
 
3
- A Vite plugin that makes Durable Objects and Workflows work with SvelteKit on Cloudflare, in both dev and production.
3
+ A Vite plugin that makes any class-based Cloudflare Worker export (Durable Objects, Workflows, `WorkerEntrypoint` RPC, voice agents, …) work with SvelteKit on Cloudflare, in both dev and production.
4
4
 
5
- **Build mode:** SvelteKit's `adapter-cloudflare` generates `_worker.js` with only a default export (the fetch handler). Cloudflare Workers requires Durable Object and Workflow classes to be **named exports**. This plugin post-processes the build output to merge your named exports with SvelteKit's default export.
5
+ **Build mode:** SvelteKit's `adapter-cloudflare` generates `_worker.js` with only a default export (the fetch handler). Cloudflare Workers requires class-based bindings (Durable Objects, Workflows, `WorkerEntrypoint`, etc.) to be **named exports**. This plugin post-processes the build output to merge your named exports with SvelteKit's default export.
6
6
 
7
- **Dev mode:** `getPlatformProxy` (used by `adapter-cloudflare` in dev) can't run internal Durable Objects or Workflows. This plugin starts a separate wrangler dev server that runs the real DO/Workflow worker with hot-reload. SvelteKit `+server.ts` handlers call DOs through `platform.env.MY_DO.<rpc>()` as usual — the plugin rewrites those bindings to point at the sidecar via wrangler's dev registry, so cross-worker calls Just Work. Clients can also connect directly to the sidecar via WebSocket on a separate port (see below).
7
+ **Dev mode:** `getPlatformProxy` (used by `adapter-cloudflare` in dev) can't run internal Durable Objects, Workflows, or other class-based bindings. This plugin starts a separate wrangler dev server that runs the real worker with hot-reload. SvelteKit `+server.ts` handlers call bindings through `platform.env.MY_BINDING.<rpc>()` as usual — the plugin rewrites those bindings to point at the sidecar via wrangler's dev registry, so cross-worker calls Just Work. WebSocket clients in dev connect to the sidecar directly on a separate port — see [WebSockets](#websockets) below.
8
+
9
+ ## What this works with
10
+
11
+ The plugin is binding-agnostic: if Cloudflare resolves it through a named class export plus a `wrangler.jsonc` binding, the plugin handles it. Confirmed working:
12
+
13
+ - Durable Objects
14
+ - Workflows (`WorkflowEntrypoint`)
15
+ - Voice agents
16
+
17
+ It should also work with `WorkerEntrypoint` RPC and any future class-based export that follows the same export-plus-binding pattern. The examples below use Durable Objects and Workflows because they're the most common, but the wiring is the same for any class-based entrypoint — export the class from your entry point, declare the binding in `wrangler.jsonc`, and call it via `platform.env.MY_BINDING` in your routes.
8
18
 
9
19
  ## Install
10
20
 
@@ -16,61 +26,17 @@ pnpm add -D @oselvar/sveltekit-add-worker-exports
16
26
 
17
27
  ## Usage
18
28
 
19
- Create a worker entry point that exports your Durable Object classes and a default fetch handler. The fetch handler is only used by the wrangler dev server — in production, SvelteKit's route handlers handle all requests.
29
+ Create a worker entry point that exports your class-based bindings:
20
30
 
21
31
  ```typescript
22
32
  // src/lib/server/index.ts
23
33
  export { MyDurableObject } from './MyDurableObject';
24
34
  export { MyWorkflow } from './MyWorkflow';
25
- export { default } from './devHandler';
26
35
  ```
27
36
 
28
37
  Workflow classes (extending `WorkflowEntrypoint`) are exported the same way as Durable Objects — the plugin merges them into `_worker.js` as named exports. Declare them in `wrangler.jsonc` under `workflows`, and they become available as bindings (e.g. `env.MY_WORKFLOW.create({ params })`) in both dev and production.
29
38
 
30
- Both the dev handler and the production SvelteKit route need to do the same thing: validate the upgrade header and forward the request to a Durable Object. Extract that into a small helper so the two callers stay in sync:
31
-
32
- ```typescript
33
- // src/lib/server/forwardWebSocket.ts
34
- export async function forwardWebSocket<T extends Rpc.DurableObjectBranded | undefined>(
35
- request: Request,
36
- namespace: DurableObjectNamespace<T>,
37
- name: string
38
- ): Promise<Response> {
39
- if (request.headers.get('upgrade') !== 'websocket') {
40
- return new Response('Expected WebSocket', { status: 426 });
41
- }
42
- const id = namespace.idFromName(name);
43
- return namespace.get(id).fetch(request);
44
- }
45
- ```
46
-
47
- The dev handler parses the URL and delegates. It's only used by the wrangler dev sidecar:
48
-
49
- ```typescript
50
- // src/lib/server/devHandler.ts
51
- import { forwardWebSocket } from './forwardWebSocket';
52
-
53
- export default {
54
- async fetch(request: Request, env: Env): Promise<Response> {
55
- const match = new URL(request.url).pathname.match(/^\/ws\/(.+)$/);
56
- if (!match) return new Response('Not found', { status: 404 });
57
- return forwardWebSocket(request, env.MY_DO, match[1]);
58
- }
59
- };
60
- ```
61
-
62
- In production, the dev handler is *not* used — SvelteKit serves the same path through a `+server.ts` route, which delegates to the same helper:
63
-
64
- ```typescript
65
- // src/routes/ws/[id]/+server.ts
66
- import { forwardWebSocket } from '$lib/server/forwardWebSocket';
67
- import type { RequestHandler } from './$types';
68
-
69
- export const GET: RequestHandler = ({ params, request, platform }) =>
70
- forwardWebSocket(request, platform!.env.MY_DO, params.id);
71
- ```
72
-
73
- Now any change to the upgrade-and-forward logic (auth, rate-limiting, response shape) lives in one place and applies to both dev and production.
39
+ If clients need to talk directly to the dev sidecar over HTTP (e.g. WebSockets), also export a default fetch handler — see [WebSockets](#websockets) below.
74
40
 
75
41
  Add the plugin to your `vite.config.ts` **after** `sveltekit()`:
76
42
 
@@ -104,36 +70,11 @@ export default {
104
70
  };
105
71
  ```
106
72
 
107
- ### Dev mode: connecting to Durable Objects
108
-
109
- In dev mode, the plugin starts a wrangler dev server on a separate port and injects `__DEV_WORKER_PORT__` as a compile-time constant. Use it to connect your client:
110
-
111
- ```typescript
112
- import { dev } from '$app/environment';
113
-
114
- let wsUrl: string;
115
- if (dev) {
116
- wsUrl = `ws://${window.location.hostname}:${__DEV_WORKER_PORT__}/ws/${id}`;
117
- } else {
118
- const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
119
- wsUrl = `${protocol}//${window.location.host}/ws/${id}`;
120
- }
121
- const ws = new WebSocket(wsUrl);
122
- ```
123
-
124
- Add the type declaration to your `src/app.d.ts`:
125
-
126
- ```typescript
127
- declare global {
128
- const __DEV_WORKER_PORT__: number;
129
- }
130
- ```
131
-
132
- The plugin auto-discovers your `wrangler.jsonc` (or `wrangler.toml`) and reads DO bindings, workflows, migrations, and compatibility settings from it. It overrides only the `main` entry point to point at your source entry.
73
+ The plugin auto-discovers your `wrangler.jsonc` (or `wrangler.toml`) and reads bindings, workflows, migrations, and compatibility settings from it. It overrides only the `main` entry point to point at your source entry.
133
74
 
134
- ### Dev mode: calling Workflows
75
+ ### Calling Workflows
135
76
 
136
- `+server.ts` calls workflows the same way it does in production:
77
+ `+server.ts` calls workflows the same way in dev and production:
137
78
 
138
79
  ```typescript
139
80
  export const POST: RequestHandler = async ({ params, request, platform }) => {
@@ -149,14 +90,14 @@ The sidecar runs the real `WorkflowEntrypoint` class; calls reach it via the `sc
149
90
 
150
91
  ### Testing the production build locally
151
92
 
152
- `vite dev` exercises the dev handler via the wrangler-dev sidecar; it does *not* exercise your `+server.ts` route or the merged `_worker.js`. To verify the production wiring (named DO exports + SvelteKit routes in the same worker), run wrangler against the build output:
93
+ `vite dev` exercises your code via the wrangler-dev sidecar; it does *not* exercise the merged `_worker.js`. To verify the production wiring (named class exports + SvelteKit routes in the same worker), run wrangler against the build output:
153
94
 
154
95
  ```bash
155
96
  pnpm build # produces .svelte-kit/cloudflare/_worker.js with merged exports
156
97
  pnpm wrangler dev # uses wrangler.jsonc → main: .svelte-kit/cloudflare/_worker.js
157
98
  ```
158
99
 
159
- This serves the exact bundle that gets deployed, with local Durable Object storage. Connect a WebSocket client to `ws://localhost:8787/ws/<id>` and confirm you get a `101 Switching Protocols` response — that proves the request flowed through the SvelteKit `+server.ts` and into your DO.
100
+ This serves the exact bundle that gets deployed, with local Durable Object storage. Exercise whichever route calls your binding (HTTP, WebSocket, whatever your app uses) and confirm the response — that proves the request flowed through the SvelteKit `+server.ts` and into your class.
160
101
 
161
102
  A handy shortcut is to add a `preview` script to `package.json`:
162
103
 
@@ -170,6 +111,89 @@ A handy shortcut is to add a `preview` script to `package.json`:
170
111
 
171
112
  > Note: `vite preview` is *not* suitable here — it only serves static assets and cannot run Durable Objects. Always use `wrangler dev` to preview the production worker.
172
113
 
114
+ ## WebSockets
115
+
116
+ `vite dev` doesn't proxy WebSocket upgrades to the wrangler-dev sidecar, so in dev mode the browser needs to connect to the sidecar directly. The plugin exposes the sidecar's port as a compile-time constant `__DEV_WORKER_PORT__` for exactly this.
117
+
118
+ The pattern: one helper handles the upgrade-and-forward, called from both the dev sidecar's fetch handler and the production `+server.ts` route, so both code paths stay in sync.
119
+
120
+ ```typescript
121
+ // src/lib/server/forwardWebSocket.ts
122
+ export async function forwardWebSocket<T extends Rpc.DurableObjectBranded | undefined>(
123
+ request: Request,
124
+ namespace: DurableObjectNamespace<T>,
125
+ name: string
126
+ ): Promise<Response> {
127
+ if (request.headers.get('upgrade') !== 'websocket') {
128
+ return new Response('Expected WebSocket', { status: 426 });
129
+ }
130
+ const id = namespace.idFromName(name);
131
+ return namespace.get(id).fetch(request);
132
+ }
133
+ ```
134
+
135
+ The dev sidecar needs a default `fetch` handler to receive the direct connection:
136
+
137
+ ```typescript
138
+ // src/lib/server/devHandler.ts
139
+ import { forwardWebSocket } from './forwardWebSocket';
140
+
141
+ export default {
142
+ async fetch(request: Request, env: Env): Promise<Response> {
143
+ const match = new URL(request.url).pathname.match(/^\/ws\/(.+)$/);
144
+ if (!match) return new Response('Not found', { status: 404 });
145
+ return forwardWebSocket(request, env.MY_DO, match[1]);
146
+ }
147
+ };
148
+ ```
149
+
150
+ Add it to the worker entry point as the default export:
151
+
152
+ ```typescript
153
+ // src/lib/server/index.ts
154
+ export { MyDurableObject } from './MyDurableObject';
155
+ export { MyWorkflow } from './MyWorkflow';
156
+ export { default } from './devHandler';
157
+ ```
158
+
159
+ In production, the dev handler is *not* used — SvelteKit serves the same path through a `+server.ts` route, which delegates to the same helper:
160
+
161
+ ```typescript
162
+ // src/routes/ws/[id]/+server.ts
163
+ import { forwardWebSocket } from '$lib/server/forwardWebSocket';
164
+ import type { RequestHandler } from './$types';
165
+
166
+ export const GET: RequestHandler = ({ params, request, platform }) =>
167
+ forwardWebSocket(request, platform!.env.MY_DO, params.id);
168
+ ```
169
+
170
+ Any change to the upgrade-and-forward logic (auth, rate-limiting, response shape) now lives in one place and applies to both dev and production.
171
+
172
+ ### Connecting from the client
173
+
174
+ Use `__DEV_WORKER_PORT__` to pick between the sidecar (dev) and the SvelteKit route (production):
175
+
176
+ ```typescript
177
+ import { dev } from '$app/environment';
178
+
179
+ let wsUrl: string;
180
+ if (dev) {
181
+ wsUrl = `ws://${window.location.hostname}:${__DEV_WORKER_PORT__}/ws/${id}`;
182
+ } else {
183
+ const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
184
+ wsUrl = `${protocol}//${window.location.host}/ws/${id}`;
185
+ }
186
+ const ws = new WebSocket(wsUrl);
187
+ ```
188
+
189
+ Add the type declaration to your `src/app.d.ts`:
190
+
191
+ ```typescript
192
+ declare global {
193
+ const __DEV_WORKER_PORT__: number;
194
+ }
195
+ ```
196
+
173
197
  ## Options
174
198
 
175
199
  | Option | Type | Required | Default | Description |
@@ -229,7 +253,7 @@ Note: the `.types-worker-wrangler.jsonc` file is generated when the dev server s
229
253
 
230
254
  ## Why this exists
231
255
 
232
- SvelteKit's adapter-cloudflare does not support named exports from the worker entry point ([sveltejs/kit#1712](https://github.com/sveltejs/kit/issues/1712)). Additionally, `getPlatformProxy` (used for local dev) cannot run internal Durable Objects because it uses an empty worker script.
256
+ SvelteKit's adapter-cloudflare does not support named exports from the worker entry point ([sveltejs/kit#1712](https://github.com/sveltejs/kit/issues/1712)) — which blocks every class-based Cloudflare binding, not just Durable Objects. Additionally, `getPlatformProxy` (used for local dev) cannot run internal class-based bindings because it uses an empty worker script.
233
257
 
234
258
  ## License
235
259
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oselvar/sveltekit-add-worker-exports",
3
- "version": "0.3.3",
3
+ "version": "0.3.4",
4
4
  "description": "Vite plugin that adds Durable Object and Workflow exports to SvelteKit Cloudflare workers",
5
5
  "type": "module",
6
6
  "publishConfig": {