From 2bc62eed017a68114a3d6a0a073959c815c91715 Mon Sep 17 00:00:00 2001 From: Alexander Zinn Date: Sat, 16 Aug 2025 23:47:55 -0400 Subject: [PATCH] created HttpRequest --- src/PCastApi.ts | 0 src/index.ts | 25 ++++++++++++++- src/lang/assertUnreachable.ts | 3 ++ src/net/http/HttpMethod.ts | 8 +++++ src/net/http/HttpRequests.ts | 58 +++++++++++++++++++++++++++++++++++ src/pcast/Channels.ts | 14 +++++++++ 6 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 src/PCastApi.ts create mode 100644 src/lang/assertUnreachable.ts create mode 100644 src/net/http/HttpMethod.ts create mode 100644 src/net/http/HttpRequests.ts create mode 100644 src/pcast/Channels.ts diff --git a/src/PCastApi.ts b/src/PCastApi.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/index.ts b/src/index.ts index 15e749d..523f563 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1 +1,24 @@ -// coming soon... \ No newline at end of file +import {HttpRequests} from './net/http/HttpRequests'; +import {Channels} from './pcast/Channels'; + +// coming soon.. +const applicationCredentials = { + id: 'phenixrts.com-alex.zinn', + secret: 'AMAsDzr.dIuGMZ.Zu52Dt~MQvP!DZwYg' +}; +const pcastUri = 'https://pcast-stg.phenixrts.com'; + +const httpRequests = new HttpRequests( + `${pcastUri}/pcast`, + new Headers({ + 'Content-Type': 'application/json', + Accept: 'application/json', + Authorization: `Basic ${btoa(`${applicationCredentials.id}:${applicationCredentials.secret}`)}` + }) +); + +const channels = new Channels(httpRequests); + +channels.getChannels().then(channels => { + console.log(channels); +}); diff --git a/src/lang/assertUnreachable.ts b/src/lang/assertUnreachable.ts new file mode 100644 index 0000000..1d26929 --- /dev/null +++ b/src/lang/assertUnreachable.ts @@ -0,0 +1,3 @@ +export default function assertUnreachable(x: never): never { + throw new Error(`Unreachable code: ${x}`); +} \ No newline at end of file diff --git a/src/net/http/HttpMethod.ts b/src/net/http/HttpMethod.ts new file mode 100644 index 0000000..67bb495 --- /dev/null +++ b/src/net/http/HttpMethod.ts @@ -0,0 +1,8 @@ +// Replace entire file with simplified string enum +export enum HttpMethod { + Get = 'GET', + Post = 'POST', + Put = 'PUT', + Patch = 'PATCH', + Delete = 'DELETE' +} \ No newline at end of file diff --git a/src/net/http/HttpRequests.ts b/src/net/http/HttpRequests.ts new file mode 100644 index 0000000..30214f5 --- /dev/null +++ b/src/net/http/HttpRequests.ts @@ -0,0 +1,58 @@ +import {HttpMethod} from './HttpMethod'; + +const defaultRequestTimeoutDurationInMilliseconds = 30_000; + +export class HttpRequests { + private readonly _baseUri: string; + private readonly _baseHeaders: Headers; + private readonly _requestTimeoutDuration: number; + + constructor(baseUri: string, baseHeaders: Headers, options: {requestTimeoutDuration?: number} = {}) { + this._baseUri = baseUri; + this._baseHeaders = baseHeaders; + this._requestTimeoutDuration = options.requestTimeoutDuration ?? defaultRequestTimeoutDurationInMilliseconds; + } + + public async request(method: HttpMethod, path: string, options?: RequestInit & {body?: Record | string}): Promise { + const abortController = new AbortController(); + const abortSignal = abortController.signal; + + let requestOptions: RequestInit = { + headers: this._baseHeaders, + method: method.toString(), // Convert enum to string + signal: abortSignal + }; + + if (options?.body && method !== HttpMethod.Get) { + requestOptions.body = typeof options.body === 'string' ? options.body : JSON.stringify(options.body); + } + + return this.makeRequest(path, requestOptions, abortController, this._requestTimeoutDuration); + } + + private async makeRequest(path: string, options: RequestInit, abortController: AbortController, timeoutDuration: number): Promise { + const requestTimeoutId = globalThis.setTimeout(() => abortController.abort(), timeoutDuration); + + try { + const requestPath = `${this._baseUri}${path}`; + const response = await fetch(requestPath, options); + + if (!response.ok) { + throw new Error(`HTTP error! status [${response.status}] ${response.statusText}`); + } + + const responseContentType = response.headers.get('content-type'); + + if (responseContentType?.includes('application/json')) { + return response.json() as T; + } + + return response.text() as T; + } catch (e) { + console.error(e); + return; + } finally { + globalThis.clearTimeout(requestTimeoutId); + } + } +} diff --git a/src/pcast/Channels.ts b/src/pcast/Channels.ts new file mode 100644 index 0000000..d6ea71c --- /dev/null +++ b/src/pcast/Channels.ts @@ -0,0 +1,14 @@ +import {HttpMethod} from '../net/http/HttpMethod'; +import type {HttpRequests} from '../net/http/HttpRequests'; + +export class Channels { + private readonly _httpRequests: HttpRequests; + + constructor(httpRequests: HttpRequests) { + this._httpRequests = httpRequests; + } + + public async getChannels() { + return this._httpRequests.request(HttpMethod.Get, '/channels'); + } +}