tiime-sdk 2.2.0 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -7,7 +7,7 @@ interface AuthConfig {
7
7
  password: string;
8
8
  }
9
9
  interface TiimeClientOptions {
10
- companyId?: number;
10
+ companyId: number;
11
11
  tokens?: AuthTokens;
12
12
  email?: string;
13
13
  password?: string;
@@ -436,28 +436,29 @@ interface MatchableDocument {
436
436
  }[];
437
437
  }
438
438
 
439
- /**
440
- * Resolve companyId from multiple sources (in priority order):
441
- * 1. Explicit option
442
- * 2. TIIME_COMPANY_ID env var
443
- * 3. ~/.config/tiime/config.json
444
- */
445
- declare const resolveCompanyId: (explicit?: number) => number;
439
+ interface TokenStorage {
440
+ load(): AuthTokens | null;
441
+ save(tokens: AuthTokens): void;
442
+ clear(): void;
443
+ }
444
+ interface CredentialStorage {
445
+ load(): {
446
+ email: string;
447
+ password: string;
448
+ } | null;
449
+ save(email: string, password: string): void;
450
+ }
446
451
  declare class TokenManager {
447
452
  private tokens;
448
453
  private credentials;
449
- private persist;
450
- /**
451
- * @param options.tokens - Use these tokens directly (no disk I/O)
452
- * @param options.email - Login with these credentials
453
- * @param options.password - Login with these credentials
454
- * @param options.persist - Save tokens/credentials to disk (default: true when no explicit auth)
455
- */
454
+ private tokenStorage;
455
+ private credentialStorage;
456
456
  constructor(options?: {
457
457
  tokens?: AuthTokens;
458
458
  email?: string;
459
459
  password?: string;
460
- persist?: boolean;
460
+ tokenStorage?: TokenStorage;
461
+ credentialStorage?: CredentialStorage;
461
462
  });
462
463
  login(email: string, password: string): Promise<AuthTokens>;
463
464
  getValidToken(): Promise<string>;
@@ -468,8 +469,6 @@ declare class TokenManager {
468
469
  expiresAt: Date | null;
469
470
  };
470
471
  private isExpired;
471
- private loadFromDisk;
472
- private saveToDisk;
473
472
  }
474
473
 
475
474
  interface FetchOptions {
@@ -480,10 +479,14 @@ interface FetchOptions {
480
479
  }
481
480
  type FetchFn = <T = unknown>(url: string, options?: FetchOptions) => Promise<T>;
482
481
 
483
- declare class BankAccountsResource {
484
- private fetch;
485
- private companyId;
482
+ declare class Resource {
483
+ protected fetch: FetchFn;
484
+ protected companyId: number;
486
485
  constructor(fetch: FetchFn, companyId: number);
486
+ protected url(path: string): string;
487
+ }
488
+
489
+ declare class BankAccountsResource extends Resource {
487
490
  list(enabled?: boolean): Promise<BankAccount[]>;
488
491
  get(bankAccountId: number): Promise<BankAccount>;
489
492
  balance(): Promise<{
@@ -505,10 +508,7 @@ interface BankTransactionsListParams {
505
508
  to?: string;
506
509
  search?: string;
507
510
  }
508
- declare class BankTransactionsResource {
509
- private fetch;
510
- private companyId;
511
- constructor(fetch: FetchFn, companyId: number);
511
+ declare class BankTransactionsResource extends Resource {
512
512
  list(params?: BankTransactionsListParams): Promise<BankTransactionsResponse>;
513
513
  listAll(params?: Omit<BankTransactionsListParams, "page">): Promise<BankTransaction[]>;
514
514
  unimputed(): Promise<BankTransaction[]>;
@@ -535,20 +535,14 @@ interface ClientCreateParams {
535
535
  siren_or_siret?: string;
536
536
  professional?: boolean;
537
537
  }
538
- declare class ClientsResource {
539
- private fetch;
540
- private companyId;
541
- constructor(fetch: FetchFn, companyId: number);
538
+ declare class ClientsResource extends Resource {
542
539
  list(params?: ClientsListParams): Promise<Client[]>;
543
540
  get(clientId: number): Promise<Client>;
544
541
  create(params: ClientCreateParams): Promise<Client>;
545
542
  search(query: string): Promise<Client[]>;
546
543
  }
547
544
 
548
- declare class CompanyResource {
549
- private fetch;
550
- private companyId;
551
- constructor(fetch: FetchFn, companyId: number);
545
+ declare class CompanyResource extends Resource {
552
546
  get(): Promise<Company>;
553
547
  users(): Promise<unknown>;
554
548
  appConfig(): Promise<unknown>;
@@ -565,10 +559,7 @@ interface DocumentsListParams {
565
559
  page?: number;
566
560
  pageSize?: number;
567
561
  }
568
- declare class DocumentsResource {
569
- private fetch;
570
- private companyId;
571
- constructor(fetch: FetchFn, companyId: number);
562
+ declare class DocumentsResource extends Resource {
572
563
  list(params?: DocumentsListParams): Promise<Document[]>;
573
564
  categories(): Promise<DocumentCategory[]>;
574
565
  preview(documentId: number): Promise<unknown>;
@@ -577,10 +568,7 @@ declare class DocumentsResource {
577
568
  download(documentId: number): Promise<ArrayBuffer>;
578
569
  }
579
570
 
580
- declare class ExpenseReportsResource {
581
- private fetch;
582
- private companyId;
583
- constructor(fetch: FetchFn, companyId: number);
571
+ declare class ExpenseReportsResource extends Resource {
584
572
  list(sorts?: string): Promise<ExpenseReport[]>;
585
573
  get(expenseReportId: number): Promise<ExpenseReport>;
586
574
  create(params: ExpenseReportCreateParams): Promise<ExpenseReport>;
@@ -592,10 +580,7 @@ interface InvoicesListParams {
592
580
  page?: number;
593
581
  pageSize?: number;
594
582
  }
595
- declare class InvoicesResource {
596
- private fetch;
597
- private companyId;
598
- constructor(fetch: FetchFn, companyId: number);
583
+ declare class InvoicesResource extends Resource {
599
584
  list(params?: InvoicesListParams): Promise<Invoice[]>;
600
585
  listAll(params?: {
601
586
  sorts?: string;
@@ -614,19 +599,13 @@ declare class InvoicesResource {
614
599
  }): Promise<Invoice>;
615
600
  }
616
601
 
617
- declare class LabelsResource {
618
- private fetch;
619
- private companyId;
620
- constructor(fetch: FetchFn, companyId: number);
602
+ declare class LabelsResource extends Resource {
621
603
  list(): Promise<Label[]>;
622
604
  standard(): Promise<Label[]>;
623
605
  tags(): Promise<Tag[]>;
624
606
  }
625
607
 
626
- declare class QuotationsResource {
627
- private fetch;
628
- private companyId;
629
- constructor(fetch: FetchFn, companyId: number);
608
+ declare class QuotationsResource extends Resource {
630
609
  list(expand?: string): Promise<Quotation[]>;
631
610
  get(quotationId: number): Promise<Quotation>;
632
611
  create(params: QuotationCreateParams): Promise<Quotation>;
@@ -635,7 +614,7 @@ declare class QuotationsResource {
635
614
  }
636
615
 
637
616
  declare class UsersResource {
638
- private fetch;
617
+ protected fetch: FetchFn;
639
618
  constructor(fetch: FetchFn);
640
619
  me(): Promise<User>;
641
620
  legalInformations(): Promise<unknown>;
@@ -646,7 +625,7 @@ declare class TiimeClient {
646
625
  readonly fetch: FetchFn;
647
626
  readonly tokenManager: TokenManager;
648
627
  readonly companyId: number;
649
- constructor(options?: TiimeClientOptions & {
628
+ constructor(options: TiimeClientOptions & {
650
629
  tokenManager?: TokenManager;
651
630
  });
652
631
  listCompanies(): Promise<Company[]>;
@@ -676,4 +655,4 @@ declare class TiimeError extends Error {
676
655
  };
677
656
  }
678
657
 
679
- export { type AccountingPeriod, type Address, type ApeCode, type AuthConfig, type AuthTokens, type Bank, type BankAccount, type BankTransaction, type BankTransactionsResponse, type Client, type Company, type Country, type DashboardBlock, type Document, type DocumentCategory, type DocumentMatching, type ExpenseReport, type ExpenseReportCreateParams, type Imputation, type ImputationLabel, type ImputationParams, type Invoice, type InvoiceCreateParams, type InvoiceLine, type InvoiceSendParams, type Label, type LabelSuggestion, type MatchableDocument, type PaginatedResponse, type Quotation, type QuotationCreateParams, type QuotationSendParams, type Tag, TiimeClient, type TiimeClientOptions, TiimeError, TokenManager, type User, type VatSystem, resolveCompanyId };
658
+ export { type AccountingPeriod, type Address, type ApeCode, type AuthConfig, type AuthTokens, type Bank, type BankAccount, type BankTransaction, type BankTransactionsResponse, type Client, type Company, type Country, type CredentialStorage, type DashboardBlock, type Document, type DocumentCategory, type DocumentMatching, type ExpenseReport, type ExpenseReportCreateParams, type Imputation, type ImputationLabel, type ImputationParams, type Invoice, type InvoiceCreateParams, type InvoiceLine, type InvoiceSendParams, type Label, type LabelSuggestion, type MatchableDocument, type PaginatedResponse, type Quotation, type QuotationCreateParams, type QuotationSendParams, type Tag, TiimeClient, type TiimeClientOptions, TiimeError, TokenManager, type TokenStorage, type User, type VatSystem };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import {execSync}from'child_process';import {existsSync,readFileSync,writeFileSync,mkdirSync}from'fs';import {homedir}from'os';import {join}from'path';var z=(s,e,t)=>{let n=new URL(e,s.endsWith("/")?s:`${s}/`);if(t)for(let[i,r]of Object.entries(t))r!=null&&n.searchParams.set(i,String(r));return n.href},U=s=>new Promise(e=>setTimeout(e,s)),J=s=>!!s?.includes("application/json")||!!s?.includes("+json"),D=s=>{let e=s.retry??0,t=s.retryDelay??500,n=new Set(s.retryStatusCodes??[]);return async(i,r)=>{let o=z(s.baseURL,i,r?.query),m=new Headers(s.headers);if(r?.headers)for(let[c,a]of Object.entries(r.headers))m.set(c,a);let u;r?.body!==void 0&&(r.body instanceof FormData?u=r.body:(m.set("Content-Type","application/json"),u=JSON.stringify(r.body))),s.onRequest&&await s.onRequest({options:{headers:m}});let h;for(let c=0;c<=e;c++){c>0&&await U(t);let a;try{a=await fetch(o,{method:r?.method??"GET",headers:m,body:u});}catch(l){if(h=l,c<e)continue;throw l}if(!a.ok&&n.has(a.status)&&(h=a,c<e))continue;if(!a.ok&&s.onResponseError){let l;try{l=await a.clone().json();}catch{}let L=Object.assign(a,{_data:l});s.onResponseError({request:o,response:L});}let R=a.headers.get("content-type");return a.status===204||!R?void 0:J(R)?a.json():a.arrayBuffer()}throw h}},q=async(s,e)=>{let t=await fetch(s,e);if(!t.ok)throw new Error(`HTTP ${t.status}: ${t.statusText}`);return t.json()};var Q="auth0.tiime.fr",G="iEbsbe3o66gcTBfGRa012kj1Rb6vjAND",K="https://chronos/",p=join(homedir(),".config","tiime"),f=join(p,"auth.json"),O=join(p,"config.json"),N="tiime-cli",B="tiime-credentials",V=(s,e)=>{try{let t=JSON.stringify({email:s,password:e});return execSync(`security add-generic-password -a "${N}" -s "${B}" -w '${t.replace(/'/g,"'\\''")}' -U`,{stdio:"ignore"}),!0}catch{return false}},W=()=>{try{let s=execSync(`security find-generic-password -a "${N}" -s "${B}" -w`,{encoding:"utf-8",stdio:["ignore","pipe","ignore"]}).trim(),e=JSON.parse(s);return typeof e=="object"&&e!==null&&"email"in e&&"password"in e&&typeof e.email=="string"&&typeof e.password=="string"?e:null}catch{return null}},Y=(s,e)=>{existsSync(p)||mkdirSync(p,{recursive:true});let t=join(p,"credentials.json");writeFileSync(t,JSON.stringify({email:s,password:e},null,2),{mode:384});},X=()=>{try{let s=join(p,"credentials.json");if(existsSync(s)){let e=JSON.parse(readFileSync(s,"utf-8"));if(typeof e=="object"&&e!==null&&"email"in e&&"password"in e&&typeof e.email=="string"&&typeof e.password=="string")return e}}catch{}return null},Z=(s,e)=>{V(s,e)||Y(s,e);},ee=()=>W()??X(),A=s=>{if(s)return s;let e=process.env.TIIME_COMPANY_ID;if(e){let t=Number.parseInt(e,10);if(!Number.isNaN(t))return t}try{if(existsSync(O)){let t=JSON.parse(readFileSync(O,"utf-8"));if(t.companyId)return t.companyId}}catch{}throw new Error("No company ID configured. Set TIIME_COMPANY_ID env var, pass companyId option, or run `tiime company use --id <ID>`.")},y=class{tokens=null;credentials=null;persist;constructor(e={}){let t=e.tokens||e.email&&e.password;if(this.persist=e.persist??!t,e.tokens){this.tokens=e.tokens;return}if(e.email&&e.password){this.credentials={email:e.email,password:e.password};return}let n=process.env.TIIME_ACCESS_TOKEN;if(n){this.tokens={access_token:n,expires_at:Number.MAX_SAFE_INTEGER};return}let i=process.env.TIIME_EMAIL,r=process.env.TIIME_PASSWORD;if(i&&r){this.credentials={email:i,password:r};return}this.loadFromDisk();}async login(e,t){let n=await q(`https://${Q}/oauth/token`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({grant_type:"password",client_id:G,audience:K,scope:"openid email",username:e,password:t})});return this.tokens={access_token:n.access_token,expires_at:Date.now()+n.expires_in*1e3},this.persist&&(this.saveToDisk(),Z(e,t)),this.tokens}async getValidToken(){if(!this.tokens||this.isExpired()){let e=this.credentials??ee();if(e)return (await this.login(e.email,e.password)).access_token;throw new Error(this.tokens?"Token expired. Provide credentials via options, TIIME_EMAIL/TIIME_PASSWORD env vars, or run `tiime auth login`.":"Not authenticated. Provide credentials via options, TIIME_EMAIL/TIIME_PASSWORD env vars, or run `tiime auth login`.")}return this.tokens.access_token}isAuthenticated(){return this.tokens!==null&&!this.isExpired()}logout(){this.tokens=null,existsSync(f)&&writeFileSync(f,"{}");}getTokenInfo(){if(!this.tokens)return {email:null,expiresAt:null};try{return {email:JSON.parse(Buffer.from(this.tokens.access_token.split(".")[1],"base64").toString())["tiime/userEmail"]||null,expiresAt:new Date(this.tokens.expires_at)}}catch{return {email:null,expiresAt:new Date(this.tokens.expires_at)}}}isExpired(){return this.tokens?Date.now()>=this.tokens.expires_at-6e4:true}loadFromDisk(){try{if(existsSync(f)){let e=JSON.parse(readFileSync(f,"utf-8"));e.access_token&&e.expires_at&&(this.tokens=e);}}catch{}}saveToDisk(){existsSync(p)||mkdirSync(p,{recursive:true}),writeFileSync(f,JSON.stringify(this.tokens,null,2));}};var b=class extends Error{constructor(t,n,i,r){super(t);this.status=n;this.endpoint=i;this.details=r;this.name="TiimeError";}toJSON(){return {error:this.name,message:this.message,status:this.status,endpoint:this.endpoint,details:this.details}}};var _=class{constructor(e,t){this.fetch=e;this.companyId=t;}list(e){return this.fetch(`/companies/${this.companyId}/bank_accounts`,{query:e!==void 0?{enabled:e}:void 0})}get(e){return this.fetch(`/companies/${this.companyId}/bank_accounts/${e}`)}async balance(){return (await this.list(true)).map(t=>({name:t.name,balance_amount:t.balance_amount,currency:t.balance_currency}))}};var I=class{constructor(e,t){this.fetch=e;this.companyId=t;}list(e){let t=((e?.page??1)-1)*(e?.pageSize??100),n=t+(e?.pageSize??100),{page:i,pageSize:r,from:o,to:m,search:u,...h}=e??{},c={...h};return o&&(c.transaction_date_start=o),m&&(c.transaction_date_end=m),u&&(c.wording=u),this.fetch(`/companies/${this.companyId}/bank_transactions`,{query:{hide_refused:false,...c},headers:{Accept:"application/vnd.tiime.bank_transactions.v2+json",Range:`items=${t}-${n}`}})}async listAll(e){let t=e?.pageSize??200,n=[],i=1,r=true;for(;r;){let o=await this.list({...e,page:i,pageSize:t});n.push(...o.transactions),r=o.transactions.length===t,i++;}return n}unimputed(){return this.fetch(`/companies/${this.companyId}/bank_transactions/unimputed`)}get(e){return this.fetch(`/companies/${this.companyId}/bank_transactions/${e}`)}labelSuggestions(e){return this.fetch(`/companies/${this.companyId}/bank_transactions/${e}/label_suggestions`,{headers:{Accept:"application/vnd.tiime.bank_transactions.label_suggestions.v2+json"}})}impute(e,t){return this.fetch(`/companies/${this.companyId}/bank_transactions/${e}`,{method:"PATCH",body:{imputations:t}})}matchDocuments(e,t){return this.fetch(`/companies/${this.companyId}/bank_transactions/${e}/document_matchings`,{method:"PUT",body:{documents:t.map(n=>({id:n}))}})}getMatchings(e){return this.fetch(`/companies/${this.companyId}/bank_transactions/${e}/matchings`)}};var v=class{constructor(e,t){this.fetch=e;this.companyId=t;}list(e){return this.fetch(`/companies/${this.companyId}/clients`,{query:e,headers:{Accept:"application/vnd.tiime.timeline.v2+json",Range:"items=0-*"}})}get(e){return this.fetch(`/companies/${this.companyId}/clients/${e}`)}create(e){return this.fetch(`/companies/${this.companyId}/clients`,{method:"POST",body:e})}search(e){return this.fetch(`/companies/${this.companyId}/clients`,{query:{search:e},headers:{Accept:"application/vnd.tiime.timeline.v2+json",Range:"items=0-*"}})}};var k=class{constructor(e,t){this.fetch=e;this.companyId=t;}get(){return this.fetch(`/companies/${this.companyId}`)}users(){return this.fetch(`/companies/${this.companyId}/users`)}appConfig(){return this.fetch(`/companies/${this.companyId}/app_config`)}accountingPeriod(e=1){return this.fetch(`/companies/${this.companyId}/accounting_period/current`,{query:{range_year:e}})}tiles(e){return this.fetch(`/companies/${this.companyId}/tiles`,{query:{keys:e.join(",")}})}dashboardBlocks(e="monitoring"){return this.fetch(`/companies/${this.companyId}/dashboard_blocks`,{query:{sorts:"rank:asc",display_group:e}})}};var w=class{constructor(e,t){this.fetch=e;this.companyId=t;}list(e){let t=((e?.page??1)-1)*(e?.pageSize??25),n=t+(e?.pageSize??25),{page:i,pageSize:r,...o}=e??{};return this.fetch(`/companies/${this.companyId}/documents`,{query:{sorts:"created_at:desc",expand:"file_family,preview_available",...o},headers:{Accept:"application/vnd.tiime.documents.v2+json,application/vnd.tiime.docs.query+json,application/vnd.tiime.docs.imputation+json",Range:`items=${t}-${n}`}})}categories(){return this.fetch(`/companies/${this.companyId}/document_categories`,{headers:{Accept:"application/vnd.tiime.documents.v3+json"}})}preview(e){return this.fetch(`/companies/${this.companyId}/documents/${e}/preview`)}upload(e,t,n){let i=new FormData;return i.append("file",new Blob([e]),t),n&&i.append("type",n),this.fetch(`/companies/${this.companyId}/documents`,{method:"POST",body:i})}searchMatchable(e){return this.fetch(`/companies/${this.companyId}/documents`,{query:{matchable:true,q:e},headers:{Accept:"application/vnd.tiime.documents.v3+json,application/vnd.tiime.docs.imputation+json",Range:"items=0-25"}})}async download(e){return this.fetch(`/companies/${this.companyId}/documents/${e}/download`,{headers:{Accept:"application/octet-stream"}})}};var T=class{constructor(e,t){this.fetch=e;this.companyId=t;}list(e="metadata.date:desc"){return this.fetch(`/companies/${this.companyId}/expense_reports`,{query:{expand:"total_amount",sorts:e},headers:{Range:"items=0-25"}})}get(e){return this.fetch(`/companies/${this.companyId}/expense_reports/${e}`)}create(e){return this.fetch(`/companies/${this.companyId}/expense_reports`,{method:"POST",body:e})}};var te={template:"advanced",status:"draft",due_date_mode:"thirty_days",title_enabled:true,free_field_enabled:false,free_field:"",discount_enabled:false,bank_detail_enabled:true,payment_condition_enabled:true,payment_condition:"En cas de retard de paiement, une p\xE9nalit\xE9 de 3 fois le taux d'int\xE9r\xEAt l\xE9gal sera appliqu\xE9e, \xE0 laquelle s'ajoutera une indemnit\xE9 forfaitaire pour frais de recouvrement de 40\u20AC.",text_lines:[]},P=class{constructor(e,t){this.fetch=e;this.companyId=t;}list(e){let t=((e?.page??1)-1)*(e?.pageSize??25),n=t+(e?.pageSize??25),i={sorts:e?.sorts??"invoice_number:desc"};return e?.status&&(i.status=e.status),this.fetch(`/companies/${this.companyId}/invoices`,{query:i,headers:{Range:`items=${t}-${n}`}})}async listAll(e){let t=e?.pageSize??100,n=[],i=1,r=true;for(;r;){let o=await this.list({sorts:e?.sorts,status:e?.status,page:i,pageSize:t});n.push(...o),r=o.length===t,i++;}return n}get(e){return this.fetch(`/companies/${this.companyId}/invoices/${e}`)}create(e){let t={...te,...e,lines:e.lines?.map(n=>({...n}))};for(let n of t.lines??[])n.line_amount=n.quantity*n.unit_amount,n.sequence??=1,n.invoicing_category_type??="benefit",n.discount_description??="",n.discount_amount??=null,n.discount_percentage??=null;return this.fetch(`/companies/${this.companyId}/invoices`,{method:"POST",body:t})}update(e,t){return this.fetch(`/companies/${this.companyId}/invoices/${e}`,{method:"PUT",body:t})}send(e,t){return this.fetch(`/companies/${this.companyId}/invoices/${e}/send`,{method:"POST",body:t})}async downloadPdf(e){return this.fetch(`/companies/${this.companyId}/invoices/${e}/pdf`,{headers:{Accept:"application/pdf"}})}delete(e){return this.fetch(`/companies/${this.companyId}/invoices/${e}`,{method:"DELETE"})}async duplicate(e,t){let n=await this.get(e),i=new Date().toISOString().split("T")[0],r=n.lines.map(o=>({description:o.description,quantity:t?.quantity??o.quantity,unit_amount:o.unit_amount,vat_type:o.vat_type,invoicing_unit:o.invoicing_unit,invoicing_category_type:o.invoicing_category_type,article:o.article}));return this.create({client:n.client_id?{id:n.client_id}:null,emission_date:t?.emission_date??i,title:n.title,title_enabled:!!n.title,lines:r,status:"draft"})}};var $=class{constructor(e,t){this.fetch=e;this.companyId=t;}list(){return this.fetch(`/companies/${this.companyId}/labels`,{headers:{Accept:"application/vnd.tiime.labels.v2+json"}})}standard(){return this.fetch(`/companies/${this.companyId}/standard_labels`)}tags(){return this.fetch(`/companies/${this.companyId}/tags`,{query:{expand:"tag_detail"}})}};var C=class{constructor(e,t){this.fetch=e;this.companyId=t;}list(e="invoices"){return this.fetch(`/companies/${this.companyId}/quotations`,{query:{expand:e},headers:{Range:"items=0-25"}})}get(e){return this.fetch(`/companies/${this.companyId}/quotations/${e}`)}create(e){let t={...e,lines:e.lines?.map(n=>({...n}))};for(let n of t.lines??[])n.line_amount=n.quantity*n.unit_amount,n.sequence??=1,n.invoicing_category_type??="benefit",n.discount_description??="",n.discount_amount??=null,n.discount_percentage??=null;return this.fetch(`/companies/${this.companyId}/quotations`,{method:"POST",body:t})}async downloadPdf(e){return this.fetch(`/companies/${this.companyId}/quotations/${e}/pdf`,{headers:{Accept:"application/pdf"}})}send(e,t){return this.fetch(`/companies/${this.companyId}/quotations/${e}/send`,{method:"POST",body:t})}};var F=class{constructor(e){this.fetch=e;}me(){return this.fetch("/users/me")}legalInformations(){return this.fetch("/users/me/legal_informations")}settings(e){return this.fetch(`/users/me/companies/${e}/settings`)}};var ne="https://chronos-api.tiime-apps.com/v1",E=class{fetch;tokenManager;companyId;constructor(e={}){this.companyId=A(e.companyId),this.tokenManager=e.tokenManager??new y({tokens:e.tokens,email:e.email,password:e.password}),this.fetch=D({baseURL:ne,retry:2,retryDelay:500,retryStatusCodes:[408,429,500,502,503,504],headers:{"tiime-app":"tiime","tiime-app-version":"4.30.3","tiime-app-platform":"cli"},onRequest:async({options:t})=>{let n=await this.tokenManager.getValidToken();t.headers.set("Authorization",`Bearer ${n}`);},onResponseError:({request:t,response:n})=>{throw new b(n.statusText||`HTTP ${n.status}`,n.status,String(t),n._data)}});}listCompanies(){return this.fetch("/companies",{headers:{Accept:"application/vnd.tiime.companies.v2+json",Range:"items=0-101"}})}get users(){return new F(this.fetch)}get company(){return new k(this.fetch,this.companyId)}get clients(){return new v(this.fetch,this.companyId)}get invoices(){return new P(this.fetch,this.companyId)}get quotations(){return new C(this.fetch,this.companyId)}get bankAccounts(){return new _(this.fetch,this.companyId)}get bankTransactions(){return new I(this.fetch,this.companyId)}get documents(){return new w(this.fetch,this.companyId)}get expenseReports(){return new T(this.fetch,this.companyId)}get labels(){return new $(this.fetch,this.companyId)}};
2
- export{E as TiimeClient,b as TiimeError,y as TokenManager,A as resolveCompanyId};
1
+ var E=(s,e,t)=>{let n=new URL(e,s.endsWith("/")?s:`${s}/`);if(t)for(let[r,i]of Object.entries(t))i!=null&&n.searchParams.set(r,String(i));return n.href},q=s=>new Promise(e=>setTimeout(e,s)),D=s=>!!s?.includes("application/json")||!!s?.includes("+json"),I=s=>{let e=s.retry??0,t=s.retryDelay??500,n=new Set(s.retryStatusCodes??[]);return async(r,i)=>{let o=E(s.baseURL,r,i?.query),h=new Headers(s.headers);if(i?.headers)for(let[u,c]of Object.entries(i.headers))h.set(u,c);let l;i?.body!==void 0&&(i.body instanceof FormData?l=i.body:(h.set("Content-Type","application/json"),l=JSON.stringify(i.body))),s.onRequest&&await s.onRequest({options:{headers:h}});let p;for(let u=0;u<=e;u++){u>0&&await q(t);let c;try{c=await fetch(o,{method:i?.method??"GET",headers:h,body:l});}catch(m){if(p=m,u<e)continue;throw m}if(!c.ok&&n.has(c.status)&&(p=c,u<e))continue;if(!c.ok&&s.onResponseError){let m;try{m=await c.clone().json();}catch{}let C=Object.assign(c,{_data:m});s.onResponseError({request:o,response:C});}let A=c.headers.get("content-type");return c.status===204||!A?void 0:D(A)?c.json():c.arrayBuffer()}throw p}},R=async(s,e)=>{let t=await fetch(s,e);if(!t.ok)throw new Error(`HTTP ${t.status}: ${t.statusText}`);return t.json()};var $="auth0.tiime.fr",B="iEbsbe3o66gcTBfGRa012kj1Rb6vjAND",M="https://chronos/",d=class{tokens=null;credentials=null;tokenStorage;credentialStorage;constructor(e={}){if(this.tokenStorage=e.tokenStorage??null,this.credentialStorage=e.credentialStorage??null,e.tokens){this.tokens=e.tokens;return}if(e.email&&e.password){this.credentials={email:e.email,password:e.password};return}let t=process.env.TIIME_ACCESS_TOKEN;if(t){this.tokens={access_token:t,expires_at:Number.MAX_SAFE_INTEGER};return}let n=process.env.TIIME_EMAIL,r=process.env.TIIME_PASSWORD;if(n&&r){this.credentials={email:n,password:r};return}this.tokenStorage&&(this.tokens=this.tokenStorage.load());}async login(e,t){let n=await R(`https://${$}/oauth/token`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({grant_type:"password",client_id:B,audience:M,scope:"openid email",username:e,password:t})});return this.tokens={access_token:n.access_token,expires_at:Date.now()+n.expires_in*1e3},this.tokenStorage?.save(this.tokens),this.credentialStorage?.save(e,t),this.tokens}async getValidToken(){if(!this.tokens||this.isExpired()){let e=this.credentials??this.credentialStorage?.load()??null;if(e)return (await this.login(e.email,e.password)).access_token;throw new Error(this.tokens?"Token expired. Provide credentials via options, TIIME_EMAIL/TIIME_PASSWORD env vars, or run `tiime auth login`.":"Not authenticated. Provide credentials via options, TIIME_EMAIL/TIIME_PASSWORD env vars, or run `tiime auth login`.")}return this.tokens.access_token}isAuthenticated(){return this.tokens!==null&&!this.isExpired()}logout(){this.tokens=null,this.tokenStorage?.clear();}getTokenInfo(){if(!this.tokens)return {email:null,expiresAt:null};try{return {email:JSON.parse(Buffer.from(this.tokens.access_token.split(".")[1],"base64").toString())["tiime/userEmail"]||null,expiresAt:new Date(this.tokens.expires_at)}}catch{return {email:null,expiresAt:new Date(this.tokens.expires_at)}}}isExpired(){return this.tokens?Date.now()>=this.tokens.expires_at-6e4:true}};var g=class extends Error{constructor(t,n,r,i){super(t);this.status=n;this.endpoint=r;this.details=i;this.name="TiimeError";}toJSON(){return {error:this.name,message:this.message,status:this.status,endpoint:this.endpoint,details:this.details}}};var a=class{constructor(e,t){this.fetch=e;this.companyId=t;}url(e){return `companies/${this.companyId}${e}`}};var f=class extends a{list(e){return this.fetch(this.url("/bank_accounts"),{query:e!==void 0?{enabled:e}:void 0})}get(e){return this.fetch(this.url(`/bank_accounts/${e}`))}async balance(){return (await this.list(true)).map(t=>({name:t.name,balance_amount:t.balance_amount,currency:t.balance_currency}))}};var y=class extends a{list(e){let t=((e?.page??1)-1)*(e?.pageSize??100),n=t+(e?.pageSize??100),{page:r,pageSize:i,from:o,to:h,search:l,...p}=e??{},u={...p};return o&&(u.transaction_date_start=o),h&&(u.transaction_date_end=h),l&&(u.wording=l),this.fetch(this.url("/bank_transactions"),{query:{hide_refused:false,...u},headers:{Accept:"application/vnd.tiime.bank_transactions.v2+json",Range:`items=${t}-${n}`}})}async listAll(e){let t=e?.pageSize??200,n=[],r=1,i=true;for(;i;){let o=await this.list({...e,page:r,pageSize:t});n.push(...o.transactions),i=o.transactions.length===t,r++;}return n}unimputed(){return this.fetch(this.url("/bank_transactions/unimputed"))}get(e){return this.fetch(this.url(`/bank_transactions/${e}`))}labelSuggestions(e){return this.fetch(this.url(`/bank_transactions/${e}/label_suggestions`),{headers:{Accept:"application/vnd.tiime.bank_transactions.label_suggestions.v2+json"}})}impute(e,t){return this.fetch(this.url(`/bank_transactions/${e}`),{method:"PATCH",body:{imputations:t}})}matchDocuments(e,t){return this.fetch(this.url(`/bank_transactions/${e}/document_matchings`),{method:"PUT",body:{documents:t.map(n=>({id:n}))}})}getMatchings(e){return this.fetch(this.url(`/bank_transactions/${e}/matchings`))}};var k=class extends a{list(e){return this.fetch(this.url("/clients"),{query:e,headers:{Accept:"application/vnd.tiime.timeline.v2+json",Range:"items=0-*"}})}get(e){return this.fetch(this.url(`/clients/${e}`))}create(e){return this.fetch(this.url("/clients"),{method:"POST",body:e})}search(e){return this.fetch(this.url("/clients"),{query:{search:e},headers:{Accept:"application/vnd.tiime.timeline.v2+json",Range:"items=0-*"}})}};var b=class extends a{get(){return this.fetch(this.url(""))}users(){return this.fetch(this.url("/users"))}appConfig(){return this.fetch(this.url("/app_config"))}accountingPeriod(e=1){return this.fetch(this.url("/accounting_period/current"),{query:{range_year:e}})}tiles(e){return this.fetch(this.url("/tiles"),{query:{keys:e.join(",")}})}dashboardBlocks(e="monitoring"){return this.fetch(this.url("/dashboard_blocks"),{query:{sorts:"rank:asc",display_group:e}})}};var _=class extends a{list(e){let t=((e?.page??1)-1)*(e?.pageSize??25),n=t+(e?.pageSize??25),{page:r,pageSize:i,...o}=e??{};return this.fetch(this.url("/documents"),{query:{sorts:"created_at:desc",expand:"file_family,preview_available",...o},headers:{Accept:"application/vnd.tiime.documents.v2+json,application/vnd.tiime.docs.query+json,application/vnd.tiime.docs.imputation+json",Range:`items=${t}-${n}`}})}categories(){return this.fetch(this.url("/document_categories"),{headers:{Accept:"application/vnd.tiime.documents.v3+json"}})}preview(e){return this.fetch(this.url(`/documents/${e}/preview`))}upload(e,t,n){let r=new FormData;return r.append("file",new Blob([e]),t),n&&r.append("type",n),this.fetch(this.url("/documents"),{method:"POST",body:r})}searchMatchable(e){return this.fetch(this.url("/documents"),{query:{matchable:true,q:e},headers:{Accept:"application/vnd.tiime.documents.v3+json,application/vnd.tiime.docs.imputation+json",Range:"items=0-25"}})}async download(e){return this.fetch(this.url(`/documents/${e}/download`),{headers:{Accept:"application/octet-stream"}})}};var v=class extends a{list(e="metadata.date:desc"){return this.fetch(this.url("/expense_reports"),{query:{expand:"total_amount",sorts:e},headers:{Range:"items=0-25"}})}get(e){return this.fetch(this.url(`/expense_reports/${e}`))}create(e){return this.fetch(this.url("/expense_reports"),{method:"POST",body:e})}};var w=class extends a{list(e){let t=((e?.page??1)-1)*(e?.pageSize??25),n=t+(e?.pageSize??25),r={sorts:e?.sorts??"invoice_number:desc"};return e?.status&&(r.status=e.status),this.fetch(this.url("/invoices"),{query:r,headers:{Range:`items=${t}-${n}`}})}async listAll(e){let t=e?.pageSize??100,n=[],r=1,i=true;for(;i;){let o=await this.list({sorts:e?.sorts,status:e?.status,page:r,pageSize:t});n.push(...o),i=o.length===t,r++;}return n}get(e){return this.fetch(this.url(`/invoices/${e}`))}create(e){let t={...e,lines:e.lines?.map(n=>({...n}))};for(let n of t.lines??[])n.line_amount=n.quantity*n.unit_amount,n.sequence??=1,n.invoicing_category_type??="benefit",n.discount_description??="",n.discount_amount??=null,n.discount_percentage??=null;return this.fetch(this.url("/invoices"),{method:"POST",body:t})}update(e,t){return this.fetch(this.url(`/invoices/${e}`),{method:"PUT",body:t})}send(e,t){return this.fetch(this.url(`/invoices/${e}/send`),{method:"POST",body:t})}async downloadPdf(e){return this.fetch(this.url(`/invoices/${e}/pdf`),{headers:{Accept:"application/pdf"}})}delete(e){return this.fetch(this.url(`/invoices/${e}`),{method:"DELETE"})}async duplicate(e,t){let n=await this.get(e),r=new Date().toISOString().split("T")[0],i=n.lines.map(o=>({description:o.description,quantity:t?.quantity??o.quantity,unit_amount:o.unit_amount,vat_type:o.vat_type,invoicing_unit:o.invoicing_unit,invoicing_category_type:o.invoicing_category_type,article:o.article}));return this.create({client:n.client_id?{id:n.client_id}:null,emission_date:t?.emission_date??r,title:n.title,title_enabled:!!n.title,lines:i,status:"draft"})}};var T=class extends a{list(){return this.fetch(this.url("/labels"),{headers:{Accept:"application/vnd.tiime.labels.v2+json"}})}standard(){return this.fetch(this.url("/standard_labels"))}tags(){return this.fetch(this.url("/tags"),{query:{expand:"tag_detail"}})}};var S=class extends a{list(e="invoices"){return this.fetch(this.url("/quotations"),{query:{expand:e},headers:{Range:"items=0-25"}})}get(e){return this.fetch(this.url(`/quotations/${e}`))}create(e){let t={...e,lines:e.lines?.map(n=>({...n}))};for(let n of t.lines??[])n.line_amount=n.quantity*n.unit_amount,n.sequence??=1,n.invoicing_category_type??="benefit",n.discount_description??="",n.discount_amount??=null,n.discount_percentage??=null;return this.fetch(this.url("/quotations"),{method:"POST",body:t})}async downloadPdf(e){return this.fetch(this.url(`/quotations/${e}/pdf`),{headers:{Accept:"application/pdf"}})}send(e,t){return this.fetch(this.url(`/quotations/${e}/send`),{method:"POST",body:t})}};var P=class{constructor(e){this.fetch=e;}me(){return this.fetch("users/me")}legalInformations(){return this.fetch("users/me/legal_informations")}settings(e){return this.fetch(`users/me/companies/${e}/settings`)}};var O="https://chronos-api.tiime-apps.com/v1",x=class{fetch;tokenManager;companyId;constructor(e){this.companyId=e.companyId,this.tokenManager=e.tokenManager??new d({tokens:e.tokens,email:e.email,password:e.password}),this.fetch=I({baseURL:O,retry:2,retryDelay:500,retryStatusCodes:[408,429,500,502,503,504],headers:{"tiime-app":"tiime","tiime-app-version":"4.30.3","tiime-app-platform":"cli"},onRequest:async({options:t})=>{let n=await this.tokenManager.getValidToken();t.headers.set("Authorization",`Bearer ${n}`);},onResponseError:({request:t,response:n})=>{throw new g(n.statusText||`HTTP ${n.status}`,n.status,String(t),n._data)}});}listCompanies(){return this.fetch("companies",{headers:{Accept:"application/vnd.tiime.companies.v2+json",Range:"items=0-101"}})}get users(){return new P(this.fetch)}get company(){return new b(this.fetch,this.companyId)}get clients(){return new k(this.fetch,this.companyId)}get invoices(){return new w(this.fetch,this.companyId)}get quotations(){return new S(this.fetch,this.companyId)}get bankAccounts(){return new f(this.fetch,this.companyId)}get bankTransactions(){return new y(this.fetch,this.companyId)}get documents(){return new _(this.fetch,this.companyId)}get expenseReports(){return new v(this.fetch,this.companyId)}get labels(){return new T(this.fetch,this.companyId)}};
2
+ export{x as TiimeClient,g as TiimeError,d as TokenManager};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tiime-sdk",
3
- "version": "2.2.0",
3
+ "version": "3.0.1",
4
4
  "description": "TypeScript SDK for Tiime accounting API",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -47,6 +47,7 @@
47
47
  "devDependencies": {
48
48
  "@biomejs/biome": "^2.4.6",
49
49
  "@types/node": "^25.3.5",
50
+ "@vitest/coverage-v8": "^4.0.18",
50
51
  "tsup": "^8.5.1",
51
52
  "typescript": "^5.9.3",
52
53
  "vitest": "^4.0.18"
@@ -56,6 +57,7 @@
56
57
  "dev": "tsup --watch",
57
58
  "lint": "biome check src/",
58
59
  "format": "biome format --write src/",
60
+ "typecheck": "tsc",
59
61
  "test": "vitest run",
60
62
  "test:watch": "vitest",
61
63
  "release": "changeset publish"