UNPKG

@timesheet/sdk

Version:
2 lines 19.3 kB
import P from"axios";var h=class r extends Error{constructor(e,t,s,m){let n=t?m?`${e} (HTTP ${t}, Code: ${m})`:`${e} (HTTP ${t})`:e;super(n),this.name="TimesheetApiError",this.statusCode=t,this.responseBody=s,this.errorCode=m,typeof Error.captureStackTrace=="function"&&Error.captureStackTrace(this,r)}};var q=class extends h{constructor(e,t=401,s){super(e,t,s,"authentication_error"),this.name="TimesheetAuthError"}};var E=class extends h{constructor(e,t){super(e,429,void 0,"rate_limit_exceeded"),this.name="TimesheetRateLimitError",this.retryAfter=t}getRetryAfterDate(){if(!this.retryAfter)return null;let e=Number(this.retryAfter);if(!isNaN(e))return new Date(e*1e3);let t=new Date(this.retryAfter);return isNaN(t.getTime())?null:t}};var L=class{constructor(e){this.config=e,this.httpClient=e.httpClient||P.create({baseURL:e.baseUrl,timeout:3e4,headers:{"User-Agent":"Timesheet-TypeScript-SDK/1.0.0","Content-Type":"application/json"}}),this.httpClient.interceptors.request.use(async t=>{if(t.url!=="/oauth/token"&&this.config.authentication){let s=await this.config.authentication.getAuthHeaders();if(s)for(let[m,n]of Object.entries(s))t.headers.set(m,n)}return t})}async request(e){let t=null,s=this.config.retryConfig;for(let m=0;m<=s.maxRetries;m++)try{return(await this.httpClient.request(e)).data}catch(n){if(t=n,P.isAxiosError(n)&&n.response?.status===401){let p=n.response.data;throw new q(p?.message||"Authentication failed",401,JSON.stringify(p))}if(P.isAxiosError(n)&&n.response?.status===429){let p=n.response.headers["retry-after"];throw new E("Rate limit exceeded",p)}let u=P.isAxiosError(n)?n.response?.status:void 0;if(m<s.maxRetries&&u&&s.retryableStatusCodes.includes(u)){let p=Math.min(s.initialDelay*Math.pow(s.backoffMultiplier,m),s.maxDelay);await this.sleep(p);continue}if(P.isAxiosError(n)){let p=n.response?.data;throw new h(p?.message||n.message,n.response?.status,JSON.stringify(p),p?.code)}else throw n instanceof Error?new h(n.message):new h("Unknown error occurred")}throw t||new Error("Unknown error")}async get(e,t){return this.request({method:"GET",url:e,params:t})}async post(e,t,s){return this.request({method:"POST",url:e,data:t,params:s})}async put(e,t,s){return this.request({method:"PUT",url:e,data:t,params:s})}async delete(e,t){return this.request({method:"DELETE",url:e,params:t})}sleep(e){return new Promise(t=>setTimeout(t,e))}};var I=class{constructor(e){this.apiKey=e;if(e===null)throw new Error("API key cannot be null");if(e===void 0)throw new Error("API key cannot be undefined");if(!e)throw new Error("API key cannot be empty");if(typeof e!="string")throw new Error("API key must be a string");if(e.trim().length===0)throw new Error("API key cannot be empty or whitespace");if(!this.isValidFormat(e))throw new Error("Invalid API key format")}isValidFormat(e){return/^ts_[a-zA-Z0-9]+\.[a-zA-Z0-9]+$/.test(e)}applyAuth(e){e.headers||(e.headers={}),e.headers.Authorization=`ApiKey ${this.apiKey}`}needsRefresh(){return!1}async refresh(){throw new Error("API keys cannot be refreshed")}async getAuthHeaders(){return{Authorization:`ApiKey ${this.apiKey}`}}isValid(){return typeof this.apiKey=="string"&&this.apiKey.trim().length>0&&this.isValidFormat(this.apiKey)}};import z from"axios";import j from"jsonwebtoken";var c=class c{constructor(e,t,s){t&&s?(this.clientId=e,this.clientSecret=t,this.refreshToken=s,this.accessToken=""):(this.accessToken=e,this.parseTokenExpiry())}applyAuth(e){e.headers||(e.headers={}),e.headers.Authorization=`Bearer ${this.accessToken}`}needsRefresh(){return this.refreshToken&&!this.accessToken?!0:!this.refreshToken||!this.tokenExpiry?!1:new Date(Date.now()+5*60*1e3)>=this.tokenExpiry}async refresh(){if(!this.refreshToken)throw new Error("Cannot refresh without refresh token");if(this.refreshPromise)return this.refreshPromise;this.refreshPromise=this.performRefresh();try{await this.refreshPromise}finally{this.refreshPromise=void 0}}async performRefresh(){try{let e=await z.post(c.TOKEN_ENDPOINT,new URLSearchParams({grant_type:"refresh_token",refresh_token:this.refreshToken,client_id:this.clientId,client_secret:this.clientSecret}),{headers:{"Content-Type":"application/x-www-form-urlencoded"}});this.accessToken=e.data.access_token,e.data.refresh_token&&(this.refreshToken=e.data.refresh_token),this.parseTokenExpiry()}catch(e){let t=e instanceof Error?e.message:"Unknown error";throw new Error(`Failed to refresh OAuth2 token: ${t}`)}}async getAuthHeaders(){return this.needsRefresh()&&await this.refresh(),{Authorization:`Bearer ${this.accessToken}`}}static async fromAuthorizationCode(e,t,s,m){try{let n=await z.post(c.TOKEN_ENDPOINT,new URLSearchParams({grant_type:"authorization_code",code:s,redirect_uri:m,client_id:e,client_secret:t}),{headers:{"Content-Type":"application/x-www-form-urlencoded"}}),u=n.data.access_token,p=n.data.refresh_token;return p?new c(e,t,p):new c(u)}catch(n){let u=n instanceof Error?n.message:"Unknown error";throw new Error(`Failed to exchange authorization code: ${u}`)}}static buildAuthorizationUrl(e,t,s){let m=new URLSearchParams({client_id:e,redirect_uri:t,response_type:"code"});return s&&m.append("state",s),`https://api.timesheet.io/oauth2/auth?${m.toString()}`}parseTokenExpiry(){try{let e=j.decode(this.accessToken);e&&e.exp&&(this.tokenExpiry=new Date(e.exp*1e3))}catch{this.tokenExpiry=new Date(Date.now()+60*60*1e3)}}};c.TOKEN_ENDPOINT="https://api.timesheet.io/oauth2/token";var l=c;var S=class r{constructor(e={}){this.maxRetries=e.maxRetries??3,this.initialDelay=e.initialDelay??100,this.maxDelay=e.maxDelay??1e4,this.backoffMultiplier=e.backoffMultiplier??2,this.retryableStatusCodes=e.retryableStatusCodes??[429,502,503,504]}static default(){return new r}};var o=class{constructor(e,t){this.items=e.items,this.params=e.params,this.nextPageLoader=t}get totalPages(){return Math.ceil((this.params?.count||0)/(this.params?.limit||25))}get hasNextPage(){return(this.params?.page||1)<this.totalPages}async nextPage(){if(!this.hasNextPage)throw new Error("No more pages available");if(!this.nextPageLoader)throw new Error("Next page loader not configured");return this.nextPageLoader((this.params.page||1)+1)}async*[Symbol.asyncIterator](){let e=this;for(;;){for(let t of e.items)yield t;if(!e.hasNextPage)break;e=await e.nextPage()}}async toArray(){let e=[];for await(let t of this)e.push(t);return e}};var i=class{constructor(e,t){this.http=e;typeof t=="string"?this.basePath=t:this.basePath=t.basePath}createNavigablePage(e,t){return new o(e,t)}};var d=class extends i{constructor(e){super(e,"/v1/organizations")}async list(e){let t=await this.http.get(this.basePath,e);return new o(t,s=>this.list({...e,page:s}))}async create(e){return this.http.post(this.basePath,e)}async update(e,t){return this.http.put(`${this.basePath}/${encodeURIComponent(e)}`,t)}async get(e){return this.http.get(`${this.basePath}/${encodeURIComponent(e)}`)}async delete(e){return this.http.delete(`${this.basePath}/${encodeURIComponent(e)}`)}async search(e){let t=await this.http.post(`${this.basePath}/search`,e);return this.createNavigablePage(t,s=>this.search({...e,page:s}))}};var f=class extends i{constructor(e){super(e,"/v1/teams")}async list(e){let t=await this.http.get(this.basePath,e);return new o(t,s=>this.list({...e,page:s}))}async create(e){return this.http.post(this.basePath,e)}async update(e,t){return this.http.put(`${this.basePath}/${encodeURIComponent(e)}`,t)}async get(e){return this.http.get(`${this.basePath}/${encodeURIComponent(e)}`)}async delete(e){return this.http.delete(`${this.basePath}/${encodeURIComponent(e)}`)}async search(e){let t=await this.http.post(`${this.basePath}/search`,e);return this.createNavigablePage(t,s=>this.search({...e,page:s}))}async listMembers(e,t){let s=await this.http.post(`${this.basePath}/${e}/members/list`,t);return this.createNavigablePage(s,m=>this.listMembers(e,{...t,page:m}))}async getMember(e,t){return this.http.get(`${this.basePath}/${e}/members/${t}`)}async getColleagues(e){let t=await this.http.post(`${this.basePath}/getColleagues`,e);return this.createNavigablePage(t,s=>this.getColleagues({...e,page:s}))}};var T=class extends i{constructor(e){super(e,"/v1/projects")}async list(e){let t=await this.http.get(this.basePath,e);return new o(t,s=>this.list({...e,page:s}))}async create(e){return this.http.post(this.basePath,e)}async update(e,t){return this.http.put(`${this.basePath}/${encodeURIComponent(e)}`,t)}async get(e){return this.http.get(`${this.basePath}/${encodeURIComponent(e)}`)}async delete(e){return this.http.delete(`${this.basePath}/${encodeURIComponent(e)}`)}async search(e){let t=await this.http.post(`${this.basePath}/search`,e);return this.createNavigablePage(t,s=>this.search({...e,page:s}))}};import g from"dayjs";import M from"dayjs/plugin/utc";import _ from"dayjs/plugin/timezone";import W from"dayjs/plugin/customParseFormat";g.extend(M);g.extend(_);g.extend(W);var F="YYYY-MM-DDTHH:mm:ssZ",K=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+-]\d{2}:\d{2}$/,a={isValidTimestampFormat(r){return K.test(r)},formatTimestamp(r){return typeof r=="string"&&this.isValidTimestampFormat(r)?r:g(r||new Date).format(F)},parseTimestamp(r){return g(r).toDate()},formatDate(r){return g(r||new Date).format("YYYY-MM-DD")},now(){return this.formatTimestamp()}};var y=class extends i{constructor(e){super(e,"/v1/tasks")}async create(e){let t={...e,startDateTime:a.formatTimestamp(e.startDateTime),endDateTime:e.endDateTime?a.formatTimestamp(e.endDateTime):void 0};return this.http.post(this.basePath,t)}async update(e,t){let s={...t,startDateTime:t.startDateTime?a.formatTimestamp(t.startDateTime):void 0,endDateTime:t.endDateTime?a.formatTimestamp(t.endDateTime):void 0};return this.http.put(`${this.basePath}/${encodeURIComponent(e)}`,s)}async get(e){return this.http.get(`${this.basePath}/${encodeURIComponent(e)}`)}async delete(e){return this.http.delete(`${this.basePath}/${encodeURIComponent(e)}`)}async search(e){let t=await this.http.post(`${this.basePath}/search`,e);return this.createNavigablePage(t,s=>this.search({...e,page:s}))}async updateStatus(e){return this.http.put(`${this.basePath}/updateStatus`,e)}async updateTimes(e){let t={...e,startDateTime:a.formatTimestamp(e.startDateTime),endDateTime:a.formatTimestamp(e.endDateTime)};return this.http.put(`${this.basePath}/updateTimes`,t)}};var b=class extends i{constructor(e){super(e,"/v1/rates")}async list(e){let t=await this.http.get(this.basePath,e);return new o(t,s=>this.list({...e,page:s}))}async create(e){return this.http.post(this.basePath,e)}async update(e,t){return this.http.put(`${this.basePath}/${encodeURIComponent(e)}`,t)}async get(e){return this.http.get(`${this.basePath}/${encodeURIComponent(e)}`)}async delete(e){return this.http.delete(`${this.basePath}/${encodeURIComponent(e)}`)}async search(e){let t=await this.http.post(`${this.basePath}/search`,e);return this.createNavigablePage(t,s=>this.search({...e,page:s}))}};var R=class extends i{constructor(e){super(e,"/v1/tags")}async list(e){let t=await this.http.get(this.basePath,e);return new o(t,s=>this.list({...e,page:s}))}async create(e){return this.http.post(this.basePath,e)}async update(e,t){return this.http.put(`${this.basePath}/${encodeURIComponent(e)}`,t)}async get(e){return this.http.get(`${this.basePath}/${encodeURIComponent(e)}`)}async delete(e){return this.http.delete(`${this.basePath}/${encodeURIComponent(e)}`)}async search(e){let t=await this.http.post(`${this.basePath}/search`,e);return this.createNavigablePage(t,s=>this.search({...e,page:s}))}};var x=class extends i{constructor(e){super(e,"/v1/expenses")}async list(e){let t=await this.http.get(this.basePath,e);return new o(t,s=>this.list({...e,page:s}))}async create(e){let t={...e,dateTime:a.formatTimestamp(e.dateTime)};return this.http.post(this.basePath,t)}async update(e,t){let s={...t,dateTime:t.dateTime?a.formatTimestamp(t.dateTime):void 0};return this.http.put(`${this.basePath}/${encodeURIComponent(e)}`,s)}async get(e){return this.http.get(`${this.basePath}/${encodeURIComponent(e)}`)}async delete(e){return this.http.delete(`${this.basePath}/${encodeURIComponent(e)}`)}async search(e){let t=await this.http.post(`${this.basePath}/search`,e);return this.createNavigablePage(t,s=>this.search({...e,page:s}))}async updateStatus(e,t){return this.http.put(`${this.basePath}/${e}/status`,t)}};var C=class extends i{constructor(e){super(e,"/v1/notes")}async list(e){let t=await this.http.get(this.basePath,e);return new o(t,s=>this.list({...e,page:s}))}async create(e){let t={...e,dateTime:a.formatTimestamp(e.dateTime)};return this.http.post(this.basePath,t)}async update(e,t){let s={...t,dateTime:a.formatTimestamp(t.dateTime)};return this.http.put(`${this.basePath}/${encodeURIComponent(e)}`,s)}async get(e){return this.http.get(`${this.basePath}/${encodeURIComponent(e)}`)}async delete(e){return this.http.delete(`${this.basePath}/${encodeURIComponent(e)}`)}async search(e){let t=await this.http.post(`${this.basePath}/search`,e);return this.createNavigablePage(t,s=>this.search({...e,page:s}))}};var w=class extends i{constructor(e){super(e,"/v1/pauses")}async list(e){let t=await this.http.get(this.basePath,e);return new o(t,s=>this.list({...e,page:s}))}async create(e){let t={...e,startDateTime:a.formatTimestamp(e.startDateTime),endDateTime:a.formatTimestamp(e.endDateTime)};return this.http.post(this.basePath,t)}async update(e,t){let s={...t,startDateTime:a.formatTimestamp(t.startDateTime),endDateTime:a.formatTimestamp(t.endDateTime)};return this.http.put(`${this.basePath}/${encodeURIComponent(e)}`,s)}async get(e){return this.http.get(`${this.basePath}/${encodeURIComponent(e)}`)}async delete(e){return this.http.delete(`${this.basePath}/${encodeURIComponent(e)}`)}async search(e){let t=await this.http.post(`${this.basePath}/search`,e);return this.createNavigablePage(t,s=>this.search({...e,page:s}))}};var D=class{constructor(e){this.http=e}async getProfile(){return this.http.get("/v1/profiles/me")}async updateProfile(e){return this.http.put("/v1/profiles/me",e)}};var k=class{constructor(e){this.http=e}async get(){return this.http.get("/v1/settings")}async update(e){return this.http.put("/v1/settings",e)}};var v=class extends i{constructor(e){super(e,"/v1/automations")}async list(e){let t=await this.http.get(this.basePath,e);return new o(t,s=>this.list({...e,page:s}))}async create(e){return this.http.post(this.basePath,e)}async update(e,t){return this.http.put(`${this.basePath}/${encodeURIComponent(e)}`,t)}async get(e){return this.http.get(`${this.basePath}/${encodeURIComponent(e)}`)}async delete(e){return this.http.delete(`${this.basePath}/${encodeURIComponent(e)}`)}async search(e){let t=await this.http.post(`${this.basePath}/search`,e);return this.createNavigablePage(t,s=>this.search({...e,page:s}))}};var A=class extends i{constructor(e){super(e,"/v1/documents")}async list(e){let t=await this.http.get(this.basePath,e);return new o(t,s=>this.list({...e,page:s}))}async create(e){return this.http.post(this.basePath,e)}async update(e,t){return this.http.put(`${this.basePath}/${encodeURIComponent(e)}`,t)}async get(e){return this.http.get(`${this.basePath}/${encodeURIComponent(e)}`)}async delete(e){return this.http.delete(`${this.basePath}/${encodeURIComponent(e)}`)}async search(e){let t=await this.http.post(`${this.basePath}/search`,e);return this.createNavigablePage(t,s=>this.search({...e,page:s}))}};var $=class{constructor(e){this.http=e}async get(){return this.http.get("/v1/timer")}async start(e){let t={...e,startDateTime:e.startDateTime?a.formatTimestamp(e.startDateTime):a.formatTimestamp()};return this.http.post("/v1/timer/start",t)}async stop(e){let t=e?{...e,endDateTime:e.endDateTime?a.formatTimestamp(e.endDateTime):a.formatTimestamp()}:{endDateTime:a.formatTimestamp()};return this.http.post("/v1/timer/stop",t)}async pause(e){let t=e?{...e,startDateTime:e.startDateTime?a.formatTimestamp(e.startDateTime):a.formatTimestamp()}:{startDateTime:a.formatTimestamp()};return this.http.post("/v1/timer/pause",t)}async resume(e){let t=e?{...e,endDateTime:e.endDateTime?a.formatTimestamp(e.endDateTime):a.formatTimestamp()}:{endDateTime:a.formatTimestamp()};return this.http.post("/v1/timer/resume",t)}async update(e){let t={...e,startDateTime:e.startDateTime?a.formatTimestamp(e.startDateTime):void 0};return this.http.put("/v1/timer/update",t)}};var N=class extends i{constructor(e){super(e,"/v1/todos")}async list(e){let t=await this.http.get(this.basePath,e);return new o(t,s=>this.list({...e,page:s}))}async create(e){let t={...e,dueDate:e.dueDate?a.formatTimestamp(e.dueDate):void 0};return this.http.post(this.basePath,t)}async update(e,t){let s={...t,dueDate:t.dueDate?a.formatTimestamp(t.dueDate):void 0};return this.http.put(`${this.basePath}/${encodeURIComponent(e)}`,s)}async get(e){return this.http.get(`${this.basePath}/${encodeURIComponent(e)}`)}async delete(e){return this.http.delete(`${this.basePath}/${encodeURIComponent(e)}`)}async search(e){let t=await this.http.post(`${this.basePath}/search`,e);return this.createNavigablePage(t,s=>this.search({...e,page:s}))}};var U=class extends i{constructor(e){super(e,"/v1/webhooks")}async list(e){let t=await this.http.get(this.basePath,e);return new o(t,s=>this.list({...e,page:s}))}async create(e){return this.http.post(this.basePath,e)}async update(e,t){return this.http.put(`${this.basePath}/${encodeURIComponent(e)}`,t)}async get(e){return this.http.get(`${this.basePath}/${encodeURIComponent(e)}`)}async delete(e){return this.http.delete(`${this.basePath}/${encodeURIComponent(e)}`)}async search(e){let t=await this.http.post(`${this.basePath}/search`,e);return this.createNavigablePage(t,s=>this.search({...e,page:s}))}};var O=class{constructor(e){let t=this.createAuthentication(e),s={baseUrl:e.baseUrl||"https://api.timesheet.io",authentication:t,retryConfig:e.retryConfig||S.default(),httpClient:e.httpClient};this.apiClient=new L(s),this.organizations=new d(this.apiClient),this.teams=new f(this.apiClient),this.projects=new T(this.apiClient),this.tasks=new y(this.apiClient),this.rates=new b(this.apiClient),this.tags=new R(this.apiClient),this.expenses=new x(this.apiClient),this.notes=new C(this.apiClient),this.pauses=new w(this.apiClient),this.profile=new D(this.apiClient),this.settings=new k(this.apiClient),this.automations=new v(this.apiClient),this.documents=new A(this.apiClient),this.timer=new $(this.apiClient),this.todos=new N(this.apiClient),this.webhooks=new U(this.apiClient)}createAuthentication(e){if(e.apiKey)return new I(e.apiKey);if(e.oauth2Token)return new l(e.oauth2Token);if(e.oauth2)return new l(e.oauth2.clientId,e.oauth2.clientSecret,e.oauth2.refreshToken);if(e.authentication)return e.authentication;throw new Error("Authentication must be configured")}};function is(r){return new O(r)}export{L as ApiClient,I as ApiKeyAuth,v as AutomationResource,A as DocumentResource,x as ExpenseResource,o as NavigablePage,C as NoteResource,l as OAuth2Auth,d as OrganizationResource,w as PauseResource,D as ProfileResource,T as ProjectResource,b as RateResource,i as Resource,S as RetryConfig,k as SettingsResource,R as TagResource,y as TaskResource,f as TeamResource,$ as TimerResource,h as TimesheetApiError,q as TimesheetAuthError,O as TimesheetClient,E as TimesheetRateLimitError,N as TodoResource,U as WebhookResource,is as createClient}; //# sourceMappingURL=index.mjs.map