• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

supabase / edge-runtime / 19085527152

04 Nov 2025 11:04PM UTC coverage: 52.353%. First build
19085527152

Pull #636

github

web-flow
Merge 77a5e46bf into 7beedc614
Pull Request #636: fix: request idle timeout should be applied differently for worker kind

20 of 40 new or added lines in 4 files covered. (50.0%)

18724 of 35765 relevant lines covered (52.35%)

7570.33 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/cli/src/flags.rs
1
use std::net::SocketAddr;
2
use std::path::PathBuf;
3

4
use clap::arg;
5
use clap::builder::BoolishValueParser;
6
use clap::builder::FalseyValueParser;
7
use clap::builder::TypedValueParser;
8
use clap::crate_version;
9
use clap::value_parser;
10
use clap::ArgAction;
11
use clap::ArgGroup;
12
use clap::Command;
13
use clap::ValueEnum;
14
use deno::deno_telemetry;
15
use deno_facade::Checksum;
16

17
#[derive(ValueEnum, Default, Clone, Copy)]
18
#[repr(u8)]
19
pub(super) enum EszipV2ChecksumKind {
20
  #[default]
21
  NoChecksum = 0,
22
  Sha256 = 1,
23
  XxHash3 = 2,
24
}
25

26
impl From<EszipV2ChecksumKind> for Option<Checksum> {
27
  fn from(value: EszipV2ChecksumKind) -> Self {
×
28
    Checksum::from_u8(value as u8)
×
29
  }
×
30
}
31

32
#[derive(ValueEnum, Clone, Copy)]
33
pub(super) enum OtelKind {
34
  Main,
35
  Event,
36
}
37

38
#[derive(ValueEnum, Default, Clone, Copy)]
39
pub(super) enum OtelConsoleConfig {
40
  #[default]
41
  Ignore,
42
  Capture,
43
  Replace,
44
}
45

46
impl From<OtelConsoleConfig> for deno_telemetry::OtelConsoleConfig {
47
  fn from(value: OtelConsoleConfig) -> Self {
×
48
    match value {
×
49
      OtelConsoleConfig::Ignore => Self::Ignore,
×
50
      OtelConsoleConfig::Capture => Self::Capture,
×
51
      OtelConsoleConfig::Replace => Self::Replace,
×
52
    }
53
  }
×
54
}
55

56
pub(super) fn get_cli() -> Command {
×
57
  Command::new(env!("CARGO_BIN_NAME"))
×
58
    .about(env!("CARGO_PKG_DESCRIPTION"))
×
59
    .version(format!(
×
60
      "{}\ndeno {} ({}, {})",
×
61
      crate_version!(),
×
62
      deno::version(),
×
63
      env!("PROFILE"),
×
64
      env!("TARGET")
×
65
    ))
×
66
    .arg_required_else_help(true)
×
67
    .arg(
×
68
      arg!(-v --verbose "Use verbose output")
×
69
        .conflicts_with("quiet")
×
70
        .global(true)
×
71
        .action(ArgAction::SetTrue),
×
72
    )
×
73
    .arg(
×
74
      arg!(-q --quiet "Do not print any log messages")
×
75
        .conflicts_with("verbose")
×
76
        .global(true)
×
77
        .action(ArgAction::SetTrue),
×
78
    )
×
79
    .arg(
×
80
      arg!(--"log-source")
×
81
        .help("Include source file and line in log messages")
×
82
        .global(true)
×
83
        .action(ArgAction::SetTrue),
×
84
    )
×
85
    .subcommand(get_start_command())
×
86
    .subcommand(get_bundle_command())
×
87
    .subcommand(get_unbundle_command())
×
88
}
×
89

90
fn get_start_command() -> Command {
×
91
  Command::new("start")
×
92
    .about("Start the server")
×
93
    .arg(
×
94
      arg!(-i --ip <HOST>)
×
95
        .help("Host IP address to listen on")
×
96
        .default_value("0.0.0.0"),
×
97
    )
×
98
    .arg(
×
99
      arg!(-p --port <PORT>)
×
100
        .help("Port to listen on")
×
101
        .env("EDGE_RUNTIME_PORT")
×
102
        .default_value("9000")
×
103
        .value_parser(value_parser!(u16)),
×
104
    )
×
105
    .arg(
×
106
      arg!(--tls[PORT])
×
107
        .env("EDGE_RUNTIME_TLS")
×
108
        .num_args(0..=1)
×
109
        .default_missing_value("443")
×
110
        .value_parser(value_parser!(u16))
×
111
        .requires("key")
×
112
        .requires("cert"),
×
113
    )
×
114
    .arg(
×
115
      arg!(--key <Path>)
×
116
        .help("Path to PEM-encoded key to be used to TLS")
×
117
        .env("EDGE_RUNTIME_TLS_KEY_PATH")
×
118
        .value_parser(value_parser!(PathBuf)),
×
119
    )
×
120
    .arg(
×
121
      arg!(--cert <Path>)
×
122
        .help("Path to PEM-encoded X.509 certificate to be used to TLS")
×
123
        .env("EDGE_RUNTIME_TLS_CERT_PATH")
×
124
        .value_parser(value_parser!(PathBuf)),
×
125
    )
×
126
    .arg(
×
127
      arg!(--"main-service" <DIR>)
×
128
        .help("Path to main service directory or eszip")
×
129
        .default_value("examples/main"),
×
130
    )
×
131
    .arg(
×
132
      arg!(--"disable-module-cache")
×
133
        .help("Disable using module cache")
×
134
        .default_value("false")
×
135
        .value_parser(FalseyValueParser::new()),
×
136
    )
×
137
    .arg(arg!(--"event-worker" <Path>).help("Path to event worker directory"))
×
138
    .arg(
×
139
      arg!(--"main-entrypoint" <Path>)
×
140
        .help("Path to entrypoint in main service (only for eszips)"),
×
141
    )
×
142
    .arg(
×
143
      arg!(--"events-entrypoint" <Path>)
×
144
        .help("Path to entrypoint in events worker (only for eszips)"),
×
145
    )
×
146
    .arg(
×
147
      arg!(--"policy" <POLICY>)
×
148
        .help("Policy to enforce in the worker pool")
×
149
        .default_value("per_worker")
×
150
        .value_parser(["per_worker", "per_request", "oneshot"]),
×
151
    )
×
152
    .arg(
×
153
      arg!(--"graceful-exit-timeout"[SECONDS])
×
154
        .help(concat!(
×
155
          "Maximum time in seconds that can wait for workers before ",
×
156
          "terminating forcibly. If providing zero value, the runtime will ",
×
157
          "not try a graceful exit."
×
158
        ))
×
159
        // NOTE(Nyannyacha): Default timeout value follows the
×
160
        // value[1] defined in moby.
×
161
        //
×
162
        // [1]: https://github.com/moby/moby/blob/master/daemon/config/config.go#L45-L47
×
163
        .default_value("15")
×
164
        .value_parser(value_parser!(u64).range(..u64::MAX)),
×
165
    )
×
166
    .arg(
×
167
      arg!(--"event-worker-exit-timeout"[SECONDS])
×
168
        .help(concat!(
×
169
          "Maximum time in seconds that can wait for the event worker before ",
×
170
          "terminating forcibly. (graceful exit)"
×
171
        ))
×
172
        .default_value("10")
×
173
        .value_parser(value_parser!(u64).range(..u64::MAX)),
×
174
    )
×
175
    .arg(
×
176
      arg!(
×
177
        --"experimental-graceful-exit-keepalive-deadline-ratio"
×
178
        <PERCENTAGE>
179
      )
180
      .help(concat!(
×
181
        "(Experimental) Maximum period of time that incoming requests ",
×
182
        "can be processed over a pre-established keep-alive HTTP connection. ",
×
183
        "This is specified as a percentage of the `--graceful-exit-timeout` ",
×
184
        "value. The percentage cannot be greater than 95."
×
185
      ))
×
186
      .value_parser(value_parser!(u64).range(..=95)),
×
187
    )
×
188
    .arg(
×
189
      arg!(--"max-parallelism" <COUNT>)
×
190
        .help(concat!(
×
191
          "Maximum count of workers that can exist in the worker pool ",
×
192
          "simultaneously"
×
193
        ))
×
194
        .value_parser(
×
195
          // NOTE: Acceptable bounds were chosen arbitrarily.
×
196
          value_parser!(u32)
×
197
            .range(1..9999)
×
198
            .map(|it| -> usize { it as usize }),
×
199
        ),
×
200
    )
×
201
    .arg(
×
202
      arg!(--"request-wait-timeout" <MILLISECONDS>)
×
203
        .help(concat!(
×
204
          "Maximum time in milliseconds that can wait to establish a ",
×
205
          "connection with a worker"
×
206
        ))
×
207
        .default_value("10000")
×
208
        .value_parser(value_parser!(u64)),
×
209
    )
×
210
    .arg(
×
NEW
211
      arg!(--"main-worker-request-idle-timeout" <MILLISECONDS>)
×
NEW
212
        .help(concat!(
×
NEW
213
          "Maximum time in milliseconds that can be waited from when a ",
×
NEW
214
          "worker takes over the request (disabled by default)"
×
NEW
215
        ))
×
NEW
216
        .value_parser(value_parser!(u64)),
×
NEW
217
    )
×
NEW
218
    .arg(
×
NEW
219
      arg!(--"user-worker-request-idle-timeout" <MILLISECONDS>)
×
220
        .help(concat!(
×
221
          "Maximum time in milliseconds that can be waited from when a ",
×
222
          "worker takes over the request (disabled by default)"
×
223
        ))
×
224
        .value_parser(value_parser!(u64)),
×
225
    )
×
226
    .arg(
×
227
      arg!(--"request-read-timeout" <MILLISECONDS>)
×
228
        .help(concat!(
×
229
          "Maximum time in milliseconds that can be waited from when the ",
×
230
          "connection is accepted until the request body is fully read ",
×
231
          "(disabled by default)"
×
232
        ))
×
233
        .value_parser(value_parser!(u64)),
×
234
    )
×
235
    .arg(
×
236
      arg!(--"inspect"[HOST_AND_PORT])
×
237
        .help("Activate inspector on host:port")
×
238
        .num_args(0..=1)
×
239
        .value_parser(value_parser!(SocketAddr))
×
240
        .require_equals(true)
×
241
        .default_missing_value("127.0.0.1:9229"),
×
242
    )
×
243
    .arg(
×
244
      arg!(--"inspect-brk"[HOST_AND_PORT])
×
245
        .help(concat!(
×
246
          "Activate inspector on host:port, wait for debugger to connect ",
×
247
          "and break at the start of user script"
×
248
        ))
×
249
        .num_args(0..=1)
×
250
        .value_parser(value_parser!(SocketAddr))
×
251
        .require_equals(true)
×
252
        .default_missing_value("127.0.0.1:9229"),
×
253
    )
×
254
    .arg(
×
255
      arg!(--"inspect-wait"[HOST_AND_PORT])
×
256
        .help(concat!(
×
257
          "Activate inspector on host:port and wait for debugger to ",
×
258
          "connect before running user code"
×
259
        ))
×
260
        .num_args(0..=1)
×
261
        .value_parser(value_parser!(SocketAddr))
×
262
        .require_equals(true)
×
263
        .default_missing_value("127.0.0.1:9229"),
×
264
    )
×
265
    .group(ArgGroup::new("inspector").args([
×
266
      "inspect",
×
267
      "inspect-brk",
×
268
      "inspect-wait",
×
269
    ]))
×
270
    .arg(
×
271
      arg!(--"inspect-main")
×
272
        .help("Allow creating inspector for main worker")
×
273
        .requires("inspector")
×
274
        .action(ArgAction::SetTrue),
×
275
    )
×
276
    .arg(
×
277
      arg!(--"static" <Path>)
×
278
        .help("Glob pattern for static files to be included")
×
279
        .action(ArgAction::Append),
×
280
    )
×
281
    .arg(
×
282
      arg!(--"tcp-nodelay"[BOOL])
×
283
        .help("Disables Nagle's algorithm")
×
284
        .num_args(0..=1)
×
285
        .value_parser(BoolishValueParser::new())
×
286
        .require_equals(true)
×
287
        .default_value("true")
×
288
        .default_missing_value("true"),
×
289
    )
×
290
    .arg(
×
291
      arg!(--"request-buffer-size" <BYTES>)
×
292
        .help(concat!(
×
293
          "The buffer size of the stream that is used to forward a request ",
×
294
          "to the worker"
×
295
        ))
×
296
        .value_parser(value_parser!(u64))
×
297
        .default_value("16384"),
×
298
    )
×
299
    .arg(
×
300
      arg!(--"dispatch-beforeunload-wall-clock-ratio" <PERCENTAGE>)
×
301
        .value_parser(value_parser!(u8).range(..=99))
×
302
        .default_value("90"),
×
303
    )
×
304
    .arg(
×
305
      arg!(--"dispatch-beforeunload-cpu-ratio" <PERCENTAGE>)
×
306
        .value_parser(value_parser!(u8).range(..=99))
×
307
        .default_value("90"),
×
308
    )
×
309
    .arg(
×
310
      arg!(--"dispatch-beforeunload-memory-ratio" <PERCENTAGE>)
×
311
        .value_parser(value_parser!(u8).range(..=99))
×
312
        .default_value("90"),
×
313
    )
×
314
    .arg(
×
315
      arg!(--"enable-otel")
×
316
        .help("Enable OpenTelemetry in the main and event workers")
×
317
        .value_delimiter(',')
×
318
        .value_parser(value_parser!(OtelKind))
×
319
        .num_args(0..=1)
×
320
        .default_missing_value("main,event")
×
321
        .action(ArgAction::Append),
×
322
    )
×
323
    .arg(
×
324
      arg!(--"otel-console" <MODE>)
×
325
        // .env("OTEL_DENO_CONSOLE")
326
        .help("Configure console auto instrumentation for OpenTelemetry Logs")
×
327
        .value_parser(value_parser!(OtelConsoleConfig)),
×
328
    )
×
329
}
×
330

331
fn get_bundle_command() -> Command {
×
332
  Command::new("bundle")
×
333
    .about(concat!(
×
334
      "Creates an 'eszip' file that can be executed by the EdgeRuntime. ",
×
335
      "Such file contains all the modules in contained in a single binary."
×
336
    ))
×
337
    .arg(
×
338
      arg!(--"output" <DIR>)
×
339
        .help("Path to output eszip file")
×
340
        .default_value("bin.eszip"),
×
341
    )
×
342
    .arg(
×
343
      arg!(--"entrypoint" <Path>)
×
344
        .help("Path to entrypoint to bundle as an eszip")
×
345
        .required(true),
×
346
    )
×
347
    .arg(
×
348
      arg!(--"static" <Path>)
×
349
        .help("Glob pattern for static files to be included")
×
350
        .action(ArgAction::Append),
×
351
    )
×
352
    .arg(
×
353
      arg!(--"import-map" <Path>).help("(DEPRECATED) Path to import map file"),
×
354
    )
×
355
    .arg(
×
356
      arg!(--"decorator" <TYPE>)
×
357
        .help(concat!(
×
358
          "(DEPRECATED) Type of decorator to use on the main worker and event worker. ",
×
359
          "If not specified, the decorator feature is disabled."
×
360
        ))
×
361
        .value_parser(["tc39", "typescript", "typescript_with_metadata"]),
×
362
    )
×
363
    .arg(
×
364
      arg!(--"checksum" <KIND>)
×
365
        .env("EDGE_RUNTIME_BUNDLE_CHECKSUM")
×
366
        .help("Hash function to use when checksum the contents")
×
367
        .value_parser(value_parser!(EszipV2ChecksumKind)),
×
368
    )
×
369
    .arg(
×
370
      arg!(--"disable-module-cache")
×
371
        .help("Disable using module cache")
×
372
        .default_value("false")
×
373
        .value_parser(FalseyValueParser::new()),
×
374
    )
×
375
    .arg(
×
376
      arg!(--"timeout" <SECONDS>)
×
377
        .help("Maximum time in seconds that can be waited for the bundle to complete.")
×
378
        .value_parser(value_parser!(u64).range(..u64::MAX))
×
379
    )
×
380
}
×
381

382
fn get_unbundle_command() -> Command {
×
383
  Command::new("unbundle")
×
384
    .about("Unbundles an .eszip file into the specified directory")
×
385
    .arg(
×
386
      arg!(--"output" <DIR>)
×
387
        .help("Path to extract the ESZIP content")
×
388
        .default_value("./"),
×
389
    )
×
390
    .arg(
×
391
      arg!(--"eszip" <Path>)
×
392
        .help("Path of eszip to extract")
×
393
        .required(true),
×
394
    )
×
395
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc