/** * @private */ class HTTP { static async get( url: string, query?: HTTP.Query, option = {} ) { return await this.fetch( HTTP.METHOD.GET, url, query, undefined, option ); } static async post( url: string, query?: HTTP.Query, body?: HTTP.Body, option = {} ) { return await this.fetch( HTTP.METHOD.POST, url, query, body, option ); } static async patch( url: string, query?: HTTP.Query, body?: HTTP.Body, option = {} ) { return await this.fetch( HTTP.METHOD.PATCH, url, query, body, option ); } static async put( url: string, query?: HTTP.Query, body?: HTTP.Body, option = {} ) { return await this.fetch( HTTP.METHOD.PUT, url, query, body, option ); } static async delete( url: string, query?: HTTP.Query, option = {} ) { return await this.fetch( HTTP.METHOD.DELETE, url, query, undefined, option ); } /** * @summary call fetch * *
     * query = {
     *     key1: value1,
     *     key2: value2,
     * };
     * URL: url?key1=value1&key2=value2
     * 
* window.fetch(); * * @private * @param method * @param url * @param {object} query * @param {object|string} body * @param {object} [option] second argument of window.fetch(url, [init]). */ static async fetch( method: string, url: string, query?: HTTP.Query, body?: HTTP.Body, option: RequestInit = {} ) { const queryText = !query ? "" : "?" + Object.keys( query ).map( k => k + "=" + query[k] ).join("&"); option.method = method; if (body) option.body = typeof(body) === "object" ? JSON.stringify(body) : body; let response; try { response = await fetch( url + queryText, option); } catch( error: unknown ) { if ( error instanceof Error) { throw new HTTP.FetchError("Failed to fetch", url, undefined, error); } else { throw new HTTP.FetchError("Failed to fetch", url, undefined, undefined); } } if ( !response.ok ) { throw new HTTP.FetchError( "Failed to fetch: " + response.statusText, url, response ); } return response; } static isJson( mimeType: string ) { return ( mimeType.startsWith( "application/json" ) || mimeType === "model/gltf+json" ); } } namespace HTTP { export interface Query { [key: string]: string | number; } export type Body = object | string; export enum METHOD { GET = "GET", POST = "POST", PATCH = "PATCH", PUT = "PUT", DELETE = "DELETE", }; export const CONTENT_TYPE = "Content-Type"; export enum RESPONSE_STATUS { NO_CONTENT = 204, }; /** * @private */ export class FetchError extends Error { name: string; url: string; response?: Response; cause?: Error; stack?: string; is_aborted: boolean; constructor( message: string, url: string, response?: Response, cause?: Error ) { super( message + " " + url ); if ( Error.captureStackTrace ) { Error.captureStackTrace( this, FetchError ); } this.name = "FetchError"; this.url = url; this.response = response; this.cause = cause; let is_aborted = false; if ( cause ) { is_aborted = cause.message === "The user aborted a request."; this.stack += "\nCaused-By: " + ( cause.stack || cause ); } this.is_aborted = is_aborted; } } } // namespace HTTP export default HTTP;