1tube 0.1.41 → 0.1.43

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
@@ -41,7 +41,12 @@ in-process on filesystem changes (including newly created function folders).
41
41
  ## npm CLI
42
42
 
43
43
  The published npm package exposes a `1tube` binary. It is a tiny Node shim that
44
- launches this repo's Deno CLI, so machines using it need Deno on `PATH`.
44
+ launches this repo's Deno CLI, so machines using it need Deno on `PATH` (set
45
+ `DENO_BIN` to point at a specific `deno` if it isn't). **Prefer this launcher
46
+ over a hand-rolled `deno run … src/server.ts` line** — it applies 1tube's
47
+ recommended defaults (see [minimum dependency age](#minimum-dependency-age)
48
+ below) before Deno starts, which a flag added _inside_ the gateway can no longer
49
+ do.
45
50
 
46
51
  ```bash
47
52
  # Show CLI help
@@ -57,6 +62,44 @@ npx 1tube package --functions supabase/functions --in dist --out fw.1tube --sign
57
62
  This replaces local-source invocations such as
58
63
  `deno run -A ../1tube/src/cli.ts ...` in downstream CI pipelines.
59
64
 
65
+ ### Passing deno flags through the launcher
66
+
67
+ Arguments before a standalone `--` are 1tube CLI args; everything **after** `--`
68
+ is forwarded verbatim to `deno run` (inserted before the entrypoint). This lets
69
+ the launcher fully replace a project's bespoke `deno run … src/server.ts`
70
+ command — including project-level deno flags like `--config`, `--env-file`,
71
+ `--node-modules-dir` and `--no-lock`:
72
+
73
+ ```bash
74
+ npx 1tube serve --functions ./supabase/functions --dev --hmr \
75
+ -- --no-lock --env-file=.env --config ./supabase/functions/deno.json --node-modules-dir=false
76
+ ```
77
+
78
+ - If your passthrough includes its own `--config`, the launcher does **not** add
79
+ 1tube's bundled `deno.json` — your import map wins (the gateway's own sources
80
+ use fully-qualified `npm:`/`jsr:` specifiers, so they resolve regardless).
81
+ - Prefer keeping the command line clean? Set the same flags in
82
+ `1TUBE_DENO_ARGS` (space-separated) instead of after `--`. CLI passthrough
83
+ takes precedence on any duplicate flag.
84
+
85
+ ### Minimum dependency age
86
+
87
+ Deno 2.9 turns on a **24-hour minimum dependency age** by default: a function
88
+ that pins a just-published npm version fails to resolve locally with
89
+ _"… is newer than the specified minimum dependency date"_. Because Deno
90
+ snapshots npm config at process startup, this can only be changed by the
91
+ launcher, not from inside the running gateway.
92
+
93
+ The `1tube` launcher (and the .NET host) therefore export
94
+ `NPM_CONFIG_MIN_RELEASE_AGE=0` for the Deno child **unless you've already set
95
+ it**. That env var is the lowest explicit tier in Deno's precedence chain
96
+ (`--minimum-dependency-age` > `deno.json` `minimumDependencyAge` > `.npmrc`
97
+ `min-release-age` > this env var > the 24h built-in default), so it cancels
98
+ _only_ the new built-in default — any age you configure through `.npmrc`,
99
+ `deno.json`, or the CLI flag still wins. To re-enable a guard, set
100
+ `NPM_CONFIG_MIN_RELEASE_AGE` (e.g. `24h`) yourself or use any higher-precedence
101
+ mechanism.
102
+
60
103
  ### Deno npm lifecycle scripts
61
104
 
62
105
  Deno does not run npm `postinstall` / build scripts unless you allow them.
@@ -67,12 +110,12 @@ dependency install — **1tube’s runtime graph is just Hono + JSR std**.
67
110
 
68
111
  ## Endpoints
69
112
 
70
- | Path | Description |
71
- | -------------------------- | ------------------------------------------------------------------------------------------------------------------- |
72
- | `GET /` | Liveness probe (`{"status":"ok"}`) — intentionally minimal so unauthenticated callers don't see the function map. |
73
- | `POST /functions/v1/:name` | Invoke an edge function |
74
- | `GET /health` | Auth-gated health (`Authorization: Bearer $INTERNAL_KEY`); without auth returns the same minimal `{"status":"ok"}`. |
75
- | `GET /metrics` | Auth-gated Prometheus exposition (same scheme). |
113
+ | Path | Description |
114
+ | -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
115
+ | `GET /` | Liveness probe (`{"status":"ok"}`) — intentionally minimal so unauthenticated callers don't see the function map. |
116
+ | `POST /functions/v1/:name` | Invoke an edge function |
117
+ | `GET /health` | Auth-gated health (`Authorization: Bearer $INTERNAL_KEY`); without auth returns the same minimal `{"status":"ok"}`. |
118
+ | `GET /metrics` | Auth-gated Prometheus exposition (same scheme). |
76
119
  | `GET /1tube/warmup` | Boot progress (deferred boot): `{ready, total, loaded, loading, queued, failed}`. CORS-enabled so frontends can poll it to drive a warm-up overlay. |
77
120
 
78
121
  ## Configuration
@@ -94,6 +137,12 @@ All knobs default to safe-but-backwards-compatible values. The TS gateway
94
137
  | `1TUBE_BODY_LIMIT_MB` | `30` | Hono `bodyLimit`; matches Supabase. Returns 413 before the handler runs. |
95
138
  | `1TUBE_BODY_READ_MS` | `30000` | Slow-loris guard. Max idle gap (ms) between body chunks before the request is aborted with **408**. NOT a total body-read deadline — large but fast uploads pass through. Set `0` to disable. |
96
139
  | `1TUBE_CORS_ORIGIN` | `*` (dev only) | Comma-separated allowlist or `*`. In prod, leaving unset disables CORS. |
140
+ | `1TUBE_CORS_ALLOW_HEADERS` | Supabase defaults | Comma-separated `Access-Control-Allow-Headers`. Replaces the built-in default list (`authorization, x-client-info, apikey, content-type`, …) when set. |
141
+ | `1TUBE_CORS_ALLOW_METHODS` | Supabase defaults | Comma-separated `Access-Control-Allow-Methods` (default `GET, POST, PUT, PATCH, DELETE, OPTIONS`). |
142
+ | `1TUBE_CORS_EXPOSE_HEADERS` | 1tube internals | Comma-separated `Access-Control-Expose-Headers`. **Merged with** (not replacing) the headers 1tube must always expose (e.g. `X-1tube-Warming`). |
143
+ | `1TUBE_CORS_MAX_AGE` | unset | Seconds for `Access-Control-Max-Age` (preflight cache). Omitted when unset. |
144
+ | `1TUBE_CORS_ALLOW_CREDENTIALS` | off | `1`/`true` emits `Access-Control-Allow-Credentials: true`. Per the Fetch spec this is incompatible with `Origin: *`, so when credentials are on, a literal `*` allowlist is rejected — set an explicit origin. |
145
+ | `--route-prefix` / `1TUBE_ROUTE_PREFIX` | `/functions/v1` | URL prefix functions are mounted under. Change it to e.g. `/api` to serve at `/api/<name>`. Normalized to a leading-slash, no-trailing-slash form; logging and rate-limiting follow it automatically. |
97
146
  | `1TUBE_TRUSTED_PROXIES` | empty | Comma-separated list of remote IPs whose `X-Forwarded-For` is honored. Anything else uses the raw socket address — XFF spoofing no longer mints fresh rate-limit buckets. |
98
147
  | `1TUBE_SHUTDOWN_GRACE_MS` | `10000` | SIGINT/SIGTERM drain budget. |
99
148
  | `INTERNAL_KEY` | unset | Required to read detailed `/health` and `/metrics`. Header-only: `Authorization: Bearer $INTERNAL_KEY`. |
@@ -105,6 +154,7 @@ All knobs default to safe-but-backwards-compatible values. The TS gateway
105
154
  | `1TUBE_SHUTDOWN_GRACE_MS` | `10000` | Total wall-clock budget (ms) for shutdown, split between (1) draining gateway in-flight requests and (2) tearing down the workerd subprocess. See [Graceful shutdown](#graceful-shutdown) below. |
106
155
  | `1TUBE_DEFAULT_RPM` | `120` | Override the gateway-wide default rate limit (requests per minute, per IP, per function). Per-function `1tube.json#rpm` still wins. |
107
156
  | `1TUBE_DISABLE_RATE_LIMIT` | unset | Set to `1` to bypass rate limiting entirely. **Load-test / dev only** — production deployments must keep the limiter on. The gateway prints a clear warning at boot when this is enabled. |
157
+ | `NPM_CONFIG_MIN_RELEASE_AGE` | `0` (set by launcher) | Deno 2.9's minimum dependency age. The `1tube` launcher and .NET host export `0` for the Deno child unless you set it, cancelling only Deno's new 24h default. See [Minimum dependency age](#minimum-dependency-age). Must be set _before_ Deno starts — a gateway flag can't change it. |
108
158
 
109
159
  ````
110
160
  ## Deferred boot & warm-up overlay
package/deno.lock CHANGED
@@ -20,8 +20,8 @@
20
20
  "npm:es-module-lexer@2": "2.1.0",
21
21
  "npm:esbuild@~0.25.5": "0.25.12",
22
22
  "npm:esbuild@~0.28.1": "0.28.1",
23
- "npm:hono@4": "4.12.25",
24
- "npm:hono@^4.12.25": "4.12.25",
23
+ "npm:hono@4": "4.12.27",
24
+ "npm:hono@^4.12.27": "4.12.27",
25
25
  "npm:typescript@latest": "6.0.3",
26
26
  "npm:zod@^3.23.0": "3.25.76"
27
27
  },
@@ -515,8 +515,8 @@
515
515
  "resolve-pkg-maps"
516
516
  ]
517
517
  },
518
- "hono@4.12.25": {
519
- "integrity": "sha512-2NFaIyNVgJmBs/ecmtGzlmluTFs5cHEWGTdu0t1HBwYzoGXOL5nUQBRMXsXWla5i4KkG//QMzVP88m1+I3fdAQ=="
518
+ "hono@4.12.27": {
519
+ "integrity": "sha512-1yrb/+w6HWQJrUCLkJ2IF5jNIPvvFkblV5RNOYl6bV+OA6p9GLcMpHFFGTosSvHvcAUibuUukRqhlYI4z32C7Q=="
520
520
  },
521
521
  "jiti@2.6.1": {
522
522
  "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==",
@@ -555,7 +555,7 @@
555
555
  "dependencies": [
556
556
  "npm:drizzle-kit@1.0.0-rc.3",
557
557
  "npm:drizzle-orm@1.0.0-rc.3",
558
- "npm:hono@^4.12.25"
558
+ "npm:hono@^4.12.27"
559
559
  ]
560
560
  }
561
561
  }
@@ -302,4 +302,4 @@
302
302
  }
303
303
  ],
304
304
  "renames": []
305
- }
305
+ }
@@ -302,4 +302,4 @@
302
302
  }
303
303
  ],
304
304
  "renames": []
305
- }
305
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "1tube",
3
- "version": "0.1.41",
3
+ "version": "0.1.43",
4
4
  "private": false,
5
5
  "description": "Self-hosted Supabase Edge Functions gateway for Deno, plus an ESLint plugin for consumers",
6
6
  "license": "MIT",
@@ -62,7 +62,7 @@
62
62
  },
63
63
  "dependencies": {
64
64
  "drizzle-orm": "1.0.0-rc.3",
65
- "hono": "^4.12.25"
65
+ "hono": "^4.12.27"
66
66
  },
67
67
  "devDependencies": {
68
68
  "drizzle-kit": "1.0.0-rc.3"
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import { spawn } from "node:child_process";
4
- import { dirname, resolve } from "node:path";
4
+ import { existsSync } from "node:fs";
5
+ import { delimiter, dirname, isAbsolute, join, resolve } from "node:path";
5
6
  import process from "node:process";
6
7
  import { fileURLToPath } from "node:url";
7
8
 
@@ -9,26 +10,112 @@ const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
9
10
  const denoConfig = resolve(packageRoot, "deno.json");
10
11
  const cliEntrypoint = resolve(packageRoot, "src", "cli.ts");
11
12
  const denoBin = process.env.DENO_BIN || "deno";
12
- const cliArgs = process.argv.slice(2);
13
- const forwardedArgs = cliArgs.length === 0 ? ["--help"] : cliArgs;
14
-
15
- const child = spawn(
16
- denoBin,
17
- [
18
- "run",
19
- "--quiet",
20
- "-A",
21
- "--config",
22
- denoConfig,
23
- cliEntrypoint,
24
- ...forwardedArgs,
25
- ],
26
- {
13
+
14
+ // Arg model
15
+ // ---------
16
+ // Everything BEFORE a standalone `--` is a 1tube CLI argument (forwarded
17
+ // after the entrypoint). Everything AFTER `--` is a raw `deno run` flag,
18
+ // inserted BEFORE the entrypoint — so this launcher can fully replace a
19
+ // hand-rolled `deno run … src/server.ts` line:
20
+ //
21
+ // 1tube serve --functions ./supabase/functions --dev --hmr \
22
+ // -- --no-lock --env-file=.env --config ./supabase/functions/deno.json --node-modules-dir=false
23
+ //
24
+ // Projects that would rather not thread flags through the command line
25
+ // can set `1TUBE_DENO_ARGS` instead (same flags, space-separated).
26
+ const rawArgs = process.argv.slice(2);
27
+ const sepIndex = rawArgs.indexOf("--");
28
+ const tubeArgs = sepIndex === -1 ? rawArgs : rawArgs.slice(0, sepIndex);
29
+ const cliDenoArgs = sepIndex === -1 ? [] : rawArgs.slice(sepIndex + 1);
30
+
31
+ // CLI passthrough is appended last so it wins on any duplicate flag
32
+ // (Deno applies last-one-wins for repeated flags).
33
+ const denoPassthrough = [
34
+ ...tokenizeArgs(
35
+ process.env.ONETUBE_DENO_ARGS ?? process.env["1TUBE_DENO_ARGS"] ?? "",
36
+ ),
37
+ ...cliDenoArgs,
38
+ ];
39
+
40
+ // Default to --help only when the user gave us nothing at all; if they
41
+ // supplied deno flags but no 1tube subcommand, let cli.ts fall through to
42
+ // its default `serve` path.
43
+ const forwardedTubeArgs = tubeArgs.length === 0 && denoPassthrough.length === 0
44
+ ? ["--help"]
45
+ : tubeArgs;
46
+
47
+ // 1tube ships its own deno.json (import map for the gateway's jsr:/npm:
48
+ // dependencies). Only inject it when the project didn't supply its own
49
+ // --config — a Supabase project points --config at
50
+ // supabase/functions/deno.json so its function workers resolve the
51
+ // project's bare specifiers. The gateway's own sources use fully
52
+ // qualified npm:/jsr: specifiers, so they resolve without our config.
53
+ const userSuppliedConfig = denoPassthrough.some((a) =>
54
+ a === "--config" || a === "-c" ||
55
+ a.startsWith("--config=") || a.startsWith("-c=")
56
+ );
57
+
58
+ const denoArgs = ["run", "--quiet", "-A"];
59
+ if (!userSuppliedConfig) denoArgs.push("--config", denoConfig);
60
+ denoArgs.push(...denoPassthrough, cliEntrypoint, ...forwardedTubeArgs);
61
+
62
+ // Deno 2.9 enables a 24h "minimum dependency age" by default, which makes
63
+ // a function that pins a freshly published npm version fail to resolve in
64
+ // local dev ("…newer than the specified minimum dependency date"). This
65
+ // setting cannot be changed from inside the process (Deno snapshots npm
66
+ // config at startup), so the launcher is the place to neutralize it.
67
+ //
68
+ // NPM_CONFIG_MIN_RELEASE_AGE is the LOWEST explicit tier in Deno's
69
+ // precedence chain (CLI flag > deno.json > .npmrc > this env var > the
70
+ // 24h built-in default). Setting it to 0 cancels ONLY the built-in
71
+ // default — any .npmrc `min-release-age`, deno.json `minimumDependencyAge`,
72
+ // or `--minimum-dependency-age` the project sets still wins. We never
73
+ // override a value the user already exported.
74
+ const childEnv = { ...process.env };
75
+ if (
76
+ childEnv.NPM_CONFIG_MIN_RELEASE_AGE === undefined &&
77
+ childEnv.npm_config_min_release_age === undefined
78
+ ) {
79
+ childEnv.NPM_CONFIG_MIN_RELEASE_AGE = "0";
80
+ }
81
+
82
+ // Resolve the real deno binary up front. Node ≥18.20 refuses to spawn a
83
+ // `.cmd`/`.bat` shim without a shell, but spawning a real `.exe` (or a
84
+ // bare binary on POSIX) with `shell:false` lets Node escape arguments
85
+ // properly — important now that we forward arbitrary deno flags, some of
86
+ // which carry paths with spaces (`--config "C:\My Project\deno.json"`).
87
+ // We only fall back to a shell when we must (a `.cmd`/`.bat` shim, or we
88
+ // couldn't locate the binary on a Windows PATH at all).
89
+ const resolvedDeno = resolveExecutable(denoBin);
90
+ const needsShell = resolvedDeno
91
+ ? /\.(cmd|bat)$/i.test(resolvedDeno)
92
+ : process.platform === "win32";
93
+ const spawnBin = resolvedDeno ?? denoBin;
94
+
95
+ let child;
96
+ if (needsShell) {
97
+ // A `.cmd`/`.bat` shim (the npm-installed deno) — or an unresolved name
98
+ // on Windows — must go through a shell. We pass a single, manually
99
+ // quoted command string instead of an args array so that (a) paths
100
+ // with spaces survive and (b) we sidestep Node's DEP0190 "args are not
101
+ // escaped" deprecation, which fires only for the shell-plus-array form.
102
+ const command = [spawnBin, ...denoArgs].map(quoteForCmd).join(" ");
103
+ child = spawn(command, {
27
104
  stdio: "inherit",
28
- shell: process.platform === "win32",
105
+ shell: true,
29
106
  windowsHide: false,
30
- },
31
- );
107
+ env: childEnv,
108
+ });
109
+ } else {
110
+ // Real executable (deno.exe / a POSIX binary): no shell, so Node
111
+ // escapes each argument for us.
112
+ child = spawn(spawnBin, denoArgs, {
113
+ stdio: "inherit",
114
+ shell: false,
115
+ windowsHide: false,
116
+ env: childEnv,
117
+ });
118
+ }
32
119
 
33
120
  for (const signal of ["SIGINT", "SIGTERM"]) {
34
121
  process.on(signal, () => {
@@ -54,3 +141,62 @@ child.on("exit", (code, signal) => {
54
141
  }
55
142
  process.exit(code ?? 1);
56
143
  });
144
+
145
+ /**
146
+ * Split a flag string into argv tokens, honoring single/double quotes so
147
+ * values with spaces (`--env-file="my env.env"`) survive. Good enough for
148
+ * a dev-time convenience var — not a full shell parser.
149
+ *
150
+ * @param {string} s
151
+ * @returns {string[]}
152
+ */
153
+ function tokenizeArgs(s) {
154
+ const out = [];
155
+ const re = /"([^"]*)"|'([^']*)'|(\S+)/g;
156
+ let m;
157
+ while ((m = re.exec(s)) !== null) {
158
+ out.push(m[1] ?? m[2] ?? m[3]);
159
+ }
160
+ return out;
161
+ }
162
+
163
+ /**
164
+ * Locate an executable on PATH (honoring PATHEXT on Windows). Returns an
165
+ * absolute path, or null when the name can't be resolved — in which case
166
+ * the caller hands the bare name to spawn and lets the OS/shell try.
167
+ *
168
+ * @param {string} bin
169
+ * @returns {string | null}
170
+ */
171
+ function resolveExecutable(bin) {
172
+ // An explicit path (absolute or containing a separator) is used as-is.
173
+ if (isAbsolute(bin) || bin.includes("/") || bin.includes("\\")) {
174
+ return existsSync(bin) ? bin : null;
175
+ }
176
+ const isWin = process.platform === "win32";
177
+ const exts = isWin
178
+ ? (process.env.PATHEXT?.split(";").map((e) => e.trim()).filter(Boolean) ??
179
+ [".EXE", ".CMD", ".BAT"])
180
+ : [""];
181
+ for (const dir of (process.env.PATH ?? "").split(delimiter).filter(Boolean)) {
182
+ for (const ext of exts) {
183
+ const candidate = join(dir, bin + ext);
184
+ if (existsSync(candidate)) return candidate;
185
+ }
186
+ }
187
+ return null;
188
+ }
189
+
190
+ /**
191
+ * Quote a single argument for cmd.exe. Leaves simple tokens untouched to
192
+ * avoid over-quoting; wraps anything with whitespace or shell
193
+ * metacharacters in double quotes, doubling any embedded quotes.
194
+ *
195
+ * @param {string} arg
196
+ * @returns {string}
197
+ */
198
+ function quoteForCmd(arg) {
199
+ if (arg === "") return '""';
200
+ if (!/[\s"&|<>^()%!]/.test(arg)) return arg;
201
+ return '"' + arg.replace(/"/g, '""') + '"';
202
+ }
@@ -59,7 +59,11 @@ export interface DenoSharedRuntime {
59
59
  * Invoke a function exported by a shared module. Throws if the
60
60
  * module or export is unknown, or if the export itself throws.
61
61
  */
62
- call(moduleId: string, exportName: string, args: readonly unknown[]): Promise<unknown>;
62
+ call(
63
+ moduleId: string,
64
+ exportName: string,
65
+ args: readonly unknown[],
66
+ ): Promise<unknown>;
63
67
  /**
64
68
  * Re-import a shared module after its source file changed. Returns
65
69
  * the new record. If the export list changed, callers should
@@ -62,10 +62,16 @@ interface SharedCallResultMessage {
62
62
 
63
63
  type HostMessage = InitMessage | DispatchMessage | SharedCallResultMessage;
64
64
 
65
- type Handler = (req: Request, auth?: AuthContext) => Response | Promise<Response>;
65
+ type Handler = (
66
+ req: Request,
67
+ auth?: AuthContext,
68
+ ) => Response | Promise<Response>;
66
69
 
67
70
  interface RegistryStub {
68
- register(handler: Handler, opts: { public: boolean; timeoutMs?: number }): void;
71
+ register(
72
+ handler: Handler,
73
+ opts: { public: boolean; timeoutMs?: number },
74
+ ): void;
69
75
  }
70
76
 
71
77
  interface CapturedHandler {
@@ -93,13 +99,23 @@ let initDone = false;
93
99
  const invocationContext = new AsyncLocalStorage<{ invocationId?: string }>();
94
100
 
95
101
  type ConsoleLevel = "debug" | "log" | "info" | "warn" | "error";
96
- const CONSOLE_LEVELS: ConsoleLevel[] = ["debug", "log", "info", "warn", "error"];
102
+ const CONSOLE_LEVELS: ConsoleLevel[] = [
103
+ "debug",
104
+ "log",
105
+ "info",
106
+ "warn",
107
+ "error",
108
+ ];
97
109
  const MAX_CONSOLE_MESSAGE = 8 * 1024;
98
110
 
99
111
  function formatConsoleArg(arg: unknown): string {
100
112
  if (typeof arg === "string") return arg;
101
113
  try {
102
- return Deno.inspect(arg, { depth: 4, colors: false, strAbbreviateSize: 4096 });
114
+ return Deno.inspect(arg, {
115
+ depth: 4,
116
+ colors: false,
117
+ strAbbreviateSize: 4096,
118
+ });
103
119
  } catch {
104
120
  try {
105
121
  return String(arg);
@@ -178,7 +194,9 @@ const stub: RegistryStub = {
178
194
  // The function called serve() twice. Last write wins, matching the
179
195
  // in-process registry's behaviour. Surface a warning so authors notice
180
196
  // the bug.
181
- console.warn(`[1tube] "${functionName}" called serve() twice; using the last handler`);
197
+ console.warn(
198
+ `[1tube] "${functionName}" called serve() twice; using the last handler`,
199
+ );
182
200
  }
183
201
  captured = {
184
202
  handler,
@@ -188,8 +206,8 @@ const stub: RegistryStub = {
188
206
  },
189
207
  };
190
208
 
191
- (globalThis as { __edgeFunctionRegistry?: RegistryStub }).__edgeFunctionRegistry =
192
- stub;
209
+ (globalThis as { __edgeFunctionRegistry?: RegistryStub })
210
+ .__edgeFunctionRegistry = stub;
193
211
 
194
212
  self.addEventListener("unhandledrejection", (e) => {
195
213
  e.preventDefault();
@@ -221,7 +239,8 @@ async function runDispatch(msg: DispatchMessage): Promise<void> {
221
239
  postMessage({
222
240
  type: "response_error",
223
241
  id: msg.id,
224
- message: `Function "${functionName}" did not call serve() before dispatch`,
242
+ message:
243
+ `Function "${functionName}" did not call serve() before dispatch`,
225
244
  });
226
245
  return;
227
246
  }