Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions src/lib/seam-http-request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ export class SeamHttpRequest<
}

const getUrlPrefix = (input: string): string => {
if (canParseUrl(input)) {
if (isAbsoluteHttpUrl(input)) {
const url = new URL(input).toString()
if (url.endsWith('/')) return url.slice(0, -1)
return url
Expand All @@ -239,11 +239,13 @@ const getUrlPrefix = (input: string): string => {
)
}

// UPSTREAM: Prefer URL.canParse when it has wider support.
// https://caniuse.com/mdn-api_url_canparse_static
const canParseUrl = (input: string): boolean => {
// An input without an http or https scheme, e.g., localhost:3000,
// may still parse as a URL with an unintended scheme, e.g., localhost:,
// and must not be treated as an absolute URL.
const isAbsoluteHttpUrl = (input: string): boolean => {
try {
return new URL(input) != null
const { protocol } = new URL(input)
return protocol === 'http:' || protocol === 'https:'
} catch {
return false
}
Expand Down
33 changes: 33 additions & 0 deletions test/seam/connect/seam-http-request.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,39 @@ test.serial(
},
)

test('SeamHttpRequest: url is a URL when endpoint has an explicit scheme and port', async (t) => {
const { seed } = await getTestServer(t)
const seam = SeamHttp.fromApiKey(seed.seam_apikey1_token, {
endpoint: 'http://localhost:3000',
})

const { url } = seam.devices.get({ device_id: 'abc123' })

t.true(url instanceof URL)
t.deepEqual(
toPlainUrlObject(url),
toPlainUrlObject(
new URL(
'http://localhost:3000/devices/get?device_id=abc123&_strict=true',
),
),
)
})

test.serial(
'SeamHttpRequest: url throws for a scheme-less host in a non-browser environment',
async (t) => {
const { seed } = await getTestServer(t)
const seam = SeamHttp.fromApiKey(seed.seam_apikey1_token, {
endpoint: 'localhost:3000',
})

const request = seam.devices.get({ device_id: 'abc123' })

t.throws(() => request.url, { message: /Cannot resolve origin/ })
},
)

test.serial(
'SeamHttpRequest: url throws if unable to resolve origin',
async (t) => {
Expand Down
Loading