Cover BOM wrapper and internal BOM baseURL handling

Co-authored-by: Eric Allam <eric@trigger.dev>
This commit is contained in:
Cursor Agent
2026-02-15 05:53:16 +00:00
parent 0d4cc703fd
commit dec91eb6b7
5 changed files with 50 additions and 2 deletions
+1 -1
View File
@@ -10,6 +10,6 @@ Add a new `@trigger.dev/ai` package with:
- reconnect-aware stream handling on top of Trigger.dev Realtime Streams v2
- strict `baseURL` normalization/validation (trimming, path-safe slash handling, absolute `http(s)` URLs only, no query/hash/credentials)
- rejection of internal whitespace characters in normalized `baseURL` values
- rejection of internal invisible separator characters (e.g. zero-width spaces) in normalized `baseURL` values
- rejection of internal invisible separator characters (e.g. zero-width/BOM characters) in normalized `baseURL` values
- deterministic baseURL validation error ordering for multi-issue inputs (internal whitespace → protocol → query/hash → credentials)
- explicit default `baseURL` behavior (`https://api.trigger.dev`) and case-insensitive `HTTP(S)` protocol acceptance
+2
View File
@@ -668,6 +668,7 @@ Examples:
- ✅ ` https://api.trigger.dev/custom-prefix/// ` (trimmed + normalized)
- ✅ `https://api.trigger.dev/custom%20prefix` (percent-encoded whitespace)
- ✅ `https://api.trigger.dev/custom%3Fprefix%23segment` (percent-encoded `?` / `#`)
- ✅ `\uFEFFhttps://api.trigger.dev/custom-prefix/\uFEFF` (BOM wrapper trimmed)
- ❌ `https://api.trigger.dev?foo=bar`
- ❌ `https://api.trigger.dev#fragment`
- ❌ `https://user:pass@api.trigger.dev`
@@ -677,6 +678,7 @@ Examples:
- ❌ `https://api.trigger.dev/\tinternal`
- ❌ `https://api.trigger.dev/\rinternal`
- ❌ `https://api.trigger.dev/\u200Binternal`
- ❌ `https://api.trigger.dev/\uFEFFinternal`
Validation errors use these exact messages:
+1 -1
View File
@@ -26,7 +26,7 @@
- Added explicit validation that `baseURL` uses `http` or `https`.
- Added explicit validation that `baseURL` excludes query parameters and hash fragments.
- Added explicit validation that `baseURL` excludes username/password credentials.
- Added explicit validation that `baseURL` excludes internal whitespace/invisible separator characters.
- Added explicit validation that `baseURL` excludes internal whitespace/invisible separator characters (including zero-width/BOM characters).
- Documented that `HTTP://` and `HTTPS://` are accepted (case-insensitive protocol matching).
- Added deterministic validation ordering for multi-issue baseURL values
(internal whitespace → protocol → query/hash → credentials).
+2
View File
@@ -175,6 +175,7 @@ Examples:
-` https://api.trigger.dev/custom-prefix/// ` (trimmed + normalized)
-`https://api.trigger.dev/custom%20prefix` (percent-encoded whitespace)
-`https://api.trigger.dev/custom%3Fprefix%23segment` (percent-encoded `?` / `#`)
-`\uFEFFhttps://api.trigger.dev/custom-prefix/\uFEFF` (BOM wrapper trimmed)
-`https://api.trigger.dev?foo=bar` (query string)
-`https://api.trigger.dev#fragment` (hash fragment)
-`https://user:pass@api.trigger.dev` (credentials)
@@ -184,6 +185,7 @@ Examples:
-`https://api.trigger.dev/\tinternal` (internal tab characters)
-`https://api.trigger.dev/\rinternal` (internal carriage-return characters)
-`https://api.trigger.dev/\u200Binternal` (internal zero-width-space characters)
-`https://api.trigger.dev/\uFEFFinternal` (internal BOM characters)
Validation errors use these exact messages:
+44
View File
@@ -764,6 +764,17 @@ describe("TriggerChatTransport", function () {
}).toThrowError("baseURL must not contain internal whitespace characters");
});
it("throws when baseURL contains internal BOM characters", function () {
expect(function () {
new TriggerChatTransport({
task: "chat-task",
accessToken: "pk_trigger",
baseURL: "https://api.trigger.dev/\uFEFFinternal",
stream: "chat-stream",
});
}).toThrowError("baseURL must not contain internal whitespace characters");
});
it("throws when baseURL is a relative path", function () {
expect(function () {
new TriggerChatTransport({
@@ -1006,6 +1017,17 @@ describe("TriggerChatTransport", function () {
}).not.toThrow();
});
it("accepts BOM-wrapped baseURL values", function () {
expect(function () {
new TriggerChatTransport({
task: "chat-task",
accessToken: "pk_trigger",
baseURL: "\uFEFFhttps://api.trigger.dev/custom-prefix/\uFEFF",
stream: "chat-stream",
});
}).not.toThrow();
});
it("accepts percent-encoded whitespace in baseURL paths", function () {
expect(function () {
new TriggerChatTransport({
@@ -3393,6 +3415,17 @@ describe("TriggerChatTransport", function () {
}).toThrowError("baseURL must not contain internal whitespace characters");
});
it("throws from factory when baseURL contains internal BOM characters", function () {
expect(function () {
createTriggerChatTransport({
task: "chat-task",
accessToken: "pk_trigger",
baseURL: "https://api.trigger.dev/\uFEFFinternal",
stream: "chat-stream",
});
}).toThrowError("baseURL must not contain internal whitespace characters");
});
it("throws from factory when baseURL protocol is not http or https", function () {
expect(function () {
createTriggerChatTransport({
@@ -3624,6 +3657,17 @@ describe("TriggerChatTransport", function () {
}).not.toThrow();
});
it("accepts BOM-wrapped baseURL values from factory", function () {
expect(function () {
createTriggerChatTransport({
task: "chat-task",
accessToken: "pk_trigger",
baseURL: "\uFEFFhttps://api.trigger.dev/custom-prefix/\uFEFF",
stream: "chat-stream",
});
}).not.toThrow();
});
it("accepts percent-encoded whitespace in baseURL paths from factory", function () {
expect(function () {
createTriggerChatTransport({