stitchkit 0.100.1 → 0.100.3
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/CHANGELOG.md +99 -0
- package/README.md +2 -1
- package/dist/application/process-lifecycle-transitions.d.ts +13 -2
- package/dist/application/process-lifecycle-transitions.d.ts.map +1 -1
- package/dist/application/process-lifecycle.d.ts +2 -1
- package/dist/application/process-lifecycle.d.ts.map +1 -1
- package/dist/entrypoints/agent-runtime/browser.js +119 -119
- package/dist/entrypoints/agent-runtime/coding-tools.js +15 -20
- package/dist/entrypoints/agent-runtime/harness.js +55 -63
- package/dist/entrypoints/agent-runtime/openrouter.js +6 -6
- package/dist/entrypoints/agent-runtime/sandbox.js +20 -25
- package/dist/entrypoints/agent-runtime/sqlite/bun.js +11 -13
- package/dist/entrypoints/agent-runtime/sqlite/node.js +11 -13
- package/dist/entrypoints/agent-runtime/testing.js +32 -34
- package/dist/entrypoints/agent-runtime.js +395 -402
- package/dist/entrypoints/application/diagnostic-journal.js +26 -27
- package/dist/entrypoints/application/directory-inbox.js +16 -17
- package/dist/entrypoints/application/grammy.js +13 -13
- package/dist/entrypoints/application/opentelemetry.js +13 -13
- package/dist/entrypoints/application/schemas.js +24 -24
- package/dist/entrypoints/application.d.ts +1 -1
- package/dist/entrypoints/application.d.ts.map +1 -1
- package/dist/entrypoints/application.js +218 -214
- package/dist/entrypoints/bin/upgrade-cli.js +2 -2
- package/dist/entrypoints/cli.js +81 -84
- package/dist/entrypoints/contract.js +54 -57
- package/dist/entrypoints/declaration.js +22 -22
- package/dist/entrypoints/files.js +18 -21
- package/dist/entrypoints/geo.js +7 -8
- package/dist/entrypoints/google.js +2 -2
- package/dist/entrypoints/index.js +150 -154
- package/dist/entrypoints/live.js +45 -47
- package/dist/entrypoints/node.js +68 -74
- package/dist/entrypoints/oauth.js +6 -6
- package/dist/entrypoints/observability.d.ts +1 -0
- package/dist/entrypoints/observability.d.ts.map +1 -1
- package/dist/entrypoints/observability.js +115 -87
- package/dist/entrypoints/primitives.js +35 -35
- package/dist/entrypoints/react/keyboard.d.ts +4 -0
- package/dist/entrypoints/react/keyboard.d.ts.map +1 -0
- package/dist/entrypoints/react/keyboard.js +397 -0
- package/dist/entrypoints/react.js +10 -10
- package/dist/entrypoints/release.js +7 -7
- package/dist/entrypoints/remote.js +15 -17
- package/dist/entrypoints/server.js +198 -200
- package/dist/entrypoints/telegram/html.d.ts +5 -0
- package/dist/entrypoints/telegram/html.d.ts.map +1 -0
- package/dist/entrypoints/telegram/html.js +26 -0
- package/dist/entrypoints/telegram/screens.d.ts +14 -0
- package/dist/entrypoints/telegram/screens.d.ts.map +1 -0
- package/dist/entrypoints/telegram/screens.js +2028 -0
- package/dist/entrypoints/telegram.d.ts +5 -1
- package/dist/entrypoints/telegram.d.ts.map +1 -1
- package/dist/entrypoints/telegram.js +548 -156
- package/dist/entrypoints/testing.js +75 -79
- package/dist/entrypoints/tools/connections.js +30 -32
- package/dist/entrypoints/tools/contract.js +18 -20
- package/dist/entrypoints/tools/invoker.js +12 -17
- package/dist/entrypoints/tools.js +223 -229
- package/dist/entrypoints/tracking/server.js +6 -6
- package/dist/entrypoints/tracking.js +40 -42
- package/dist/entrypoints/voice.js +3 -3
- package/dist/{index-eaysmdtq.js → index-0ndk4555.js} +56 -56
- package/dist/{index-2bxbyab0.js → index-1bx1xv1k.js} +1 -1
- package/dist/{index-p5r7qpdd.js → index-1eeqpadm.js} +4 -4
- package/dist/{index-hadmp193.js → index-3ct7hhq6.js} +210 -210
- package/dist/{index-e01r054k.js → index-45tdvqa3.js} +26 -26
- package/dist/{index-dce5wxcq.js → index-4s0fep5q.js} +8 -8
- package/dist/index-50ejfz75.js +9 -0
- package/dist/{index-m9zn6cpq.js → index-5107q189.js} +12 -12
- package/dist/{index-sbdmyz75.js → index-5cc6r990.js} +3 -3
- package/dist/index-6k1svmjh.js +141 -0
- package/dist/{index-c6bxbsnc.js → index-7kxw1awn.js} +2 -2
- package/dist/{index-0r0fbk7z.js → index-7z9ym7t8.js} +28 -28
- package/dist/{index-p7hgdyhb.js → index-814xpr6d.js} +2 -2
- package/dist/{index-gtj3zc53.js → index-8kzct1d6.js} +99 -99
- package/dist/{index-1zbbzgfh.js → index-9pw0a51a.js} +4 -4
- package/dist/{index-79z7m7dt.js → index-a9pd417t.js} +35 -35
- package/dist/{index-ham5sz8m.js → index-aaj97r4y.js} +45 -45
- package/dist/{index-s812ycat.js → index-aan6hqb9.js} +3 -3
- package/dist/{index-10x2hbx0.js → index-adhgd4pe.js} +44 -44
- package/dist/{index-2gxzjvyr.js → index-afjvjyck.js} +23 -23
- package/dist/{index-qmy6zq75.js → index-ahvsksw5.js} +16 -16
- package/dist/{index-hr4tzbwq.js → index-ap3gqe56.js} +3 -3
- package/dist/{index-znfs20ss.js → index-b2bz0kdm.js} +16 -16
- package/dist/{index-r066jx76.js → index-c6aagrbd.js} +148 -120
- package/dist/{index-k30h66yd.js → index-ck71tbcf.js} +9 -9
- package/dist/{index-eqqvgdb3.js → index-ct75c3at.js} +2 -2
- package/dist/{index-gw8bmyaj.js → index-djjhhgjk.js} +41 -41
- package/dist/{index-8qt2m66q.js → index-dvwj1f43.js} +43 -43
- package/dist/{index-fhwqwxe0.js → index-ej331hah.js} +1 -1
- package/dist/{index-9t3dffv2.js → index-ewbaywk5.js} +5 -5
- package/dist/{index-7p1f0vp2.js → index-fyphpknh.js} +25 -25
- package/dist/{index-83bdg97s.js → index-g149e9pk.js} +1 -1
- package/dist/{index-ngfhvfpd.js → index-g6a3na27.js} +4 -4
- package/dist/{index-qwhynv6x.js → index-h124epcf.js} +20 -20
- package/dist/{index-h6eeys1v.js → index-h97tqqp1.js} +9 -9
- package/dist/{index-zcgf3gqf.js → index-hby4qhax.js} +10 -10
- package/dist/{index-5jrd0yng.js → index-j8aw2c0e.js} +4 -4
- package/dist/{index-d8m8zpd8.js → index-jzczzcsb.js} +14 -14
- package/dist/index-kqnd6a85.js +26 -0
- package/dist/{index-35mh5bhj.js → index-kw1ws0cs.js} +12 -12
- package/dist/{index-yw4fzmdz.js → index-kzz6rq0w.js} +28 -17
- package/dist/{index-4gcnakhx.js → index-m38ej7e5.js} +2 -2
- package/dist/{index-ndddxtyx.js → index-m79yr938.js} +19 -19
- package/dist/{index-5p2m7we1.js → index-m9fndta6.js} +11 -11
- package/dist/index-mnkk1ffz.js +150 -0
- package/dist/index-n15jny5b.js +4 -0
- package/dist/{index-2vtcmrj9.js → index-n53mswt3.js} +20 -20
- package/dist/{index-bt14acb5.js → index-n5yqxd3d.js} +21 -21
- package/dist/{index-bm5rx7n6.js → index-n9tyz8z3.js} +12 -12
- package/dist/index-nmaqxdx7.js +463 -0
- package/dist/{index-mqv3b5rh.js → index-nx6eazt0.js} +30 -30
- package/dist/{index-3z73fh2c.js → index-q0th8q0z.js} +9 -9
- package/dist/{index-v260df89.js → index-qjeggp8w.js} +20 -20
- package/dist/{index-zfrhfn82.js → index-r1z0rd9c.js} +20 -20
- package/dist/{index-wqgjfstn.js → index-r5c4fedv.js} +3 -3
- package/dist/{index-cyktc4bp.js → index-r5hsgkz5.js} +118 -118
- package/dist/{index-0b1ess5e.js → index-r7a7r7ha.js} +1 -1
- package/dist/{index-1j8pm5t0.js → index-rfkh44ss.js} +15 -15
- package/dist/index-rz3z13e9.js +47 -0
- package/dist/{index-k6gpggb9.js → index-s80awwsw.js} +35 -35
- package/dist/{index-vqr7f7v8.js → index-ssmd41kh.js} +124 -124
- package/dist/{index-ensd0r74.js → index-v14qqmys.js} +41 -41
- package/dist/{index-paz6sgdp.js → index-v4a2fjtk.js} +36 -36
- package/dist/{index-d9pk3td4.js → index-w0eqdhyd.js} +14 -14
- package/dist/{index-5qn1kx34.js → index-w575evhq.js} +29 -29
- package/dist/{index-3gye9wzb.js → index-w5s3xftz.js} +26 -26
- package/dist/{index-tm88avnj.js → index-wykebpn9.js} +3 -3
- package/dist/{index-sh3s86na.js → index-xkhk5d6g.js} +17 -17
- package/dist/{index-6k1937bx.js → index-xqhede6x.js} +4 -4
- package/dist/{index-f4fx2tza.js → index-zs4ms3th.js} +181 -181
- package/dist/{index-k5hsfc3j.js → index-zsvzhjtk.js} +1 -1
- package/dist/observability/bounded-logger.d.ts +2 -0
- package/dist/observability/bounded-logger.d.ts.map +1 -1
- package/dist/observability/sanitize.d.ts +6 -0
- package/dist/observability/sanitize.d.ts.map +1 -1
- package/dist/observability/secret-values.d.ts +25 -0
- package/dist/observability/secret-values.d.ts.map +1 -0
- package/dist/react/keyboard/hooks.d.ts +71 -0
- package/dist/react/keyboard/hooks.d.ts.map +1 -0
- package/dist/react/keyboard/layers.d.ts +98 -0
- package/dist/react/keyboard/layers.d.ts.map +1 -0
- package/dist/react/keyboard/zone.d.ts +139 -0
- package/dist/react/keyboard/zone.d.ts.map +1 -0
- package/dist/telegram/crypto.d.ts +16 -0
- package/dist/telegram/crypto.d.ts.map +1 -0
- package/dist/telegram/html/check.d.ts +26 -0
- package/dist/telegram/html/check.d.ts.map +1 -0
- package/dist/telegram/html/nodes.d.ts +70 -0
- package/dist/telegram/html/nodes.d.ts.map +1 -0
- package/dist/telegram/html/parse.d.ts +35 -0
- package/dist/telegram/html/parse.d.ts.map +1 -0
- package/dist/telegram/html/render.d.ts +39 -0
- package/dist/telegram/html/render.d.ts.map +1 -0
- package/dist/telegram/init-data.d.ts.map +1 -1
- package/dist/telegram/operator-channel.d.ts +36 -3
- package/dist/telegram/operator-channel.d.ts.map +1 -1
- package/dist/telegram/screens/callback-codec.d.ts +34 -0
- package/dist/telegram/screens/callback-codec.d.ts.map +1 -0
- package/dist/telegram/screens/dispatch.d.ts +22 -0
- package/dist/telegram/screens/dispatch.d.ts.map +1 -0
- package/dist/telegram/screens/display.d.ts +70 -0
- package/dist/telegram/screens/display.d.ts.map +1 -0
- package/dist/telegram/screens/execute.d.ts +57 -0
- package/dist/telegram/screens/execute.d.ts.map +1 -0
- package/dist/telegram/screens/html.d.ts +36 -0
- package/dist/telegram/screens/html.d.ts.map +1 -0
- package/dist/telegram/screens/outcome.d.ts +74 -0
- package/dist/telegram/screens/outcome.d.ts.map +1 -0
- package/dist/telegram/screens/path.d.ts +29 -0
- package/dist/telegram/screens/path.d.ts.map +1 -0
- package/dist/telegram/screens/reconcile.d.ts +67 -0
- package/dist/telegram/screens/reconcile.d.ts.map +1 -0
- package/dist/telegram/screens/registry.d.ts +23 -0
- package/dist/telegram/screens/registry.d.ts.map +1 -0
- package/dist/telegram/screens/render.d.ts +70 -0
- package/dist/telegram/screens/render.d.ts.map +1 -0
- package/dist/telegram/screens/runtime.d.ts +133 -0
- package/dist/telegram/screens/runtime.d.ts.map +1 -0
- package/dist/telegram/screens/screen-types.d.ts +130 -0
- package/dist/telegram/screens/screen-types.d.ts.map +1 -0
- package/dist/telegram/screens/screen.d.ts +138 -0
- package/dist/telegram/screens/screen.d.ts.map +1 -0
- package/dist/telegram/screens/state.d.ts +115 -0
- package/dist/telegram/screens/state.d.ts.map +1 -0
- package/dist/telegram/screens/telegram-screens.d.ts +18 -0
- package/dist/telegram/screens/telegram-screens.d.ts.map +1 -0
- package/dist/telegram/screens/test-chat-telegram.d.ts +47 -0
- package/dist/telegram/screens/test-chat-telegram.d.ts.map +1 -0
- package/dist/telegram/screens/test-chat.d.ts +66 -0
- package/dist/telegram/screens/test-chat.d.ts.map +1 -0
- package/dist/telegram/screens/view.d.ts +89 -0
- package/dist/telegram/screens/view.d.ts.map +1 -0
- package/dist/telegram/send-failure.d.ts +19 -0
- package/dist/telegram/send-failure.d.ts.map +1 -1
- package/dist/telegram/update-intake.d.ts +78 -0
- package/dist/telegram/update-intake.d.ts.map +1 -0
- package/dist/telegram/update-store-sqlite.d.ts +26 -0
- package/dist/telegram/update-store-sqlite.d.ts.map +1 -0
- package/dist/telegram/update-store.d.ts +67 -0
- package/dist/telegram/update-store.d.ts.map +1 -0
- package/dist/telegram/webhook.d.ts +105 -0
- package/dist/telegram/webhook.d.ts.map +1 -0
- package/dist/tools/cli/args-fields.d.ts +8 -0
- package/dist/tools/cli/args-fields.d.ts.map +1 -1
- package/dist/tools/cli/args-route.d.ts.map +1 -1
- package/dist/tools/cli/args.d.ts.map +1 -1
- package/dist/tools/cli/help.d.ts.map +1 -1
- package/llms/application.2.txt +26 -1
- package/llms/application.txt +18 -27
- package/llms/cli.txt +8 -2
- package/llms/observability.txt +2 -1
- package/llms/react-keyboard.txt +193 -0
- package/llms/stitchkit.txt +7 -1
- package/llms/telegram-html.txt +35 -0
- package/llms/telegram-screens.txt +365 -0
- package/llms/telegram.txt +160 -4
- package/llms-full.txt +755 -11
- package/llms.txt +11 -6
- package/package.json +17 -2
- package/dist/index-2k4yrqkc.js +0 -9
- package/dist/index-crykmqkr.js +0 -26
- package/dist/index-y64k3s0h.js +0 -150
- package/dist/index-y91ry9h2.js +0 -4
- /package/dist/{index-hffjpmq9.js → index-9jfdm5tg.js} +0 -0
- /package/dist/{index-zjjyynm3.js → index-d4pa2bgs.js} +0 -0
- /package/dist/{index-rxfy4cq7.js → index-dwfv9a85.js} +0 -0
- /package/dist/{index-5jm36zv1.js → index-gfk7nrht.js} +0 -0
- /package/dist/{index-b9aefrqd.js → index-n1dqr7gf.js} +0 -0
- /package/dist/{index-q3pyb9n0.js → index-sykb5ag7.js} +0 -0
- /package/dist/{index-48ffdxgk.js → index-xhzq2qvj.js} +0 -0
- /package/dist/{index-kzfs85xp.js → index-zsarb5xp.js} +0 -0
|
@@ -1,71 +1,71 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
AppError2,
|
|
3
|
+
badRequest2,
|
|
4
|
+
forbidden2
|
|
5
|
+
} from "./index-n53mswt3.js";
|
|
4
6
|
import {
|
|
5
|
-
ShutdownOptionsSchema,
|
|
6
7
|
isStreamCancellation,
|
|
7
8
|
ownHttpStream,
|
|
8
|
-
settleStreamCleanup
|
|
9
|
-
|
|
9
|
+
settleStreamCleanup,
|
|
10
|
+
ShutdownOptionsSchema2
|
|
11
|
+
} from "./index-q0th8q0z.js";
|
|
10
12
|
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
errorCode2,
|
|
14
|
+
recordedErrorMessage,
|
|
15
|
+
normalizeError2,
|
|
16
|
+
validateDeclaredOutput
|
|
17
|
+
} from "./index-b2bz0kdm.js";
|
|
14
18
|
import {
|
|
15
|
-
|
|
16
|
-
} from "./index-
|
|
19
|
+
resolveTraceContext2
|
|
20
|
+
} from "./index-hby4qhax.js";
|
|
17
21
|
import {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
assertCorsConfig,
|
|
21
|
-
bindContract,
|
|
22
|
-
corsPreflightResponse,
|
|
23
|
-
defaultSignalSource,
|
|
24
|
-
guardSignalCallback,
|
|
25
|
-
isOriginAllowed,
|
|
26
|
-
mediaTypeEssence,
|
|
27
|
-
reportSignalError,
|
|
28
|
-
requestCorsHeaders
|
|
29
|
-
} from "./index-2gxzjvyr.js";
|
|
22
|
+
DEFAULT_CONTRACT_STREAM_FRAME_BYTES2
|
|
23
|
+
} from "./index-r5c4fedv.js";
|
|
30
24
|
import {
|
|
31
25
|
joinRoutePath,
|
|
32
26
|
parseTrailingWildcard
|
|
33
|
-
} from "./index-
|
|
27
|
+
} from "./index-m38ej7e5.js";
|
|
34
28
|
import {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
setRequestError
|
|
39
|
-
} from "./index-zfrhfn82.js";
|
|
29
|
+
createValidatedRealtimeSocket,
|
|
30
|
+
isModuleNotFound
|
|
31
|
+
} from "./index-m79yr938.js";
|
|
40
32
|
import {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
parseQueryParams,
|
|
44
|
-
resolveSocketIp,
|
|
45
|
-
resolveTraceId
|
|
46
|
-
} from "./index-35mh5bhj.js";
|
|
33
|
+
RELEASE_HEADER2
|
|
34
|
+
} from "./index-n15jny5b.js";
|
|
47
35
|
import {
|
|
48
|
-
|
|
49
|
-
|
|
36
|
+
assertCorsConfig,
|
|
37
|
+
isOriginAllowed,
|
|
38
|
+
corsPreflightResponse2,
|
|
39
|
+
requestCorsHeaders,
|
|
40
|
+
applyCors,
|
|
41
|
+
bindContract,
|
|
42
|
+
DEFAULT_PROCESS_SIGNALS,
|
|
43
|
+
defaultSignalSource,
|
|
44
|
+
reportSignalError,
|
|
45
|
+
guardSignalCallback,
|
|
46
|
+
mediaTypeEssence
|
|
47
|
+
} from "./index-afjvjyck.js";
|
|
50
48
|
import {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
} from "./index-
|
|
49
|
+
runWithRequestContext2,
|
|
50
|
+
getRequestContext2,
|
|
51
|
+
setRequestEndpoint2,
|
|
52
|
+
setRequestError2
|
|
53
|
+
} from "./index-r1z0rd9c.js";
|
|
56
54
|
import {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
55
|
+
resolveTraceId2,
|
|
56
|
+
resolveSocketIp2,
|
|
57
|
+
extractIp2,
|
|
58
|
+
getClientInfo2,
|
|
59
|
+
parseQueryParams
|
|
60
|
+
} from "./index-kw1ws0cs.js";
|
|
61
61
|
import {
|
|
62
62
|
isUnsafeKey,
|
|
63
63
|
safeJsonParse
|
|
64
|
-
} from "./index-
|
|
64
|
+
} from "./index-zsarb5xp.js";
|
|
65
65
|
import {
|
|
66
66
|
isRecord,
|
|
67
67
|
transportResult
|
|
68
|
-
} from "./index-
|
|
68
|
+
} from "./index-9jfdm5tg.js";
|
|
69
69
|
|
|
70
70
|
// src/server/multipart-reader.ts
|
|
71
71
|
var DEFAULT_MAX_HEADER_BYTES = 64 * 1024;
|
|
@@ -119,40 +119,40 @@ function concatBytes(left, right) {
|
|
|
119
119
|
function parseBoundary(req) {
|
|
120
120
|
const contentType = req.headers.get("content-type") ?? "";
|
|
121
121
|
if (!contentType.toLowerCase().startsWith("multipart/form-data")) {
|
|
122
|
-
|
|
122
|
+
badRequest2("Request body must be multipart/form-data");
|
|
123
123
|
}
|
|
124
124
|
const match = /(?:^|;)\s*boundary=(?:"([^"]+)"|([^;\s]+))/i.exec(contentType);
|
|
125
125
|
const boundary = match?.[1] ?? match?.[2];
|
|
126
126
|
if (!boundary || boundary.length > 200)
|
|
127
|
-
|
|
127
|
+
badRequest2("Invalid multipart boundary");
|
|
128
128
|
return boundary;
|
|
129
129
|
}
|
|
130
130
|
function parseDisposition(value) {
|
|
131
131
|
if (!value)
|
|
132
|
-
|
|
132
|
+
badRequest2("Invalid multipart disposition");
|
|
133
133
|
const separator = value.indexOf(";");
|
|
134
134
|
const kind = separator < 0 ? value : value.slice(0, separator);
|
|
135
135
|
if (trimWhitespace(kind).toLowerCase() !== "form-data") {
|
|
136
|
-
|
|
136
|
+
badRequest2("Invalid multipart disposition");
|
|
137
137
|
}
|
|
138
138
|
const parameters = new Map;
|
|
139
139
|
let rest = separator < 0 ? "" : value.slice(separator);
|
|
140
140
|
while (rest.length > 0) {
|
|
141
141
|
const match = /^;[ \t]*([^=; \t]+)[ \t]*=[ \t]*(?:"((?:[^"\\]|\\.)*)"|([^; \t]+))[ \t]*/.exec(rest);
|
|
142
142
|
if (!match?.[1])
|
|
143
|
-
|
|
143
|
+
badRequest2("Invalid multipart disposition parameter");
|
|
144
144
|
const key = match[1].toLowerCase();
|
|
145
145
|
if (!TOKEN.test(key) || parameters.has(key)) {
|
|
146
|
-
|
|
146
|
+
badRequest2("Invalid or duplicate multipart disposition parameter");
|
|
147
147
|
}
|
|
148
148
|
const quoted = match[2];
|
|
149
149
|
const raw = match[3] ?? "";
|
|
150
150
|
if (quoted === undefined && !TOKEN.test(raw)) {
|
|
151
|
-
|
|
151
|
+
badRequest2("Invalid multipart disposition parameter");
|
|
152
152
|
}
|
|
153
153
|
const decoded = quoted === undefined ? raw : quoted.replace(/\\(.)/g, "$1");
|
|
154
154
|
if (hasControl(decoded))
|
|
155
|
-
|
|
155
|
+
badRequest2("Invalid multipart disposition parameter");
|
|
156
156
|
parameters.set(key, decoded);
|
|
157
157
|
rest = rest.slice(match[0].length);
|
|
158
158
|
}
|
|
@@ -162,17 +162,17 @@ function parseDisposition(value) {
|
|
|
162
162
|
if (extended !== undefined) {
|
|
163
163
|
const match = /^utf-8'[A-Za-z0-9-]*'((?:[!#$&+.^_`|~0-9A-Za-z-]|%[0-9A-Fa-f]{2})*)$/i.exec(extended);
|
|
164
164
|
if (!match)
|
|
165
|
-
|
|
165
|
+
badRequest2("Invalid multipart extended filename");
|
|
166
166
|
try {
|
|
167
167
|
filename = decodeURIComponent(match[1] ?? "");
|
|
168
168
|
} catch {
|
|
169
|
-
|
|
169
|
+
badRequest2("Invalid multipart extended filename");
|
|
170
170
|
}
|
|
171
171
|
if (hasControl(filename))
|
|
172
|
-
|
|
172
|
+
badRequest2("Invalid multipart extended filename");
|
|
173
173
|
}
|
|
174
174
|
if (!name || isUnsafeKey(name))
|
|
175
|
-
|
|
175
|
+
badRequest2("Invalid multipart field name");
|
|
176
176
|
return filename === undefined ? { name } : { name, filename };
|
|
177
177
|
}
|
|
178
178
|
function parsePartHeaders(bytes) {
|
|
@@ -180,21 +180,21 @@ function parsePartHeaders(bytes) {
|
|
|
180
180
|
try {
|
|
181
181
|
text = headerDecoder.decode(bytes);
|
|
182
182
|
} catch {
|
|
183
|
-
|
|
183
|
+
badRequest2("Invalid multipart headers");
|
|
184
184
|
}
|
|
185
185
|
const headers = new Map;
|
|
186
186
|
for (const line of text.split(`\r
|
|
187
187
|
`)) {
|
|
188
188
|
const separator = line.indexOf(":");
|
|
189
189
|
if (separator <= 0)
|
|
190
|
-
|
|
190
|
+
badRequest2("Invalid multipart header");
|
|
191
191
|
const name = line.slice(0, separator).toLowerCase();
|
|
192
192
|
const value = trimWhitespace(line.slice(separator + 1));
|
|
193
193
|
if (!TOKEN.test(name) || hasControl(value, true)) {
|
|
194
|
-
|
|
194
|
+
badRequest2("Invalid multipart header");
|
|
195
195
|
}
|
|
196
196
|
if (headers.has(name))
|
|
197
|
-
|
|
197
|
+
badRequest2("Duplicate multipart header");
|
|
198
198
|
headers.set(name, value);
|
|
199
199
|
}
|
|
200
200
|
const disposition = parseDisposition(headers.get("content-disposition") ?? undefined);
|
|
@@ -202,11 +202,11 @@ function parsePartHeaders(bytes) {
|
|
|
202
202
|
const contentType = rawContentType?.split(";", 1)[0]?.trim().toLowerCase();
|
|
203
203
|
const rawSize = headers.get("content-length");
|
|
204
204
|
if (rawSize !== undefined && !/^[0-9]+$/.test(rawSize)) {
|
|
205
|
-
|
|
205
|
+
badRequest2("Invalid multipart part content-length");
|
|
206
206
|
}
|
|
207
207
|
const declaredSize = rawSize === undefined ? undefined : Number(rawSize);
|
|
208
208
|
if (declaredSize !== undefined && (!Number.isSafeInteger(declaredSize) || declaredSize < 0)) {
|
|
209
|
-
|
|
209
|
+
badRequest2("Invalid multipart part content-length");
|
|
210
210
|
}
|
|
211
211
|
return { ...disposition, contentType, declaredSize };
|
|
212
212
|
}
|
|
@@ -223,7 +223,7 @@ class MultipartStreamReader {
|
|
|
223
223
|
#activePart = false;
|
|
224
224
|
constructor(req, boundary, maxRequestBytes) {
|
|
225
225
|
if (!req.body)
|
|
226
|
-
|
|
226
|
+
badRequest2("Multipart request body is empty");
|
|
227
227
|
this.#reader = req.body.getReader();
|
|
228
228
|
this.#delimiter = encoder.encode(`\r
|
|
229
229
|
--${boundary}`);
|
|
@@ -233,14 +233,14 @@ class MultipartStreamReader {
|
|
|
233
233
|
async start() {
|
|
234
234
|
await this.#ensure(this.#initialBoundary.length + 2);
|
|
235
235
|
if (!startsWithBytes(this.#buffer, this.#initialBoundary)) {
|
|
236
|
-
|
|
236
|
+
badRequest2("Malformed multipart body");
|
|
237
237
|
}
|
|
238
238
|
this.#consume(this.#initialBoundary.length);
|
|
239
239
|
await this.#consumeBoundarySuffix();
|
|
240
240
|
}
|
|
241
241
|
async nextPart() {
|
|
242
242
|
if (this.#activePart)
|
|
243
|
-
|
|
243
|
+
badRequest2("Multipart part stream was not fully consumed");
|
|
244
244
|
if (this.#terminal)
|
|
245
245
|
return null;
|
|
246
246
|
const header = await this.#readUntil(HEADER_END, DEFAULT_MAX_HEADER_BYTES);
|
|
@@ -296,7 +296,7 @@ class MultipartStreamReader {
|
|
|
296
296
|
return { value, end: false };
|
|
297
297
|
}
|
|
298
298
|
if (this.#sourceDone)
|
|
299
|
-
|
|
299
|
+
badRequest2("Incomplete multipart body");
|
|
300
300
|
await this.#readMore();
|
|
301
301
|
}
|
|
302
302
|
}
|
|
@@ -310,7 +310,7 @@ class MultipartStreamReader {
|
|
|
310
310
|
return;
|
|
311
311
|
}
|
|
312
312
|
if (!startsWithBytes(this.#buffer, CRLF))
|
|
313
|
-
|
|
313
|
+
badRequest2("Malformed multipart boundary");
|
|
314
314
|
this.#consume(2);
|
|
315
315
|
}
|
|
316
316
|
async#readUntil(marker, maxBytes) {
|
|
@@ -318,15 +318,15 @@ class MultipartStreamReader {
|
|
|
318
318
|
const index = indexOfBytes(this.#buffer, marker);
|
|
319
319
|
if (index >= 0) {
|
|
320
320
|
if (index > maxBytes)
|
|
321
|
-
|
|
321
|
+
badRequest2("Multipart part headers are too large");
|
|
322
322
|
const value = this.#buffer.slice(0, index);
|
|
323
323
|
this.#consume(index + marker.length);
|
|
324
324
|
return value;
|
|
325
325
|
}
|
|
326
326
|
if (this.#buffer.length > maxBytes)
|
|
327
|
-
|
|
327
|
+
badRequest2("Multipart part headers are too large");
|
|
328
328
|
if (this.#sourceDone)
|
|
329
|
-
|
|
329
|
+
badRequest2("Incomplete multipart headers");
|
|
330
330
|
await this.#readMore();
|
|
331
331
|
}
|
|
332
332
|
}
|
|
@@ -334,7 +334,7 @@ class MultipartStreamReader {
|
|
|
334
334
|
while (this.#buffer.length < length && !this.#sourceDone)
|
|
335
335
|
await this.#readMore();
|
|
336
336
|
if (this.#buffer.length < length)
|
|
337
|
-
|
|
337
|
+
badRequest2("Incomplete multipart body");
|
|
338
338
|
}
|
|
339
339
|
async#readMore() {
|
|
340
340
|
const result = await this.#reader.read();
|
|
@@ -345,7 +345,7 @@ class MultipartStreamReader {
|
|
|
345
345
|
this.#readBytes += result.value.length;
|
|
346
346
|
if (this.#readBytes > this.#maxRequestBytes) {
|
|
347
347
|
this.cancel();
|
|
348
|
-
|
|
348
|
+
badRequest2(`Multipart request exceeds ${this.#maxRequestBytes} bytes`);
|
|
349
349
|
}
|
|
350
350
|
this.#buffer = concatBytes(this.#buffer, result.value);
|
|
351
351
|
}
|
|
@@ -388,7 +388,7 @@ function limitedStream(stream, maxBytes, label, signal) {
|
|
|
388
388
|
total += result.value.length;
|
|
389
389
|
if (total > maxBytes) {
|
|
390
390
|
cancelReader();
|
|
391
|
-
|
|
391
|
+
badRequest2(`${label} exceeds ${maxBytes} bytes`);
|
|
392
392
|
}
|
|
393
393
|
controller.enqueue(result.value);
|
|
394
394
|
} catch (error) {
|
|
@@ -424,7 +424,7 @@ async function readText(stream, maxBytes) {
|
|
|
424
424
|
total += result.value.length;
|
|
425
425
|
if (total > maxBytes) {
|
|
426
426
|
await reader.cancel();
|
|
427
|
-
|
|
427
|
+
badRequest2(`Multipart text field exceeds ${maxBytes} bytes`);
|
|
428
428
|
}
|
|
429
429
|
chunks.push(result.value);
|
|
430
430
|
}
|
|
@@ -439,11 +439,11 @@ async function readText(stream, maxBytes) {
|
|
|
439
439
|
function required(policy) {
|
|
440
440
|
return policy.required !== false;
|
|
441
441
|
}
|
|
442
|
-
async function
|
|
442
|
+
async function parseMultipart2(req, descriptor, fieldsSchema, receivers) {
|
|
443
443
|
const maxRequestBytes = descriptor.maxRequestBytes ?? DEFAULT_MAX_REQUEST_BYTES;
|
|
444
444
|
const contentLength = Number(req.headers.get("content-length"));
|
|
445
445
|
if (Number.isFinite(contentLength) && contentLength > maxRequestBytes) {
|
|
446
|
-
|
|
446
|
+
badRequest2(`Multipart request exceeds ${maxRequestBytes} bytes`);
|
|
447
447
|
}
|
|
448
448
|
const boundary = parseBoundary(req);
|
|
449
449
|
const parser = new MultipartStreamReader(req, boundary, maxRequestBytes);
|
|
@@ -473,39 +473,39 @@ async function parseMultipart(req, descriptor, fieldsSchema, receivers) {
|
|
|
473
473
|
break;
|
|
474
474
|
partCount += 1;
|
|
475
475
|
if (partCount > DEFAULT_MAX_PARTS)
|
|
476
|
-
|
|
476
|
+
badRequest2("Too many multipart parts");
|
|
477
477
|
const policy = descriptor.files[part.name];
|
|
478
478
|
const isFile = part.filename !== undefined || part.contentType !== undefined;
|
|
479
479
|
if (!isFile) {
|
|
480
480
|
if (policy)
|
|
481
|
-
|
|
481
|
+
badRequest2(`Multipart file field "${part.name}" must contain a file`);
|
|
482
482
|
if (Object.hasOwn(fields, part.name)) {
|
|
483
|
-
|
|
483
|
+
badRequest2(`Duplicate multipart text field: ${part.name}`);
|
|
484
484
|
}
|
|
485
485
|
fields[part.name] = await readText(part.stream, descriptor.maxFieldBytes ?? DEFAULT_MAX_FIELD_BYTES);
|
|
486
486
|
continue;
|
|
487
487
|
}
|
|
488
488
|
if (!policy)
|
|
489
|
-
|
|
489
|
+
badRequest2(`Unexpected multipart file field: ${part.name}`);
|
|
490
490
|
const count = (counts.get(part.name) ?? 0) + 1;
|
|
491
491
|
counts.set(part.name, count);
|
|
492
492
|
const maxFiles = policy.multiple === true ? policy.maxFiles ?? DEFAULT_MAX_PARTS : 1;
|
|
493
493
|
if (count > maxFiles)
|
|
494
|
-
|
|
494
|
+
badRequest2(`Too many files for multipart field: ${part.name}`);
|
|
495
495
|
const contentType = part.contentType ?? "";
|
|
496
496
|
if (policy.contentTypes && !matchesContentType(contentType, policy.contentTypes)) {
|
|
497
|
-
|
|
497
|
+
badRequest2(`Unsupported content type for multipart field "${part.name}"`);
|
|
498
498
|
}
|
|
499
499
|
const maxFileBytes = policy.maxBytes ?? maxRequestBytes;
|
|
500
500
|
if (part.declaredSize !== undefined && part.declaredSize > maxFileBytes) {
|
|
501
|
-
|
|
501
|
+
badRequest2(`Multipart field "${part.name}" exceeds ${maxFileBytes} bytes`);
|
|
502
502
|
}
|
|
503
503
|
const stream = limitedStream(part.stream, maxFileBytes, `Multipart field "${part.name}"`, req.signal);
|
|
504
504
|
let value;
|
|
505
505
|
if (descriptor.delivery === "stream") {
|
|
506
506
|
const receiver = receivers?.[part.name];
|
|
507
507
|
if (!receiver)
|
|
508
|
-
|
|
508
|
+
badRequest2(`Missing multipart receiver for field: ${part.name}`);
|
|
509
509
|
const result = await receiver({
|
|
510
510
|
metadata: {
|
|
511
511
|
field: part.name,
|
|
@@ -519,7 +519,7 @@ async function parseMultipart(req, descriptor, fieldsSchema, receivers) {
|
|
|
519
519
|
cleanups.push(result.cleanup);
|
|
520
520
|
if (!part.consumed()) {
|
|
521
521
|
await stream.cancel();
|
|
522
|
-
|
|
522
|
+
badRequest2(`Multipart receiver for "${part.name}" did not consume its stream`);
|
|
523
523
|
}
|
|
524
524
|
value = result.value;
|
|
525
525
|
} else {
|
|
@@ -538,7 +538,7 @@ async function parseMultipart(req, descriptor, fieldsSchema, receivers) {
|
|
|
538
538
|
for (const [field, policy] of Object.entries(descriptor.files)) {
|
|
539
539
|
const count = counts.get(field) ?? 0;
|
|
540
540
|
if (required(policy) && count === 0)
|
|
541
|
-
|
|
541
|
+
badRequest2(`Missing multipart file field: ${field}`);
|
|
542
542
|
if (policy.multiple === true && count === 0 && !required(policy))
|
|
543
543
|
files[field] = [];
|
|
544
544
|
}
|
|
@@ -588,7 +588,7 @@ async function pumpFrames(p) {
|
|
|
588
588
|
if (!isStreamCancellation(error, p.departed))
|
|
589
589
|
p.pumped.reject(error);
|
|
590
590
|
} else
|
|
591
|
-
p.send(p.framing.frame(
|
|
591
|
+
p.send(p.framing.frame(normalizeError2(error).toJSON()));
|
|
592
592
|
} finally {
|
|
593
593
|
p.pumped.resolve();
|
|
594
594
|
p.end();
|
|
@@ -596,7 +596,7 @@ async function pumpFrames(p) {
|
|
|
596
596
|
}
|
|
597
597
|
|
|
598
598
|
// src/server/streaming-route.ts
|
|
599
|
-
var
|
|
599
|
+
var DEFAULT_STREAM_HEARTBEAT_MS2 = 5000;
|
|
600
600
|
var FRAMINGS = {
|
|
601
601
|
ndjson: {
|
|
602
602
|
contentType: "application/x-ndjson",
|
|
@@ -645,7 +645,7 @@ function responseHeaders(framing, extra) {
|
|
|
645
645
|
return headers;
|
|
646
646
|
}
|
|
647
647
|
function streamingTimings(options) {
|
|
648
|
-
const heartbeatMs = options.heartbeatMs ??
|
|
648
|
+
const heartbeatMs = options.heartbeatMs ?? DEFAULT_STREAM_HEARTBEAT_MS2;
|
|
649
649
|
if (!Number.isFinite(heartbeatMs) || heartbeatMs <= 0) {
|
|
650
650
|
throw new TypeError("heartbeatMs must be a finite positive number of milliseconds");
|
|
651
651
|
}
|
|
@@ -655,7 +655,7 @@ function streamingTimings(options) {
|
|
|
655
655
|
}
|
|
656
656
|
return { heartbeatMs, idleTimeoutSeconds };
|
|
657
657
|
}
|
|
658
|
-
function
|
|
658
|
+
function streamingRoute2(options) {
|
|
659
659
|
const { heartbeatMs, idleTimeoutSeconds } = streamingTimings(options);
|
|
660
660
|
const framing = FRAMINGS[options.format ?? "ndjson"];
|
|
661
661
|
return {
|
|
@@ -686,7 +686,7 @@ function streamingRoute(options) {
|
|
|
686
686
|
settled.reject(error);
|
|
687
687
|
throw error;
|
|
688
688
|
}
|
|
689
|
-
const
|
|
689
|
+
const encoder = new TextEncoder;
|
|
690
690
|
let heartbeat = null;
|
|
691
691
|
let closed = false;
|
|
692
692
|
const stopHeartbeat = () => {
|
|
@@ -729,7 +729,7 @@ function streamingRoute(options) {
|
|
|
729
729
|
if (closed)
|
|
730
730
|
return false;
|
|
731
731
|
try {
|
|
732
|
-
controller.enqueue(
|
|
732
|
+
controller.enqueue(encoder.encode(text));
|
|
733
733
|
return true;
|
|
734
734
|
} catch {
|
|
735
735
|
release();
|
|
@@ -789,11 +789,11 @@ function streamingRoute(options) {
|
|
|
789
789
|
}
|
|
790
790
|
};
|
|
791
791
|
}
|
|
792
|
-
function
|
|
793
|
-
return
|
|
792
|
+
function ndjsonRoute2(options) {
|
|
793
|
+
return streamingRoute2({ ...options, format: "ndjson" });
|
|
794
794
|
}
|
|
795
|
-
function
|
|
796
|
-
return
|
|
795
|
+
function sseRoute2(options) {
|
|
796
|
+
return streamingRoute2({ ...options, format: "sse" });
|
|
797
797
|
}
|
|
798
798
|
|
|
799
799
|
// src/server/context-contribution.ts
|
|
@@ -938,7 +938,7 @@ async function readRequestText(req, maxBytes) {
|
|
|
938
938
|
total += value.byteLength;
|
|
939
939
|
if (total > maxBytes) {
|
|
940
940
|
await reader.cancel();
|
|
941
|
-
|
|
941
|
+
badRequest2(`JSON body exceeds the ${maxBytes}-byte limit`);
|
|
942
942
|
}
|
|
943
943
|
chunks.push(value);
|
|
944
944
|
}
|
|
@@ -963,22 +963,22 @@ function parseJsonBody(req, text, method, cors) {
|
|
|
963
963
|
const essence = mediaTypeEssence(req.headers.get("content-type"));
|
|
964
964
|
if (essence !== "application/json") {
|
|
965
965
|
if (!method.safelistedBody || essence !== "text/plain") {
|
|
966
|
-
|
|
966
|
+
badRequest2("Request body must be application/json");
|
|
967
967
|
}
|
|
968
968
|
const origin = req.headers.get("origin");
|
|
969
969
|
if (!cors || cors.origin === undefined || cors.origin === "*") {
|
|
970
|
-
|
|
970
|
+
forbidden2("A text/plain body requires an explicit cors.origin allow-list on the server");
|
|
971
971
|
}
|
|
972
972
|
if (!origin)
|
|
973
|
-
|
|
973
|
+
forbidden2("A text/plain body requires an Origin header");
|
|
974
974
|
if (!isOriginAllowed(cors, origin)) {
|
|
975
|
-
|
|
975
|
+
forbidden2("A text/plain body is accepted only from an allowed origin");
|
|
976
976
|
}
|
|
977
977
|
}
|
|
978
978
|
try {
|
|
979
979
|
return safeJsonParse(text);
|
|
980
980
|
} catch {
|
|
981
|
-
|
|
981
|
+
badRequest2("Invalid JSON body");
|
|
982
982
|
}
|
|
983
983
|
}
|
|
984
984
|
function buildBaseContext(req, url, pathParams, traceId, clientIp) {
|
|
@@ -997,7 +997,7 @@ function buildBaseContext(req, url, pathParams, traceId, clientIp) {
|
|
|
997
997
|
signal: req.signal,
|
|
998
998
|
...safePathParams,
|
|
999
999
|
traceId,
|
|
1000
|
-
...
|
|
1000
|
+
...getClientInfo2(req, clientIp)
|
|
1001
1001
|
};
|
|
1002
1002
|
}
|
|
1003
1003
|
function parsePathParamsInto(ctx, method) {
|
|
@@ -1007,7 +1007,7 @@ function parsePathParamsInto(ctx, method) {
|
|
|
1007
1007
|
}
|
|
1008
1008
|
async function parseRequestPayloadInto(ctx, req, url, method, maxJsonBodyBytes, cors) {
|
|
1009
1009
|
if (method.multipart) {
|
|
1010
|
-
const multipart = await
|
|
1010
|
+
const multipart = await parseMultipart2(req, method.multipart, method.inputSchema, method.multipartReceivers);
|
|
1011
1011
|
ctx.input = multipart.fields;
|
|
1012
1012
|
ctx.files = multipart.files;
|
|
1013
1013
|
return multipart;
|
|
@@ -1043,7 +1043,7 @@ function buildErrorContext(req, url, traceId, clientIp) {
|
|
|
1043
1043
|
headers: req.headers,
|
|
1044
1044
|
signal: req.signal,
|
|
1045
1045
|
traceId,
|
|
1046
|
-
...
|
|
1046
|
+
...getClientInfo2(req, clientIp)
|
|
1047
1047
|
};
|
|
1048
1048
|
}
|
|
1049
1049
|
|
|
@@ -1069,7 +1069,7 @@ function waitForAbort(signal) {
|
|
|
1069
1069
|
}
|
|
1070
1070
|
function contractStreamResponse(request, context, source, descriptor, operationAbort) {
|
|
1071
1071
|
const format = descriptor.format ?? "ndjson";
|
|
1072
|
-
const maxFrameBytes = descriptor.maxFrameBytes ??
|
|
1072
|
+
const maxFrameBytes = descriptor.maxFrameBytes ?? DEFAULT_CONTRACT_STREAM_FRAME_BYTES2;
|
|
1073
1073
|
const iterator = source[Symbol.asyncIterator]();
|
|
1074
1074
|
let pendingNext;
|
|
1075
1075
|
const settled = Promise.withResolvers();
|
|
@@ -1116,7 +1116,7 @@ function contractStreamResponse(request, context, source, descriptor, operationA
|
|
|
1116
1116
|
const parsed = descriptor.item.safeParse(next.value);
|
|
1117
1117
|
if (!parsed.success) {
|
|
1118
1118
|
console.error("[stitchkit] contract stream item failed validation:", parsed.error);
|
|
1119
|
-
throw new
|
|
1119
|
+
throw new AppError2("STREAM_ITEM_INVALID", "Stream item did not match its contract", 500);
|
|
1120
1120
|
}
|
|
1121
1121
|
const item = parsed.data;
|
|
1122
1122
|
const terminal = descriptor.terminal?.safeParse(item).success ?? false;
|
|
@@ -1125,14 +1125,14 @@ function contractStreamResponse(request, context, source, descriptor, operationA
|
|
|
1125
1125
|
const frame = descriptor.framing === "item" ? item : { type: "data", data: item };
|
|
1126
1126
|
if (encodedBytes(frame, format) > maxFrameBytes) {
|
|
1127
1127
|
console.error(`[stitchkit] contract stream frame exceeded its ${maxFrameBytes} byte limit`);
|
|
1128
|
-
throw new
|
|
1128
|
+
throw new AppError2("STREAM_FRAME_TOO_LARGE", "Stream item exceeded its declared frame limit", 500);
|
|
1129
1129
|
}
|
|
1130
1130
|
yield frame;
|
|
1131
1131
|
if (terminal && descriptor.completion === "terminal")
|
|
1132
1132
|
return;
|
|
1133
1133
|
}
|
|
1134
1134
|
if (!terminalSeen) {
|
|
1135
|
-
throw new
|
|
1135
|
+
throw new AppError2("STREAM_TERMINAL_MISSING", "Stream completed before its declared terminal item", 500);
|
|
1136
1136
|
}
|
|
1137
1137
|
if (descriptor.framing !== "item")
|
|
1138
1138
|
yield { type: "end" };
|
|
@@ -1144,22 +1144,22 @@ function contractStreamResponse(request, context, source, descriptor, operationA
|
|
|
1144
1144
|
return;
|
|
1145
1145
|
yield {
|
|
1146
1146
|
type: "error",
|
|
1147
|
-
error: new
|
|
1147
|
+
error: new AppError2("STREAM_LIFETIME_EXCEEDED", "Stream exceeded its declared lifetime", 408).toJSON().error
|
|
1148
1148
|
};
|
|
1149
1149
|
return;
|
|
1150
1150
|
}
|
|
1151
1151
|
if (descriptor.framing === "item") {
|
|
1152
|
-
console.error("[stitchkit] item-framed contract stream failed:",
|
|
1152
|
+
console.error("[stitchkit] item-framed contract stream failed:", normalizeError2(error));
|
|
1153
1153
|
return;
|
|
1154
1154
|
}
|
|
1155
|
-
yield { type: "error", error:
|
|
1155
|
+
yield { type: "error", error: normalizeError2(error).toJSON().error };
|
|
1156
1156
|
} finally {
|
|
1157
1157
|
if (lifetime !== undefined)
|
|
1158
1158
|
clearTimeout(lifetime);
|
|
1159
1159
|
closeSource();
|
|
1160
1160
|
}
|
|
1161
1161
|
};
|
|
1162
|
-
const route =
|
|
1162
|
+
const route = streamingRoute2({
|
|
1163
1163
|
path: "/",
|
|
1164
1164
|
format,
|
|
1165
1165
|
heartbeatMs: descriptor.heartbeatMs,
|
|
@@ -1220,7 +1220,7 @@ async function endpointResponse(state, request, outcome) {
|
|
|
1220
1220
|
}
|
|
1221
1221
|
function rawEndpointResponse(state, request, { match: { method }, result }) {
|
|
1222
1222
|
if (!(result instanceof Response)) {
|
|
1223
|
-
throw new
|
|
1223
|
+
throw new AppError2("INTERNAL_SERVER_ERROR", `Raw endpoint ${method.serviceName}.${method.key} must return a Response`, 500);
|
|
1224
1224
|
}
|
|
1225
1225
|
const response = method.method === "HEAD" ? new Response(null, {
|
|
1226
1226
|
status: result.status,
|
|
@@ -1235,7 +1235,7 @@ async function streamEndpointResponse(state, request, { match: { method, pathPar
|
|
|
1235
1235
|
const { req, server, clientIp } = request;
|
|
1236
1236
|
if (!method.stream || !streamAbort || !isAsyncIterable(result)) {
|
|
1237
1237
|
streamAbort?.abort();
|
|
1238
|
-
throw new
|
|
1238
|
+
throw new AppError2("INTERNAL_SERVER_ERROR", `Streaming endpoint ${method.serviceName}.${method.key} must return an AsyncIterable`, 500);
|
|
1239
1239
|
}
|
|
1240
1240
|
const response = await contractStreamResponse(req, { params: pathParams, server, ipAddress: clientIp.socketIp }, result, method.stream, streamAbort);
|
|
1241
1241
|
const withCors = applyCors(response, state.cors, req);
|
|
@@ -1262,27 +1262,27 @@ async function dataEndpointResponse(state, request, {
|
|
|
1262
1262
|
result = transformed;
|
|
1263
1263
|
}
|
|
1264
1264
|
if (result instanceof Response) {
|
|
1265
|
-
throw new
|
|
1265
|
+
throw new AppError2("INTERNAL_SERVER_ERROR", `${method.serviceName}.${method.key} produced a Response on the data path — only a \`rawResponse: true\` endpoint may return one (an afterHandle hook must return data)`, 500);
|
|
1266
1266
|
}
|
|
1267
1267
|
const checked = validateDeclaredOutput(method.outputSchema, result, config.warnOnOutputStrip && method.outputSchema ? (paths) => {
|
|
1268
1268
|
warn(`[stitchkit] output strip ${method.serviceName}.${method.key}: ${paths.join(", ")}`);
|
|
1269
1269
|
} : undefined);
|
|
1270
1270
|
if (!checked.ok) {
|
|
1271
|
-
throw new
|
|
1271
|
+
throw new AppError2("INTERNAL_SERVER_ERROR", checked.message, 500);
|
|
1272
1272
|
}
|
|
1273
1273
|
result = checked.data;
|
|
1274
1274
|
const responseStatus = method.responseMeta?.status ?? (method.outputSchema ? 200 : 204);
|
|
1275
1275
|
if (method.outputSchema && (responseStatus === 204 || responseStatus === 205)) {
|
|
1276
|
-
throw new
|
|
1276
|
+
throw new AppError2("INTERNAL_SERVER_ERROR", `${method.serviceName}.${method.key} cannot combine output with bodyless status ${responseStatus}`, 500);
|
|
1277
1277
|
}
|
|
1278
|
-
const
|
|
1279
|
-
applyResponseMetadata(
|
|
1278
|
+
const responseHeaders = new Headers(requestCorsHeaders(cors, req));
|
|
1279
|
+
applyResponseMetadata(responseHeaders, responseMetadata, `${method.serviceName}.${method.key}`);
|
|
1280
1280
|
if (!method.outputSchema) {
|
|
1281
|
-
const empty = new Response(null, { status: responseStatus, headers:
|
|
1281
|
+
const empty = new Response(null, { status: responseStatus, headers: responseHeaders });
|
|
1282
1282
|
request.complete(responseStatus);
|
|
1283
1283
|
return empty;
|
|
1284
1284
|
}
|
|
1285
|
-
const body = Response.json(result, { status: responseStatus, headers:
|
|
1285
|
+
const body = Response.json(result, { status: responseStatus, headers: responseHeaders });
|
|
1286
1286
|
request.complete(responseStatus);
|
|
1287
1287
|
return body;
|
|
1288
1288
|
}
|
|
@@ -1293,7 +1293,7 @@ async function dispatchEndpoint(state, request, match) {
|
|
|
1293
1293
|
const { req, url, traceId, clientIp } = request;
|
|
1294
1294
|
const { method, pathParams, groupHooks } = match;
|
|
1295
1295
|
const ctx = buildBaseContext(req, url, pathParams, traceId, clientIp);
|
|
1296
|
-
|
|
1296
|
+
setRequestEndpoint2(method.serviceName, method.key);
|
|
1297
1297
|
let multipartLifecycle;
|
|
1298
1298
|
try {
|
|
1299
1299
|
parsePathParamsInto(ctx, method);
|
|
@@ -1372,11 +1372,11 @@ function createErrorResponder(state, request) {
|
|
|
1372
1372
|
return response;
|
|
1373
1373
|
}
|
|
1374
1374
|
const recordFailure = (normalized) => {
|
|
1375
|
-
if (
|
|
1375
|
+
if (getRequestContext2()?.error !== undefined)
|
|
1376
1376
|
return;
|
|
1377
|
-
const known =
|
|
1378
|
-
const code = known?.code ??
|
|
1379
|
-
|
|
1377
|
+
const known = AppError2.is(err) ? err : normalized;
|
|
1378
|
+
const code = known?.code ?? errorCode2(err) ?? "INTERNAL_SERVER_ERROR";
|
|
1379
|
+
setRequestError2({
|
|
1380
1380
|
code,
|
|
1381
1381
|
message: recordedErrorMessage(code, known?.message, err),
|
|
1382
1382
|
details: known?.details
|
|
@@ -1392,13 +1392,13 @@ function createErrorResponder(state, request) {
|
|
|
1392
1392
|
respond: (response) => {
|
|
1393
1393
|
recordFailure();
|
|
1394
1394
|
const withCors = applyCors(response, cors, req);
|
|
1395
|
-
complete(withCors.status,
|
|
1395
|
+
complete(withCors.status, errorCode2(err));
|
|
1396
1396
|
return withCors;
|
|
1397
1397
|
}
|
|
1398
1398
|
});
|
|
1399
1399
|
if (customResponse)
|
|
1400
1400
|
return customResponse;
|
|
1401
|
-
const appErr =
|
|
1401
|
+
const appErr = normalizeError2(err);
|
|
1402
1402
|
recordFailure(appErr);
|
|
1403
1403
|
complete(appErr.status, appErr.code);
|
|
1404
1404
|
return Response.json(appErr.toJSON(), {
|
|
@@ -1461,7 +1461,7 @@ function shouldSkipLog(config, req, url) {
|
|
|
1461
1461
|
function collectExtraLogFields(config, req, url, outcome) {
|
|
1462
1462
|
const fields = {};
|
|
1463
1463
|
const enrichKeys = [];
|
|
1464
|
-
const ctx =
|
|
1464
|
+
const ctx = getRequestContext2();
|
|
1465
1465
|
if (ctx) {
|
|
1466
1466
|
if (ctx.userId)
|
|
1467
1467
|
fields.userId = ctx.userId;
|
|
@@ -1813,15 +1813,15 @@ function createTraceIdResolver(customTraceId, warn) {
|
|
|
1813
1813
|
let traceResolverBroken = false;
|
|
1814
1814
|
return (req, fallback) => {
|
|
1815
1815
|
if (!customTraceId)
|
|
1816
|
-
return fallback ??
|
|
1816
|
+
return fallback ?? resolveTraceId2(req);
|
|
1817
1817
|
try {
|
|
1818
|
-
return customTraceId(req) ?? fallback ??
|
|
1818
|
+
return customTraceId(req) ?? fallback ?? resolveTraceId2(req);
|
|
1819
1819
|
} catch (err) {
|
|
1820
1820
|
if (!traceResolverBroken) {
|
|
1821
1821
|
traceResolverBroken = true;
|
|
1822
1822
|
warn("[stitchkit] `traceId` resolver threw — falling back to the framework resolver. " + "Ids will not match your observability context until it is fixed: " + `${err instanceof Error ? err.message : String(err)}`);
|
|
1823
1823
|
}
|
|
1824
|
-
return fallback ??
|
|
1824
|
+
return fallback ?? resolveTraceId2(req);
|
|
1825
1825
|
}
|
|
1826
1826
|
};
|
|
1827
1827
|
}
|
|
@@ -1880,11 +1880,11 @@ function levelForStatus(status) {
|
|
|
1880
1880
|
return "warn";
|
|
1881
1881
|
return "info";
|
|
1882
1882
|
}
|
|
1883
|
-
function buildLogFields(method, path, status, durationMs, traceId,
|
|
1883
|
+
function buildLogFields(method, path, status, durationMs, traceId, errorCode) {
|
|
1884
1884
|
const fields = { traceId, method, path, status, durationMs };
|
|
1885
|
-
if (
|
|
1885
|
+
if (errorCode === undefined && status >= 400)
|
|
1886
1886
|
return fields;
|
|
1887
|
-
return { ...fields, errorCode
|
|
1887
|
+
return { ...fields, errorCode };
|
|
1888
1888
|
}
|
|
1889
1889
|
function formatMs(ms) {
|
|
1890
1890
|
if (ms >= 1000)
|
|
@@ -1936,21 +1936,21 @@ function structuredLine(own, extra) {
|
|
|
1936
1936
|
}
|
|
1937
1937
|
}
|
|
1938
1938
|
function logOutgoing(entry) {
|
|
1939
|
-
const { req, pathname, status, log, ipAddress, errorCode
|
|
1939
|
+
const { req, pathname, status, log, ipAddress, errorCode, durationMs, format, extra } = entry;
|
|
1940
1940
|
const ms = durationMs;
|
|
1941
1941
|
if (format === "json") {
|
|
1942
1942
|
const own = {
|
|
1943
1943
|
ts: new Date().toISOString(),
|
|
1944
1944
|
level: levelForStatus(status),
|
|
1945
1945
|
msg: `${req.method} ${pathname} ${status}`,
|
|
1946
|
-
...buildLogFields(req.method, pathname, status, durationMs, log.traceId,
|
|
1946
|
+
...buildLogFields(req.method, pathname, status, durationMs, log.traceId, errorCode),
|
|
1947
1947
|
ip: ipAddress
|
|
1948
1948
|
};
|
|
1949
1949
|
console.log(structuredLine(own, extra));
|
|
1950
1950
|
return;
|
|
1951
1951
|
}
|
|
1952
1952
|
const mc = METHOD_COLOR[req.method] ?? c.dim;
|
|
1953
|
-
const code =
|
|
1953
|
+
const code = errorCode ? ` ${c.red}${errorCode}${c.reset}` : "";
|
|
1954
1954
|
console.log(`${c.gray}[${timestamp()}]${c.reset} ${mc}${req.method}${c.reset} ${c.dim}${log.traceId}${c.reset} ${c.cyan}←${c.reset} ${safePath(pathname)} ${statusColor(status)}${status}${c.reset}${code} ${durationColor(ms)}${formatMs(ms)}${c.reset} ${ipLabel(ipAddress ?? "")}`);
|
|
1955
1955
|
}
|
|
1956
1956
|
|
|
@@ -1980,7 +1980,7 @@ function openRequestCompletion(state, window) {
|
|
|
1980
1980
|
} catch {}
|
|
1981
1981
|
}
|
|
1982
1982
|
let completed = false;
|
|
1983
|
-
return (status,
|
|
1983
|
+
return (status, errorCode, outcome) => {
|
|
1984
1984
|
if (completed)
|
|
1985
1985
|
return;
|
|
1986
1986
|
completed = true;
|
|
@@ -1991,12 +1991,12 @@ function openRequestCompletion(state, window) {
|
|
|
1991
1991
|
reqLog,
|
|
1992
1992
|
status,
|
|
1993
1993
|
durationMs,
|
|
1994
|
-
errorCode
|
|
1994
|
+
errorCode,
|
|
1995
1995
|
logFormat
|
|
1996
1996
|
});
|
|
1997
1997
|
} catch {}
|
|
1998
1998
|
}
|
|
1999
|
-
const context =
|
|
1999
|
+
const context = getRequestContext2();
|
|
2000
2000
|
if (observability && context) {
|
|
2001
2001
|
try {
|
|
2002
2002
|
observability.complete({
|
|
@@ -2013,13 +2013,13 @@ function openRequestCompletion(state, window) {
|
|
|
2013
2013
|
function writeCompletionLine(state, logConfig, window, line) {
|
|
2014
2014
|
const { customLogger, useDefaultLog, warnedEnrichKeys, warn } = state;
|
|
2015
2015
|
const { req, url, ipAddress } = window;
|
|
2016
|
-
const { reqLog, status, durationMs, errorCode
|
|
2016
|
+
const { reqLog, status, durationMs, errorCode, logFormat } = line;
|
|
2017
2017
|
const collected = collectExtraLogFields(logConfig, req, url, {
|
|
2018
2018
|
status,
|
|
2019
2019
|
durationMs,
|
|
2020
|
-
errorCode
|
|
2020
|
+
errorCode
|
|
2021
2021
|
});
|
|
2022
|
-
const frameworkFields = buildLogFields(req.method, url.pathname, status, durationMs, reqLog.traceId,
|
|
2022
|
+
const frameworkFields = buildLogFields(req.method, url.pathname, status, durationMs, reqLog.traceId, errorCode);
|
|
2023
2023
|
const ownedFields = { ...frameworkFields, ip: ipAddress };
|
|
2024
2024
|
for (const key of collected.enrichKeys) {
|
|
2025
2025
|
const ownedByActiveSink = Object.hasOwn(ownedFields, key) || useDefaultLog && (key === "ts" || key === "level" || key === "msg");
|
|
@@ -2036,7 +2036,7 @@ function writeCompletionLine(state, logConfig, window, line) {
|
|
|
2036
2036
|
status,
|
|
2037
2037
|
log: reqLog,
|
|
2038
2038
|
ipAddress,
|
|
2039
|
-
errorCode
|
|
2039
|
+
errorCode,
|
|
2040
2040
|
durationMs,
|
|
2041
2041
|
format: logFormat,
|
|
2042
2042
|
extra
|
|
@@ -2044,7 +2044,7 @@ function writeCompletionLine(state, logConfig, window, line) {
|
|
|
2044
2044
|
}
|
|
2045
2045
|
if (customLogger) {
|
|
2046
2046
|
const level = levelForStatus(status);
|
|
2047
|
-
customLogger[level](`${req.method} ${url.pathname} ${status}${
|
|
2047
|
+
customLogger[level](`${req.method} ${url.pathname} ${status}${errorCode ? ` ${errorCode}` : ""} ${durationMs}ms`, {
|
|
2048
2048
|
...extra,
|
|
2049
2049
|
...frameworkFields,
|
|
2050
2050
|
ip: ipAddress
|
|
@@ -2053,7 +2053,7 @@ function writeCompletionLine(state, logConfig, window, line) {
|
|
|
2053
2053
|
}
|
|
2054
2054
|
|
|
2055
2055
|
// src/server/create.ts
|
|
2056
|
-
function
|
|
2056
|
+
function createHandler2(config) {
|
|
2057
2057
|
const { trustProxy = false, observability } = config;
|
|
2058
2058
|
const state = createHandlerState(config);
|
|
2059
2059
|
return async (req, server) => {
|
|
@@ -2061,29 +2061,29 @@ function createHandler(config) {
|
|
|
2061
2061
|
const requestStartedAt = process.hrtime.bigint();
|
|
2062
2062
|
const clientIp = {
|
|
2063
2063
|
trustProxy,
|
|
2064
|
-
socketIp:
|
|
2064
|
+
socketIp: resolveSocketIp2(req, server)
|
|
2065
2065
|
};
|
|
2066
|
-
const run = async (
|
|
2067
|
-
const response = await dispatch(state, req, url,
|
|
2066
|
+
const run = async (traceId, startedAt) => {
|
|
2067
|
+
const response = await dispatch(state, req, url, traceId, server, clientIp, startedAt);
|
|
2068
2068
|
try {
|
|
2069
|
-
response.headers.set("x-request-id",
|
|
2069
|
+
response.headers.set("x-request-id", traceId);
|
|
2070
2070
|
const buildId = config.release?.current();
|
|
2071
2071
|
if (buildId)
|
|
2072
|
-
response.headers.set(
|
|
2072
|
+
response.headers.set(RELEASE_HEADER2, buildId);
|
|
2073
2073
|
} catch {}
|
|
2074
2074
|
return response;
|
|
2075
2075
|
};
|
|
2076
|
-
const activeContext =
|
|
2076
|
+
const activeContext = getRequestContext2();
|
|
2077
2077
|
if (activeContext) {
|
|
2078
2078
|
return run(activeContext.trace.traceId, activeContext.startedAt);
|
|
2079
2079
|
}
|
|
2080
2080
|
if (!observability)
|
|
2081
2081
|
return run(state.resolveId(req), requestStartedAt);
|
|
2082
|
-
const trace =
|
|
2082
|
+
const trace = resolveTraceContext2(req);
|
|
2083
2083
|
const traceId = state.resolveId(req, trace.traceId);
|
|
2084
|
-
const ipAddress =
|
|
2084
|
+
const ipAddress = extractIp2(req, clientIp) || undefined;
|
|
2085
2085
|
const userAgent = req.headers.get("user-agent") ?? undefined;
|
|
2086
|
-
return
|
|
2086
|
+
return runWithRequestContext2({
|
|
2087
2087
|
source: "http",
|
|
2088
2088
|
method: req.method,
|
|
2089
2089
|
path: url.pathname,
|
|
@@ -2096,7 +2096,7 @@ function createHandler(config) {
|
|
|
2096
2096
|
}
|
|
2097
2097
|
async function dispatch(state, req, url, traceId, server, clientIp, startedAt) {
|
|
2098
2098
|
const { cors, hooks, config, routeMap } = state;
|
|
2099
|
-
const ipAddress =
|
|
2099
|
+
const ipAddress = extractIp2(req, clientIp) || undefined;
|
|
2100
2100
|
const complete = openRequestCompletion(state, { req, url, traceId, startedAt, ipAddress });
|
|
2101
2101
|
const respondError = createErrorResponder(state, { req, url, traceId, clientIp, complete });
|
|
2102
2102
|
const request = {
|
|
@@ -2110,7 +2110,7 @@ async function dispatch(state, req, url, traceId, server, clientIp, startedAt) {
|
|
|
2110
2110
|
respondError
|
|
2111
2111
|
};
|
|
2112
2112
|
if (cors && req.method === "OPTIONS") {
|
|
2113
|
-
const response =
|
|
2113
|
+
const response = corsPreflightResponse2(cors, req);
|
|
2114
2114
|
complete(response.status);
|
|
2115
2115
|
return response;
|
|
2116
2116
|
}
|
|
@@ -2130,7 +2130,7 @@ async function dispatch(state, req, url, traceId, server, clientIp, startedAt) {
|
|
|
2130
2130
|
const rawMatch = matchRawRoute(config.rawRoutes, req.method, url.pathname);
|
|
2131
2131
|
if (rawMatch) {
|
|
2132
2132
|
if (rawMatch.route.serviceName !== undefined) {
|
|
2133
|
-
|
|
2133
|
+
setRequestEndpoint2(rawMatch.route.serviceName, rawMatch.route.action);
|
|
2134
2134
|
}
|
|
2135
2135
|
try {
|
|
2136
2136
|
const res = await rawMatch.route.handler(req, {
|
|
@@ -2150,17 +2150,17 @@ async function dispatch(state, req, url, traceId, server, clientIp, startedAt) {
|
|
|
2150
2150
|
if (!match) {
|
|
2151
2151
|
const allow = allowedMethods(routeMap, url.pathname);
|
|
2152
2152
|
if (allow.length > 0) {
|
|
2153
|
-
const res = await respondError(new
|
|
2153
|
+
const res = await respondError(new AppError2("METHOD_NOT_ALLOWED", `Method ${req.method} not allowed`, 405));
|
|
2154
2154
|
res.headers.set("Allow", allow.join(", "));
|
|
2155
2155
|
return res;
|
|
2156
2156
|
}
|
|
2157
|
-
return respondError(new
|
|
2157
|
+
return respondError(new AppError2("NOT_FOUND", "Not found", 404));
|
|
2158
2158
|
}
|
|
2159
2159
|
return dispatchEndpoint(state, request, match);
|
|
2160
2160
|
}
|
|
2161
2161
|
|
|
2162
2162
|
// src/server/implement-registry.ts
|
|
2163
|
-
function
|
|
2163
|
+
function createScopedImplementRegistry2() {
|
|
2164
2164
|
return (contracts, handlers, options) => transportResult(bindRegistry(contracts, handlers, options));
|
|
2165
2165
|
}
|
|
2166
2166
|
function isImplementationContract(value) {
|
|
@@ -2214,16 +2214,16 @@ function bindRegistry(contracts, handlers, options) {
|
|
|
2214
2214
|
Object.defineProperty(services, "byKey", { value: byKey, enumerable: false });
|
|
2215
2215
|
return transportResult(services);
|
|
2216
2216
|
}
|
|
2217
|
-
function
|
|
2217
|
+
function implementRegistry2(contracts, handlers, options) {
|
|
2218
2218
|
return transportResult(bindRegistry(contracts, handlers, options));
|
|
2219
2219
|
}
|
|
2220
|
-
function
|
|
2220
|
+
function createImplementRegistry2() {
|
|
2221
2221
|
return (contracts, handlers, options) => transportResult(bindRegistry(contracts, handlers, options));
|
|
2222
2222
|
}
|
|
2223
2223
|
|
|
2224
2224
|
// src/server/process-signals.ts
|
|
2225
2225
|
var bound = new WeakSet;
|
|
2226
|
-
function
|
|
2226
|
+
function bindProcessSignals2(handle, options = {}) {
|
|
2227
2227
|
if (bound.has(handle)) {
|
|
2228
2228
|
throw new Error("[stitchkit] bindProcessSignals: this server is already bound; close the first binding before creating another");
|
|
2229
2229
|
}
|
|
@@ -2231,7 +2231,7 @@ function bindProcessSignals(handle, options = {}) {
|
|
|
2231
2231
|
const signals = [...new Set(options.signals ?? DEFAULT_PROCESS_SIGNALS)];
|
|
2232
2232
|
const source = options.signalSource ?? defaultSignalSource;
|
|
2233
2233
|
const requested = options.shutdown ?? {};
|
|
2234
|
-
const validated =
|
|
2234
|
+
const validated = ShutdownOptionsSchema2.omit({ signal: true }).parse(requested);
|
|
2235
2235
|
const budgets = {
|
|
2236
2236
|
...requested.gracePeriodMs !== undefined && { gracePeriodMs: validated.gracePeriodMs },
|
|
2237
2237
|
...requested.forceTimeoutMs !== undefined && {
|
|
@@ -2342,7 +2342,7 @@ function bindProcessSignals(handle, options = {}) {
|
|
|
2342
2342
|
}
|
|
2343
2343
|
|
|
2344
2344
|
// src/server/realtime.ts
|
|
2345
|
-
function
|
|
2345
|
+
function bindRealtimeServer2(contract, handle, options = {}) {
|
|
2346
2346
|
const outbound = (target) => createValidatedRealtimeSocket({
|
|
2347
2347
|
target,
|
|
2348
2348
|
inbound: {},
|
|
@@ -2391,7 +2391,7 @@ function bindRealtimeServer(contract, handle, options = {}) {
|
|
|
2391
2391
|
}
|
|
2392
2392
|
|
|
2393
2393
|
// src/server/websocket.ts
|
|
2394
|
-
function
|
|
2394
|
+
function webSocketLane2(lane) {
|
|
2395
2395
|
const { match, handlers } = lane;
|
|
2396
2396
|
return {
|
|
2397
2397
|
match,
|
|
@@ -2423,7 +2423,7 @@ function webSocketLane(lane) {
|
|
|
2423
2423
|
}
|
|
2424
2424
|
};
|
|
2425
2425
|
}
|
|
2426
|
-
function
|
|
2426
|
+
function composeWebSocketHandlers2(lanes, config) {
|
|
2427
2427
|
const pick = (ws) => lanes.find((lane) => lane.match(ws));
|
|
2428
2428
|
return {
|
|
2429
2429
|
...config,
|
|
@@ -2502,7 +2502,7 @@ function handshakeMiddleware(handshake) {
|
|
|
2502
2502
|
});
|
|
2503
2503
|
};
|
|
2504
2504
|
}
|
|
2505
|
-
async function
|
|
2505
|
+
async function createSocketIOServer2(config) {
|
|
2506
2506
|
const path = config.path ?? "/socket.io/";
|
|
2507
2507
|
const onBun = "Bun" in globalThis;
|
|
2508
2508
|
const transports = config.transports ?? (onBun ? ["websocket", "polling"] : ["websocket"]);
|
|
@@ -2626,15 +2626,15 @@ async function createSocketIOServer(config) {
|
|
|
2626
2626
|
...lifecycle
|
|
2627
2627
|
};
|
|
2628
2628
|
}
|
|
2629
|
-
function
|
|
2630
|
-
return
|
|
2629
|
+
function socketIoLane2(websocket) {
|
|
2630
|
+
return webSocketLane2({
|
|
2631
2631
|
match: (_ws) => true,
|
|
2632
2632
|
handlers: websocket
|
|
2633
2633
|
});
|
|
2634
2634
|
}
|
|
2635
2635
|
|
|
2636
2636
|
// src/server/unix-client-error.ts
|
|
2637
|
-
class
|
|
2637
|
+
class UnixClientTransportError2 extends Error {
|
|
2638
2638
|
code;
|
|
2639
2639
|
delivery;
|
|
2640
2640
|
constructor(code, message, delivery = "possibly-dispatched", options) {
|
|
@@ -2658,7 +2658,7 @@ async function readBoundedRequestBody(request, maxBytes) {
|
|
|
2658
2658
|
return;
|
|
2659
2659
|
const declared = request.headers.get("content-length");
|
|
2660
2660
|
if (declared !== null && Number(declared) > maxBytes) {
|
|
2661
|
-
throw new
|
|
2661
|
+
throw new UnixClientTransportError2("UNIX_REQUEST_TOO_LARGE", `Unix request body exceeds the ${maxBytes} byte limit`, "not-dispatched");
|
|
2662
2662
|
}
|
|
2663
2663
|
const reader = body.getReader();
|
|
2664
2664
|
const chunks = [];
|
|
@@ -2672,7 +2672,7 @@ async function readBoundedRequestBody(request, maxBytes) {
|
|
|
2672
2672
|
break;
|
|
2673
2673
|
total += chunk.value.byteLength;
|
|
2674
2674
|
if (total > maxBytes) {
|
|
2675
|
-
throw new
|
|
2675
|
+
throw new UnixClientTransportError2("UNIX_REQUEST_TOO_LARGE", `Unix request body exceeds the ${maxBytes} byte limit`, "not-dispatched");
|
|
2676
2676
|
}
|
|
2677
2677
|
chunks.push(chunk.value);
|
|
2678
2678
|
}
|
|
@@ -2705,7 +2705,7 @@ function incomingResponseBody(response, maxBytes, onDone) {
|
|
|
2705
2705
|
if (maxBytes !== undefined)
|
|
2706
2706
|
received += chunk.byteLength;
|
|
2707
2707
|
if (maxBytes !== undefined && received > maxBytes) {
|
|
2708
|
-
controller.error(new
|
|
2708
|
+
controller.error(new UnixClientTransportError2("UNIX_RESPONSE_TOO_LARGE", `Unix response body exceeds the ${maxBytes} byte limit`, "response-received"));
|
|
2709
2709
|
response.destroy();
|
|
2710
2710
|
finish();
|
|
2711
2711
|
return;
|
|
@@ -2722,13 +2722,13 @@ function incomingResponseBody(response, maxBytes, onDone) {
|
|
|
2722
2722
|
response.once("aborted", () => {
|
|
2723
2723
|
if (finished)
|
|
2724
2724
|
return;
|
|
2725
|
-
controller.error(new
|
|
2725
|
+
controller.error(new UnixClientTransportError2("UNIX_RESPONSE_ABORTED", "Unix response was aborted before completion", "response-received"));
|
|
2726
2726
|
finish();
|
|
2727
2727
|
});
|
|
2728
2728
|
response.once("error", (error) => {
|
|
2729
2729
|
if (finished)
|
|
2730
2730
|
return;
|
|
2731
|
-
controller.error(new
|
|
2731
|
+
controller.error(new UnixClientTransportError2("UNIX_RESPONSE_ABORTED", "Unix response failed before completion", "response-received", { cause: error }));
|
|
2732
2732
|
finish();
|
|
2733
2733
|
});
|
|
2734
2734
|
},
|
|
@@ -2988,15 +2988,15 @@ function hasBunUnixRuntime() {
|
|
|
2988
2988
|
return runtimeBun() !== undefined;
|
|
2989
2989
|
}
|
|
2990
2990
|
function classifyTransportFailure(error, headersReceived, dispatched) {
|
|
2991
|
-
if (error instanceof
|
|
2991
|
+
if (error instanceof UnixClientTransportError2)
|
|
2992
2992
|
return error;
|
|
2993
2993
|
if (headersReceived) {
|
|
2994
|
-
return new
|
|
2994
|
+
return new UnixClientTransportError2("UNIX_RESPONSE_ABORTED", "Unix response failed before completion", "response-received", { cause: error });
|
|
2995
2995
|
}
|
|
2996
|
-
return new
|
|
2996
|
+
return new UnixClientTransportError2(dispatched ? "UNIX_DELIVERY_UNCERTAIN" : "UNIX_CONNECT_FAILED", dispatched ? "Unix request transport failed after dispatch may have begun" : "Unix socket connection failed before request dispatch", dispatched ? "possibly-dispatched" : "not-dispatched", { cause: error });
|
|
2997
2997
|
}
|
|
2998
2998
|
function limitError(limit, what, bytes) {
|
|
2999
|
-
return limit === "body" ? new
|
|
2999
|
+
return limit === "body" ? new UnixClientTransportError2("UNIX_RESPONSE_TOO_LARGE", `Unix response body exceeds the ${bytes} byte limit`, "response-received") : new UnixClientTransportError2("UNIX_HEADERS_TOO_LARGE", `Unix response ${what} exceed the ${bytes} byte limit`, "response-received");
|
|
3000
3000
|
}
|
|
3001
3001
|
function socketHandlers(events) {
|
|
3002
3002
|
return {
|
|
@@ -3033,7 +3033,7 @@ function bunUnixRequest(options) {
|
|
|
3033
3033
|
let socket;
|
|
3034
3034
|
let controller;
|
|
3035
3035
|
const wire = new WireBuffer;
|
|
3036
|
-
let
|
|
3036
|
+
let decoder;
|
|
3037
3037
|
let responseResolved = false;
|
|
3038
3038
|
let headersReceived = false;
|
|
3039
3039
|
let settled = false;
|
|
@@ -3053,10 +3053,10 @@ function bunUnixRequest(options) {
|
|
|
3053
3053
|
});
|
|
3054
3054
|
const transportFailure = (error) => classifyTransportFailure(error, headersReceived, writeOffset > 0);
|
|
3055
3055
|
const clearRegistration = options.registerAbort(() => {
|
|
3056
|
-
fail(new
|
|
3056
|
+
fail(new UnixClientTransportError2("UNIX_CLIENT_CLOSED", "Unix client transport is closed"));
|
|
3057
3057
|
});
|
|
3058
3058
|
const timer = setTimeout(() => {
|
|
3059
|
-
fail(new
|
|
3059
|
+
fail(new UnixClientTransportError2("UNIX_HEADERS_TIMEOUT", `Unix response headers did not arrive within ${options.headersTimeoutMs}ms`));
|
|
3060
3060
|
}, options.headersTimeoutMs);
|
|
3061
3061
|
timer.unref();
|
|
3062
3062
|
const removeRequestAbort = () => {
|
|
@@ -3107,13 +3107,13 @@ function bunUnixRequest(options) {
|
|
|
3107
3107
|
};
|
|
3108
3108
|
const headersTooLarge = (what) => limitError("headers", what, options.maxHeaderBytes);
|
|
3109
3109
|
const pump = () => {
|
|
3110
|
-
while (controller &&
|
|
3110
|
+
while (controller && decoder && !settled && !bodyComplete) {
|
|
3111
3111
|
if ((controller.desiredSize ?? 1) <= 0)
|
|
3112
3112
|
return;
|
|
3113
|
-
const step =
|
|
3113
|
+
const step = decoder.next(wire, peerEnded);
|
|
3114
3114
|
if (step.kind === "data") {
|
|
3115
3115
|
enqueue(step.value);
|
|
3116
|
-
if (!settled &&
|
|
3116
|
+
if (!settled && decoder.plainComplete)
|
|
3117
3117
|
completeBody();
|
|
3118
3118
|
} else if (step.kind === "more")
|
|
3119
3119
|
resumeForData();
|
|
@@ -3145,7 +3145,7 @@ function bunUnixRequest(options) {
|
|
|
3145
3145
|
fail(limitError("body", "body", options.maxResponseBytes));
|
|
3146
3146
|
return;
|
|
3147
3147
|
}
|
|
3148
|
-
|
|
3148
|
+
decoder = new BodyDecoder(head.chunked, contentLength, options.maxHeaderBytes);
|
|
3149
3149
|
const bodyless = options.request.method === "HEAD" || status === 204 || status === 205 || status === 304;
|
|
3150
3150
|
clearTimeout(timer);
|
|
3151
3151
|
responseResolved = true;
|
|
@@ -3300,7 +3300,7 @@ function redirectStep(request, status, location, redirectCount, maxRedirects) {
|
|
|
3300
3300
|
if (!location || !isRedirect(status))
|
|
3301
3301
|
return "return";
|
|
3302
3302
|
if (request.redirect === "error" || redirectCount >= maxRedirects) {
|
|
3303
|
-
throw new
|
|
3303
|
+
throw new UnixClientTransportError2("UNIX_REDIRECT_REFUSED", redirectCount >= maxRedirects ? `Unix redirect limit ${maxRedirects} exceeded` : "Unix request redirect mode is error", "response-received");
|
|
3304
3304
|
}
|
|
3305
3305
|
return request.redirect === "follow" ? "follow" : "return";
|
|
3306
3306
|
}
|
|
@@ -3343,7 +3343,7 @@ function nodeUnixRequest(input) {
|
|
|
3343
3343
|
});
|
|
3344
3344
|
activeRequests.add(outgoing);
|
|
3345
3345
|
const timer = setTimeout(() => {
|
|
3346
|
-
const error = new
|
|
3346
|
+
const error = new UnixClientTransportError2("UNIX_HEADERS_TIMEOUT", `Unix response headers did not arrive within ${headersTimeoutMs}ms`);
|
|
3347
3347
|
rejectOnce(error);
|
|
3348
3348
|
outgoing.destroy(error);
|
|
3349
3349
|
}, headersTimeoutMs);
|
|
@@ -3351,11 +3351,11 @@ function nodeUnixRequest(input) {
|
|
|
3351
3351
|
outgoing.once("response", () => clearTimeout(timer));
|
|
3352
3352
|
outgoing.once("error", (error) => {
|
|
3353
3353
|
if (isNodeHeaderOverflow(error)) {
|
|
3354
|
-
rejectOnce(new
|
|
3354
|
+
rejectOnce(new UnixClientTransportError2("UNIX_HEADERS_TOO_LARGE", `Unix response headers exceed the ${maxHeaderBytes} byte limit`, "response-received", { cause: error }));
|
|
3355
3355
|
return;
|
|
3356
3356
|
}
|
|
3357
3357
|
const dispatched = connected && (outgoing.socket?.bytesWritten ?? requestStartBytes) > requestStartBytes;
|
|
3358
|
-
rejectOnce(new
|
|
3358
|
+
rejectOnce(new UnixClientTransportError2(dispatched ? "UNIX_DELIVERY_UNCERTAIN" : "UNIX_CONNECT_FAILED", dispatched ? "Unix request transport failed after dispatch may have begun" : "Unix socket connection failed before request dispatch", dispatched ? "possibly-dispatched" : "not-dispatched", { cause: error }));
|
|
3359
3359
|
});
|
|
3360
3360
|
outgoing.once("close", () => {
|
|
3361
3361
|
clearTimeout(timer);
|
|
@@ -3364,7 +3364,7 @@ function nodeUnixRequest(input) {
|
|
|
3364
3364
|
outgoing.end(input.body);
|
|
3365
3365
|
});
|
|
3366
3366
|
}
|
|
3367
|
-
function
|
|
3367
|
+
function createUnixClientTransport2(config) {
|
|
3368
3368
|
const {
|
|
3369
3369
|
maxRequestBytes,
|
|
3370
3370
|
maxResponseBytes,
|
|
@@ -3378,9 +3378,9 @@ function createUnixClientTransport(config) {
|
|
|
3378
3378
|
const activeResponses = new Set;
|
|
3379
3379
|
const bunAborters = new Set;
|
|
3380
3380
|
let closed = false;
|
|
3381
|
-
const
|
|
3381
|
+
const dispatch = async (request, redirectCount) => {
|
|
3382
3382
|
if (closed) {
|
|
3383
|
-
throw new
|
|
3383
|
+
throw new UnixClientTransportError2("UNIX_CLIENT_CLOSED", "Unix client transport is closed", "not-dispatched");
|
|
3384
3384
|
}
|
|
3385
3385
|
if (request.signal.aborted)
|
|
3386
3386
|
throw request.signal.reason;
|
|
@@ -3391,7 +3391,7 @@ function createUnixClientTransport(config) {
|
|
|
3391
3391
|
const body = await readBoundedRequestBody(request, maxRequestBytes);
|
|
3392
3392
|
if (hasBunUnixRuntime()) {
|
|
3393
3393
|
if (bunAborters.size >= maxConnections) {
|
|
3394
|
-
throw new
|
|
3394
|
+
throw new UnixClientTransportError2("UNIX_CONNECTION_LIMIT", `Unix client connection limit ${maxConnections} reached`, "not-dispatched");
|
|
3395
3395
|
}
|
|
3396
3396
|
const response = await bunUnixRequest({
|
|
3397
3397
|
socketPath: config.socketPath,
|
|
@@ -3405,17 +3405,17 @@ function createUnixClientTransport(config) {
|
|
|
3405
3405
|
return () => void bunAborters.delete(abort);
|
|
3406
3406
|
}
|
|
3407
3407
|
});
|
|
3408
|
-
const
|
|
3409
|
-
let
|
|
3408
|
+
const location = response.headers.get("location");
|
|
3409
|
+
let step;
|
|
3410
3410
|
try {
|
|
3411
|
-
|
|
3411
|
+
step = redirectStep(request, response.status, location, redirectCount, maxRedirects);
|
|
3412
3412
|
} catch (error) {
|
|
3413
3413
|
await response.body?.cancel();
|
|
3414
3414
|
throw error;
|
|
3415
3415
|
}
|
|
3416
|
-
if (
|
|
3416
|
+
if (step === "follow" && location) {
|
|
3417
3417
|
await response.body?.cancel();
|
|
3418
|
-
return
|
|
3418
|
+
return dispatch(redirectedRequest(request, location, response.status, body), redirectCount + 1);
|
|
3419
3419
|
}
|
|
3420
3420
|
return response;
|
|
3421
3421
|
}
|
|
@@ -3442,7 +3442,7 @@ function createUnixClientTransport(config) {
|
|
|
3442
3442
|
if (declaredResponseBytes !== null && /^\d+$/.test(declaredResponseBytes) && maxResponseBytes !== undefined && Number(declaredResponseBytes) > maxResponseBytes) {
|
|
3443
3443
|
incoming.destroy();
|
|
3444
3444
|
releaseResponse();
|
|
3445
|
-
throw new
|
|
3445
|
+
throw new UnixClientTransportError2("UNIX_RESPONSE_TOO_LARGE", `Unix response body exceeds the ${maxResponseBytes} byte limit`, "response-received");
|
|
3446
3446
|
}
|
|
3447
3447
|
const location = responseHeadersValue.get("location");
|
|
3448
3448
|
let step;
|
|
@@ -3456,7 +3456,7 @@ function createUnixClientTransport(config) {
|
|
|
3456
3456
|
if (step === "follow" && location) {
|
|
3457
3457
|
incoming.destroy();
|
|
3458
3458
|
releaseResponse();
|
|
3459
|
-
return
|
|
3459
|
+
return dispatch(redirectedRequest(request, location, status, body), redirectCount + 1);
|
|
3460
3460
|
}
|
|
3461
3461
|
const bodyless = request.method === "HEAD" || status === 204 || status === 205 || status === 304;
|
|
3462
3462
|
if (bodyless) {
|
|
@@ -3476,7 +3476,7 @@ function createUnixClientTransport(config) {
|
|
|
3476
3476
|
};
|
|
3477
3477
|
return {
|
|
3478
3478
|
fetch(input, init) {
|
|
3479
|
-
return
|
|
3479
|
+
return dispatch(new Request(input, init), 0);
|
|
3480
3480
|
},
|
|
3481
3481
|
get closed() {
|
|
3482
3482
|
return closed;
|
|
@@ -3499,4 +3499,4 @@ function createUnixClientTransport(config) {
|
|
|
3499
3499
|
};
|
|
3500
3500
|
}
|
|
3501
3501
|
|
|
3502
|
-
export { contextContributionDelta, prepareContextContribution, mergeContextContribution,
|
|
3502
|
+
export { contextContributionDelta, prepareContextContribution, mergeContextContribution, parseMultipart2, DEFAULT_STREAM_HEARTBEAT_MS2, streamingRoute2, ndjsonRoute2, sseRoute2, createHandler2, createScopedImplementRegistry2, implementRegistry2, createImplementRegistry2, bindProcessSignals2, bindRealtimeServer2, webSocketLane2, composeWebSocketHandlers2, createSocketIOServer2, socketIoLane2, UnixClientTransportError2, createUnixClientTransport2 };
|