{"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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"}