1tube 0.1.31 → 0.1.33
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 +97 -0
- package/deno.json +2 -0
- package/deno.lock +152 -6
- package/dist/edge-caller.d.ts +30 -0
- package/dist/edge-caller.js +77 -0
- package/dist/edge.d.ts +176 -0
- package/dist/edge.js +435 -0
- package/dist/warmup-client.d.ts +94 -0
- package/dist/warmup-client.js +100 -0
- package/drizzle/20260610161429_init_logs/migration.sql +30 -0
- package/drizzle/20260610161429_init_logs/snapshot.json +305 -0
- package/drizzle/20260610161430_logs_fts/migration.sql +25 -0
- package/drizzle/20260610161430_logs_fts/snapshot.json +305 -0
- package/package.json +27 -6
- package/src/backends/deno/hot-reloader.ts +82 -1
- package/src/backends/deno/worker-entry.ts +78 -5
- package/src/backends/deno/worker-host.ts +501 -16
- package/src/backends/workerd/bundler.ts +69 -1
- package/src/gateway/cors.ts +7 -0
- package/src/gateway/logging.ts +81 -0
- package/src/logs/api.ts +141 -0
- package/src/logs/console-marker.ts +54 -0
- package/src/logs/db.ts +83 -0
- package/src/logs/id.ts +33 -0
- package/src/logs/query.ts +372 -0
- package/src/logs/schema.ts +95 -0
- package/src/logs/writer.ts +388 -0
- package/src/registry.ts +7 -0
- package/src/server.ts +489 -22
- package/src/warmup-client.ts +159 -0
package/README.md
CHANGED
|
@@ -73,6 +73,7 @@ dependency install — **1tube’s runtime graph is just Hono + JSR std**.
|
|
|
73
73
|
| `POST /functions/v1/:name` | Invoke an edge function |
|
|
74
74
|
| `GET /health` | Auth-gated health (`Authorization: Bearer $INTERNAL_KEY`); without auth returns the same minimal `{"status":"ok"}`. |
|
|
75
75
|
| `GET /metrics` | Auth-gated Prometheus exposition (same scheme). |
|
|
76
|
+
| `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. |
|
|
76
77
|
|
|
77
78
|
## Configuration
|
|
78
79
|
|
|
@@ -87,6 +88,9 @@ All knobs default to safe-but-backwards-compatible values. The TS gateway
|
|
|
87
88
|
| `--timeout` / `FUNCTION_TIMEOUT_MS` | `150000` | Per-request wall-clock cap (also overridable per function) |
|
|
88
89
|
| `--dev` / `1TUBE_DEV` | off | Applies the well-known local Supabase JWT/secrets. **Refuses to start in prod when JWT_SECRET / SUPABASE_SERVICE_ROLE_KEY are missing or are the public dev defaults.** |
|
|
89
90
|
| `--hmr` / `1TUBE_HMR` | off | File-watch + per-function reload (dev only). |
|
|
91
|
+
| `--defer-boot` / `1TUBE_DEFER_BOOT` | on in dev/HMR | Deferred boot (Deno backend only): the gateway accepts requests immediately while function Workers spawn in the background. Requests for cold functions jump the boot queue and get `503` + `X-1tube-Warming: 1` until ready. Disable with `--no-defer-boot` / `1TUBE_DEFER_BOOT=0`. See [Deferred boot & warm-up overlay](#deferred-boot--warm-up-overlay). |
|
|
92
|
+
| `--warmup-grace-ms` / `1TUBE_WARMUP_GRACE_MS` | `250` | How long the dispatcher waits for a warming function before answering with the warming 503. Keeps fast-loading functions from flashing the frontend overlay. |
|
|
93
|
+
| `1TUBE_HMR_FRESH_WAIT_MS` | `2500` | How long a request waits for an in-flight HMR respawn of its function before falling back to the previous (stale) worker. Fresh code wins by default (Vite-style "request stalls until the rebuild lands"). Stale fallbacks carry `X-1tube-Stale: 1`. `0` = never wait. |
|
|
90
94
|
| `1TUBE_BODY_LIMIT_MB` | `30` | Hono `bodyLimit`; matches Supabase. Returns 413 before the handler runs. |
|
|
91
95
|
| `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. |
|
|
92
96
|
| `1TUBE_CORS_ORIGIN` | `*` (dev only) | Comma-separated allowlist or `*`. In prod, leaving unset disables CORS. |
|
|
@@ -103,6 +107,99 @@ All knobs default to safe-but-backwards-compatible values. The TS gateway
|
|
|
103
107
|
| `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. |
|
|
104
108
|
|
|
105
109
|
````
|
|
110
|
+
## Deferred boot & warm-up overlay
|
|
111
|
+
|
|
112
|
+
On the Deno backend, eager boot pays the full cost of spawning one Worker per
|
|
113
|
+
function (each transpiling + evaluating its whole import graph) **before** the
|
|
114
|
+
gateway accepts a single request. With dozens of functions importing heavy
|
|
115
|
+
deps, that's tens of seconds of staring at a boot log.
|
|
116
|
+
|
|
117
|
+
Deferred boot (default whenever `--dev` or `--hmr` is on) flips the order:
|
|
118
|
+
|
|
119
|
+
1. Functions are **discovered instantly** and registered as known-but-queued —
|
|
120
|
+
the gateway starts serving right away. Time-to-first-request drops from
|
|
121
|
+
"sum of all worker spawns" to effectively zero.
|
|
122
|
+
2. Workers spawn in the background with the usual bounded concurrency
|
|
123
|
+
(`--deno-worker-concurrency`, default 8).
|
|
124
|
+
3. A request for a function whose Worker isn't ready yet **bumps it to the
|
|
125
|
+
front of the queue** and spawns it immediately, out-of-band. If it becomes
|
|
126
|
+
ready within the grace window (`--warmup-grace-ms`, default 250 ms) the
|
|
127
|
+
request is served normally and the client never notices. Otherwise the
|
|
128
|
+
gateway answers with the **warming contract** below, and the rest of the
|
|
129
|
+
queue keeps loading in the background.
|
|
130
|
+
|
|
131
|
+
### The warming contract
|
|
132
|
+
|
|
133
|
+
```http
|
|
134
|
+
HTTP/1.1 503 Service Unavailable
|
|
135
|
+
X-1tube-Warming: 1
|
|
136
|
+
Retry-After: 1
|
|
137
|
+
|
|
138
|
+
{"error":"Backend is warming up","reason":"function_warming","function":"hello",
|
|
139
|
+
"state":"loading","warmup":{"total":53,"ready":12,"loading":8,"queued":33,"failed":0}}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
`X-1tube-Warming: 1` is the discriminator — a plain 503 (circuit breaker,
|
|
143
|
+
overload) never carries it. It's in `Access-Control-Expose-Headers`, so browser
|
|
144
|
+
JS can read it cross-origin. This is what lets a frontend distinguish "the
|
|
145
|
+
backend is still warming up" from real latency or a real outage and show an
|
|
146
|
+
honest overlay instead of a spinner.
|
|
147
|
+
|
|
148
|
+
`GET /1tube/warmup` reports overall progress (also CORS-enabled), e.g. for a
|
|
149
|
+
progress bar inside the overlay.
|
|
150
|
+
|
|
151
|
+
### Frontend helper
|
|
152
|
+
|
|
153
|
+
The npm package ships a tiny client (`1tube/warmup-client`, no dependencies):
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
import { createWarmupFetch } from "1tube/warmup-client";
|
|
157
|
+
|
|
158
|
+
const warmFetch = createWarmupFetch({
|
|
159
|
+
onWarmingChange: (warming) => setShowWarmupOverlay(warming), // drive the overlay
|
|
160
|
+
retryDelayMs: 400,
|
|
161
|
+
maxWaitMs: 120_000,
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// Drop-in fetch replacement: retries through warm-up transparently.
|
|
165
|
+
const resp = await warmFetch(`${GATEWAY}/functions/v1/hello`, { method: "POST" });
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
`isWarmingResponse(resp)` and `fetchWarmupStatus(baseUrl)` are exported for
|
|
169
|
+
hand-rolled integrations.
|
|
170
|
+
|
|
171
|
+
### Smart HMR reloads
|
|
172
|
+
|
|
173
|
+
The same queue machinery powers HMR on the Deno backend, so a save costs as
|
|
174
|
+
little as possible:
|
|
175
|
+
|
|
176
|
+
- **No-op saves are dropped.** Changed files are content-hashed; a re-save
|
|
177
|
+
with identical bytes (format-on-save no-ops, `touch`, agent tooling
|
|
178
|
+
re-writing the same file) never respawns a Worker.
|
|
179
|
+
- **Leading-edge debounce.** The first fs event of a quiet period flushes
|
|
180
|
+
after ~40 ms instead of the full 200 ms trailing window — a single editor
|
|
181
|
+
save starts reloading almost immediately. Stragglers still coalesce.
|
|
182
|
+
- **MRU-first respawns.** When one edit affects more functions than there are
|
|
183
|
+
spawn lanes (shared-module edits), the functions you've hit most recently
|
|
184
|
+
respawn first; idle ones go last.
|
|
185
|
+
- **Request-driven priority + freshness.** A request for a function whose
|
|
186
|
+
respawn is still queued/in-flight bumps it to the front (out-of-band, above
|
|
187
|
+
the concurrency bound) and waits up to `1TUBE_HMR_FRESH_WAIT_MS` (default
|
|
188
|
+
2500 ms) so save → refresh returns the **new** code, like Vite. If the
|
|
189
|
+
respawn takes longer, the previous worker answers and the response is marked
|
|
190
|
+
`X-1tube-Stale: 1` (CORS-exposed) so tooling can tell the difference.
|
|
191
|
+
- **Targeted re-discovery.** Reloading one function stats only that function's
|
|
192
|
+
dir instead of re-scanning the whole functions root.
|
|
193
|
+
|
|
194
|
+
Notes:
|
|
195
|
+
|
|
196
|
+
- Deferred boot is **off by default in prod** (eager boot keeps the "everything
|
|
197
|
+
is ready before traffic" contract); opt in with `--defer-boot`.
|
|
198
|
+
- Functions that fail to boot answer `503 {"reason":"function_boot_failed"}`
|
|
199
|
+
(not a warming response — retrying won't help).
|
|
200
|
+
- Workerd backend is unaffected: it boots all functions as a single bundle
|
|
201
|
+
generation.
|
|
202
|
+
|
|
106
203
|
## Workerd backend
|
|
107
204
|
|
|
108
205
|
1tube can optionally run each function inside a [workerd](https://github.com/cloudflare/workerd) subprocess instead of importing it directly into the gateway. This gives:
|
package/deno.json
CHANGED
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
"@std/path": "jsr:@std/path@^1",
|
|
16
16
|
"@std/assert": "jsr:@std/assert@^1",
|
|
17
17
|
"hono": "npm:hono@4",
|
|
18
|
+
"drizzle-orm": "npm:drizzle-orm@1.0.0-rc.3",
|
|
19
|
+
"drizzle-orm/": "npm:/drizzle-orm@1.0.0-rc.3/",
|
|
18
20
|
"esbuild": "npm:esbuild@^0.28",
|
|
19
21
|
"es-module-lexer": "npm:es-module-lexer@^2",
|
|
20
22
|
"@deno/esbuild-plugin": "jsr:@deno/esbuild-plugin@^1.2",
|
package/deno.lock
CHANGED
|
@@ -11,12 +11,19 @@
|
|
|
11
11
|
"jsr:@std/path@^1.1.1": "1.1.4",
|
|
12
12
|
"jsr:@std/path@^1.1.4": "1.1.4",
|
|
13
13
|
"jsr:@zip-js/zip-js@^2.8.0": "2.8.26",
|
|
14
|
+
"npm:@ai-sdk/anthropic@2": "2.0.80_zod@3.25.76",
|
|
15
|
+
"npm:@ai-sdk/google@2": "2.0.74_zod@3.25.76",
|
|
16
|
+
"npm:@ai-sdk/openai@2": "2.0.106_zod@3.25.76",
|
|
17
|
+
"npm:ai@6": "6.0.193_zod@3.25.76",
|
|
18
|
+
"npm:drizzle-kit@1.0.0-rc.3": "1.0.0-rc.3",
|
|
19
|
+
"npm:drizzle-orm@1.0.0-rc.3": "1.0.0-rc.3_zod@3.25.76",
|
|
14
20
|
"npm:es-module-lexer@2": "2.1.0",
|
|
15
21
|
"npm:esbuild@0.28": "0.28.0",
|
|
16
22
|
"npm:esbuild@~0.25.5": "0.25.12",
|
|
17
|
-
"npm:hono@4": "4.12.
|
|
18
|
-
"npm:hono@^4.12.
|
|
19
|
-
"npm:typescript@latest": "6.0.3"
|
|
23
|
+
"npm:hono@4": "4.12.25",
|
|
24
|
+
"npm:hono@^4.12.25": "4.12.25",
|
|
25
|
+
"npm:typescript@latest": "6.0.3",
|
|
26
|
+
"npm:zod@^3.23.0": "3.25.76"
|
|
20
27
|
},
|
|
21
28
|
"jsr": {
|
|
22
29
|
"@deno/esbuild-plugin@1.2.1": {
|
|
@@ -59,6 +66,72 @@
|
|
|
59
66
|
}
|
|
60
67
|
},
|
|
61
68
|
"npm": {
|
|
69
|
+
"@ai-sdk/anthropic@2.0.80_zod@3.25.76": {
|
|
70
|
+
"integrity": "sha512-7HPgFv91YFFYyZapM/Vy3tRbBq/hL44omh0vz/WikiU7fJFxdOjCeg6Nb089lxM7+qBe7blZsoSRJ7gfAkEo1w==",
|
|
71
|
+
"dependencies": [
|
|
72
|
+
"@ai-sdk/provider@2.0.3",
|
|
73
|
+
"@ai-sdk/provider-utils@3.0.25_zod@3.25.76",
|
|
74
|
+
"zod"
|
|
75
|
+
]
|
|
76
|
+
},
|
|
77
|
+
"@ai-sdk/gateway@3.0.121_zod@3.25.76": {
|
|
78
|
+
"integrity": "sha512-uY248djJRxa5W68MHiyqO8WLdOeKQoRClGg7PVX/VPhVW8SJNM7/l5DcrA5WAM3YfQrLyNkgZa2VOu8T0t8LUw==",
|
|
79
|
+
"dependencies": [
|
|
80
|
+
"@ai-sdk/provider@3.0.10",
|
|
81
|
+
"@ai-sdk/provider-utils@4.0.27_zod@3.25.76",
|
|
82
|
+
"@vercel/oidc",
|
|
83
|
+
"zod"
|
|
84
|
+
]
|
|
85
|
+
},
|
|
86
|
+
"@ai-sdk/google@2.0.74_zod@3.25.76": {
|
|
87
|
+
"integrity": "sha512-Lhw1742RXc+4pRIvqVXa0jdl5+qdpmw8lj0lm6OchUg9rVGHzymlaxe7CDiYX5U2af4jbjKeTY22LDi3bIycgQ==",
|
|
88
|
+
"dependencies": [
|
|
89
|
+
"@ai-sdk/provider@2.0.3",
|
|
90
|
+
"@ai-sdk/provider-utils@3.0.25_zod@3.25.76",
|
|
91
|
+
"zod"
|
|
92
|
+
]
|
|
93
|
+
},
|
|
94
|
+
"@ai-sdk/openai@2.0.106_zod@3.25.76": {
|
|
95
|
+
"integrity": "sha512-EFC0rpo1wfe4HIz5KZCE72edP2J7fOeR7wPXzjCDljaTRB1wectKDIKRLowpU4F0mbcJ+XScAsoYNPK/Z20aVQ==",
|
|
96
|
+
"dependencies": [
|
|
97
|
+
"@ai-sdk/provider@2.0.3",
|
|
98
|
+
"@ai-sdk/provider-utils@3.0.25_zod@3.25.76",
|
|
99
|
+
"zod"
|
|
100
|
+
]
|
|
101
|
+
},
|
|
102
|
+
"@ai-sdk/provider-utils@3.0.25_zod@3.25.76": {
|
|
103
|
+
"integrity": "sha512-CvsRu+32Y8a167s+lrIBtsybvgTHp8j9y+6BeTvLeoW3Q+okw/b4CnNUFOLIXsRaKHQKAH+IHNJPYWywfpw0LA==",
|
|
104
|
+
"dependencies": [
|
|
105
|
+
"@ai-sdk/provider@2.0.3",
|
|
106
|
+
"@standard-schema/spec",
|
|
107
|
+
"eventsource-parser",
|
|
108
|
+
"zod"
|
|
109
|
+
]
|
|
110
|
+
},
|
|
111
|
+
"@ai-sdk/provider-utils@4.0.27_zod@3.25.76": {
|
|
112
|
+
"integrity": "sha512-ubkAJ+xODouwtmN1tYlvTPphH1hPOBfZaEQe8U7skGvFAnIRs9PPpsq57bC2+Ky/MB4yzhd6YOsxTAx9sGpazw==",
|
|
113
|
+
"dependencies": [
|
|
114
|
+
"@ai-sdk/provider@3.0.10",
|
|
115
|
+
"@standard-schema/spec",
|
|
116
|
+
"eventsource-parser",
|
|
117
|
+
"zod"
|
|
118
|
+
]
|
|
119
|
+
},
|
|
120
|
+
"@ai-sdk/provider@2.0.3": {
|
|
121
|
+
"integrity": "sha512-h88OPkavHTiN9tMn2l5awAznGB0lXzjcLhgR1/rvjB2zlLprsNxbM2tt6OJsHUxduLC3klq0/eqaSf6fX5XVww==",
|
|
122
|
+
"dependencies": [
|
|
123
|
+
"json-schema"
|
|
124
|
+
]
|
|
125
|
+
},
|
|
126
|
+
"@ai-sdk/provider@3.0.10": {
|
|
127
|
+
"integrity": "sha512-Q3BZ27qfpYqnCYGvE3vt+Qi6LGOF9R5Nmzn+9JoM1lCRsD9mYaIhfJLkSunN48nfGXJ6n+XNV0J/XVpqGQl7Dw==",
|
|
128
|
+
"dependencies": [
|
|
129
|
+
"json-schema"
|
|
130
|
+
]
|
|
131
|
+
},
|
|
132
|
+
"@drizzle-team/brocli@0.11.0": {
|
|
133
|
+
"integrity": "sha512-hD3pekGiPg0WPCCGAZmusBBJsDqGUR66Y452YgQsZOnkdQ7ViEPKuyP4huUGEZQefp8g34RRodXYmJ2TbCH+tg=="
|
|
134
|
+
},
|
|
62
135
|
"@esbuild/aix-ppc64@0.25.12": {
|
|
63
136
|
"integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
|
|
64
137
|
"os": ["aix"],
|
|
@@ -319,6 +392,51 @@
|
|
|
319
392
|
"os": ["win32"],
|
|
320
393
|
"cpu": ["x64"]
|
|
321
394
|
},
|
|
395
|
+
"@js-temporal/polyfill@0.5.1": {
|
|
396
|
+
"integrity": "sha512-hloP58zRVCRSpgDxmqCWJNlizAlUgJFqG2ypq79DCvyv9tHjRYMDOcPFjzfl/A1/YxDvRCZz8wvZvmapQnKwFQ==",
|
|
397
|
+
"dependencies": [
|
|
398
|
+
"jsbi"
|
|
399
|
+
]
|
|
400
|
+
},
|
|
401
|
+
"@opentelemetry/api@1.9.1": {
|
|
402
|
+
"integrity": "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q=="
|
|
403
|
+
},
|
|
404
|
+
"@standard-schema/spec@1.1.0": {
|
|
405
|
+
"integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="
|
|
406
|
+
},
|
|
407
|
+
"@vercel/oidc@3.2.0": {
|
|
408
|
+
"integrity": "sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug=="
|
|
409
|
+
},
|
|
410
|
+
"ai@6.0.193_zod@3.25.76": {
|
|
411
|
+
"integrity": "sha512-VQOTOse8+X8kMtg61DNSXlYJzwOW4NjMLDJNk/qxClWsFe4oiyFJDHGGG1oezfGcFzuYuQe/8Z7r4kwiZWh2YQ==",
|
|
412
|
+
"dependencies": [
|
|
413
|
+
"@ai-sdk/gateway",
|
|
414
|
+
"@ai-sdk/provider@3.0.10",
|
|
415
|
+
"@ai-sdk/provider-utils@4.0.27_zod@3.25.76",
|
|
416
|
+
"@opentelemetry/api",
|
|
417
|
+
"zod"
|
|
418
|
+
]
|
|
419
|
+
},
|
|
420
|
+
"drizzle-kit@1.0.0-rc.3": {
|
|
421
|
+
"integrity": "sha512-lwGIi6GTlEYzrtac1Szecns+zrAxjaOoNysfBCKRQOXQE7nMJ8IU2I2S6ek3FHEm1N30dQb5YrTdqV7zNH6n7A==",
|
|
422
|
+
"dependencies": [
|
|
423
|
+
"@drizzle-team/brocli",
|
|
424
|
+
"@js-temporal/polyfill",
|
|
425
|
+
"esbuild@0.25.12",
|
|
426
|
+
"get-tsconfig",
|
|
427
|
+
"jiti"
|
|
428
|
+
],
|
|
429
|
+
"bin": true
|
|
430
|
+
},
|
|
431
|
+
"drizzle-orm@1.0.0-rc.3_zod@3.25.76": {
|
|
432
|
+
"integrity": "sha512-akZOa5UxapFbdBG8IDkfBRpSZJpMHaOJtGgp7oi1oHaiU8S3KN92waHo2l5aRuv1D9tMGYpv3BQFOsiGcNjLTQ==",
|
|
433
|
+
"dependencies": [
|
|
434
|
+
"zod"
|
|
435
|
+
],
|
|
436
|
+
"optionalPeers": [
|
|
437
|
+
"zod"
|
|
438
|
+
]
|
|
439
|
+
},
|
|
322
440
|
"es-module-lexer@2.1.0": {
|
|
323
441
|
"integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ=="
|
|
324
442
|
},
|
|
@@ -388,12 +506,37 @@
|
|
|
388
506
|
"scripts": true,
|
|
389
507
|
"bin": true
|
|
390
508
|
},
|
|
391
|
-
"
|
|
392
|
-
"integrity": "sha512-
|
|
509
|
+
"eventsource-parser@3.1.0": {
|
|
510
|
+
"integrity": "sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg=="
|
|
511
|
+
},
|
|
512
|
+
"get-tsconfig@4.14.0": {
|
|
513
|
+
"integrity": "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==",
|
|
514
|
+
"dependencies": [
|
|
515
|
+
"resolve-pkg-maps"
|
|
516
|
+
]
|
|
517
|
+
},
|
|
518
|
+
"hono@4.12.25": {
|
|
519
|
+
"integrity": "sha512-2NFaIyNVgJmBs/ecmtGzlmluTFs5cHEWGTdu0t1HBwYzoGXOL5nUQBRMXsXWla5i4KkG//QMzVP88m1+I3fdAQ=="
|
|
520
|
+
},
|
|
521
|
+
"jiti@2.6.1": {
|
|
522
|
+
"integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==",
|
|
523
|
+
"bin": true
|
|
524
|
+
},
|
|
525
|
+
"jsbi@4.3.2": {
|
|
526
|
+
"integrity": "sha512-9fqMSQbhJykSeii05nxKl4m6Eqn2P6rOlYiS+C5Dr/HPIU/7yZxu5qzbs40tgaFORiw2Amd0mirjxatXYMkIew=="
|
|
527
|
+
},
|
|
528
|
+
"json-schema@0.4.0": {
|
|
529
|
+
"integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="
|
|
530
|
+
},
|
|
531
|
+
"resolve-pkg-maps@1.0.0": {
|
|
532
|
+
"integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="
|
|
393
533
|
},
|
|
394
534
|
"typescript@6.0.3": {
|
|
395
535
|
"integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
|
|
396
536
|
"bin": true
|
|
537
|
+
},
|
|
538
|
+
"zod@3.25.76": {
|
|
539
|
+
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="
|
|
397
540
|
}
|
|
398
541
|
},
|
|
399
542
|
"workspace": {
|
|
@@ -403,13 +546,16 @@
|
|
|
403
546
|
"jsr:@std/assert@1",
|
|
404
547
|
"jsr:@std/path@1",
|
|
405
548
|
"jsr:@zip-js/zip-js@^2.8.0",
|
|
549
|
+
"npm:drizzle-orm@1.0.0-rc.3",
|
|
406
550
|
"npm:es-module-lexer@2",
|
|
407
551
|
"npm:esbuild@0.28",
|
|
408
552
|
"npm:hono@4"
|
|
409
553
|
],
|
|
410
554
|
"packageJson": {
|
|
411
555
|
"dependencies": [
|
|
412
|
-
"npm:
|
|
556
|
+
"npm:drizzle-kit@1.0.0-rc.3",
|
|
557
|
+
"npm:drizzle-orm@1.0.0-rc.3",
|
|
558
|
+
"npm:hono@^4.12.25"
|
|
413
559
|
]
|
|
414
560
|
}
|
|
415
561
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export type CallEdgeAuth = "service" | "user" | {
|
|
2
|
+
bearer: string;
|
|
3
|
+
};
|
|
4
|
+
export interface CallEdgeFunctionOptions {
|
|
5
|
+
/** HTTP method. Defaults to `"POST"`. */
|
|
6
|
+
method?: string;
|
|
7
|
+
/** Optional route path inside the target function, e.g. `/sync`. */
|
|
8
|
+
path?: string;
|
|
9
|
+
/** Query parameters appended to the function URL. */
|
|
10
|
+
query?: Record<string, string | number | boolean | null | undefined>;
|
|
11
|
+
body?: unknown;
|
|
12
|
+
auth?: CallEdgeAuth;
|
|
13
|
+
headers?: Record<string, string>;
|
|
14
|
+
signal?: AbortSignal;
|
|
15
|
+
}
|
|
16
|
+
export interface EdgeCallerRuntime {
|
|
17
|
+
getBaseUrl(): string;
|
|
18
|
+
/** Public prefix in front of the function name. Defaults to `/functions/v1`. */
|
|
19
|
+
pathPrefix?: string;
|
|
20
|
+
getServiceRoleToken(): string | null | undefined;
|
|
21
|
+
getAnonKey?(): string | null | undefined;
|
|
22
|
+
getUserToken(): string | null | undefined;
|
|
23
|
+
fetch?: typeof fetch;
|
|
24
|
+
waitUntil?: (promise: Promise<unknown>) => void;
|
|
25
|
+
}
|
|
26
|
+
export interface EdgeCaller {
|
|
27
|
+
callEdgeFunction(name: string, options?: CallEdgeFunctionOptions): Promise<Response>;
|
|
28
|
+
callEdgeFunctionInBackground(name: string, options?: CallEdgeFunctionOptions): void;
|
|
29
|
+
}
|
|
30
|
+
export declare function createEdgeCaller(runtime: EdgeCallerRuntime): EdgeCaller;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
function resolveBearerToken(runtime, auth) {
|
|
2
|
+
if (typeof auth === "object" && auth !== null && "bearer" in auth) {
|
|
3
|
+
return auth.bearer;
|
|
4
|
+
}
|
|
5
|
+
if (auth === "user") {
|
|
6
|
+
const token = runtime.getUserToken();
|
|
7
|
+
if (!token) {
|
|
8
|
+
throw new Error("[callEdgeFunction] auth: 'user' requires an authenticated caller " +
|
|
9
|
+
"(no user token available).");
|
|
10
|
+
}
|
|
11
|
+
return token;
|
|
12
|
+
}
|
|
13
|
+
const serviceToken = runtime.getServiceRoleToken();
|
|
14
|
+
if (!serviceToken) {
|
|
15
|
+
throw new Error("[callEdgeFunction] service auth requires a service role token.");
|
|
16
|
+
}
|
|
17
|
+
return serviceToken;
|
|
18
|
+
}
|
|
19
|
+
function serializeBody(body) {
|
|
20
|
+
if (body === undefined || body === null)
|
|
21
|
+
return { body: undefined };
|
|
22
|
+
if (typeof body === "string") {
|
|
23
|
+
return { body, contentType: "text/plain;charset=UTF-8" };
|
|
24
|
+
}
|
|
25
|
+
if (body instanceof Uint8Array ||
|
|
26
|
+
body instanceof ArrayBuffer ||
|
|
27
|
+
body instanceof Blob ||
|
|
28
|
+
body instanceof FormData ||
|
|
29
|
+
body instanceof ReadableStream) {
|
|
30
|
+
return { body: body };
|
|
31
|
+
}
|
|
32
|
+
return { body: JSON.stringify(body), contentType: "application/json" };
|
|
33
|
+
}
|
|
34
|
+
function buildRequest(runtime, name, opts) {
|
|
35
|
+
const pathPrefix = runtime.pathPrefix ?? "/functions/v1";
|
|
36
|
+
const routePath = opts.path ? `/${opts.path.replace(/^\/+/, "")}` : "";
|
|
37
|
+
const url = new URL(`${runtime.getBaseUrl().replace(/\/+$/, "")}/${pathPrefix.replace(/^\/+|\/+$/g, "")}/${name}${routePath}`);
|
|
38
|
+
for (const [key, value] of Object.entries(opts.query ?? {})) {
|
|
39
|
+
if (value !== undefined && value !== null) {
|
|
40
|
+
url.searchParams.set(key, String(value));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const token = resolveBearerToken(runtime, opts.auth ?? "service");
|
|
44
|
+
const { body, contentType } = serializeBody(opts.body);
|
|
45
|
+
const anonKey = runtime.getAnonKey?.();
|
|
46
|
+
const headers = {
|
|
47
|
+
Authorization: `Bearer ${token}`,
|
|
48
|
+
...(contentType ? { "Content-Type": contentType } : {}),
|
|
49
|
+
...(anonKey ? { apikey: anonKey } : {}),
|
|
50
|
+
...opts.headers,
|
|
51
|
+
};
|
|
52
|
+
return {
|
|
53
|
+
url: url.toString(),
|
|
54
|
+
init: {
|
|
55
|
+
method: opts.method ?? "POST",
|
|
56
|
+
headers,
|
|
57
|
+
body: body ?? null,
|
|
58
|
+
signal: opts.signal,
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
export function createEdgeCaller(runtime) {
|
|
63
|
+
const fetchImpl = runtime.fetch ?? fetch;
|
|
64
|
+
return {
|
|
65
|
+
async callEdgeFunction(name, options = {}) {
|
|
66
|
+
const { url, init } = buildRequest(runtime, name, options);
|
|
67
|
+
return await fetchImpl(url, init);
|
|
68
|
+
},
|
|
69
|
+
callEdgeFunctionInBackground(name, options = {}) {
|
|
70
|
+
const { url, init } = buildRequest(runtime, name, options);
|
|
71
|
+
const promise = fetchImpl(url, init).catch((err) => {
|
|
72
|
+
console.error(`[callEdgeFunctionInBackground] ${name} failed:`, err);
|
|
73
|
+
});
|
|
74
|
+
runtime.waitUntil?.(promise);
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
}
|
package/dist/edge.d.ts
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
export type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
2
|
+
export interface SchemaLike<T = unknown> {
|
|
3
|
+
safeParse(value: unknown): {
|
|
4
|
+
success: true;
|
|
5
|
+
data: T;
|
|
6
|
+
} | {
|
|
7
|
+
success: false;
|
|
8
|
+
error: unknown;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export interface JsonSchemaAdapter {
|
|
12
|
+
toJsonSchema(schema: SchemaLike): unknown;
|
|
13
|
+
}
|
|
14
|
+
type SchemaOutput<TSchema> = TSchema extends SchemaLike<infer T> ? T : undefined;
|
|
15
|
+
type ParamsOutput<TSchema> = TSchema extends SchemaLike<infer T> ? T : Record<string, string>;
|
|
16
|
+
type QueryOutput<TSchema> = TSchema extends SchemaLike<infer T> ? T : Record<string, string>;
|
|
17
|
+
export declare enum HttpStatus {
|
|
18
|
+
OK = 200,
|
|
19
|
+
CREATED = 201,
|
|
20
|
+
ACCEPTED = 202,
|
|
21
|
+
NO_CONTENT = 204,
|
|
22
|
+
RESET_CONTENT = 205,
|
|
23
|
+
BAD_REQUEST = 400,
|
|
24
|
+
UNAUTHORIZED = 401,
|
|
25
|
+
FORBIDDEN = 403,
|
|
26
|
+
NOT_FOUND = 404,
|
|
27
|
+
METHOD_NOT_ALLOWED = 405,
|
|
28
|
+
REQUEST_TIMEOUT = 408,
|
|
29
|
+
CONFLICT = 409,
|
|
30
|
+
GONE = 410,
|
|
31
|
+
PRECONDITION_FAILED = 412,
|
|
32
|
+
PAYLOAD_TOO_LARGE = 413,
|
|
33
|
+
UNSUPPORTED_MEDIA_TYPE = 415,
|
|
34
|
+
UNPROCESSABLE_CONTENT = 422,
|
|
35
|
+
LOCKED = 423,
|
|
36
|
+
PRECONDITION_REQUIRED = 428,
|
|
37
|
+
TOO_MANY_REQUESTS = 429,
|
|
38
|
+
INTERNAL_SERVER_ERROR = 500,
|
|
39
|
+
NOT_IMPLEMENTED = 501,
|
|
40
|
+
BAD_GATEWAY = 502,
|
|
41
|
+
SERVICE_UNAVAILABLE = 503,
|
|
42
|
+
GATEWAY_TIMEOUT = 504
|
|
43
|
+
}
|
|
44
|
+
export type ErrorHttpStatus = HttpStatus.BAD_REQUEST | HttpStatus.UNAUTHORIZED | HttpStatus.FORBIDDEN | HttpStatus.NOT_FOUND | HttpStatus.METHOD_NOT_ALLOWED | HttpStatus.REQUEST_TIMEOUT | HttpStatus.CONFLICT | HttpStatus.GONE | HttpStatus.PRECONDITION_FAILED | HttpStatus.PAYLOAD_TOO_LARGE | HttpStatus.UNSUPPORTED_MEDIA_TYPE | HttpStatus.UNPROCESSABLE_CONTENT | HttpStatus.LOCKED | HttpStatus.PRECONDITION_REQUIRED | HttpStatus.TOO_MANY_REQUESTS | HttpStatus.INTERNAL_SERVER_ERROR | HttpStatus.NOT_IMPLEMENTED | HttpStatus.BAD_GATEWAY | HttpStatus.SERVICE_UNAVAILABLE | HttpStatus.GATEWAY_TIMEOUT;
|
|
45
|
+
export type RouteResponses = Partial<Record<HttpStatus, SchemaLike>>;
|
|
46
|
+
export interface RouteRequest<TParams extends SchemaLike | undefined, TQuery extends SchemaLike | undefined, TBody extends SchemaLike | undefined, TContext> {
|
|
47
|
+
req: Request;
|
|
48
|
+
ctx: TContext;
|
|
49
|
+
params: ParamsOutput<TParams>;
|
|
50
|
+
query: QueryOutput<TQuery>;
|
|
51
|
+
body: SchemaOutput<TBody>;
|
|
52
|
+
}
|
|
53
|
+
export type RouteHandler<TParams extends SchemaLike | undefined = undefined, TQuery extends SchemaLike | undefined = undefined, TBody extends SchemaLike | undefined = undefined, TContext = unknown> = (input: RouteRequest<TParams, TQuery, TBody, TContext>) => Response | Promise<Response>;
|
|
54
|
+
export interface RouteOptions<TParams extends SchemaLike | undefined = undefined, TQuery extends SchemaLike | undefined = undefined, TBody extends SchemaLike | undefined = undefined, TResponses extends RouteResponses = RouteResponses, TContext = unknown> {
|
|
55
|
+
params?: TParams;
|
|
56
|
+
query?: TQuery;
|
|
57
|
+
body?: TBody;
|
|
58
|
+
responses: TResponses;
|
|
59
|
+
summary?: string;
|
|
60
|
+
description?: string;
|
|
61
|
+
handler: RouteHandler<TParams, TQuery, TBody, TContext>;
|
|
62
|
+
}
|
|
63
|
+
export interface RouteDefinition<TParams extends SchemaLike | undefined = SchemaLike | undefined, TQuery extends SchemaLike | undefined = SchemaLike | undefined, TBody extends SchemaLike | undefined = SchemaLike | undefined, TResponses extends RouteResponses = RouteResponses, TContext = unknown> extends RouteOptions<TParams, TQuery, TBody, TResponses, TContext> {
|
|
64
|
+
method: HttpMethod;
|
|
65
|
+
path: string;
|
|
66
|
+
operationId: string;
|
|
67
|
+
}
|
|
68
|
+
export interface JsonRouteOptions<TBody extends SchemaLike, TResponses extends RouteResponses, TContext> {
|
|
69
|
+
body: TBody;
|
|
70
|
+
responses: TResponses;
|
|
71
|
+
summary?: string;
|
|
72
|
+
description?: string;
|
|
73
|
+
handler: RouteHandler<undefined, undefined, TBody, TContext>;
|
|
74
|
+
}
|
|
75
|
+
export declare function createRouteBuilder<TDefaultContext = unknown>(): {
|
|
76
|
+
get: <TParams extends SchemaLike | undefined = undefined, TQuery extends SchemaLike | undefined = undefined, TResponses extends RouteResponses = Partial<Record<HttpStatus, SchemaLike<unknown>>>>(path: string, operationId: string, options: RouteOptions<TParams, TQuery, undefined, TResponses, TDefaultContext>) => RouteDefinition<TParams, TQuery, undefined, TResponses, TDefaultContext>;
|
|
77
|
+
post: <TParams extends SchemaLike | undefined = undefined, TQuery extends SchemaLike | undefined = undefined, TBody extends SchemaLike | undefined = undefined, TResponses extends RouteResponses = Partial<Record<HttpStatus, SchemaLike<unknown>>>>(path: string, operationId: string, options: RouteOptions<TParams, TQuery, TBody, TResponses, TDefaultContext>) => RouteDefinition<TParams, TQuery, TBody, TResponses, TDefaultContext>;
|
|
78
|
+
put: <TParams extends SchemaLike | undefined = undefined, TQuery extends SchemaLike | undefined = undefined, TBody extends SchemaLike | undefined = undefined, TResponses extends RouteResponses = Partial<Record<HttpStatus, SchemaLike<unknown>>>>(path: string, operationId: string, options: RouteOptions<TParams, TQuery, TBody, TResponses, TDefaultContext>) => RouteDefinition<TParams, TQuery, TBody, TResponses, TDefaultContext>;
|
|
79
|
+
patch: <TParams extends SchemaLike | undefined = undefined, TQuery extends SchemaLike | undefined = undefined, TBody extends SchemaLike | undefined = undefined, TResponses extends RouteResponses = Partial<Record<HttpStatus, SchemaLike<unknown>>>>(path: string, operationId: string, options: RouteOptions<TParams, TQuery, TBody, TResponses, TDefaultContext>) => RouteDefinition<TParams, TQuery, TBody, TResponses, TDefaultContext>;
|
|
80
|
+
delete: <TParams extends SchemaLike | undefined = undefined, TQuery extends SchemaLike | undefined = undefined, TResponses extends RouteResponses = Partial<Record<HttpStatus, SchemaLike<unknown>>>>(path: string, operationId: string, options: RouteOptions<TParams, TQuery, undefined, TResponses, TDefaultContext>) => RouteDefinition<TParams, TQuery, undefined, TResponses, TDefaultContext>;
|
|
81
|
+
postJson: <TBody extends SchemaLike, TResponses extends RouteResponses>(path: string, operationId: string, options: JsonRouteOptions<TBody, TResponses, TDefaultContext>) => RouteDefinition<undefined, undefined, TBody, TResponses, TDefaultContext>;
|
|
82
|
+
putJson: <TBody extends SchemaLike, TResponses extends RouteResponses>(path: string, operationId: string, options: JsonRouteOptions<TBody, TResponses, TDefaultContext>) => RouteDefinition<undefined, undefined, TBody, TResponses, TDefaultContext>;
|
|
83
|
+
patchJson: <TBody extends SchemaLike, TResponses extends RouteResponses>(path: string, operationId: string, options: JsonRouteOptions<TBody, TResponses, TDefaultContext>) => RouteDefinition<undefined, undefined, TBody, TResponses, TDefaultContext>;
|
|
84
|
+
};
|
|
85
|
+
export declare const route: {
|
|
86
|
+
get: <TParams extends SchemaLike | undefined = undefined, TQuery extends SchemaLike | undefined = undefined, TResponses extends Partial<Record<HttpStatus, SchemaLike<unknown>>> = Partial<Record<HttpStatus, SchemaLike<unknown>>>>(path: string, operationId: string, options: RouteOptions<TParams, TQuery, undefined, TResponses, unknown>) => RouteDefinition<TParams, TQuery, undefined, TResponses, unknown>;
|
|
87
|
+
post: <TParams extends SchemaLike | undefined = undefined, TQuery extends SchemaLike | undefined = undefined, TBody extends SchemaLike | undefined = undefined, TResponses extends Partial<Record<HttpStatus, SchemaLike<unknown>>> = Partial<Record<HttpStatus, SchemaLike<unknown>>>>(path: string, operationId: string, options: RouteOptions<TParams, TQuery, TBody, TResponses, unknown>) => RouteDefinition<TParams, TQuery, TBody, TResponses, unknown>;
|
|
88
|
+
put: <TParams extends SchemaLike | undefined = undefined, TQuery extends SchemaLike | undefined = undefined, TBody extends SchemaLike | undefined = undefined, TResponses extends Partial<Record<HttpStatus, SchemaLike<unknown>>> = Partial<Record<HttpStatus, SchemaLike<unknown>>>>(path: string, operationId: string, options: RouteOptions<TParams, TQuery, TBody, TResponses, unknown>) => RouteDefinition<TParams, TQuery, TBody, TResponses, unknown>;
|
|
89
|
+
patch: <TParams extends SchemaLike | undefined = undefined, TQuery extends SchemaLike | undefined = undefined, TBody extends SchemaLike | undefined = undefined, TResponses extends Partial<Record<HttpStatus, SchemaLike<unknown>>> = Partial<Record<HttpStatus, SchemaLike<unknown>>>>(path: string, operationId: string, options: RouteOptions<TParams, TQuery, TBody, TResponses, unknown>) => RouteDefinition<TParams, TQuery, TBody, TResponses, unknown>;
|
|
90
|
+
delete: <TParams extends SchemaLike | undefined = undefined, TQuery extends SchemaLike | undefined = undefined, TResponses extends Partial<Record<HttpStatus, SchemaLike<unknown>>> = Partial<Record<HttpStatus, SchemaLike<unknown>>>>(path: string, operationId: string, options: RouteOptions<TParams, TQuery, undefined, TResponses, unknown>) => RouteDefinition<TParams, TQuery, undefined, TResponses, unknown>;
|
|
91
|
+
postJson: <TBody extends SchemaLike, TResponses extends Partial<Record<HttpStatus, SchemaLike<unknown>>>>(path: string, operationId: string, options: JsonRouteOptions<TBody, TResponses, unknown>) => RouteDefinition<undefined, undefined, TBody, TResponses, unknown>;
|
|
92
|
+
putJson: <TBody extends SchemaLike, TResponses extends Partial<Record<HttpStatus, SchemaLike<unknown>>>>(path: string, operationId: string, options: JsonRouteOptions<TBody, TResponses, unknown>) => RouteDefinition<undefined, undefined, TBody, TResponses, unknown>;
|
|
93
|
+
patchJson: <TBody extends SchemaLike, TResponses extends Partial<Record<HttpStatus, SchemaLike<unknown>>>>(path: string, operationId: string, options: JsonRouteOptions<TBody, TResponses, unknown>) => RouteDefinition<undefined, undefined, TBody, TResponses, unknown>;
|
|
94
|
+
};
|
|
95
|
+
export declare function json(body: unknown, init?: number | ResponseInit): Response;
|
|
96
|
+
export declare function jsonError(status: ErrorHttpStatus, message: string, details?: unknown, init?: Omit<ResponseInit, "status">): Response;
|
|
97
|
+
export declare function empty(status?: HttpStatus, init?: Omit<ResponseInit, "status">): Response;
|
|
98
|
+
export declare function text(body: string, init?: number | ResponseInit): Response;
|
|
99
|
+
export declare function created(body: unknown, init?: Omit<ResponseInit, "status">): Response;
|
|
100
|
+
export declare function accepted(body: unknown, init?: Omit<ResponseInit, "status">): Response;
|
|
101
|
+
export declare function noContent(init?: Omit<ResponseInit, "status">): Response;
|
|
102
|
+
export declare function badRequest(message?: string, details?: unknown): Response;
|
|
103
|
+
export declare function unauthorized(message?: string, details?: unknown): Response;
|
|
104
|
+
export declare function forbidden(message?: string, details?: unknown): Response;
|
|
105
|
+
export declare function notFound(message?: string, details?: unknown): Response;
|
|
106
|
+
export declare function methodNotAllowed(allowedMethods: readonly string[], message?: string): Response;
|
|
107
|
+
export declare function conflict(message?: string, details?: unknown): Response;
|
|
108
|
+
export declare function gone(message?: string, details?: unknown): Response;
|
|
109
|
+
export declare function payloadTooLarge(message?: string, details?: unknown): Response;
|
|
110
|
+
export declare function unsupportedMediaType(message?: string, details?: unknown): Response;
|
|
111
|
+
export declare function unprocessableContent(message?: string, details?: unknown): Response;
|
|
112
|
+
export declare function tooManyRequests(input?: {
|
|
113
|
+
message?: string;
|
|
114
|
+
retryAfterSeconds?: number;
|
|
115
|
+
}): Response;
|
|
116
|
+
export declare function internalServerError(message?: string, details?: unknown): Response;
|
|
117
|
+
export declare function badGateway(message?: string, details?: unknown): Response;
|
|
118
|
+
export declare function serviceUnavailable(message?: string, details?: unknown): Response;
|
|
119
|
+
export declare function gatewayTimeout(message?: string, details?: unknown): Response;
|
|
120
|
+
export interface ServeConfig<TContext = unknown> {
|
|
121
|
+
name: string;
|
|
122
|
+
require: unknown;
|
|
123
|
+
timeoutMs?: number;
|
|
124
|
+
routes: RouteDefinition<SchemaLike | undefined, SchemaLike | undefined, SchemaLike | undefined, RouteResponses, TContext>[];
|
|
125
|
+
/**
|
|
126
|
+
* Public prefix in front of the function name. Supabase-compatible hosts use
|
|
127
|
+
* `/functions/v1`; ASP.NET Core or other reverse proxies may expose a
|
|
128
|
+
* different prefix while keeping the same function modules.
|
|
129
|
+
*/
|
|
130
|
+
pathPrefix?: string;
|
|
131
|
+
}
|
|
132
|
+
export interface MatchedRoute {
|
|
133
|
+
route: RouteDefinition<SchemaLike | undefined, SchemaLike | undefined, SchemaLike | undefined, RouteResponses, any>;
|
|
134
|
+
params: Record<string, string>;
|
|
135
|
+
}
|
|
136
|
+
export interface RouteDispatcherContext<TContext> {
|
|
137
|
+
req: Request;
|
|
138
|
+
ctx: TContext;
|
|
139
|
+
matched: MatchedRoute;
|
|
140
|
+
getCorsHeaders?: (req: Request) => HeadersInit;
|
|
141
|
+
}
|
|
142
|
+
export type RouteDispatcher<TContext> = (input: RouteDispatcherContext<TContext>) => Promise<Response>;
|
|
143
|
+
export interface FetchHandlerOptions<TContext> {
|
|
144
|
+
getContext(req: Request): TContext | Response | Promise<TContext | Response>;
|
|
145
|
+
getCorsHeaders?: (req: Request) => HeadersInit;
|
|
146
|
+
onError?: (error: unknown, req: Request) => Response | Promise<Response>;
|
|
147
|
+
afterResponse?: (response: Response, req: Request) => Response | Promise<Response>;
|
|
148
|
+
}
|
|
149
|
+
export interface StandaloneServe {
|
|
150
|
+
(handler: (req: Request) => Response | Promise<Response>): void;
|
|
151
|
+
}
|
|
152
|
+
export declare function getStandaloneServe(): StandaloneServe | null;
|
|
153
|
+
export declare function normalizeRoutePath(path: string): string;
|
|
154
|
+
export declare function splitPath(path: string): string[];
|
|
155
|
+
export declare function getRelativePath(req: Request, functionName: string): string;
|
|
156
|
+
export declare function getRelativePathFromParts(pathnameParts: string[], functionName: string, pathPrefix?: string): string;
|
|
157
|
+
export declare function matchRoutePath(routePath: string, requestPath: string): Record<string, string> | null;
|
|
158
|
+
export declare function findRoute<TContext>(config: ServeConfig<TContext>, req: Request): MatchedRoute | Response;
|
|
159
|
+
export declare function queryObject(req: Request): Record<string, string>;
|
|
160
|
+
export declare function parseJsonBody(req: Request): Promise<unknown>;
|
|
161
|
+
export declare function flattenSchemaError(error: unknown): unknown;
|
|
162
|
+
export declare function validateSchema<TSchema extends SchemaLike | undefined>(schema: TSchema, value: unknown, label: "params" | "query" | "body"): SchemaOutput<TSchema> | Response;
|
|
163
|
+
export declare function withCors(req: Request, response: Response, getCorsHeaders?: (req: Request) => HeadersInit): Response;
|
|
164
|
+
export declare function dispatchRoute<TContext>(input: RouteDispatcherContext<TContext>): Promise<Response>;
|
|
165
|
+
export declare function createFetchHandler<TContext>(config: ServeConfig<TContext>, options: FetchHandlerOptions<TContext>): (req: Request) => Promise<Response>;
|
|
166
|
+
export interface OpenApiDocumentOptions {
|
|
167
|
+
title: string;
|
|
168
|
+
version: string;
|
|
169
|
+
servers?: {
|
|
170
|
+
url: string;
|
|
171
|
+
description?: string;
|
|
172
|
+
}[];
|
|
173
|
+
schemaAdapter?: JsonSchemaAdapter;
|
|
174
|
+
}
|
|
175
|
+
export declare function createOpenApiDocument(config: ServeConfig<any>, options: OpenApiDocumentOptions): Record<string, unknown>;
|
|
176
|
+
export {};
|