env-runner 0.2.2 → 0.3.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.
@@ -1,5 +1,4 @@
1
1
  import { BaseEnvRunner } from "./common-base-runner.mjs";
2
- import { hostEnv } from "./common-host-env.mjs";
3
2
  import { fileURLToPath } from "node:url";
4
3
  import { existsSync } from "node:fs";
5
4
  import { spawn } from "node:child_process";
@@ -26,7 +25,7 @@ var DenoProcessEnvRunner = class extends BaseEnvRunner {
26
25
  }
27
26
  async _closeRuntime() {
28
27
  if (!this.#process) return;
29
- this.#process.removeAllListeners?.();
28
+ this.#process.removeAllListeners();
30
29
  try {
31
30
  this.#process.kill();
32
31
  } catch {}
@@ -37,10 +36,6 @@ var DenoProcessEnvRunner = class extends BaseEnvRunner {
37
36
  this.close(`process entry not found in "${this._workerEntry}".`);
38
37
  return;
39
38
  }
40
- const env = hostEnv({
41
- ENV_RUNNER_NAME: this._name,
42
- ENV_RUNNER_DATA: JSON.stringify(this._data || {})
43
- });
44
39
  const child = spawn("deno", [
45
40
  "run",
46
41
  "-A",
@@ -49,28 +44,17 @@ var DenoProcessEnvRunner = class extends BaseEnvRunner {
49
44
  ...execArgv || [],
50
45
  this._workerEntry
51
46
  ], {
52
- env,
47
+ env: this._processEnv(),
53
48
  stdio: [
54
49
  "pipe",
55
50
  "pipe",
56
- "pipe"
57
- ]
58
- });
59
- const exited = new Promise((resolve) => {
60
- child.once("exit", (code) => resolve(code ?? 1));
51
+ "pipe",
52
+ "ipc"
53
+ ],
54
+ serialization: "json"
61
55
  });
62
- const handle = {
63
- pid: child.pid,
64
- kill: () => child.kill(),
65
- send: (message) => {
66
- child.stdin.write(JSON.stringify(message) + "\n");
67
- },
68
- exited,
69
- _exitCode: void 0,
70
- removeAllListeners: () => child.removeAllListeners()
71
- };
72
56
  child.once("exit", (code) => {
73
- handle._exitCode = code;
57
+ child._exitCode = code;
74
58
  this.close(`process exited with code ${code}`);
75
59
  });
76
60
  child.on("error", (error) => {
@@ -79,22 +63,12 @@ var DenoProcessEnvRunner = class extends BaseEnvRunner {
79
63
  this.close(error);
80
64
  }
81
65
  });
82
- let buffer = "";
83
- child.stdout.on("data", (chunk) => {
84
- buffer += chunk.toString();
85
- let newlineIdx;
86
- while ((newlineIdx = buffer.indexOf("\n")) !== -1) {
87
- const line = buffer.slice(0, newlineIdx);
88
- buffer = buffer.slice(newlineIdx + 1);
89
- if (line.startsWith("{")) try {
90
- this._handleMessage(JSON.parse(line));
91
- continue;
92
- } catch {}
93
- process.stdout.write(line + "\n");
94
- }
66
+ child.on("message", (message) => {
67
+ this._handleProcessMessage(message);
95
68
  });
69
+ child.stdout?.pipe(process.stdout);
96
70
  child.stderr?.pipe(process.stderr);
97
- this.#process = handle;
71
+ this.#process = child;
98
72
  }
99
73
  };
100
74
  export { DenoProcessEnvRunner };
@@ -0,0 +1,2 @@
1
+ import { init, initSync, parse } from "./libs/cjs-module-lexer.mjs";
2
+ export { init, initSync, parse };
@@ -142,4 +142,9 @@ function init() {
142
142
  A = Q;
143
143
  })());
144
144
  }
145
- export { init, parse };
145
+ function initSync() {
146
+ if (A) return;
147
+ const B = new WebAssembly.Module(w()), { exports: Q } = new WebAssembly.Instance(B);
148
+ A = Q;
149
+ }
150
+ export { init, initSync, parse };
@@ -1,16 +1,9 @@
1
- import { WorkerHooks } from "./types.mjs";
1
+ import { ResolvedVirtualModule, WorkerHooks } from "./types.mjs";
2
2
  import { BaseEnvRunner, EnvRunnerData } from "./common-base-runner.mjs";
3
3
  import { RuntimeDep } from "./common-runtime-deps.mjs";
4
4
  import { IncomingMessage } from "node:http";
5
5
  import { Socket } from "node:net";
6
- /**
7
- * The `wrangler` package, as imported by the consumer.
8
- *
9
- * `wrangler` is not a dependency of `env-runner` — pass the module namespace
10
- * (`import * as wrangler from "wrangler"`) or a specifier for it, so the
11
- * dependency stays owned by the application. It is only imported here as a
12
- * fallback when neither is passed.
13
- */
6
+ /** The `wrangler` package namespace, as imported by the app. */
14
7
  export interface WranglerModule {
15
8
  unstable_readConfig?: (...args: any[]) => any;
16
9
  unstable_getMiniflareWorkerOptions?: (...args: any[]) => any;
@@ -26,21 +19,13 @@ export interface TransformResult {
26
19
  export interface MiniflareExportInfo {
27
20
  type?: "DurableObject" | "WorkerEntrypoint" | "class";
28
21
  }
29
- /**
30
- * The `miniflare` package, as imported by the consumer.
31
- *
32
- * `miniflare` is not a dependency of `env-runner` — pass the module namespace
33
- * (`import * as miniflare from "miniflare"`) so the dependency stays owned by
34
- * the application. The runner only falls back to importing it itself when this
35
- * is omitted.
36
- */
22
+ /** The `miniflare` package namespace (v4 or v5), as imported by the app. */
37
23
  export interface MiniflareModule {
38
24
  Miniflare: new (options: any) => any;
39
- /**
40
- * Newest compatibility date supported by the installed `workerd` binary,
41
- * clamped to today. Used as the default `compatibilityDate`.
42
- */
25
+ /** Newest date the installed `workerd` supports, clamped to today (v4 only). */
43
26
  supportedCompatibilityDate?: string;
27
+ /** Converts v4 options, which the runner builds, to the v5 format (v5 only). */
28
+ convertV4MiniflareOptions?: (options: any) => any;
44
29
  [key: string]: unknown;
45
30
  }
46
31
  export interface MiniflareEnvRunnerOptions {
@@ -48,98 +33,75 @@ export interface MiniflareEnvRunnerOptions {
48
33
  hooks?: WorkerHooks;
49
34
  data?: EnvRunnerData;
50
35
  /**
51
- * The `miniflare` package: the imported module, or a specifier for it.
52
- *
53
- * ```ts
54
- * import * as miniflare from "miniflare";
55
- * new MiniflareEnvRunner({ name: "app", miniflare, data: { entry } });
56
- *
57
- * // or, equivalently
58
- * new MiniflareEnvRunner({ name: "app", miniflare: "miniflare", data: { entry } });
59
- * ```
60
- *
61
- * Passing it explicitly is preferred (`miniflare` is not a dependency of
62
- * `env-runner`, so the version you install is the version that runs). Bare
63
- * specifiers resolve from the current working directory. When omitted, the
64
- * runner falls back to importing `miniflare` itself and only fails if that
65
- * is unavailable too.
36
+ * The `miniflare` package (`import * as miniflare from "miniflare"`) or a
37
+ * specifier resolved from cwd. Omitted: imported optionally.
66
38
  */
67
39
  miniflare?: RuntimeDep<MiniflareModule>;
68
40
  /** Options passed directly to the Miniflare constructor. */
69
41
  miniflareOptions?: Record<string, unknown>;
70
42
  /**
71
- * Optional module transform callback. When provided, the module fallback
72
- * service calls this instead of reading raw files from disk.
73
- *
74
- * This enables integration with Vite's transform pipeline — pass
75
- * `environment.transformRequest` to get TS/JSX/etc. compiled on the fly.
76
- *
77
- * @param id - Absolute file path of the module to transform
78
- * @returns Transformed code, or null/undefined to fall back to raw disk read
43
+ * `"latest"` uses the installed workerd's newest date. Precedence:
44
+ * `miniflareOptions` > this > wrangler config > newest supported. Dates newer
45
+ * than workerd supports fall back with a warning (workerd refuses them).
79
46
  */
80
- transformRequest?: (id: string) => Promise<TransformResult | null | undefined>;
47
+ compatibilityDate?: "latest" | (string & {});
81
48
  /**
82
- * Declare named exports (Durable Objects, WorkerEntrypoints) to auto-wire
83
- * bindings and generate re-exports in the wrapper module.
84
- *
85
- * When set to `true`, `export class` declarations are auto-detected from
86
- * the entry file. When set to a record, the listed exports are used
87
- * (merged with auto-detected ones). Disabled by default.
49
+ * Transform modules served by the fallback service (e.g. Vite's
50
+ * `environment.transformRequest`). `id` is an absolute path; return nullish to
51
+ * read from disk.
88
52
  */
89
- exports?: Record<string, MiniflareExportInfo> | boolean;
53
+ transformRequest?: (id: string) => Promise<TransformResult | null | undefined>;
90
54
  /**
91
- * When `true`, the Miniflare instance is cached and reused across runner
92
- * swaps (e.g. via `RunnerManager.reload()`). `close()` tears down IPC but
93
- * keeps Miniflare alive. Call `dispose()` to fully destroy it.
55
+ * Named exports (Durable Objects, WorkerEntrypoints) to bind and re-export.
56
+ * Default (or `true`): detect `export class` in the entry and auto-bind them;
57
+ * a record merges with detected ones; `false` disables it.
58
+ * A module specifier (absolute path or `data.virtual` key; relative paths
59
+ * resolve from the entry's directory) is re-exported with `export *` instead:
60
+ * nothing is detected or auto-bound (configure bindings with `wrangler` or
61
+ * `miniflareOptions`) and the entry's own classes are not re-exported.
62
+ * Exports load at startup, so changes need a new runner (not `reloadModule()`).
94
63
  */
64
+ exports?: Record<string, MiniflareExportInfo> | boolean | string;
65
+ /** Reuse the Miniflare instance across runner swaps; only `dispose()` destroys it. */
95
66
  persistent?: boolean;
96
67
  /** Wrap the user's `fetch` in a try/catch that returns structured JSON error responses. Default: `true`. */
97
68
  captureErrors?: boolean;
98
- /**
99
- * Export conditions for bare-specifier module resolution in the module
100
- * fallback service. Ensures packages with conditional exports (e.g.
101
- * `"workerd"`) resolve to the correct entry instead of the Node.js one.
102
- *
103
- * Defaults to `["workerd", "worker"]`.
104
- */
69
+ /** Export conditions for the fallback service (default `["workerd", "worker"]`). */
105
70
  exportConditions?: string[];
106
71
  /**
107
- * Load a Cloudflare `wrangler` config to populate Miniflare options
108
- * (compatibility date/flags and bindings: `vars`, KV, R2, D1, Durable
109
- * Objects, queues).
72
+ * Load a wrangler config into Miniflare options (compat date/flags, bindings).
110
73
  *
111
- * - `true` — auto-discover `wrangler.{json,jsonc,toml}` next to the entry
112
- * file, then in the current working directory.
113
- * - `string` — explicit path to a wrangler config file.
114
- * - `object` — an inline raw (snake_case) wrangler config, as you would
115
- * write in `wrangler.json` (no file needed). A config file is still
116
- * auto-discovered (next to the entry, then cwd) and the inline config is
117
- * merged on top of it (inline wins per key, binding records merge,
118
- * `compatibilityFlags` are unioned).
74
+ * - `true` — auto-discover `wrangler.{json,jsonc,toml}` near the entry or cwd
75
+ * - `string` — config file path
76
+ * - `object` — inline raw config merged over the file config (inline wins per
77
+ * key, binding records merge, flags union); its top level is used when it
78
+ * lacks the selected env
119
79
  *
120
- * The `wrangler` package is used for full fidelity when available (TOML,
121
- * `env` inheritance, `.dev.vars`, every binding type; an inline config is
122
- * normalized through a short-lived temp file) — passed explicitly as
123
- * `wranglerModule`, or imported optionally. Otherwise a built-in minimal
124
- * reader handles plain JSON files and inline objects (common fields only);
125
- * JSONC and TOML files are skipped with a warning. Values from
126
- * `miniflareOptions` always win over config-derived ones; binding records
127
- * (e.g. `bindings`) merge per key and `compatibilityFlags` are unioned.
80
+ * Options a single dev worker can't run (`assets`, services, queue consumers,
81
+ * workflows, tails, other-script Durable Objects) are dropped with a warning.
82
+ * `defaultPersistRoot` (v5: `resourcePersistencePath`) defaults to
83
+ * `.wrangler/state/v3` next to the config (else cwd), shared with
84
+ * `wrangler dev`. The `wrangler` package gives full fidelity (and may run its
85
+ * npm update check); without it only plain JSON is read. `miniflareOptions`
86
+ * always win.
128
87
  */
129
88
  wrangler?: boolean | string | WranglerInlineConfig;
130
89
  /**
131
- * Wrangler environment (`--env`) to select when loading the config.
132
- * Defaults to the `CLOUDFLARE_ENV` environment variable.
90
+ * Config file instead of auto-discovery when `wrangler` is `true` or inline
91
+ * (resolved from cwd). Ignored when `wrangler` is a path.
133
92
  */
93
+ wranglerConfigPath?: string;
94
+ /** Wrangler `--env` to select (default: `CLOUDFLARE_ENV`). */
134
95
  wranglerEnv?: string;
135
96
  /**
136
- * The imported `wrangler` package (`import * as wrangler from "wrangler"`),
137
- * used to parse the {@link MiniflareEnvRunnerOptions.wrangler} config with
138
- * full fidelity. When omitted, `import("wrangler")` is tried, and a built-in
139
- * minimal reader (plain JSON configs and inline objects) handles the rest.
140
- *
141
- * Pass `false` to skip the `wrangler` package entirely and always use the
142
- * built-in minimal reader.
97
+ * `.env` files for dev vars (like `getPlatformProxy({ envFiles })`), resolved
98
+ * from the config dir; later files win. Non-empty skips `.dev.vars`; `[]`
99
+ * reads only `.dev.vars`. Requires the `wrangler` package.
100
+ */
101
+ wranglerEnvFiles?: string[];
102
+ /**
103
+ * The `wrangler` package for full-fidelity config parsing. Omitted: imported
104
+ * optionally. `false`: always use the minimal JSON reader.
143
105
  */
144
106
  wranglerModule?: RuntimeDep<WranglerModule>;
145
107
  }
@@ -152,26 +114,13 @@ export declare class MiniflareEnvRunner extends BaseEnvRunner {
152
114
  dispose(): Promise<void>;
153
115
  fetch(input: string | URL | Request, init?: RequestInit): Promise<Response>;
154
116
  sendMessage(message: unknown): void;
155
- /**
156
- * Hot-reload the user entry module without recreating the Miniflare instance.
157
- *
158
- * Sends `reload-module` event over the WebSocket. The worker wrapper uses
159
- * `unsafeEvalBinding` to re-import the entry with a cache-busting query string
160
- * and responds with `module-reloaded` when done.
161
- */
117
+ /** Hot-reload the entry without recreating the Miniflare instance. */
162
118
  reloadModule(timeout?: number): Promise<void>;
163
119
  /**
164
- * Invalidate a virtual module so the next `reloadModule()` re-evaluates it.
165
- *
166
- * Host-side only (no worker round-trip): the module fallback service serves
167
- * virtual sources from a live map, so re-running a factory source and
168
- * bumping the per-specifier versions — the module plus its transitive
169
- * virtual importers — is enough. Import specifiers in re-served module code
170
- * are rewritten to the versioned form, giving workerd fresh module
171
- * identities (it caches by name). A `persistent` instance is evicted from
172
- * the cache, since its served sources no longer match the cache key.
120
+ * Host-side only: the fallback service serves a live map, so changing it and
121
+ * bumping versions (see {@link applyVirtualVersions}) is enough.
173
122
  */
174
- invalidateModule(specifier: string, _timeout?: number): Promise<void>;
123
+ protected _applyVirtualUpdates(changes: Record<string, ResolvedVirtualModule | null>): Promise<void>;
175
124
  protected _hasRuntime(): boolean;
176
125
  protected _runtimeType(): string;
177
126
  protected _closeRuntime(): Promise<void>;