tiime-sdk 2.1.1 → 2.2.0

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
@@ -1,5 +1,3 @@
1
- import { $Fetch } from 'ofetch';
2
-
3
1
  interface AuthTokens {
4
2
  access_token: string;
5
3
  expires_at: number;
@@ -153,10 +151,16 @@ interface BankTransaction {
153
151
  status: string;
154
152
  status_code: string;
155
153
  comment: string | null;
156
- tags: unknown[];
154
+ tags: Tag[];
157
155
  short_bank_name: string;
158
- beneficiary: unknown | null;
159
- merchant: unknown | null;
156
+ beneficiary: {
157
+ id: number;
158
+ name: string;
159
+ } | null;
160
+ merchant: {
161
+ id: number;
162
+ name: string;
163
+ } | null;
160
164
  transfer_label: string | null;
161
165
  imputations: Imputation[];
162
166
  count_documents: number;
@@ -165,7 +169,9 @@ interface BankTransaction {
165
169
  interface BankTransactionsResponse {
166
170
  metadata: {
167
171
  has_multiple_cardholder: boolean;
168
- accountant_detail_request_data: unknown[];
172
+ accountant_detail_request_data: {
173
+ id: number;
174
+ }[];
169
175
  total_amount: number;
170
176
  };
171
177
  transactions: BankTransaction[];
@@ -218,7 +224,9 @@ interface InvoiceCreateParams {
218
224
  title?: string | null;
219
225
  title_enabled?: boolean;
220
226
  lines: InvoiceLine[];
221
- text_lines?: unknown[];
227
+ text_lines?: {
228
+ text: string;
229
+ }[];
222
230
  status?: "draft" | "saved";
223
231
  template?: string;
224
232
  free_field?: string;
@@ -246,7 +254,7 @@ interface Invoice {
246
254
  lines: InvoiceLine[];
247
255
  type: string;
248
256
  color: string;
249
- tags: unknown[];
257
+ tags: Tag[];
250
258
  totals_per_vat_type: Record<string, {
251
259
  total_excluding_taxes: number;
252
260
  vat_amount: number;
@@ -414,7 +422,11 @@ interface MatchableDocument {
414
422
  mime_type: string;
415
423
  name: string;
416
424
  type: string;
417
- metadata: Record<string, unknown>[];
425
+ metadata: {
426
+ date?: string | null;
427
+ amount?: number | null;
428
+ supplier_name?: string | null;
429
+ }[];
418
430
  created_at: string;
419
431
  tags: {
420
432
  id: number;
@@ -460,10 +472,18 @@ declare class TokenManager {
460
472
  private saveToDisk;
461
473
  }
462
474
 
475
+ interface FetchOptions {
476
+ method?: string;
477
+ headers?: Record<string, string>;
478
+ body?: unknown;
479
+ query?: Record<string, unknown> | object;
480
+ }
481
+ type FetchFn = <T = unknown>(url: string, options?: FetchOptions) => Promise<T>;
482
+
463
483
  declare class BankAccountsResource {
464
484
  private fetch;
465
485
  private companyId;
466
- constructor(fetch: $Fetch, companyId: number);
486
+ constructor(fetch: FetchFn, companyId: number);
467
487
  list(enabled?: boolean): Promise<BankAccount[]>;
468
488
  get(bankAccountId: number): Promise<BankAccount>;
469
489
  balance(): Promise<{
@@ -488,7 +508,7 @@ interface BankTransactionsListParams {
488
508
  declare class BankTransactionsResource {
489
509
  private fetch;
490
510
  private companyId;
491
- constructor(fetch: $Fetch, companyId: number);
511
+ constructor(fetch: FetchFn, companyId: number);
492
512
  list(params?: BankTransactionsListParams): Promise<BankTransactionsResponse>;
493
513
  listAll(params?: Omit<BankTransactionsListParams, "page">): Promise<BankTransaction[]>;
494
514
  unimputed(): Promise<BankTransaction[]>;
@@ -518,7 +538,7 @@ interface ClientCreateParams {
518
538
  declare class ClientsResource {
519
539
  private fetch;
520
540
  private companyId;
521
- constructor(fetch: $Fetch, companyId: number);
541
+ constructor(fetch: FetchFn, companyId: number);
522
542
  list(params?: ClientsListParams): Promise<Client[]>;
523
543
  get(clientId: number): Promise<Client>;
524
544
  create(params: ClientCreateParams): Promise<Client>;
@@ -528,13 +548,13 @@ declare class ClientsResource {
528
548
  declare class CompanyResource {
529
549
  private fetch;
530
550
  private companyId;
531
- constructor(fetch: $Fetch, companyId: number);
551
+ constructor(fetch: FetchFn, companyId: number);
532
552
  get(): Promise<Company>;
533
- users(): Promise<any>;
534
- appConfig(): Promise<any>;
553
+ users(): Promise<unknown>;
554
+ appConfig(): Promise<unknown>;
535
555
  accountingPeriod(rangeYear?: number): Promise<AccountingPeriod>;
536
- tiles(keys: string[]): Promise<any>;
537
- dashboardBlocks(displayGroup?: string): Promise<any>;
556
+ tiles(keys: string[]): Promise<unknown>;
557
+ dashboardBlocks(displayGroup?: string): Promise<unknown>;
538
558
  }
539
559
 
540
560
  interface DocumentsListParams {
@@ -548,10 +568,10 @@ interface DocumentsListParams {
548
568
  declare class DocumentsResource {
549
569
  private fetch;
550
570
  private companyId;
551
- constructor(fetch: $Fetch, companyId: number);
571
+ constructor(fetch: FetchFn, companyId: number);
552
572
  list(params?: DocumentsListParams): Promise<Document[]>;
553
573
  categories(): Promise<DocumentCategory[]>;
554
- preview(documentId: number): Promise<any>;
574
+ preview(documentId: number): Promise<unknown>;
555
575
  upload(file: Uint8Array, filename: string, type?: string): Promise<Document>;
556
576
  searchMatchable(query: string): Promise<MatchableDocument[]>;
557
577
  download(documentId: number): Promise<ArrayBuffer>;
@@ -560,7 +580,7 @@ declare class DocumentsResource {
560
580
  declare class ExpenseReportsResource {
561
581
  private fetch;
562
582
  private companyId;
563
- constructor(fetch: $Fetch, companyId: number);
583
+ constructor(fetch: FetchFn, companyId: number);
564
584
  list(sorts?: string): Promise<ExpenseReport[]>;
565
585
  get(expenseReportId: number): Promise<ExpenseReport>;
566
586
  create(params: ExpenseReportCreateParams): Promise<ExpenseReport>;
@@ -575,7 +595,7 @@ interface InvoicesListParams {
575
595
  declare class InvoicesResource {
576
596
  private fetch;
577
597
  private companyId;
578
- constructor(fetch: $Fetch, companyId: number);
598
+ constructor(fetch: FetchFn, companyId: number);
579
599
  list(params?: InvoicesListParams): Promise<Invoice[]>;
580
600
  listAll(params?: {
581
601
  sorts?: string;
@@ -597,7 +617,7 @@ declare class InvoicesResource {
597
617
  declare class LabelsResource {
598
618
  private fetch;
599
619
  private companyId;
600
- constructor(fetch: $Fetch, companyId: number);
620
+ constructor(fetch: FetchFn, companyId: number);
601
621
  list(): Promise<Label[]>;
602
622
  standard(): Promise<Label[]>;
603
623
  tags(): Promise<Tag[]>;
@@ -606,7 +626,7 @@ declare class LabelsResource {
606
626
  declare class QuotationsResource {
607
627
  private fetch;
608
628
  private companyId;
609
- constructor(fetch: $Fetch, companyId: number);
629
+ constructor(fetch: FetchFn, companyId: number);
610
630
  list(expand?: string): Promise<Quotation[]>;
611
631
  get(quotationId: number): Promise<Quotation>;
612
632
  create(params: QuotationCreateParams): Promise<Quotation>;
@@ -616,14 +636,14 @@ declare class QuotationsResource {
616
636
 
617
637
  declare class UsersResource {
618
638
  private fetch;
619
- constructor(fetch: $Fetch);
639
+ constructor(fetch: FetchFn);
620
640
  me(): Promise<User>;
621
- legalInformations(): Promise<any>;
622
- settings(companyId: number): Promise<any>;
641
+ legalInformations(): Promise<unknown>;
642
+ settings(companyId: number): Promise<unknown>;
623
643
  }
624
644
 
625
645
  declare class TiimeClient {
626
- readonly fetch: $Fetch;
646
+ readonly fetch: FetchFn;
627
647
  readonly tokenManager: TokenManager;
628
648
  readonly companyId: number;
629
649
  constructor(options?: TiimeClientOptions & {
package/dist/index.js CHANGED
@@ -1,811 +1,2 @@
1
- // src/auth.ts
2
- import { execSync } from "child_process";
3
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
4
- import { homedir } from "os";
5
- import { join } from "path";
6
- import { ofetch } from "ofetch";
7
- var AUTH0_DOMAIN = "auth0.tiime.fr";
8
- var AUTH0_CLIENT_ID = "iEbsbe3o66gcTBfGRa012kj1Rb6vjAND";
9
- var AUTH0_AUDIENCE = "https://chronos/";
10
- var CONFIG_DIR = join(homedir(), ".config", "tiime");
11
- var AUTH_FILE = join(CONFIG_DIR, "auth.json");
12
- var CONFIG_FILE = join(CONFIG_DIR, "config.json");
13
- var KEYCHAIN_ACCOUNT = "tiime-cli";
14
- var KEYCHAIN_SERVICE = "tiime-credentials";
15
- var saveCredentialsToKeychain = (email, password) => {
16
- try {
17
- const payload = JSON.stringify({ email, password });
18
- execSync(
19
- `security add-generic-password -a "${KEYCHAIN_ACCOUNT}" -s "${KEYCHAIN_SERVICE}" -w '${payload.replace(/'/g, "'\\''")}' -U`,
20
- { stdio: "ignore" }
21
- );
22
- return true;
23
- } catch {
24
- return false;
25
- }
26
- };
27
- var loadCredentialsFromKeychain = () => {
28
- try {
29
- const raw = execSync(
30
- `security find-generic-password -a "${KEYCHAIN_ACCOUNT}" -s "${KEYCHAIN_SERVICE}" -w`,
31
- { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }
32
- ).trim();
33
- const data = JSON.parse(raw);
34
- if (typeof data === "object" && data !== null && "email" in data && "password" in data && typeof data.email === "string" && typeof data.password === "string") {
35
- return data;
36
- }
37
- return null;
38
- } catch {
39
- return null;
40
- }
41
- };
42
- var saveCredentialsToFile = (email, password) => {
43
- if (!existsSync(CONFIG_DIR)) {
44
- mkdirSync(CONFIG_DIR, { recursive: true });
45
- }
46
- const filePath = join(CONFIG_DIR, "credentials.json");
47
- writeFileSync(filePath, JSON.stringify({ email, password }, null, 2), {
48
- mode: 384
49
- });
50
- };
51
- var loadCredentialsFromFile = () => {
52
- try {
53
- const filePath = join(CONFIG_DIR, "credentials.json");
54
- if (existsSync(filePath)) {
55
- const data = JSON.parse(readFileSync(filePath, "utf-8"));
56
- if (typeof data === "object" && data !== null && "email" in data && "password" in data && typeof data.email === "string" && typeof data.password === "string") {
57
- return data;
58
- }
59
- }
60
- } catch {
61
- }
62
- return null;
63
- };
64
- var saveCredentials = (email, password) => {
65
- if (!saveCredentialsToKeychain(email, password)) {
66
- saveCredentialsToFile(email, password);
67
- }
68
- };
69
- var loadCredentials = () => {
70
- return loadCredentialsFromKeychain() ?? loadCredentialsFromFile();
71
- };
72
- var resolveCompanyId = (explicit) => {
73
- if (explicit) return explicit;
74
- const envId = process.env.TIIME_COMPANY_ID;
75
- if (envId) {
76
- const parsed = Number.parseInt(envId, 10);
77
- if (!Number.isNaN(parsed)) return parsed;
78
- }
79
- try {
80
- if (existsSync(CONFIG_FILE)) {
81
- const config = JSON.parse(readFileSync(CONFIG_FILE, "utf-8"));
82
- if (config.companyId) return config.companyId;
83
- }
84
- } catch {
85
- }
86
- throw new Error(
87
- "No company ID configured. Set TIIME_COMPANY_ID env var, pass companyId option, or run `tiime company use --id <ID>`."
88
- );
89
- };
90
- var TokenManager = class {
91
- tokens = null;
92
- credentials = null;
93
- persist;
94
- /**
95
- * @param options.tokens - Use these tokens directly (no disk I/O)
96
- * @param options.email - Login with these credentials
97
- * @param options.password - Login with these credentials
98
- * @param options.persist - Save tokens/credentials to disk (default: true when no explicit auth)
99
- */
100
- constructor(options = {}) {
101
- const hasExplicitAuth = options.tokens || options.email && options.password;
102
- this.persist = options.persist ?? !hasExplicitAuth;
103
- if (options.tokens) {
104
- this.tokens = options.tokens;
105
- return;
106
- }
107
- if (options.email && options.password) {
108
- this.credentials = { email: options.email, password: options.password };
109
- return;
110
- }
111
- const envToken = process.env.TIIME_ACCESS_TOKEN;
112
- if (envToken) {
113
- this.tokens = {
114
- access_token: envToken,
115
- // No expiry info from env — assume valid, never auto-refresh
116
- expires_at: Number.MAX_SAFE_INTEGER
117
- };
118
- return;
119
- }
120
- const envEmail = process.env.TIIME_EMAIL;
121
- const envPassword = process.env.TIIME_PASSWORD;
122
- if (envEmail && envPassword) {
123
- this.credentials = { email: envEmail, password: envPassword };
124
- return;
125
- }
126
- this.loadFromDisk();
127
- }
128
- async login(email, password) {
129
- const response = await ofetch(`https://${AUTH0_DOMAIN}/oauth/token`, {
130
- method: "POST",
131
- body: {
132
- grant_type: "password",
133
- client_id: AUTH0_CLIENT_ID,
134
- audience: AUTH0_AUDIENCE,
135
- scope: "openid email",
136
- username: email,
137
- password
138
- }
139
- });
140
- this.tokens = {
141
- access_token: response.access_token,
142
- expires_at: Date.now() + response.expires_in * 1e3
143
- };
144
- if (this.persist) {
145
- this.saveToDisk();
146
- saveCredentials(email, password);
147
- }
148
- return this.tokens;
149
- }
150
- async getValidToken() {
151
- if (!this.tokens || this.isExpired()) {
152
- const creds = this.credentials ?? loadCredentials();
153
- if (creds) {
154
- const tokens = await this.login(creds.email, creds.password);
155
- return tokens.access_token;
156
- }
157
- throw new Error(
158
- 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`."
159
- );
160
- }
161
- return this.tokens.access_token;
162
- }
163
- isAuthenticated() {
164
- return this.tokens !== null && !this.isExpired();
165
- }
166
- logout() {
167
- this.tokens = null;
168
- if (existsSync(AUTH_FILE)) {
169
- writeFileSync(AUTH_FILE, "{}");
170
- }
171
- }
172
- getTokenInfo() {
173
- if (!this.tokens) {
174
- return { email: null, expiresAt: null };
175
- }
176
- try {
177
- const payload = JSON.parse(
178
- Buffer.from(
179
- this.tokens.access_token.split(".")[1],
180
- "base64"
181
- ).toString()
182
- );
183
- return {
184
- email: payload["tiime/userEmail"] || null,
185
- expiresAt: new Date(this.tokens.expires_at)
186
- };
187
- } catch {
188
- return { email: null, expiresAt: new Date(this.tokens.expires_at) };
189
- }
190
- }
191
- isExpired() {
192
- if (!this.tokens) return true;
193
- return Date.now() >= this.tokens.expires_at - 6e4;
194
- }
195
- loadFromDisk() {
196
- try {
197
- if (existsSync(AUTH_FILE)) {
198
- const data = JSON.parse(readFileSync(AUTH_FILE, "utf-8"));
199
- if (data.access_token && data.expires_at) {
200
- this.tokens = data;
201
- }
202
- }
203
- } catch {
204
- }
205
- }
206
- saveToDisk() {
207
- if (!existsSync(CONFIG_DIR)) {
208
- mkdirSync(CONFIG_DIR, { recursive: true });
209
- }
210
- writeFileSync(AUTH_FILE, JSON.stringify(this.tokens, null, 2));
211
- }
212
- };
213
-
214
- // src/client.ts
215
- import { ofetch as ofetch2 } from "ofetch";
216
-
217
- // src/errors.ts
218
- var TiimeError = class extends Error {
219
- constructor(message, status, endpoint, details) {
220
- super(message);
221
- this.status = status;
222
- this.endpoint = endpoint;
223
- this.details = details;
224
- this.name = "TiimeError";
225
- }
226
- toJSON() {
227
- return {
228
- error: this.name,
229
- message: this.message,
230
- status: this.status,
231
- endpoint: this.endpoint,
232
- details: this.details
233
- };
234
- }
235
- };
236
-
237
- // src/resources/bank-accounts.ts
238
- var BankAccountsResource = class {
239
- constructor(fetch, companyId) {
240
- this.fetch = fetch;
241
- this.companyId = companyId;
242
- }
243
- list(enabled) {
244
- return this.fetch(
245
- `/companies/${this.companyId}/bank_accounts`,
246
- { query: enabled !== void 0 ? { enabled } : void 0 }
247
- );
248
- }
249
- get(bankAccountId) {
250
- return this.fetch(
251
- `/companies/${this.companyId}/bank_accounts/${bankAccountId}`
252
- );
253
- }
254
- async balance() {
255
- const accounts = await this.list(true);
256
- return accounts.map((a) => ({
257
- name: a.name,
258
- balance_amount: a.balance_amount,
259
- currency: a.balance_currency
260
- }));
261
- }
262
- };
263
-
264
- // src/resources/bank-transactions.ts
265
- var BankTransactionsResource = class {
266
- constructor(fetch, companyId) {
267
- this.fetch = fetch;
268
- this.companyId = companyId;
269
- }
270
- list(params) {
271
- const start = ((params?.page ?? 1) - 1) * (params?.pageSize ?? 100);
272
- const end = start + (params?.pageSize ?? 100);
273
- const { page: _, pageSize: __, from, to, search, ...query } = params ?? {};
274
- if (from) query.transaction_date_start = from;
275
- if (to) query.transaction_date_end = to;
276
- if (search) query.wording = search;
277
- return this.fetch(
278
- `/companies/${this.companyId}/bank_transactions`,
279
- {
280
- query: { hide_refused: false, ...query },
281
- headers: {
282
- Accept: "application/vnd.tiime.bank_transactions.v2+json",
283
- Range: `items=${start}-${end}`
284
- }
285
- }
286
- );
287
- }
288
- async listAll(params) {
289
- const pageSize = params?.pageSize ?? 200;
290
- const all = [];
291
- let page = 1;
292
- let hasMore = true;
293
- while (hasMore) {
294
- const response = await this.list({ ...params, page, pageSize });
295
- all.push(...response.transactions);
296
- hasMore = response.transactions.length === pageSize;
297
- page++;
298
- }
299
- return all;
300
- }
301
- unimputed() {
302
- return this.fetch(
303
- `/companies/${this.companyId}/bank_transactions/unimputed`
304
- );
305
- }
306
- get(transactionId) {
307
- return this.fetch(
308
- `/companies/${this.companyId}/bank_transactions/${transactionId}`
309
- );
310
- }
311
- labelSuggestions(transactionId) {
312
- return this.fetch(
313
- `/companies/${this.companyId}/bank_transactions/${transactionId}/label_suggestions`,
314
- {
315
- headers: {
316
- Accept: "application/vnd.tiime.bank_transactions.label_suggestions.v2+json"
317
- }
318
- }
319
- );
320
- }
321
- impute(transactionId, imputations) {
322
- return this.fetch(
323
- `/companies/${this.companyId}/bank_transactions/${transactionId}`,
324
- {
325
- method: "PATCH",
326
- body: { imputations }
327
- }
328
- );
329
- }
330
- matchDocuments(transactionId, documentIds) {
331
- return this.fetch(
332
- `/companies/${this.companyId}/bank_transactions/${transactionId}/document_matchings`,
333
- {
334
- method: "PUT",
335
- body: { documents: documentIds.map((id) => ({ id })) }
336
- }
337
- );
338
- }
339
- getMatchings(transactionId) {
340
- return this.fetch(
341
- `/companies/${this.companyId}/bank_transactions/${transactionId}/matchings`
342
- );
343
- }
344
- };
345
-
346
- // src/resources/clients.ts
347
- var ClientsResource = class {
348
- constructor(fetch, companyId) {
349
- this.fetch = fetch;
350
- this.companyId = companyId;
351
- }
352
- list(params) {
353
- return this.fetch(`/companies/${this.companyId}/clients`, {
354
- query: params,
355
- headers: {
356
- Accept: "application/vnd.tiime.timeline.v2+json",
357
- Range: "items=0-*"
358
- }
359
- });
360
- }
361
- get(clientId) {
362
- return this.fetch(
363
- `/companies/${this.companyId}/clients/${clientId}`
364
- );
365
- }
366
- create(params) {
367
- return this.fetch(`/companies/${this.companyId}/clients`, {
368
- method: "POST",
369
- body: params
370
- });
371
- }
372
- search(query) {
373
- return this.fetch(`/companies/${this.companyId}/clients`, {
374
- query: { search: query },
375
- headers: {
376
- Accept: "application/vnd.tiime.timeline.v2+json",
377
- Range: "items=0-*"
378
- }
379
- });
380
- }
381
- };
382
-
383
- // src/resources/company.ts
384
- var CompanyResource = class {
385
- constructor(fetch, companyId) {
386
- this.fetch = fetch;
387
- this.companyId = companyId;
388
- }
389
- get() {
390
- return this.fetch(`/companies/${this.companyId}`);
391
- }
392
- users() {
393
- return this.fetch(`/companies/${this.companyId}/users`);
394
- }
395
- appConfig() {
396
- return this.fetch(`/companies/${this.companyId}/app_config`);
397
- }
398
- accountingPeriod(rangeYear = 1) {
399
- return this.fetch(
400
- `/companies/${this.companyId}/accounting_period/current`,
401
- { query: { range_year: rangeYear } }
402
- );
403
- }
404
- tiles(keys) {
405
- return this.fetch(`/companies/${this.companyId}/tiles`, {
406
- query: { keys: keys.join(",") }
407
- });
408
- }
409
- dashboardBlocks(displayGroup = "monitoring") {
410
- return this.fetch(`/companies/${this.companyId}/dashboard_blocks`, {
411
- query: { sorts: "rank:asc", display_group: displayGroup }
412
- });
413
- }
414
- };
415
-
416
- // src/resources/documents.ts
417
- var DocumentsResource = class {
418
- constructor(fetch, companyId) {
419
- this.fetch = fetch;
420
- this.companyId = companyId;
421
- }
422
- list(params) {
423
- const start = ((params?.page ?? 1) - 1) * (params?.pageSize ?? 25);
424
- const end = start + (params?.pageSize ?? 25);
425
- const { page: _, pageSize: __, ...query } = params ?? {};
426
- return this.fetch(`/companies/${this.companyId}/documents`, {
427
- query: {
428
- sorts: "created_at:desc",
429
- expand: "file_family,preview_available",
430
- ...query
431
- },
432
- headers: {
433
- Accept: "application/vnd.tiime.documents.v2+json,application/vnd.tiime.docs.query+json,application/vnd.tiime.docs.imputation+json",
434
- Range: `items=${start}-${end}`
435
- }
436
- });
437
- }
438
- categories() {
439
- return this.fetch(
440
- `/companies/${this.companyId}/document_categories`,
441
- {
442
- headers: {
443
- Accept: "application/vnd.tiime.documents.v3+json"
444
- }
445
- }
446
- );
447
- }
448
- preview(documentId) {
449
- return this.fetch(
450
- `/companies/${this.companyId}/documents/${documentId}/preview`
451
- );
452
- }
453
- upload(file, filename, type) {
454
- const formData = new FormData();
455
- formData.append("file", new Blob([file]), filename);
456
- if (type) {
457
- formData.append("type", type);
458
- }
459
- return this.fetch(`/companies/${this.companyId}/documents`, {
460
- method: "POST",
461
- body: formData
462
- });
463
- }
464
- searchMatchable(query) {
465
- return this.fetch(
466
- `/companies/${this.companyId}/documents`,
467
- {
468
- query: { matchable: true, q: query },
469
- headers: {
470
- Accept: "application/vnd.tiime.documents.v3+json,application/vnd.tiime.docs.imputation+json",
471
- Range: "items=0-25"
472
- }
473
- }
474
- );
475
- }
476
- async download(documentId) {
477
- return this.fetch(
478
- `/companies/${this.companyId}/documents/${documentId}/download`,
479
- {
480
- headers: { Accept: "application/octet-stream" }
481
- }
482
- );
483
- }
484
- };
485
-
486
- // src/resources/expense-reports.ts
487
- var ExpenseReportsResource = class {
488
- constructor(fetch, companyId) {
489
- this.fetch = fetch;
490
- this.companyId = companyId;
491
- }
492
- list(sorts = "metadata.date:desc") {
493
- return this.fetch(
494
- `/companies/${this.companyId}/expense_reports`,
495
- {
496
- query: { expand: "total_amount", sorts },
497
- headers: { Range: "items=0-25" }
498
- }
499
- );
500
- }
501
- get(expenseReportId) {
502
- return this.fetch(
503
- `/companies/${this.companyId}/expense_reports/${expenseReportId}`
504
- );
505
- }
506
- create(params) {
507
- return this.fetch(
508
- `/companies/${this.companyId}/expense_reports`,
509
- {
510
- method: "POST",
511
- body: params
512
- }
513
- );
514
- }
515
- };
516
-
517
- // src/resources/invoices.ts
518
- var DEFAULT_INVOICE_TEMPLATE = {
519
- template: "advanced",
520
- status: "draft",
521
- due_date_mode: "thirty_days",
522
- title_enabled: true,
523
- free_field_enabled: false,
524
- free_field: "",
525
- discount_enabled: false,
526
- bank_detail_enabled: true,
527
- payment_condition_enabled: true,
528
- 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.",
529
- text_lines: []
530
- };
531
- var InvoicesResource = class {
532
- constructor(fetch, companyId) {
533
- this.fetch = fetch;
534
- this.companyId = companyId;
535
- }
536
- list(params) {
537
- const start = ((params?.page ?? 1) - 1) * (params?.pageSize ?? 25);
538
- const end = start + (params?.pageSize ?? 25);
539
- const query = {
540
- sorts: params?.sorts ?? "invoice_number:desc"
541
- };
542
- if (params?.status) query.status = params.status;
543
- return this.fetch(`/companies/${this.companyId}/invoices`, {
544
- query,
545
- headers: { Range: `items=${start}-${end}` }
546
- });
547
- }
548
- async listAll(params) {
549
- const pageSize = params?.pageSize ?? 100;
550
- const all = [];
551
- let page = 1;
552
- let hasMore = true;
553
- while (hasMore) {
554
- const batch = await this.list({
555
- sorts: params?.sorts,
556
- status: params?.status,
557
- page,
558
- pageSize
559
- });
560
- all.push(...batch);
561
- hasMore = batch.length === pageSize;
562
- page++;
563
- }
564
- return all;
565
- }
566
- get(invoiceId) {
567
- return this.fetch(
568
- `/companies/${this.companyId}/invoices/${invoiceId}`
569
- );
570
- }
571
- create(params) {
572
- const body = { ...DEFAULT_INVOICE_TEMPLATE, ...params };
573
- for (const line of body.lines ?? []) {
574
- line.line_amount = line.quantity * line.unit_amount;
575
- line.sequence ??= 1;
576
- line.invoicing_category_type ??= "benefit";
577
- line.discount_description ??= "";
578
- line.discount_amount ??= null;
579
- line.discount_percentage ??= null;
580
- }
581
- return this.fetch(`/companies/${this.companyId}/invoices`, {
582
- method: "POST",
583
- body
584
- });
585
- }
586
- update(invoiceId, params) {
587
- return this.fetch(
588
- `/companies/${this.companyId}/invoices/${invoiceId}`,
589
- {
590
- method: "PUT",
591
- body: params
592
- }
593
- );
594
- }
595
- send(invoiceId, params) {
596
- return this.fetch(
597
- `/companies/${this.companyId}/invoices/${invoiceId}/send`,
598
- {
599
- method: "POST",
600
- body: params
601
- }
602
- );
603
- }
604
- async downloadPdf(invoiceId) {
605
- return this.fetch(
606
- `/companies/${this.companyId}/invoices/${invoiceId}/pdf`,
607
- {
608
- headers: { Accept: "application/pdf" }
609
- }
610
- );
611
- }
612
- delete(invoiceId) {
613
- return this.fetch(
614
- `/companies/${this.companyId}/invoices/${invoiceId}`,
615
- { method: "DELETE" }
616
- );
617
- }
618
- async duplicate(invoiceId, overrides) {
619
- const source = await this.get(invoiceId);
620
- const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
621
- const lines = source.lines.map((l) => ({
622
- description: l.description,
623
- quantity: overrides?.quantity ?? l.quantity,
624
- unit_amount: l.unit_amount,
625
- vat_type: l.vat_type,
626
- invoicing_unit: l.invoicing_unit,
627
- invoicing_category_type: l.invoicing_category_type,
628
- article: l.article
629
- }));
630
- return this.create({
631
- client: source.client_id ? { id: source.client_id } : null,
632
- emission_date: overrides?.emission_date ?? today,
633
- title: source.title,
634
- title_enabled: !!source.title,
635
- lines,
636
- status: "draft"
637
- });
638
- }
639
- };
640
-
641
- // src/resources/labels.ts
642
- var LabelsResource = class {
643
- constructor(fetch, companyId) {
644
- this.fetch = fetch;
645
- this.companyId = companyId;
646
- }
647
- list() {
648
- return this.fetch(`/companies/${this.companyId}/labels`, {
649
- headers: {
650
- Accept: "application/vnd.tiime.labels.v2+json"
651
- }
652
- });
653
- }
654
- standard() {
655
- return this.fetch(`/companies/${this.companyId}/standard_labels`);
656
- }
657
- tags() {
658
- return this.fetch(`/companies/${this.companyId}/tags`, {
659
- query: { expand: "tag_detail" }
660
- });
661
- }
662
- };
663
-
664
- // src/resources/quotations.ts
665
- var QuotationsResource = class {
666
- constructor(fetch, companyId) {
667
- this.fetch = fetch;
668
- this.companyId = companyId;
669
- }
670
- list(expand = "invoices") {
671
- return this.fetch(`/companies/${this.companyId}/quotations`, {
672
- query: { expand },
673
- headers: { Range: "items=0-25" }
674
- });
675
- }
676
- get(quotationId) {
677
- return this.fetch(
678
- `/companies/${this.companyId}/quotations/${quotationId}`
679
- );
680
- }
681
- create(params) {
682
- for (const line of params.lines ?? []) {
683
- line.line_amount = line.quantity * line.unit_amount;
684
- line.sequence ??= 1;
685
- line.invoicing_category_type ??= "benefit";
686
- line.discount_description ??= "";
687
- line.discount_amount ??= null;
688
- line.discount_percentage ??= null;
689
- }
690
- return this.fetch(`/companies/${this.companyId}/quotations`, {
691
- method: "POST",
692
- body: params
693
- });
694
- }
695
- async downloadPdf(quotationId) {
696
- return this.fetch(
697
- `/companies/${this.companyId}/quotations/${quotationId}/pdf`,
698
- {
699
- headers: { Accept: "application/pdf" }
700
- }
701
- );
702
- }
703
- send(quotationId, params) {
704
- return this.fetch(
705
- `/companies/${this.companyId}/quotations/${quotationId}/send`,
706
- {
707
- method: "POST",
708
- body: params
709
- }
710
- );
711
- }
712
- };
713
-
714
- // src/resources/users.ts
715
- var UsersResource = class {
716
- constructor(fetch) {
717
- this.fetch = fetch;
718
- }
719
- me() {
720
- return this.fetch("/users/me");
721
- }
722
- legalInformations() {
723
- return this.fetch("/users/me/legal_informations");
724
- }
725
- settings(companyId) {
726
- return this.fetch(`/users/me/companies/${companyId}/settings`);
727
- }
728
- };
729
-
730
- // src/client.ts
731
- var BASE_URL = "https://chronos-api.tiime-apps.com/v1";
732
- var TiimeClient = class {
733
- fetch;
734
- tokenManager;
735
- companyId;
736
- constructor(options = {}) {
737
- this.companyId = resolveCompanyId(options.companyId);
738
- this.tokenManager = options.tokenManager ?? new TokenManager({
739
- tokens: options.tokens,
740
- email: options.email,
741
- password: options.password
742
- });
743
- this.fetch = ofetch2.create({
744
- baseURL: BASE_URL,
745
- retry: 2,
746
- retryDelay: 500,
747
- retryStatusCodes: [408, 429, 500, 502, 503, 504],
748
- headers: {
749
- "tiime-app": "tiime",
750
- "tiime-app-version": "4.30.3",
751
- "tiime-app-platform": "cli"
752
- },
753
- onRequest: async ({ options: options2 }) => {
754
- const token = await this.tokenManager.getValidToken();
755
- options2.headers.set("Authorization", `Bearer ${token}`);
756
- },
757
- onResponseError: ({ request, response }) => {
758
- throw new TiimeError(
759
- response.statusText || `HTTP ${response.status}`,
760
- response.status,
761
- String(request),
762
- response._data
763
- );
764
- }
765
- });
766
- }
767
- listCompanies() {
768
- return this.fetch("/companies", {
769
- headers: {
770
- Accept: "application/vnd.tiime.companies.v2+json",
771
- Range: "items=0-101"
772
- }
773
- });
774
- }
775
- get users() {
776
- return new UsersResource(this.fetch);
777
- }
778
- get company() {
779
- return new CompanyResource(this.fetch, this.companyId);
780
- }
781
- get clients() {
782
- return new ClientsResource(this.fetch, this.companyId);
783
- }
784
- get invoices() {
785
- return new InvoicesResource(this.fetch, this.companyId);
786
- }
787
- get quotations() {
788
- return new QuotationsResource(this.fetch, this.companyId);
789
- }
790
- get bankAccounts() {
791
- return new BankAccountsResource(this.fetch, this.companyId);
792
- }
793
- get bankTransactions() {
794
- return new BankTransactionsResource(this.fetch, this.companyId);
795
- }
796
- get documents() {
797
- return new DocumentsResource(this.fetch, this.companyId);
798
- }
799
- get expenseReports() {
800
- return new ExpenseReportsResource(this.fetch, this.companyId);
801
- }
802
- get labels() {
803
- return new LabelsResource(this.fetch, this.companyId);
804
- }
805
- };
806
- export {
807
- TiimeClient,
808
- TiimeError,
809
- TokenManager,
810
- resolveCompanyId
811
- };
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};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tiime-sdk",
3
- "version": "2.1.1",
3
+ "version": "2.2.0",
4
4
  "description": "TypeScript SDK for Tiime accounting API",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -44,9 +44,6 @@
44
44
  "engines": {
45
45
  "node": ">=20"
46
46
  },
47
- "dependencies": {
48
- "ofetch": "^1.5.1"
49
- },
50
47
  "devDependencies": {
51
48
  "@biomejs/biome": "^2.4.6",
52
49
  "@types/node": "^25.3.5",