env-runner 0.1.16 → 0.2.1

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,6 @@
1
1
  import { expandVirtualInvalidation, stripVirtualTypeScript, virtualModuleFormat } from "./virtual-loader.mjs";
2
2
  import { BaseEnvRunner } from "./common-base-runner.mjs";
3
+ import { resolveRuntimeDep } from "./common-runtime-deps.mjs";
3
4
  import { init, parse } from "./libs/cjs-module-lexer.mjs";
4
5
  import { init as init$1, parse as parse$1 } from "./libs/es-module-lexer.mjs";
5
6
  import { isVirtualSpecifier } from "./common-worker-utils.mjs";
@@ -7,8 +8,8 @@ import { createRequire } from "node:module";
7
8
  import { proxyUpgrade } from "httpxy";
8
9
  import { fileURLToPath } from "node:url";
9
10
  import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
10
- import { basename, dirname, extname, isAbsolute, join, resolve } from "node:path";
11
11
  import { resolveModulePath } from "exsolve";
12
+ import { basename, dirname, extname, isAbsolute, join, resolve } from "node:path";
12
13
  import { tmpdir } from "node:os";
13
14
  const IPC_PATH = "/__env_runner_ipc";
14
15
  const IPC_BINDING = "__ENV_RUNNER_IPC";
@@ -180,10 +181,6 @@ export default {
180
181
  };
181
182
  `;
182
183
  }
183
- function importWrangler() {
184
- return import("wrangler");
185
- }
186
- let _warnedNoWrangler = false;
187
184
  const WRANGLER_CONFIG_FILENAMES = [
188
185
  "wrangler.json",
189
186
  "wrangler.jsonc",
@@ -207,7 +204,7 @@ function isPlainObject(value) {
207
204
  function isInlineWranglerConfig(opt) {
208
205
  return isPlainObject(opt);
209
206
  }
210
- async function loadWranglerConfig(opt, env, entryPath) {
207
+ async function loadWranglerConfig(opt, env, entryPath, wranglerModule) {
211
208
  if (!opt) return;
212
209
  const inline = isInlineWranglerConfig(opt) ? opt : void 0;
213
210
  let configPath;
@@ -224,16 +221,12 @@ async function loadWranglerConfig(opt, env, entryPath) {
224
221
  return;
225
222
  }
226
223
  } else configPath = findWranglerConfig(entryPath);
227
- let wrangler;
228
- try {
229
- wrangler = await importWrangler();
230
- } catch {
231
- if (!_warnedNoWrangler) {
232
- _warnedNoWrangler = true;
233
- console.warn("[env-runner] 'wrangler' is not installed; using the built-in minimal config reader (plain JSON, common fields only). Install 'wrangler' for full fidelity (JSONC, TOML, env inheritance, all binding types).");
234
- }
235
- return mergeWranglerMiniflareOptions(configPath ? readWranglerConfigMinimal(configPath, env) : void 0, inline ? mapWranglerConfigToMiniflare(applyWranglerEnv(inline, env)) : void 0);
236
- }
224
+ const wrangler = await resolveRuntimeDep({
225
+ name: "wrangler",
226
+ option: "wranglerModule",
227
+ value: wranglerModule
228
+ });
229
+ if (!wrangler?.unstable_readConfig || !wrangler.unstable_getMiniflareWorkerOptions) return mergeWranglerMiniflareOptions(configPath ? readWranglerConfigMinimal(configPath, env) : void 0, inline ? mapWranglerConfigToMiniflare(applyWranglerEnv(inline, env)) : void 0);
237
230
  try {
238
231
  return mergeWranglerMiniflareOptions(configPath ? pickWranglerMiniflareOptions(wrangler.unstable_getMiniflareWorkerOptions(wrangler.unstable_readConfig({
239
232
  config: configPath,
@@ -374,11 +367,14 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
374
367
  #exportConditions;
375
368
  #wrangler;
376
369
  #wranglerEnv;
370
+ #wranglerModule;
371
+ #miniflareModule;
377
372
  constructor(opts) {
378
373
  super({
379
374
  ...opts,
380
375
  workerEntry: ""
381
376
  });
377
+ this.#miniflareModule = opts.miniflare;
382
378
  this.#miniflareOptions = opts.miniflareOptions || {};
383
379
  this.#transformRequest = opts.transformRequest;
384
380
  this.#persistent = opts.persistent ?? false;
@@ -387,6 +383,7 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
387
383
  this.#exportConditions = opts.exportConditions ?? ["workerd", "worker"];
388
384
  this.#wrangler = opts.wrangler ?? false;
389
385
  this.#wranglerEnv = opts.wranglerEnv ?? process.env.CLOUDFLARE_ENV;
386
+ this.#wranglerModule = opts.wranglerModule;
390
387
  this._initWithVirtualData(() => this.#init());
391
388
  }
392
389
  static async disposeAll() {
@@ -482,6 +479,17 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
482
479
  } else await this.#miniflare.dispose();
483
480
  this.#miniflare = void 0;
484
481
  }
482
+ async #resolveMiniflare() {
483
+ this.#miniflareModule = await resolveRuntimeDep({
484
+ name: "miniflare",
485
+ option: "miniflare",
486
+ value: this.#miniflareModule,
487
+ expect: "Miniflare",
488
+ required: true,
489
+ hint: "MiniflareEnvRunner cannot run without it (`import * as miniflare from \"miniflare\"`)."
490
+ });
491
+ return this.#miniflareModule;
492
+ }
485
493
  #init() {
486
494
  this.#initAsync().catch((error) => {
487
495
  console.error("Miniflare runner init error:", error);
@@ -517,11 +525,11 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
517
525
  });
518
526
  }
519
527
  async #initAsync() {
520
- const { Miniflare, supportedCompatibilityDate } = await import("miniflare");
528
+ const { Miniflare, supportedCompatibilityDate } = await this.#resolveMiniflare();
521
529
  const entryPath = this._data?.entry;
522
530
  const virtual = await this.#prepareVirtualModules();
523
531
  this.#virtual = virtual;
524
- const wranglerOptions = await loadWranglerConfig(this.#wrangler, this.#wranglerEnv, entryPath);
532
+ const wranglerOptions = await loadWranglerConfig(this.#wrangler, this.#wranglerEnv, entryPath, this.#wranglerModule);
525
533
  const userFlags = this.#miniflareOptions.compatibilityFlags || [];
526
534
  const wranglerFlags = wranglerOptions?.compatibilityFlags || [];
527
535
  const userDirectSockets = this.#miniflareOptions.unsafeDirectSockets || [];
@@ -662,7 +670,8 @@ var MiniflareEnvRunner = class extends BaseEnvRunner {
662
670
  }
663
671
  } else {
664
672
  const referrerKey = referrer.startsWith("/") ? referrer.slice(1) : referrer;
665
- const referrerDir = dirname(modulePathMap.get(referrerKey) || (referrer.startsWith("/") ? referrer : "/" + referrer));
673
+ const referrerReal = modulePathMap.get(referrerKey) || (referrer.startsWith("/") ? referrer : "/" + referrer);
674
+ const referrerDir = dirname(referrerReal);
666
675
  const raw = cleanRaw || cleanSpecifier;
667
676
  if (raw.startsWith(".")) resolvedPath = resolve(referrerDir, raw);
668
677
  else if (cleanSpecifier.startsWith("/")) resolvedPath = cleanSpecifier;
@@ -1,14 +1,39 @@
1
1
  import { WorkerHooks } from "./types.mjs";
2
2
  import { EnvRunnerData } from "./common-base-runner.mjs";
3
3
  import { NodeWorkerEnvRunner } from "./node-worker-runner.mjs";
4
+ interface NetlifyEnvRunnerOptions {
5
+ name: string;
6
+ workerEntry?: string;
7
+ hooks?: WorkerHooks;
8
+ data?: EnvRunnerData;
9
+ /**
10
+ * Module specifier for the `@netlify/runtime` package, used inside the
11
+ * worker to call `startRuntime()` (full `globalThis.Netlify` + `caches`
12
+ * setup). `env-runner` does not depend on `@netlify/runtime`.
13
+ *
14
+ * Unlike the other runtime-dependency options, this one takes a **specifier
15
+ * only**: the runtime has to be instantiated inside the worker thread, and a
16
+ * live module instance cannot cross that boundary.
17
+ *
18
+ * ```ts
19
+ * new NetlifyEnvRunner({
20
+ * name: "app",
21
+ * netlifyRuntime: import.meta.resolve("@netlify/runtime"),
22
+ * data: { entry },
23
+ * });
24
+ * ```
25
+ *
26
+ * A bare specifier (e.g. `"@netlify/runtime"`) is resolved from the current
27
+ * working directory. When omitted, the worker tries
28
+ * `import("@netlify/runtime")` and falls back to a lightweight
29
+ * `globalThis.Netlify` shim (env access only) if it isn't installed. Pass
30
+ * `false` to always use the shim.
31
+ */
32
+ netlifyRuntime?: string | URL | false;
33
+ }
4
34
  declare class NetlifyEnvRunner extends NodeWorkerEnvRunner {
5
- constructor(opts: {
6
- name: string;
7
- workerEntry?: string;
8
- hooks?: WorkerHooks;
9
- data?: EnvRunnerData;
10
- });
35
+ constructor(opts: NetlifyEnvRunnerOptions);
11
36
  fetch(input: string | URL | Request, init?: RequestInit): Promise<Response>;
12
37
  protected _runtimeType(): string;
13
38
  }
14
- export { NetlifyEnvRunner };
39
+ export { NetlifyEnvRunner, NetlifyEnvRunnerOptions };
@@ -1,12 +1,18 @@
1
+ import { resolveRuntimeDepSpecifier } from "./common-runtime-deps.mjs";
1
2
  import { NodeWorkerEnvRunner } from "./node-worker-runner.mjs";
2
3
  import { fileURLToPath } from "node:url";
3
4
  let _defaultEntry;
4
5
  var NetlifyEnvRunner = class extends NodeWorkerEnvRunner {
5
6
  constructor(opts) {
6
7
  _defaultEntry ||= fileURLToPath(import.meta.resolve("env-runner/runners/netlify/worker"));
8
+ const netlifyRuntime = resolveNetlifyRuntime(opts.netlifyRuntime);
7
9
  super({
8
10
  ...opts,
9
- workerEntry: opts.workerEntry || _defaultEntry
11
+ workerEntry: opts.workerEntry || _defaultEntry,
12
+ data: netlifyRuntime === void 0 ? opts.data : {
13
+ ...opts.data,
14
+ netlifyRuntime
15
+ }
10
16
  });
11
17
  }
12
18
  async fetch(input, init) {
@@ -43,4 +49,7 @@ var NetlifyEnvRunner = class extends NodeWorkerEnvRunner {
43
49
  return "netlify";
44
50
  }
45
51
  };
52
+ function resolveNetlifyRuntime(runtime) {
53
+ return resolveRuntimeDepSpecifier(runtime, "netlifyRuntime");
54
+ }
46
55
  export { NetlifyEnvRunner };
@@ -1,4 +1,5 @@
1
1
  import { BaseEnvRunner } from "./common-base-runner.mjs";
2
+ import { hostEnv } from "./common-host-env.mjs";
2
3
  import { fileURLToPath } from "node:url";
3
4
  import { existsSync } from "node:fs";
4
5
  import { Worker } from "node:worker_threads";
@@ -37,7 +38,7 @@ var NodeWorkerEnvRunner = class extends BaseEnvRunner {
37
38
  return;
38
39
  }
39
40
  const worker = new Worker(this._workerEntry, {
40
- env: { ...process.env },
41
+ env: hostEnv(),
41
42
  workerData: {
42
43
  name: this._name,
43
44
  ...this._data
@@ -300,6 +300,7 @@ var EnvServer = class extends RunnerManager {
300
300
  }
301
301
  async _createRunner() {
302
302
  return loadRunner(this._opts.runner || "node-worker", {
303
+ ...this._opts.runnerOptions,
303
304
  name: this._opts.name || this._opts.entry,
304
305
  hooks: this._opts.hooks,
305
306
  data: {
package/dist/cli.d.mts CHANGED
@@ -1 +1 @@
1
- export { };
1
+ export {}
package/dist/cli.mjs CHANGED
File without changes
package/dist/index.d.mts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { EnvRunner, FetchHandler, NodeUpgradeContext, RPCOptions, RunnerMessageListener, RunnerRPCHooks, UpgradeContext, UpgradeHandler, WorkerAddress, WorkerHooks } from "./_chunks/types.mjs";
2
2
  import { BaseEnvRunner, EnvRunnerData, VirtualModuleSource, VirtualModules } from "./_chunks/common-base-runner.mjs";
3
+ import { RuntimeDep, resolveRuntimeDep } from "./_chunks/common-runtime-deps.mjs";
3
4
  import { DenoProcessEnvRunner } from "./_chunks/deno-process-runner.mjs";
4
5
  import { MiniflareEnvRunner, MiniflareEnvRunnerOptions, MiniflareExportInfo, TransformResult } from "./_chunks/miniflare-runner.mjs";
5
6
  import { VercelEnvRunner } from "./_chunks/vercel-runner.mjs";
@@ -84,7 +85,11 @@ interface LoadRunnerOptions {
84
85
  hooks?: WorkerHooks;
85
86
  data?: EnvRunnerData;
86
87
  execArgv?: string[];
87
- /** Additional runner-specific options (passed through to the runner constructor). */
88
+ /**
89
+ * Additional runner-specific options (passed through to the runner
90
+ * constructor) — e.g. `miniflare` (the imported `miniflare` package) for the
91
+ * `miniflare` runner, which this package never imports itself.
92
+ */
88
93
  [key: string]: unknown;
89
94
  }
90
95
  declare function loadRunner(runner: RunnerName, opts: LoadRunnerOptions): Promise<EnvRunner>;
@@ -105,6 +110,14 @@ interface EnvServerOptions {
105
110
  watch?: boolean;
106
111
  /** Additional paths to watch (directories or files). */
107
112
  watchPaths?: string[];
113
+ /**
114
+ * Extra runner-specific constructor options, spread into the runner.
115
+ *
116
+ * Runners that build on external packages take them as explicit options —
117
+ * e.g. `{ miniflare }` for the `miniflare` runner (env-runner never imports
118
+ * those packages itself).
119
+ */
120
+ runnerOptions?: Record<string, unknown>;
108
121
  }
109
122
  declare class EnvServer extends RunnerManager {
110
123
  private _opts;
@@ -156,4 +169,4 @@ interface AppEntry {
156
169
  plugins?: ServerOptions["plugins"];
157
170
  ipc?: AppEntryIPC;
158
171
  }
159
- export { type AppEntry, type AppEntryIPC, type AppEntryIPCContext, BaseEnvRunner, DenoProcessEnvRunner, type EnvRunnerData as DenoProcessEnvRunnerData, type EnvRunner, type EnvRunnerData, EnvServer, type EnvServerOptions, type FetchHandler, type LoadRunnerOptions, MiniflareEnvRunner, type MiniflareEnvRunnerOptions, type MiniflareExportInfo, NetlifyEnvRunner, type NodeUpgradeContext, type RPCOptions, RunnerManager, type RunnerMessageListener, type RunnerName, type RunnerRPCHooks, type TransformResult, type UpgradeContext, type UpgradeHandler, VercelEnvRunner, type VirtualModuleSource, type VirtualModules, type WorkerAddress, type WorkerHooks, loadRunner };
172
+ export { type AppEntry, type AppEntryIPC, type AppEntryIPCContext, BaseEnvRunner, DenoProcessEnvRunner, type EnvRunnerData as DenoProcessEnvRunnerData, type EnvRunner, type EnvRunnerData, EnvServer, type EnvServerOptions, type FetchHandler, type LoadRunnerOptions, MiniflareEnvRunner, type MiniflareEnvRunnerOptions, type MiniflareExportInfo, NetlifyEnvRunner, type NodeUpgradeContext, type RPCOptions, RunnerManager, type RunnerMessageListener, type RunnerName, type RunnerRPCHooks, type RuntimeDep, type TransformResult, type UpgradeContext, type UpgradeHandler, VercelEnvRunner, type VirtualModuleSource, type VirtualModules, type WorkerAddress, type WorkerHooks, loadRunner, resolveRuntimeDep };
package/dist/index.mjs CHANGED
@@ -1,7 +1,8 @@
1
1
  import { BaseEnvRunner } from "./_chunks/common-base-runner.mjs";
2
2
  import { EnvServer, RunnerManager, loadRunner } from "./_chunks/server.mjs";
3
+ import { resolveRuntimeDep } from "./_chunks/common-runtime-deps.mjs";
3
4
  import { DenoProcessEnvRunner } from "./_chunks/deno-process-runner.mjs";
4
5
  import { MiniflareEnvRunner } from "./_chunks/miniflare-runner.mjs";
5
6
  import { VercelEnvRunner } from "./_chunks/vercel-runner.mjs";
6
7
  import { NetlifyEnvRunner } from "./_chunks/netlify-runner.mjs";
7
- export { BaseEnvRunner, DenoProcessEnvRunner, EnvServer, MiniflareEnvRunner, NetlifyEnvRunner, RunnerManager, VercelEnvRunner, loadRunner };
8
+ export { BaseEnvRunner, DenoProcessEnvRunner, EnvServer, MiniflareEnvRunner, NetlifyEnvRunner, RunnerManager, VercelEnvRunner, loadRunner, resolveRuntimeDep };
@@ -1,4 +1,5 @@
1
1
  import { BaseEnvRunner } from "../../_chunks/common-base-runner.mjs";
2
+ import { hostEnv } from "../../_chunks/common-host-env.mjs";
2
3
  import { fileURLToPath } from "node:url";
3
4
  import { existsSync } from "node:fs";
4
5
  import { execSync, spawn } from "node:child_process";
@@ -56,11 +57,10 @@ var BunProcessEnvRunner = class extends BaseEnvRunner {
56
57
  this.close(`process entry not found in "${this._workerEntry}".`);
57
58
  return;
58
59
  }
59
- const env = {
60
- ...process.env,
60
+ const env = hostEnv({
61
61
  ENV_RUNNER_NAME: this._name,
62
62
  ENV_RUNNER_DATA: JSON.stringify(this._data || {})
63
- };
63
+ });
64
64
  if (_isBun) this.#initBunProcess(execArgv, env);
65
65
  else this.#initNodeProcess(execArgv, env);
66
66
  }
@@ -1 +1 @@
1
- export { };
1
+ export {}
@@ -1 +1 @@
1
- export { };
1
+ export {}
@@ -1,3 +1,3 @@
1
1
  import { EnvRunnerData } from "../../_chunks/common-base-runner.mjs";
2
- import { MiniflareEnvRunner, MiniflareEnvRunnerOptions, MiniflareExportInfo, TransformResult, WranglerInlineConfig } from "../../_chunks/miniflare-runner.mjs";
3
- export { MiniflareEnvRunner, type EnvRunnerData as MiniflareEnvRunnerData, MiniflareEnvRunnerOptions, MiniflareExportInfo, TransformResult, type WranglerInlineConfig };
2
+ import { MiniflareEnvRunner, MiniflareEnvRunnerOptions, MiniflareExportInfo, MiniflareModule, TransformResult, WranglerInlineConfig, WranglerModule } from "../../_chunks/miniflare-runner.mjs";
3
+ export { MiniflareEnvRunner, type EnvRunnerData as MiniflareEnvRunnerData, MiniflareEnvRunnerOptions, MiniflareExportInfo, MiniflareModule, TransformResult, type WranglerInlineConfig, type WranglerModule };
@@ -1,3 +1,3 @@
1
1
  import { EnvRunnerData } from "../../_chunks/common-base-runner.mjs";
2
- import { NetlifyEnvRunner } from "../../_chunks/netlify-runner.mjs";
3
- export { type EnvRunnerData, NetlifyEnvRunner };
2
+ import { NetlifyEnvRunner, NetlifyEnvRunnerOptions } from "../../_chunks/netlify-runner.mjs";
3
+ export { type EnvRunnerData, NetlifyEnvRunner, NetlifyEnvRunnerOptions };
@@ -1 +1 @@
1
- export { };
1
+ export {}
@@ -1,41 +1,40 @@
1
- try {
2
- const { startRuntime } = await import("@netlify/runtime");
1
+ import { resolveRuntimeDep } from "../../_chunks/common-runtime-deps.mjs";
2
+ import { workerData } from "node:worker_threads";
3
+ const netlifyEnv = {
4
+ get: (key) => process.env[key],
5
+ has: (key) => key in process.env,
6
+ set: (key, value) => {
7
+ process.env[key] = value;
8
+ },
9
+ delete: (key) => {
10
+ delete process.env[key];
11
+ },
12
+ toObject: () => ({ ...process.env })
13
+ };
14
+ const runtimeSpecifier = (workerData || {}).netlifyRuntime;
15
+ let started = false;
16
+ if (runtimeSpecifier !== false) try {
17
+ const startRuntime = (await resolveRuntimeDep({
18
+ name: "@netlify/runtime",
19
+ option: "netlifyRuntime",
20
+ value: runtimeSpecifier,
21
+ expect: "startRuntime"
22
+ }))?.startRuntime;
23
+ if (!startRuntime) throw new Error("`@netlify/runtime` is not installed");
3
24
  startRuntime({
4
25
  deployID: "0",
5
26
  siteID: "0",
6
- env: {
7
- get: (key) => process.env[key],
8
- has: (key) => key in process.env,
9
- set: (key, value) => {
10
- process.env[key] = value;
11
- },
12
- delete: (key) => {
13
- delete process.env[key];
14
- },
15
- toObject: () => ({ ...process.env })
16
- },
27
+ env: netlifyEnv,
17
28
  getRequestContext: () => null,
18
29
  cache: { getCacheAPIContext: () => null }
19
30
  });
20
- } catch {
21
- if (!process.env.__ENV_RUNNER_NETLIFY_WARNED) {
22
- process.env.__ENV_RUNNER_NETLIFY_WARNED = "1";
23
- console.warn("@netlify/runtime is not installed. Install it for full Netlify runtime emulation: npx nypm i -D @netlify/runtime");
24
- }
25
- globalThis.Netlify = {
26
- context: null,
27
- env: {
28
- get: (key) => process.env[key],
29
- has: (key) => key in process.env,
30
- set: (key, value) => {
31
- process.env[key] = value;
32
- },
33
- delete: (key) => {
34
- delete process.env[key];
35
- },
36
- toObject: () => ({ ...process.env })
37
- }
38
- };
31
+ started = true;
32
+ } catch (error) {
33
+ if (runtimeSpecifier) console.warn(`[env-runner] failed to start the Netlify runtime from "${runtimeSpecifier}": ${error?.cause?.message || error?.message || error}`);
39
34
  }
35
+ if (!started) globalThis.Netlify = {
36
+ context: null,
37
+ env: netlifyEnv
38
+ };
40
39
  await import("../node-worker/worker.mjs");
41
40
  export {};
@@ -1,4 +1,5 @@
1
1
  import { BaseEnvRunner } from "../../_chunks/common-base-runner.mjs";
2
+ import { hostEnv } from "../../_chunks/common-host-env.mjs";
2
3
  import { fileURLToPath } from "node:url";
3
4
  import { existsSync } from "node:fs";
4
5
  import { fork } from "node:child_process";
@@ -35,11 +36,10 @@ var NodeProcessEnvRunner = class extends BaseEnvRunner {
35
36
  return;
36
37
  }
37
38
  const child = fork(this._workerEntry, [], {
38
- env: {
39
- ...process.env,
39
+ env: hostEnv({
40
40
  ENV_RUNNER_NAME: this._name,
41
41
  ENV_RUNNER_DATA: JSON.stringify(this._data || {})
42
- },
42
+ }),
43
43
  stdio: [
44
44
  "pipe",
45
45
  "pipe",
@@ -1 +1 @@
1
- export { };
1
+ export {}
@@ -1 +1 @@
1
- export { };
1
+ export {}
@@ -1,9 +1,64 @@
1
- import { MessageHandler, RetryHandler } from "@vercel/queue";
1
+ import { RuntimeDep } from "../../_chunks/common-runtime-deps.mjs";
2
+ /** Metadata passed alongside each delivered message (`MessageMetadata`). */
3
+ interface VercelQueueMessageMetadata {
4
+ messageId: string;
5
+ deliveryCount: number;
6
+ createdAt: Date;
7
+ expiresAt: Date;
8
+ topicName: string;
9
+ consumerGroup: string;
10
+ /** Vercel region the client is targeting. */
11
+ region: string;
12
+ }
13
+ /** Instruction returned by a retry handler when the message handler throws. */
14
+ type VercelQueueRetryDirective = {
15
+ afterSeconds: number;
16
+ } | {
17
+ acknowledge: true;
18
+ };
19
+ /** Function invoked with each delivered message (`MessageHandler`). */
20
+ type VercelQueueMessageHandler<T = unknown> = (message: T, metadata: VercelQueueMessageMetadata) => Promise<void> | void;
21
+ /** Called when the message handler throws (`RetryHandler`). */
22
+ type VercelQueueRetryHandler = (error: unknown, metadata: VercelQueueMessageMetadata) => VercelQueueRetryDirective | void | undefined;
23
+ /**
24
+ * The `@vercel/queue` package, as imported by the consumer
25
+ * (`import * as queue from "@vercel/queue"`). Structurally typed so the real
26
+ * module namespace is assignable without env-runner depending on the package.
27
+ */
28
+ interface VercelQueueSdk {
29
+ QueueClient: new (options?: any) => any;
30
+ registerDevConsumer?: (options: {
31
+ topic: string;
32
+ client: any;
33
+ handler: VercelQueueMessageHandler;
34
+ consumerGroup?: string;
35
+ visibilityTimeoutSeconds?: number;
36
+ retry?: VercelQueueRetryHandler;
37
+ }) => () => void;
38
+ [key: string]: unknown;
39
+ }
2
40
  interface VercelQueueDevConsumer {
41
+ /**
42
+ * The `@vercel/queue` package: the imported module, or a specifier for it.
43
+ *
44
+ * ```ts
45
+ * import * as sdk from "@vercel/queue";
46
+ * await registerVercelQueueConsumer({ sdk, topic: "orders", handler });
47
+ *
48
+ * // or, equivalently
49
+ * await registerVercelQueueConsumer({ sdk: "@vercel/queue", topic, handler });
50
+ * ```
51
+ *
52
+ * Passing it explicitly is preferred (`@vercel/queue` is not a dependency of
53
+ * `env-runner`). Bare specifiers resolve from the current working directory.
54
+ * When omitted, `@vercel/queue` is imported optionally and registration
55
+ * becomes a no-op if the package isn't installed.
56
+ */
57
+ sdk?: RuntimeDep<VercelQueueSdk>;
3
58
  /** Topic name. Wildcard patterns (e.g. `"user-*"`) are supported. */
4
59
  topic: string;
5
60
  /** Function invoked with each delivered message. */
6
- handler: MessageHandler;
61
+ handler: VercelQueueMessageHandler;
7
62
  /**
8
63
  * Logical consumer identifier. Re-registering with the same group on the same
9
64
  * topic replaces the previous handler (HMR-safe). Use distinct groups to fan
@@ -27,18 +82,18 @@ interface VercelQueueDevConsumer {
27
82
  * `{ afterSeconds }` to reschedule, `{ acknowledge: true }` to drop, or
28
83
  * `undefined` to let the error propagate.
29
84
  */
30
- retry?: RetryHandler;
85
+ retry?: VercelQueueRetryHandler;
31
86
  }
32
87
  /**
33
88
  * Bind a handler to a topic. Resolves to an unregister function.
34
89
  *
35
- * The first call across the worker process lazily loads `@vercel/queue` and
36
- * constructs a shared `QueueClient`. Re-registering with the same topic
90
+ * A shared `QueueClient` is constructed from `sdk` on first use and reused for
91
+ * later registrations with the same SDK. Re-registering with the same topic
37
92
  * replaces the handler (HMR-safe; the SDK keys consumers by `consumerGroup`,
38
93
  * so calling unregister on a replaced registration is a no-op).
39
94
  *
40
- * If `@vercel/queue` is not installed or does not expose `registerDevConsumer`,
95
+ * If no SDK is available, or it does not expose `registerDevConsumer`,
41
96
  * resolves to a no-op unregister and logs a one-time warning.
42
97
  */
43
98
  declare function registerVercelQueueConsumer(consumer: VercelQueueDevConsumer): Promise<() => void>;
44
- export { VercelQueueDevConsumer, registerVercelQueueConsumer };
99
+ export { VercelQueueDevConsumer, VercelQueueMessageHandler, VercelQueueMessageMetadata, VercelQueueRetryDirective, VercelQueueRetryHandler, VercelQueueSdk, registerVercelQueueConsumer };
@@ -1,10 +1,31 @@
1
+ import { resolveRuntimeDep } from "../../_chunks/common-runtime-deps.mjs";
1
2
  const DEFAULT_CONSUMER_GROUP = "env-runner-vercel-dev";
2
- let sdkPromise;
3
- let client;
3
+ const _clients = /* @__PURE__ */ new WeakMap();
4
+ const _warned = /* @__PURE__ */ new WeakSet();
5
+ let _warnedMissing = false;
6
+ let _importedSdk;
4
7
  const noop = () => {};
5
8
  async function registerVercelQueueConsumer(consumer) {
6
- const sdk = await ensureSdk();
7
- if (!sdk || !client) return noop;
9
+ const sdk = await resolveSdk(consumer.sdk);
10
+ if (!sdk) {
11
+ if (!_warnedMissing) {
12
+ _warnedMissing = true;
13
+ console.warn("[env-runner:vercel-queue] `@vercel/queue` is not installed and no `sdk` was passed. Local queue delivery is disabled.");
14
+ }
15
+ return noop;
16
+ }
17
+ if (typeof sdk.registerDevConsumer !== "function") {
18
+ if (!_warned.has(sdk)) {
19
+ _warned.add(sdk);
20
+ console.warn("[env-runner:vercel-queue] The `@vercel/queue` SDK in use does not export `registerDevConsumer`. Upgrade @vercel/queue@^0.2.0 to enable local queue delivery.");
21
+ }
22
+ return noop;
23
+ }
24
+ let client = _clients.get(sdk);
25
+ if (!client) {
26
+ client = new sdk.QueueClient();
27
+ _clients.set(sdk, client);
28
+ }
8
29
  return sdk.registerDevConsumer({
9
30
  topic: consumer.topic,
10
31
  client,
@@ -14,23 +35,16 @@ async function registerVercelQueueConsumer(consumer) {
14
35
  retry: consumer.retry ?? (consumer.retryAfterSeconds === void 0 ? void 0 : () => ({ afterSeconds: consumer.retryAfterSeconds }))
15
36
  });
16
37
  }
17
- function ensureSdk() {
18
- if (sdkPromise) return sdkPromise;
19
- sdkPromise = (async () => {
20
- let mod;
21
- try {
22
- mod = await import("@vercel/queue");
23
- } catch {
24
- console.warn("[env-runner:vercel-queue] `@vercel/queue` is not installed. Local queue delivery is disabled.");
25
- return null;
26
- }
27
- if (typeof mod.registerDevConsumer !== "function") {
28
- console.warn("[env-runner:vercel-queue] Installed `@vercel/queue` does not export `registerDevConsumer`. Upgrade @vercel/queue@^0.2.0 to enable local queue delivery.");
29
- return null;
30
- }
31
- client = new mod.QueueClient();
32
- return mod;
33
- })();
34
- return sdkPromise;
38
+ function resolveSdk(value) {
39
+ if (value !== void 0) return resolveRuntimeDep({
40
+ name: "@vercel/queue",
41
+ option: "sdk",
42
+ value
43
+ });
44
+ _importedSdk ??= resolveRuntimeDep({
45
+ name: "@vercel/queue",
46
+ option: "sdk"
47
+ });
48
+ return _importedSdk;
35
49
  }
36
50
  export { registerVercelQueueConsumer };
@@ -1 +1 @@
1
- export { };
1
+ export {}