UNPKG

millheat-js

Version:

Javascript client to ease the use of the Mill Open API V2

1 lines 11.8 kB
{"version":3,"file":"millheat-js.mjs","sources":["../src/authenticator.ts","../src/api.ts","../src/client.ts"],"sourcesContent":["import { MillCredential } from \"./client\";\nimport fetch from \"node-fetch\";\nimport { type Agent } from \"https\";\n\ninterface Token {\n idToken: string;\n refreshToken: string;\n expireTime: number;\n refreshExpireTime: number;\n}\n\ninterface TokenPayload {\n sub: string;\n type: 'id',\n iat: number;\n exp: number;\n}\n\ntype AuthenticatorStatus =\n | {\n type: \"initial\";\n }\n | {\n type: \"authenticating\";\n result: Promise<Token>;\n }\n | {\n type: \"authenticated\";\n accessToken: Token;\n }\n | {\n type: \"failed\";\n error: unknown;\n };\n\nexport class Authenticator {\n private status: AuthenticatorStatus = { type: \"initial\" };\n\n constructor(\n private credentials: MillCredential,\n private apiURL: string,\n private agent?: Agent\n ) {}\n\n async authenticate() {\n let accessToken = await this.retrieveAccessToken();\n return [[\"Authorization\", `Bearer ${accessToken.idToken}`]];\n }\n\n private async retrieveAccessToken(): Promise<Token> {\n switch (this.status.type) {\n case \"authenticating\":\n return await this.status.result;\n case \"authenticated\":\n if (Date.now() < this.status.accessToken.expireTime) {\n return this.status.accessToken;\n }\n }\n let result = this.refresh();\n this.status = { type: \"authenticating\", result };\n try {\n let accessToken = await result;\n this.status = { type: \"authenticated\", accessToken };\n return accessToken;\n } catch (error) {\n this.status = { type: \"failed\", error };\n return Promise.reject(error);\n }\n }\n\n private async refresh(): Promise<Token> {\n let response = await fetch(`${this.apiURL}/customer/auth/sign-in\n `, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n login: this.credentials.username,\n password: this.credentials.password\n }),\n agent: this.agent,\n });\n \n let payload = (await response.json()) as {idToken: string, refreshToken: string};\n try {\n let parts = payload.idToken.split(\".\");\n let decoded = JSON.parse(Buffer.from(parts[1], \"base64\").toString()) as TokenPayload;\n return {\n ...payload,\n expireTime: Date.now() + decoded.exp - decoded.iat,\n refreshExpireTime: Date.now() + decoded.exp - decoded.iat,\n }\n } catch (error) {\n return Promise.reject(error);\n }\n }\n}\n","import fetch, { Headers, RequestInfo, RequestInit } from \"node-fetch\";\nimport type { AbortSignal } from \"node-fetch/externals\";\nimport { Result } from \"./models/result\";\n\nexport enum APIErrorCode {\n unauthorized = 401,\n}\n\nexport class APIError extends Error {\n code: APIErrorCode;\n message: string;\n\n constructor(apiError: APIErrorResponse) {\n super(apiError.message);\n Object.setPrototypeOf(this, APIError.prototype);\n this.name = \"APIError\";\n this.code = apiError.errorCode;\n this.message = apiError.message;\n }\n\n toString() {\n return `APIError(code: ${this.code} type: ${this.type} : ${this.message}`;\n }\n\n get type() {\n switch (this.code) {\n case APIErrorCode.unauthorized:\n return \"unauthorized\";\n default:\n return \"unknown\";\n }\n }\n}\n\nexport interface APIMethod {\n parameters: {} | null;\n resultType: {};\n}\n\nexport interface APIErrorResponse {\n errorCode: APIErrorCode | number;\n statusCode: number;\n success: boolean;\n message: string;\n}\n\nexport async function execute<T extends APIMethod>(\n input: RequestInfo,\n init: RequestInit,\n request: T[\"parameters\"],\n timeout: number\n): Promise<T[\"resultType\"]> {\n const AbortController =\n globalThis.AbortController || (await import(\"abort-controller\")).default;\n\n let cancelController = new AbortController();\n let cancelTimeout = setTimeout(() => {\n cancelController.abort();\n }, timeout);\n\n let headers = new Headers(init.headers);\n\n let requestInit = {\n ...init,\n body: request ? JSON.stringify(request) : undefined,\n method: request ? \"POST\" : \"GET\",\n signal: cancelController.signal as AbortSignal,\n headers: headers,\n };\n try {\n let response = await fetch(input, requestInit);\n if (response.status === 200) {\n let payload = (await response.json()) as Result<T>;\n return payload\n }\n return Promise.reject(new Error(\"Unexpected error\"));\n } catch (error) {\n return Promise.reject(error);\n } finally {\n clearTimeout(cancelTimeout);\n }\n}\n","import { Authenticator } from \"./authenticator\";\nimport { Agent } from \"https\";\nimport { Headers, RequestInit } from \"node-fetch\";\nimport { APIMethod, execute } from \"./api\";\nimport { HouseAPIMethod, HouseResponse } from \"./models/house\";\nimport { DevicesForHouseAPIMethod, DevicesForHouseResponse, IndependentDevicesForHouseAPIMethod, IndependentDevicesForHouseResponse } from \"./models/device\";\n\nconst apiUrl = \"https://api.millnorwaycloud.com\";\n\nexport type MillCredential = {\n username: string;\n password: string;\n};\n\n\ntype Options = {\n timeout: number;\n};\n\nexport class MillheatAPI {\n declare authenticator: Authenticator;\n declare credential: MillCredential;\n\n options: Options;\n agent = new Agent({ keepAlive: true });\n\n constructor(credential: MillCredential, options: Partial<Options> = {}) {\n this.options = {\n timeout: 30000,\n ...options,\n };\n this.credential = credential;\n this.authenticator = new Authenticator(credential, apiUrl, this.agent);\n }\n\n private async api<T extends APIMethod>(\n input: string,\n init: RequestInit,\n request: T[\"parameters\"]\n ) {\n let headers = new Headers(init ? init.headers : undefined);\n let authenticationHeaders = await this.authenticator.authenticate();\n for (let [key, value] of authenticationHeaders) {\n headers.append(key, value);\n }\n\n let requestInit = {\n ...init,\n agent: this.agent,\n headers: headers,\n };\n\n return execute(input, requestInit, request, this.options.timeout);\n }\n\n async getHouses(): Promise<HouseResponse> {\n let response = await this.api<HouseAPIMethod>(\n `${apiUrl}/houses`,\n {},\n null\n );\n return response;\n }\n async getDevicesForHouse(houseId:string): Promise<DevicesForHouseResponse[]> {\n let response = await this.api<DevicesForHouseAPIMethod>(\n `${apiUrl}/houses/${houseId}/devices`,\n {},\n null\n );\n return response;\n }\n async getIndependentDevicesForHouse(houseId:string): Promise<IndependentDevicesForHouseResponse> {\n let response = await this.api<IndependentDevicesForHouseAPIMethod>(\n `${apiUrl}/houses/${houseId}/devices/independent`,\n {},\n null\n );\n return response;\n }\n}"],"names":[],"mappings":"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"}