@oselvar/sveltekit-add-worker-exports 0.3.0 → 0.3.2

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/README.md CHANGED
@@ -87,7 +87,7 @@ export default defineConfig({
87
87
  });
88
88
  ```
89
89
 
90
- Point `adapter-cloudflare`'s platform proxy at the generated `.platform-proxy-wrangler.jsonc`. The plugin writes this file with internal Durable Object bindings rewritten to cross-worker form (each gets a `script_name` pointing at the sidecar). Workflows and migrations are stripped — calling a Workflow from `platform.env` in vite dev isn't supported, but it works in production. Without this config path, `getPlatformProxy` would try to run the classes itself and warn that it can't:
90
+ Point `adapter-cloudflare`'s platform proxy at the generated `.platform-proxy-wrangler.jsonc`. The plugin writes this file with internal Durable Object and Workflow bindings rewritten to cross-worker form (each gets a `script_name` pointing at the sidecar), so `platform.env.MY_DO` and `platform.env.MY_WORKFLOW` calls in `+server.ts` reach the sidecar via the wrangler dev registry. Without this config path, `getPlatformProxy` would try to run the DO/Workflow classes itself and warn that it can't:
91
91
 
92
92
  ```javascript
93
93
  // svelte.config.js
@@ -129,7 +129,23 @@ declare global {
129
129
  }
130
130
  ```
131
131
 
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.
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.
133
+
134
+ ### Dev mode: calling Workflows
135
+
136
+ `+server.ts` calls workflows the same way it does in production:
137
+
138
+ ```typescript
139
+ export const POST: RequestHandler = async ({ params, request, platform }) => {
140
+ const userMessage = await request.text();
141
+ const instance = await platform!.env.MY_WORKFLOW.create({
142
+ params: { ... }
143
+ });
144
+ return new Response(instance.id);
145
+ };
146
+ ```
147
+
148
+ The sidecar runs the real `WorkflowEntrypoint` class; calls reach it via the `script_name` rewrite in the platform-proxy config. This requires `wrangler >= 4.98.0` ([cloudflare/workers-sdk#13863](https://github.com/cloudflare/workers-sdk/pull/13863)).
133
149
 
134
150
  ### Testing the production build locally
135
151
 
@@ -191,10 +207,10 @@ The plugin reads your wrangler config, creates a temporary config with `main` po
191
207
 
192
208
  ### Generating typed bindings
193
209
 
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:
210
+ The dev plugin creates a temporary `.types-worker-wrangler.jsonc` with `main` pointing to your source entry point. Use it to generate fully generic Cloudflare types:
195
211
 
196
212
  ```bash
197
- wrangler types --config .dev-worker-wrangler.jsonc
213
+ wrangler types --config .types-worker-wrangler.jsonc
198
214
  ```
199
215
 
200
216
  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).
@@ -204,12 +220,12 @@ Add this to your `package.json` scripts for convenience:
204
220
  ```json
205
221
  {
206
222
  "scripts": {
207
- "types": "wrangler types --config .dev-worker-wrangler.jsonc"
223
+ "types": "wrangler types --config .types-worker-wrangler.jsonc"
208
224
  }
209
225
  }
210
226
  ```
211
227
 
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`.
228
+ Note: the `.types-worker-wrangler.jsonc` file is generated when the dev server starts. Run `pnpm dev` at least once before running `wrangler types`.
213
229
 
214
230
  ## Why this exists
215
231
 
@@ -0,0 +1,10 @@
1
+ /**
2
+ * SvelteKit `Handle` re-exported for users.
3
+ *
4
+ * Kept as a no-op for backwards compatibility. Wrangler 4.98.0 fixed
5
+ * cross-worker workflow routing in `getPlatformProxy`
6
+ * (cloudflare/workers-sdk#13863), so `platform.env.MY_WORKFLOW` is now a
7
+ * real binding in both dev and production — no synthesis required.
8
+ */
9
+ import type { Handle } from '@sveltejs/kit';
10
+ export declare const handle: Handle;
package/dist/hooks.js ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * SvelteKit `Handle` re-exported for users.
3
+ *
4
+ * Kept as a no-op for backwards compatibility. Wrangler 4.98.0 fixed
5
+ * cross-worker workflow routing in `getPlatformProxy`
6
+ * (cloudflare/workers-sdk#13863), so `platform.env.MY_WORKFLOW` is now a
7
+ * real binding in both dev and production — no synthesis required.
8
+ */
9
+ export const handle = ({ event, resolve }) => resolve(event);
package/dist/index.d.ts CHANGED
@@ -7,9 +7,13 @@
7
7
  * exports. This plugin post-processes the build output to merge them.
8
8
  *
9
9
  * **Dev mode:** `getPlatformProxy` (used by adapter-cloudflare in dev) can't
10
- * run internal Durable Objects. This plugin starts a separate wrangler dev
11
- * server via `unstable_startWorker` that runs the real DO worker. Clients
12
- * connect directly to it via WebSocket on a separate port.
10
+ * run internal Durable Objects or Workflows. This plugin starts a separate
11
+ * wrangler dev server via `unstable_startWorker` that runs the real
12
+ * DO/Workflow worker. Internal DO and Workflow bindings in the platform-proxy
13
+ * config get a `script_name` pointing at that sidecar; miniflare routes calls
14
+ * across the dev registry (cloudflare/workers-sdk#7459, fixed in wrangler
15
+ * 4.98.0). Clients connect directly to the sidecar via WebSocket on a
16
+ * separate port.
13
17
  *
14
18
  * Usage in vite.config.ts:
15
19
  * addWorkerExports({ entryPoint: 'src/lib/server/index.ts' })
package/dist/index.js CHANGED
@@ -7,9 +7,13 @@
7
7
  * exports. This plugin post-processes the build output to merge them.
8
8
  *
9
9
  * **Dev mode:** `getPlatformProxy` (used by adapter-cloudflare in dev) can't
10
- * run internal Durable Objects. This plugin starts a separate wrangler dev
11
- * server via `unstable_startWorker` that runs the real DO worker. Clients
12
- * connect directly to it via WebSocket on a separate port.
10
+ * run internal Durable Objects or Workflows. This plugin starts a separate
11
+ * wrangler dev server via `unstable_startWorker` that runs the real
12
+ * DO/Workflow worker. Internal DO and Workflow bindings in the platform-proxy
13
+ * config get a `script_name` pointing at that sidecar; miniflare routes calls
14
+ * across the dev registry (cloudflare/workers-sdk#7459, fixed in wrangler
15
+ * 4.98.0). Clients connect directly to the sidecar via WebSocket on a
16
+ * separate port.
13
17
  *
14
18
  * Usage in vite.config.ts:
15
19
  * addWorkerExports({ entryPoint: 'src/lib/server/index.ts' })
@@ -23,6 +27,16 @@ import { homedir } from 'node:os';
23
27
  import { join, resolve } from 'node:path';
24
28
  import { parse as parseJsonc } from 'jsonc-parser';
25
29
  const DEFAULT_DEV_PORT = 8787;
30
+ const NODE_BUILTINS = [
31
+ 'assert', 'async_hooks', 'buffer', 'child_process', 'cluster', 'console',
32
+ 'constants', 'crypto', 'dgram', 'diagnostics_channel', 'dns', 'domain',
33
+ 'events', 'fs', 'fs/promises', 'http', 'http2', 'https', 'inspector',
34
+ 'module', 'net', 'os', 'path', 'path/posix', 'path/win32', 'perf_hooks',
35
+ 'process', 'punycode', 'querystring', 'readline', 'repl', 'stream',
36
+ 'stream/consumers', 'stream/promises', 'stream/web', 'string_decoder',
37
+ 'sys', 'timers', 'timers/promises', 'tls', 'trace_events', 'tty', 'url',
38
+ 'util', 'util/types', 'v8', 'vm', 'wasi', 'worker_threads', 'zlib'
39
+ ];
26
40
  /**
27
41
  * Mirrors wrangler's internal getRegistryPath() — wrangler doesn't export it.
28
42
  * Workers register themselves under this path so cross-worker `script_name`
@@ -77,14 +91,30 @@ function buildPlugin(options) {
77
91
  if (!(await exists(workerPath))) {
78
92
  return;
79
93
  }
80
- // Bundle the named exports
94
+ // Bundle the named exports.
95
+ //
96
+ // `conditions: ['workerd', 'worker', 'browser']` is the Cloudflare-
97
+ // recommended set for bundlers targeting Workers — it picks the
98
+ // workerd-specific or browser-shimmed variants of packages over
99
+ // their Node variants (e.g. nanoid's webcrypto-via-`globalThis.crypto`
100
+ // browser build instead of the `node:crypto`-importing main build).
101
+ //
102
+ // Node built-ins are externalised so transitive deps that do
103
+ // `require('path')` or `import 'node:async_hooks'` survive bundling;
104
+ // the Workers runtime resolves them when `nodejs_compat` is enabled
105
+ // in wrangler.jsonc.
81
106
  await build({
82
107
  entryPoints: [options.entryPoint],
83
108
  bundle: true,
84
109
  format: 'esm',
85
110
  sourcemap: true,
86
111
  target: 'esnext',
87
- external: ['cloudflare:*'],
112
+ conditions: ['workerd', 'worker', 'browser'],
113
+ external: [
114
+ 'cloudflare:*',
115
+ ...NODE_BUILTINS,
116
+ ...NODE_BUILTINS.map((m) => `node:${m}`)
117
+ ],
88
118
  outfile: exportsPath
89
119
  });
90
120
  // Rename original worker and create merged entry point
@@ -98,6 +128,12 @@ function devPlugin(options) {
98
128
  let worker = null;
99
129
  let tempConfigPath = null;
100
130
  let proxyConfigPath = null;
131
+ let typesConfigPath = null;
132
+ async function readRawConfig() {
133
+ const { unstable_readConfig } = await import('wrangler');
134
+ const parsed = unstable_readConfig(options.wranglerConfig ? { config: options.wranglerConfig } : {});
135
+ return readFile(parsed.configPath, 'utf-8');
136
+ }
101
137
  return {
102
138
  name: 'add-worker-exports-dev',
103
139
  apply: 'serve',
@@ -109,36 +145,51 @@ function devPlugin(options) {
109
145
  };
110
146
  },
111
147
  async configureServer(server) {
112
- const { unstable_readConfig, unstable_startWorker } = await import('wrangler');
113
- // Read the project's wrangler config (auto-discovers wrangler.jsonc/toml)
114
- const wranglerConfig = unstable_readConfig(options.wranglerConfig ? { config: options.wranglerConfig } : {});
148
+ const { unstable_startWorker } = await import('wrangler');
149
+ const rawJson = await readRawConfig();
150
+ const sidecarName = `${parseJsonc(rawJson).name ?? 'sveltekit'}-dev-worker`;
115
151
  // Copy the raw config and override main, name, and dev port
116
- const rawJson = await readFile(wranglerConfig.configPath, 'utf-8');
117
152
  const devConfig = parseJsonc(rawJson);
118
- devConfig.name = `${devConfig.name ?? 'sveltekit'}-dev-worker`;
153
+ devConfig.name = sidecarName;
119
154
  devConfig.main = options.entryPoint;
120
155
  devConfig.dev = { ...devConfig.dev, port: devPort };
121
156
  delete devConfig.assets;
122
157
  tempConfigPath = resolve('.dev-worker-wrangler.jsonc');
123
158
  await writeFile(tempConfigPath, JSON.stringify(devConfig, null, '\t'));
124
- // Also write a wrangler config for adapter-cloudflare's getPlatformProxy
159
+ // Write a wrangler config for adapter-cloudflare's getPlatformProxy
125
160
  // (used by vite dev for platform.env). It can't run internal DOs or
126
161
  // Workflows itself -- those are served by the sidecar above. Rewrite
127
- // internal DO bindings as cross-worker bindings (script_name pointing
128
- // at the sidecar) so platform.env.MY_DO calls in +server.ts reach the
129
- // sidecar via the wrangler dev registry. Workflows and migrations get
130
- // stripped (Workflow bindings have no script_name equivalent).
162
+ // internal DO and Workflow bindings as cross-worker bindings
163
+ // (script_name pointing at the sidecar) so platform.env.MY_DO and
164
+ // platform.env.MY_WORKFLOW calls in +server.ts reach the sidecar via
165
+ // the wrangler dev registry.
131
166
  const proxyConfig = parseJsonc(rawJson);
132
167
  if (proxyConfig.durable_objects?.bindings) {
133
- proxyConfig.durable_objects.bindings = proxyConfig.durable_objects.bindings.map((b) => b.script_name ? b : { ...b, script_name: devConfig.name });
168
+ proxyConfig.durable_objects.bindings = proxyConfig.durable_objects.bindings.map((b) => b.script_name ? b : { ...b, script_name: sidecarName });
169
+ }
170
+ if (Array.isArray(proxyConfig.workflows)) {
171
+ proxyConfig.workflows = proxyConfig.workflows.map((w) => w.script_name ? w : { ...w, script_name: sidecarName });
134
172
  }
135
- delete proxyConfig.workflows;
136
173
  delete proxyConfig.migrations;
137
174
  proxyConfigPath = resolve('.platform-proxy-wrangler.jsonc');
138
175
  await writeFile(proxyConfigPath, JSON.stringify(proxyConfig, null, '\t'));
176
+ // A third config used only by `wrangler types`. Identical to the dev
177
+ // config but kept separate so the user can run `wrangler types
178
+ // --config .types-worker-wrangler.jsonc` to get typed bindings like
179
+ // DurableObjectNamespace<EchoDO> resolved from the source entry.
180
+ const typesConfig = parseJsonc(rawJson);
181
+ typesConfig.name = sidecarName;
182
+ typesConfig.main = options.entryPoint;
183
+ typesConfig.dev = { ...typesConfig.dev, port: devPort };
184
+ delete typesConfig.assets;
185
+ typesConfigPath = resolve('.types-worker-wrangler.jsonc');
186
+ await writeFile(typesConfigPath, JSON.stringify(typesConfig, null, '\t'));
139
187
  worker = await unstable_startWorker({
140
188
  config: tempConfigPath,
141
- dev: { registry: getWranglerRegistryPath() }
189
+ dev: { registry: getWranglerRegistryPath() },
190
+ build: {
191
+ nodejsCompatMode: (parsedConfig) => getNodejsCompatMode(parsedConfig.compatibility_date, parsedConfig.compatibility_flags)
192
+ }
142
193
  });
143
194
  server.httpServer?.on('close', async () => {
144
195
  if (worker) {
@@ -151,6 +202,9 @@ function devPlugin(options) {
151
202
  if (proxyConfigPath) {
152
203
  await unlink(proxyConfigPath).catch(() => { });
153
204
  }
205
+ if (typesConfigPath) {
206
+ await unlink(typesConfigPath).catch(() => { });
207
+ }
154
208
  });
155
209
  }
156
210
  };
@@ -164,3 +218,24 @@ async function exists(path) {
164
218
  return false;
165
219
  }
166
220
  }
221
+ /**
222
+ * Mirrors miniflare's `getNodeCompat` mode resolution. wrangler's
223
+ * `unstable_startWorker` does not derive this from the config flags
224
+ * automatically — without it, wrangler falls back to the legacy nodejs
225
+ * compat plugin and warns about `node:*` imports even when
226
+ * `nodejs_compat` is enabled.
227
+ */
228
+ function getNodejsCompatMode(compatibilityDate, compatibilityFlags = []) {
229
+ const hasCompat = compatibilityFlags.includes('nodejs_compat');
230
+ const hasCompatV2 = compatibilityFlags.includes('nodejs_compat_v2');
231
+ const hasNoCompatV2 = compatibilityFlags.includes('no_nodejs_compat_v2');
232
+ const hasAls = compatibilityFlags.includes('nodejs_als');
233
+ const date = compatibilityDate ?? '2000-01-01';
234
+ if (hasCompatV2 || (hasCompat && date >= '2024-09-23' && !hasNoCompatV2))
235
+ return 'v2';
236
+ if (hasCompat)
237
+ return 'v1';
238
+ if (hasAls)
239
+ return 'als';
240
+ return null;
241
+ }
package/package.json CHANGED
@@ -1,51 +1,62 @@
1
1
  {
2
- "name": "@oselvar/sveltekit-add-worker-exports",
3
- "version": "0.3.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
- }
2
+ "name": "@oselvar/sveltekit-add-worker-exports",
3
+ "version": "0.3.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
+ "./hooks": {
15
+ "types": "./dist/hooks.d.ts",
16
+ "import": "./dist/hooks.js"
17
+ }
18
+ },
19
+ "files": [
20
+ "dist"
21
+ ],
22
+ "license": "MIT",
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "git+https://github.com/oselvar/sveltekit-add-worker-exports.git"
26
+ },
27
+ "keywords": [
28
+ "sveltekit",
29
+ "cloudflare",
30
+ "workers",
31
+ "durable-objects",
32
+ "workflows",
33
+ "vite-plugin"
34
+ ],
35
+ "peerDependencies": {
36
+ "@sveltejs/kit": ">=2",
37
+ "esbuild": ">=0.17",
38
+ "miniflare": ">=4",
39
+ "vite": ">=5",
40
+ "wrangler": ">=4"
41
+ },
42
+ "devDependencies": {
43
+ "@release-it/conventional-changelog": "^11.0.1",
44
+ "@sveltejs/kit": "^2.62.0",
45
+ "@types/node": "^25.9.1",
46
+ "esbuild": "^0.28.0",
47
+ "miniflare": "^4.20260601.0",
48
+ "release-it": "^20.2.0",
49
+ "typescript": "^6.0.3",
50
+ "vite": "^8.0.16",
51
+ "wrangler": "^4.98.0"
52
+ },
53
+ "scripts": {
54
+ "build": "tsc",
55
+ "test": "tsc --noEmit",
56
+ "prepublishOnly": "tsc",
57
+ "release": "release-it"
58
+ },
59
+ "dependencies": {
60
+ "jsonc-parser": "^3.3.1"
61
+ }
62
+ }