Reject baseURL query and hash components

Co-authored-by: Eric Allam <eric@trigger.dev>
This commit is contained in:
Cursor Agent
2026-02-15 04:27:03 +00:00
parent 915573cfd0
commit db22f32e72
5 changed files with 51 additions and 1 deletions
+1 -1
View File
@@ -656,7 +656,7 @@ If `onError` is omitted, reconnect still returns `null` and continues without ca
`baseURL` supports optional path prefixes and trailing slashes; both trigger and stream URLs
are normalized consistently, surrounding whitespace is trimmed before normalization, and
the resulting value must not be empty. The value must also be a valid absolute URL using
the `http` or `https` protocol.
the `http` or `https` protocol, without query parameters or hash fragments.
For richer TypeScript ergonomics in app code, `@trigger.dev/ai` also exports:
+1
View File
@@ -24,3 +24,4 @@
- Added explicit validation that `baseURL` is non-empty after normalization.
- Added explicit validation that `baseURL` is a valid absolute URL.
- Added explicit validation that `baseURL` uses `http` or `https`.
- Added explicit validation that `baseURL` excludes query parameters and hash fragments.
+1
View File
@@ -163,6 +163,7 @@ both cleanup steps (`set` inactive state and `delete`) even if one of them fails
- `baseURL` must not be empty after trimming/normalization.
- `baseURL` must be a valid absolute URL.
- `baseURL` must use the `http` or `https` protocol.
- `baseURL` must not include query parameters or hash fragments.
## `ai.tool(...)` example
+44
View File
@@ -653,6 +653,28 @@ describe("TriggerChatTransport", function () {
}).toThrowError("baseURL must use http or https protocol");
});
it("throws when baseURL includes query parameters", function () {
expect(function () {
new TriggerChatTransport({
task: "chat-task",
accessToken: "pk_trigger",
baseURL: "https://example.com/base?query=1",
stream: "chat-stream",
});
}).toThrowError("baseURL must not include query parameters or hash fragments");
});
it("throws when baseURL includes hash fragments", function () {
expect(function () {
new TriggerChatTransport({
task: "chat-task",
accessToken: "pk_trigger",
baseURL: "https://example.com/base#fragment",
stream: "chat-stream",
});
}).toThrowError("baseURL must not include query parameters or hash fragments");
});
it("accepts uppercase http protocol in baseURL", async function () {
let observedTriggerPath: string | undefined;
let observedStreamPath: string | undefined;
@@ -2870,6 +2892,28 @@ describe("TriggerChatTransport", function () {
}).toThrowError("baseURL must use http or https protocol");
});
it("throws from factory when baseURL includes query parameters", function () {
expect(function () {
createTriggerChatTransport({
task: "chat-task",
accessToken: "pk_trigger",
baseURL: "https://example.com/base?query=1",
stream: "chat-stream",
});
}).toThrowError("baseURL must not include query parameters or hash fragments");
});
it("throws from factory when baseURL includes hash fragments", function () {
expect(function () {
createTriggerChatTransport({
task: "chat-task",
accessToken: "pk_trigger",
baseURL: "https://example.com/base#fragment",
stream: "chat-stream",
});
}).toThrowError("baseURL must not include query parameters or hash fragments");
});
it("continues streaming when onTriggeredRun callback throws", async function () {
let callbackCalled = false;
const errors: TriggerChatTransportError[] = [];
+4
View File
@@ -480,6 +480,10 @@ function normalizeBaseUrl(baseURL: string) {
throw new Error("baseURL must use http or https protocol");
}
if (parsedBaseUrl.search.length > 0 || parsedBaseUrl.hash.length > 0) {
throw new Error("baseURL must not include query parameters or hash fragments");
}
return normalizedBaseUrl;
}