@oselvar/sveltekit-add-worker-exports 0.1.2 → 0.2.0

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 (3) hide show
  1. package/README.md +108 -9
  2. package/dist/index.js +19 -0
  3. package/package.json +50 -51
package/README.md CHANGED
@@ -16,26 +16,62 @@ pnpm add -D @oselvar/sveltekit-add-worker-exports
16
16
 
17
17
  ## Usage
18
18
 
19
- Create a file that exports your Durable Object and/or Workflow classes:
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.
20
20
 
21
21
  ```typescript
22
22
  // src/lib/server/index.ts
23
23
  export { MyDurableObject } from './MyDurableObject';
24
+ export { MyWorkflow } from './MyWorkflow';
25
+ export { default } from './devHandler';
26
+ ```
27
+
28
+ 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
+
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';
24
52
 
25
53
  export default {
26
54
  async fetch(request: Request, env: Env): Promise<Response> {
27
- // Route WebSocket upgrades to Durable Objects (used during dev)
28
- const url = new URL(request.url);
29
- const match = url.pathname.match(/^\/ws\/(.+)$/);
30
- if (match && request.headers.get('Upgrade') === 'websocket') {
31
- const id = env.MY_DO.idFromName(match[1]);
32
- return env.MY_DO.get(id).fetch(request);
33
- }
34
- return new Response('Not found', { status: 404 });
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]);
35
58
  }
36
59
  };
37
60
  ```
38
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.
74
+
39
75
  Add the plugin to your `vite.config.ts` **after** `sveltekit()`:
40
76
 
41
77
  ```typescript
@@ -51,6 +87,23 @@ export default defineConfig({
51
87
  });
52
88
  ```
53
89
 
90
+ Point `adapter-cloudflare`'s platform proxy at the generated `.platform-proxy-wrangler.jsonc`. This is a copy of your wrangler config with internal Durable Object bindings, Workflows, and migrations stripped — those are served by the dev sidecar on a separate port, and `getPlatformProxy` would otherwise log warnings about classes it can't load:
91
+
92
+ ```javascript
93
+ // svelte.config.js
94
+ import adapter from '@sveltejs/adapter-cloudflare';
95
+
96
+ export default {
97
+ kit: {
98
+ adapter: adapter({
99
+ platformProxy: {
100
+ configPath: '.platform-proxy-wrangler.jsonc'
101
+ }
102
+ })
103
+ }
104
+ };
105
+ ```
106
+
54
107
  ### Dev mode: connecting to Durable Objects
55
108
 
56
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:
@@ -78,6 +131,29 @@ declare global {
78
131
 
79
132
  The plugin auto-discovers your `wrangler.jsonc` (or `wrangler.toml`) and reads DO bindings, migrations, and compatibility settings from it. It overrides only the `main` entry point to use your source file instead of the SvelteKit build output.
80
133
 
134
+ ### Testing the production build locally
135
+
136
+ `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:
137
+
138
+ ```bash
139
+ pnpm build # produces .svelte-kit/cloudflare/_worker.js with merged exports
140
+ pnpm wrangler dev # uses wrangler.jsonc → main: .svelte-kit/cloudflare/_worker.js
141
+ ```
142
+
143
+ 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.
144
+
145
+ A handy shortcut is to add a `preview` script to `package.json`:
146
+
147
+ ```json
148
+ {
149
+ "scripts": {
150
+ "preview": "wrangler dev"
151
+ }
152
+ }
153
+ ```
154
+
155
+ > 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.
156
+
81
157
  ## Options
82
158
 
83
159
  | Option | Type | Required | Default | Description |
@@ -111,6 +187,29 @@ The plugin reads your wrangler config, creates a temporary config with `main` po
111
187
  - Real workerd runtime (not emulated)
112
188
  - Hot-reload when you change DO code
113
189
  - Same WebSocket protocol as production
190
+ - Fully typed Durable Object bindings (see below)
191
+
192
+ ### Generating typed bindings
193
+
194
+ The dev plugin creates a temporary `.dev-worker-wrangler.jsonc` with `main` pointing to your source entry point. You can use this to generate fully generic Cloudflare types:
195
+
196
+ ```bash
197
+ wrangler types --config .dev-worker-wrangler.jsonc
198
+ ```
199
+
200
+ This produces typed DO bindings like `DurableObjectNamespace<MyDurableObject>` instead of the untyped `DurableObjectNamespace` you get from the default `wrangler.jsonc` (whose `main` points to the SvelteKit build output, which doesn't exist during dev).
201
+
202
+ Add this to your `package.json` scripts for convenience:
203
+
204
+ ```json
205
+ {
206
+ "scripts": {
207
+ "types": "wrangler types --config .dev-worker-wrangler.jsonc"
208
+ }
209
+ }
210
+ ```
211
+
212
+ Note: the `.dev-worker-wrangler.jsonc` file is generated when the dev server starts. Run `pnpm dev` at least once before running `wrangler types`.
114
213
 
115
214
  ## Why this exists
116
215
 
package/dist/index.js CHANGED
@@ -76,6 +76,7 @@ function devPlugin(options) {
76
76
  const devPort = options.devPort ?? DEFAULT_DEV_PORT;
77
77
  let worker = null;
78
78
  let tempConfigPath = null;
79
+ let proxyConfigPath = null;
79
80
  return {
80
81
  name: 'add-worker-exports-dev',
81
82
  apply: 'serve',
@@ -99,6 +100,21 @@ function devPlugin(options) {
99
100
  delete devConfig.assets;
100
101
  tempConfigPath = resolve('.dev-worker-wrangler.jsonc');
101
102
  await writeFile(tempConfigPath, JSON.stringify(devConfig, null, '\t'));
103
+ // Also write a wrangler config for adapter-cloudflare's getPlatformProxy
104
+ // (used by vite dev for platform.env). It can't run internal DOs or
105
+ // Workflows -- those are served by the sidecar above. Strip them so it
106
+ // doesn't emit warnings about classes it can't load.
107
+ const proxyConfig = parseJsonc(rawJson);
108
+ if (proxyConfig.durable_objects?.bindings) {
109
+ proxyConfig.durable_objects.bindings = proxyConfig.durable_objects.bindings.filter((b) => b.script_name);
110
+ if (proxyConfig.durable_objects.bindings.length === 0) {
111
+ delete proxyConfig.durable_objects;
112
+ }
113
+ }
114
+ delete proxyConfig.workflows;
115
+ delete proxyConfig.migrations;
116
+ proxyConfigPath = resolve('.platform-proxy-wrangler.jsonc');
117
+ await writeFile(proxyConfigPath, JSON.stringify(proxyConfig, null, '\t'));
102
118
  worker = await unstable_startWorker({ config: tempConfigPath });
103
119
  server.httpServer?.on('close', async () => {
104
120
  if (worker) {
@@ -108,6 +124,9 @@ function devPlugin(options) {
108
124
  if (tempConfigPath) {
109
125
  await unlink(tempConfigPath).catch(() => { });
110
126
  }
127
+ if (proxyConfigPath) {
128
+ await unlink(proxyConfigPath).catch(() => { });
129
+ }
111
130
  });
112
131
  }
113
132
  };
package/package.json CHANGED
@@ -1,52 +1,51 @@
1
1
  {
2
- "name": "@oselvar/sveltekit-add-worker-exports",
3
- "version": "0.1.2",
4
- "description": "Vite plugin that adds Durable Object and Workflow exports to SvelteKit Cloudflare workers",
5
- "type": "module",
6
- "publishConfig": {
7
- "access": "public"
8
- },
9
- "exports": {
10
- ".": {
11
- "types": "./dist/index.d.ts",
12
- "import": "./dist/index.js"
13
- }
14
- },
15
- "files": [
16
- "dist"
17
- ],
18
- "license": "MIT",
19
- "repository": {
20
- "type": "git",
21
- "url": "https://github.com/oselvar/sveltekit-add-worker-exports"
22
- },
23
- "keywords": [
24
- "sveltekit",
25
- "cloudflare",
26
- "workers",
27
- "durable-objects",
28
- "workflows",
29
- "vite-plugin"
30
- ],
31
- "peerDependencies": {
32
- "esbuild": ">=0.17",
33
- "vite": ">=5",
34
- "wrangler": ">=4"
35
- },
36
- "devDependencies": {
37
- "@types/node": "^22.0.0",
38
- "esbuild": "^0.25.0",
39
- "np": "^11.2.0",
40
- "typescript": "^5.0.0",
41
- "vite": "^6.0.0",
42
- "wrangler": "^4.84.0"
43
- },
44
- "scripts": {
45
- "build": "tsc",
46
- "test": "tsc --noEmit",
47
- "prepublishOnly": "tsc"
48
- },
49
- "dependencies": {
50
- "jsonc-parser": "^3.3.1"
51
- }
52
- }
2
+ "name": "@oselvar/sveltekit-add-worker-exports",
3
+ "version": "0.2.0",
4
+ "description": "Vite plugin that adds Durable Object and Workflow exports to SvelteKit Cloudflare workers",
5
+ "type": "module",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "license": "MIT",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/oselvar/sveltekit-add-worker-exports.git"
22
+ },
23
+ "keywords": [
24
+ "sveltekit",
25
+ "cloudflare",
26
+ "workers",
27
+ "durable-objects",
28
+ "workflows",
29
+ "vite-plugin"
30
+ ],
31
+ "peerDependencies": {
32
+ "esbuild": ">=0.17",
33
+ "vite": ">=5",
34
+ "wrangler": ">=4"
35
+ },
36
+ "devDependencies": {
37
+ "@types/node": "^22.0.0",
38
+ "esbuild": "^0.25.0",
39
+ "np": "^11.2.0",
40
+ "typescript": "^5.0.0",
41
+ "vite": "^6.0.0",
42
+ "wrangler": "^4.84.0"
43
+ },
44
+ "dependencies": {
45
+ "jsonc-parser": "^3.3.1"
46
+ },
47
+ "scripts": {
48
+ "build": "tsc",
49
+ "test": "tsc --noEmit"
50
+ }
51
+ }