@t4dhg/mcp-factorial 4.0.0 → 6.0.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/api.js CHANGED
@@ -5,8 +5,12 @@
5
5
  */
6
6
  import { fetchList, fetchOne, postOne, patchOne, deleteOne, postAction } from './http-client.js';
7
7
  import { cache, cached, CACHE_TTL, CacheManager } from './cache.js';
8
+ import { debug } from './config.js';
8
9
  import { buildPaginationParams, paginateResponse, sliceForPagination, } from './pagination.js';
9
10
  import { AuditAction, auditedOperation } from './audit.js';
11
+ import { validateId } from './utils.js';
12
+ import { ENDPOINTS, endpointWithId, endpointWithAction } from './endpoints.js';
13
+ import { NotFoundError } from './errors.js';
10
14
  // ============================================================================
11
15
  // Employee endpoints
12
16
  // ============================================================================
@@ -19,7 +23,7 @@ export async function listEmployees(options) {
19
23
  // For filtered requests, we need to fetch all and filter client-side
20
24
  // because the API doesn't reliably filter
21
25
  if (options?.team_id || options?.location_id) {
22
- const allEmployees = await cached('employees:all', () => fetchList('/employees/employees'), CACHE_TTL.employees);
26
+ const allEmployees = await cached('employees:all', () => fetchList(ENDPOINTS.employees), CACHE_TTL.employees);
23
27
  let filtered = allEmployees;
24
28
  if (options.team_id) {
25
29
  filtered = filtered.filter(e => e.team_ids?.includes(options.team_id));
@@ -30,7 +34,7 @@ export async function listEmployees(options) {
30
34
  return sliceForPagination(filtered, params);
31
35
  }
32
36
  // Without filters, use pagination directly
33
- const employees = await cached(cacheKey, () => fetchList('/employees/employees', { params }), CACHE_TTL.employees);
37
+ const employees = await cached(cacheKey, () => fetchList(ENDPOINTS.employees, { params }), CACHE_TTL.employees);
34
38
  return paginateResponse(employees, params.page, params.limit);
35
39
  }
36
40
  /**
@@ -41,12 +45,10 @@ export async function listEmployees(options) {
41
45
  * if the direct endpoint fails or returns no data.
42
46
  */
43
47
  export async function getEmployee(id) {
44
- if (!id || id <= 0) {
45
- throw new Error('Invalid employee ID. Please provide a positive number.');
46
- }
48
+ validateId(id, 'employee');
47
49
  // Try the direct endpoint first
48
50
  try {
49
- const employee = await cached(`employee:${id}`, () => fetchOne(`/employees/employees/${id}`), CACHE_TTL.employees);
51
+ const employee = await cached(`employee:${id}`, () => fetchOne(endpointWithId(ENDPOINTS.employees, id)), CACHE_TTL.employees);
50
52
  // If we got a valid employee, return it
51
53
  if (employee) {
52
54
  return employee;
@@ -55,13 +57,13 @@ export async function getEmployee(id) {
55
57
  catch (error) {
56
58
  // If direct fetch fails with NotFoundError, try fallback
57
59
  // (other errors will be re-thrown below)
58
- if (!(error instanceof Error && error.message.includes('not found'))) {
60
+ if (!(error instanceof NotFoundError)) {
59
61
  throw error;
60
62
  }
61
63
  }
62
64
  // Fallback: Fetch all employees and filter (same approach as searchEmployees)
63
65
  // This works around Factorial API limitations with the individual employee endpoint
64
- const allEmployees = await cached('employees:all', () => fetchList('/employees/employees'), CACHE_TTL.employees);
66
+ const allEmployees = await cached('employees:all', () => fetchList(ENDPOINTS.employees), CACHE_TTL.employees);
65
67
  const employee = allEmployees.find(emp => emp.id === id);
66
68
  if (!employee) {
67
69
  throw new Error(`Employee with ID ${id} not found.`);
@@ -75,7 +77,7 @@ export async function searchEmployees(query) {
75
77
  if (!query || query.trim().length < 2) {
76
78
  throw new Error('Search query must be at least 2 characters long.');
77
79
  }
78
- const allEmployees = await cached('employees:all', () => fetchList('/employees/employees'), CACHE_TTL.employees);
80
+ const allEmployees = await cached('employees:all', () => fetchList(ENDPOINTS.employees), CACHE_TTL.employees);
79
81
  const lowerQuery = query.toLowerCase().trim();
80
82
  return allEmployees.filter(emp => emp.full_name?.toLowerCase().includes(lowerQuery) ||
81
83
  emp.email?.toLowerCase().includes(lowerQuery) ||
@@ -91,17 +93,15 @@ export async function searchEmployees(query) {
91
93
  export async function listTeams(options) {
92
94
  const params = buildPaginationParams(options);
93
95
  const cacheKey = CacheManager.key('teams', options);
94
- const teams = await cached(cacheKey, () => fetchList('/teams/teams'), CACHE_TTL.teams);
96
+ const teams = await cached(cacheKey, () => fetchList(ENDPOINTS.teams), CACHE_TTL.teams);
95
97
  return sliceForPagination(teams, params);
96
98
  }
97
99
  /**
98
100
  * Get a specific team by ID
99
101
  */
100
102
  export async function getTeam(id) {
101
- if (!id || id <= 0) {
102
- throw new Error('Invalid team ID. Please provide a positive number.');
103
- }
104
- return cached(`team:${id}`, () => fetchOne(`/teams/teams/${id}`), CACHE_TTL.teams);
103
+ validateId(id, 'team');
104
+ return cached(`team:${id}`, () => fetchOne(endpointWithId(ENDPOINTS.teams, id)), CACHE_TTL.teams);
105
105
  }
106
106
  // ============================================================================
107
107
  // Location endpoints
@@ -112,17 +112,15 @@ export async function getTeam(id) {
112
112
  export async function listLocations(options) {
113
113
  const params = buildPaginationParams(options);
114
114
  const cacheKey = CacheManager.key('locations', options);
115
- const locations = await cached(cacheKey, () => fetchList('/locations/locations'), CACHE_TTL.locations);
115
+ const locations = await cached(cacheKey, () => fetchList(ENDPOINTS.locations), CACHE_TTL.locations);
116
116
  return sliceForPagination(locations, params);
117
117
  }
118
118
  /**
119
119
  * Get a specific location by ID
120
120
  */
121
121
  export async function getLocation(id) {
122
- if (!id || id <= 0) {
123
- throw new Error('Invalid location ID. Please provide a positive number.');
124
- }
125
- return cached(`location:${id}`, () => fetchOne(`/locations/locations/${id}`), CACHE_TTL.locations);
122
+ validateId(id, 'location');
123
+ return cached(`location:${id}`, () => fetchOne(endpointWithId(ENDPOINTS.locations, id)), CACHE_TTL.locations);
126
124
  }
127
125
  // ============================================================================
128
126
  // Contract endpoints
@@ -131,13 +129,13 @@ export async function getLocation(id) {
131
129
  * List contracts, optionally filtered by employee ID
132
130
  */
133
131
  export async function listContracts(employeeId, options) {
134
- if (employeeId !== undefined && employeeId <= 0) {
135
- throw new Error('Invalid employee ID. Please provide a positive number.');
132
+ if (employeeId !== undefined) {
133
+ validateId(employeeId, 'employee');
136
134
  }
137
135
  const params = buildPaginationParams(options);
138
136
  // Note: The API doesn't reliably filter by employee_id query param,
139
137
  // so we fetch all contracts and filter client-side
140
- const allContracts = await cached('contracts:all', () => fetchList('/contracts/contract-versions'), CACHE_TTL.contracts);
138
+ const allContracts = await cached('contracts:all', () => fetchList(ENDPOINTS.contracts), CACHE_TTL.contracts);
141
139
  const filtered = employeeId !== undefined
142
140
  ? allContracts.filter(c => c.employee_id === employeeId)
143
141
  : allContracts;
@@ -163,32 +161,28 @@ export async function listLeaves(options) {
163
161
  queryParams.start_on_gte = options.start_on_gte;
164
162
  if (options?.start_on_lte)
165
163
  queryParams.start_on_lte = options.start_on_lte;
166
- const leaves = await fetchList('/timeoff/leaves', { params: queryParams });
164
+ const leaves = await fetchList(ENDPOINTS.leaves, { params: queryParams });
167
165
  return paginateResponse(leaves, params.page, params.limit);
168
166
  }
169
167
  /**
170
168
  * Get a specific leave by ID
171
169
  */
172
170
  export async function getLeave(id) {
173
- if (!id || id <= 0) {
174
- throw new Error('Invalid leave ID. Please provide a positive number.');
175
- }
176
- return fetchOne(`/timeoff/leaves/${id}`);
171
+ validateId(id, 'leave');
172
+ return fetchOne(endpointWithId(ENDPOINTS.leaves, id));
177
173
  }
178
174
  /**
179
175
  * List all leave types
180
176
  */
181
177
  export async function listLeaveTypes() {
182
- return cached('leave-types:all', () => fetchList('/timeoff/leave-types'), CACHE_TTL.leaves);
178
+ return cached('leave-types:all', () => fetchList(ENDPOINTS.leaveTypes), CACHE_TTL.leaves);
183
179
  }
184
180
  /**
185
181
  * Get a specific leave type by ID
186
182
  */
187
183
  export async function getLeaveType(id) {
188
- if (!id || id <= 0) {
189
- throw new Error('Invalid leave type ID. Please provide a positive number.');
190
- }
191
- return fetchOne(`/timeoff/leave-types/${id}`);
184
+ validateId(id, 'leave type');
185
+ return fetchOne(endpointWithId(ENDPOINTS.leaveTypes, id));
192
186
  }
193
187
  /**
194
188
  * List allowances with optional filtering by employee
@@ -201,7 +195,7 @@ export async function listAllowances(options) {
201
195
  };
202
196
  if (options?.employee_id)
203
197
  queryParams.employee_id = options.employee_id;
204
- const allowances = await fetchList('/timeoff/allowances', { params: queryParams });
198
+ const allowances = await fetchList(ENDPOINTS.allowances, { params: queryParams });
205
199
  return paginateResponse(allowances, params.page, params.limit);
206
200
  }
207
201
  // ============================================================================
@@ -222,17 +216,15 @@ export async function listShifts(options) {
222
216
  queryParams.clock_in_gte = options.clock_in_gte;
223
217
  if (options?.clock_in_lte)
224
218
  queryParams.clock_in_lte = options.clock_in_lte;
225
- const shifts = await fetchList('/attendance/shifts', { params: queryParams });
219
+ const shifts = await fetchList(ENDPOINTS.shifts, { params: queryParams });
226
220
  return paginateResponse(shifts, params.page, params.limit);
227
221
  }
228
222
  /**
229
223
  * Get a specific shift by ID
230
224
  */
231
225
  export async function getShift(id) {
232
- if (!id || id <= 0) {
233
- throw new Error('Invalid shift ID. Please provide a positive number.');
234
- }
235
- return fetchOne(`/attendance/shifts/${id}`);
226
+ validateId(id, 'shift');
227
+ return fetchOne(endpointWithId(ENDPOINTS.shifts, id));
236
228
  }
237
229
  // ============================================================================
238
230
  // Document endpoints (Read-only)
@@ -241,16 +233,14 @@ export async function getShift(id) {
241
233
  * List all folders
242
234
  */
243
235
  export async function listFolders() {
244
- return cached('folders:all', () => fetchList('/documents/folders'), CACHE_TTL.default);
236
+ return cached('folders:all', () => fetchList(ENDPOINTS.folders), CACHE_TTL.default);
245
237
  }
246
238
  /**
247
239
  * Get a specific folder by ID
248
240
  */
249
241
  export async function getFolder(id) {
250
- if (!id || id <= 0) {
251
- throw new Error('Invalid folder ID. Please provide a positive number.');
252
- }
253
- return fetchOne(`/documents/folders/${id}`);
242
+ validateId(id, 'folder');
243
+ return fetchOne(endpointWithId(ENDPOINTS.folders, id));
254
244
  }
255
245
  /**
256
246
  * List documents with optional filtering by folder
@@ -271,20 +261,56 @@ export async function listDocuments(options) {
271
261
  const baseParams = new URLSearchParams(queryParams).toString();
272
262
  const fullParams = baseParams ? `${baseParams}&${employeeIdsParam}` : employeeIdsParam;
273
263
  // Make request with custom query string
274
- const documents = await fetchList(`/documents/documents?${fullParams}`);
264
+ const documents = await fetchList(`${ENDPOINTS.documents}?${fullParams}`);
265
+ debug(`listDocuments returned ${documents.length} documents`, {
266
+ sampleDocument: documents[0],
267
+ missingNames: documents.filter(d => !d.name).length,
268
+ });
275
269
  return paginateResponse(documents, params.page, params.limit);
276
270
  }
277
- const documents = await fetchList('/documents/documents', { params: queryParams });
271
+ const documents = await fetchList(ENDPOINTS.documents, { params: queryParams });
272
+ debug(`listDocuments returned ${documents.length} documents`, {
273
+ sampleDocument: documents[0],
274
+ missingNames: documents.filter(d => !d.name).length,
275
+ });
278
276
  return paginateResponse(documents, params.page, params.limit);
279
277
  }
280
278
  /**
281
279
  * Get a specific document by ID
280
+ *
281
+ * Note: The Factorial API's individual document endpoint (/documents/documents/{id})
282
+ * can be unreliable, similar to the employee endpoint. This function implements a fallback
283
+ * to listing all documents and filtering if the direct endpoint fails or returns no data.
284
+ *
285
+ * @param id - The document ID
286
+ * @returns The document object
287
+ * @throws Error if document is not found
282
288
  */
283
289
  export async function getDocument(id) {
284
- if (!id || id <= 0) {
285
- throw new Error('Invalid document ID. Please provide a positive number.');
290
+ validateId(id, 'document');
291
+ // Try the direct endpoint first
292
+ try {
293
+ const document = await fetchOne(endpointWithId(ENDPOINTS.documents, id));
294
+ // If we got a valid document, return it
295
+ if (document) {
296
+ return document;
297
+ }
298
+ }
299
+ catch (error) {
300
+ // If direct fetch fails with NotFoundError, try fallback
301
+ if (!(error instanceof NotFoundError)) {
302
+ throw error;
303
+ }
304
+ debug(`getDocument(${id}) - direct endpoint failed, using fallback`);
286
305
  }
287
- return fetchOne(`/documents/documents/${id}`);
306
+ // Fallback: Fetch all documents and filter by ID
307
+ // This works around Factorial API limitations with the individual document endpoint
308
+ const allDocuments = await fetchList(ENDPOINTS.documents);
309
+ const document = allDocuments.find(doc => doc.id === id);
310
+ if (!document) {
311
+ throw new Error(`Document with ID ${id} not found.`);
312
+ }
313
+ return document;
288
314
  }
289
315
  // ============================================================================
290
316
  // Job Catalog endpoints
@@ -293,31 +319,27 @@ export async function getDocument(id) {
293
319
  * List all job roles
294
320
  */
295
321
  export async function listJobRoles() {
296
- return cached('job-roles:all', () => fetchList('/job_catalog/roles'), CACHE_TTL.default);
322
+ return cached('job-roles:all', () => fetchList(ENDPOINTS.jobRoles), CACHE_TTL.default);
297
323
  }
298
324
  /**
299
325
  * Get a specific job role by ID
300
326
  */
301
327
  export async function getJobRole(id) {
302
- if (!id || id <= 0) {
303
- throw new Error('Invalid job role ID. Please provide a positive number.');
304
- }
305
- return fetchOne(`/job_catalog/roles/${id}`);
328
+ validateId(id, 'job role');
329
+ return fetchOne(endpointWithId(ENDPOINTS.jobRoles, id));
306
330
  }
307
331
  /**
308
332
  * List all job levels
309
333
  */
310
334
  export async function listJobLevels() {
311
- return cached('job-levels:all', () => fetchList('/job_catalog/levels'), CACHE_TTL.default);
335
+ return cached('job-levels:all', () => fetchList(ENDPOINTS.jobLevels), CACHE_TTL.default);
312
336
  }
313
337
  /**
314
338
  * Get a specific job level by ID
315
339
  */
316
340
  export async function getJobLevel(id) {
317
- if (!id || id <= 0) {
318
- throw new Error('Invalid job level ID. Please provide a positive number.');
319
- }
320
- return fetchOne(`/job_catalog/levels/${id}`);
341
+ validateId(id, 'job level');
342
+ return fetchOne(endpointWithId(ENDPOINTS.jobLevels, id));
321
343
  }
322
344
  // ============================================================================
323
345
  // Cache utilities
@@ -342,7 +364,7 @@ export function invalidateCache(resourceType) {
342
364
  */
343
365
  export async function createEmployee(input) {
344
366
  return auditedOperation(AuditAction.CREATE, 'employee', undefined, async () => {
345
- const employee = await postOne('/employees/employees', input);
367
+ const employee = await postOne(ENDPOINTS.employees, input);
346
368
  cache.invalidatePrefix('employees');
347
369
  return employee;
348
370
  }, Object.fromEntries(Object.entries(input).map(([k, v]) => [k, { to: v }])));
@@ -351,11 +373,9 @@ export async function createEmployee(input) {
351
373
  * Update an existing employee
352
374
  */
353
375
  export async function updateEmployee(id, input) {
354
- if (!id || id <= 0) {
355
- throw new Error('Invalid employee ID. Please provide a positive number.');
356
- }
376
+ validateId(id, 'employee');
357
377
  return auditedOperation(AuditAction.UPDATE, 'employee', id, async () => {
358
- const employee = await patchOne(`/employees/employees/${id}`, input);
378
+ const employee = await patchOne(endpointWithId(ENDPOINTS.employees, id), input);
359
379
  cache.invalidate(`employee:${id}`);
360
380
  cache.invalidatePrefix('employees');
361
381
  return employee;
@@ -365,11 +385,9 @@ export async function updateEmployee(id, input) {
365
385
  * Terminate an employee (soft delete)
366
386
  */
367
387
  export async function terminateEmployee(id, terminatedOn, reason) {
368
- if (!id || id <= 0) {
369
- throw new Error('Invalid employee ID. Please provide a positive number.');
370
- }
388
+ validateId(id, 'employee');
371
389
  return auditedOperation(AuditAction.TERMINATE, 'employee', id, async () => {
372
- const employee = await patchOne(`/employees/employees/${id}`, {
390
+ const employee = await patchOne(endpointWithId(ENDPOINTS.employees, id), {
373
391
  terminated_on: terminatedOn,
374
392
  });
375
393
  cache.invalidate(`employee:${id}`);
@@ -385,7 +403,7 @@ export async function terminateEmployee(id, terminatedOn, reason) {
385
403
  */
386
404
  export async function createTeam(input) {
387
405
  return auditedOperation(AuditAction.CREATE, 'team', undefined, async () => {
388
- const team = await postOne('/teams/teams', input);
406
+ const team = await postOne(ENDPOINTS.teams, input);
389
407
  cache.invalidatePrefix('teams');
390
408
  return team;
391
409
  });
@@ -394,11 +412,9 @@ export async function createTeam(input) {
394
412
  * Update an existing team
395
413
  */
396
414
  export async function updateTeam(id, input) {
397
- if (!id || id <= 0) {
398
- throw new Error('Invalid team ID. Please provide a positive number.');
399
- }
415
+ validateId(id, 'team');
400
416
  return auditedOperation(AuditAction.UPDATE, 'team', id, async () => {
401
- const team = await patchOne(`/teams/teams/${id}`, input);
417
+ const team = await patchOne(endpointWithId(ENDPOINTS.teams, id), input);
402
418
  cache.invalidate(`team:${id}`);
403
419
  cache.invalidatePrefix('teams');
404
420
  return team;
@@ -408,11 +424,9 @@ export async function updateTeam(id, input) {
408
424
  * Delete a team
409
425
  */
410
426
  export async function deleteTeam(id) {
411
- if (!id || id <= 0) {
412
- throw new Error('Invalid team ID. Please provide a positive number.');
413
- }
427
+ validateId(id, 'team');
414
428
  return auditedOperation(AuditAction.DELETE, 'team', id, async () => {
415
- await deleteOne(`/teams/teams/${id}`);
429
+ await deleteOne(endpointWithId(ENDPOINTS.teams, id));
416
430
  cache.invalidate(`team:${id}`);
417
431
  cache.invalidatePrefix('teams');
418
432
  });
@@ -425,7 +439,7 @@ export async function deleteTeam(id) {
425
439
  */
426
440
  export async function createLocation(input) {
427
441
  return auditedOperation(AuditAction.CREATE, 'location', undefined, async () => {
428
- const location = await postOne('/locations/locations', input);
442
+ const location = await postOne(ENDPOINTS.locations, input);
429
443
  cache.invalidatePrefix('locations');
430
444
  return location;
431
445
  });
@@ -434,11 +448,9 @@ export async function createLocation(input) {
434
448
  * Update an existing location
435
449
  */
436
450
  export async function updateLocation(id, input) {
437
- if (!id || id <= 0) {
438
- throw new Error('Invalid location ID. Please provide a positive number.');
439
- }
451
+ validateId(id, 'location');
440
452
  return auditedOperation(AuditAction.UPDATE, 'location', id, async () => {
441
- const location = await patchOne(`/locations/locations/${id}`, input);
453
+ const location = await patchOne(endpointWithId(ENDPOINTS.locations, id), input);
442
454
  cache.invalidate(`location:${id}`);
443
455
  cache.invalidatePrefix('locations');
444
456
  return location;
@@ -448,11 +460,9 @@ export async function updateLocation(id, input) {
448
460
  * Delete a location
449
461
  */
450
462
  export async function deleteLocation(id) {
451
- if (!id || id <= 0) {
452
- throw new Error('Invalid location ID. Please provide a positive number.');
453
- }
463
+ validateId(id, 'location');
454
464
  return auditedOperation(AuditAction.DELETE, 'location', id, async () => {
455
- await deleteOne(`/locations/locations/${id}`);
465
+ await deleteOne(endpointWithId(ENDPOINTS.locations, id));
456
466
  cache.invalidate(`location:${id}`);
457
467
  cache.invalidatePrefix('locations');
458
468
  });
@@ -465,7 +475,7 @@ export async function deleteLocation(id) {
465
475
  */
466
476
  export async function createLeave(input) {
467
477
  return auditedOperation(AuditAction.CREATE, 'leave', undefined, async () => {
468
- const leave = await postOne('/timeoff/leaves', input);
478
+ const leave = await postOne(ENDPOINTS.leaves, input);
469
479
  return leave;
470
480
  });
471
481
  }
@@ -473,11 +483,9 @@ export async function createLeave(input) {
473
483
  * Update a leave request
474
484
  */
475
485
  export async function updateLeave(id, input) {
476
- if (!id || id <= 0) {
477
- throw new Error('Invalid leave ID. Please provide a positive number.');
478
- }
486
+ validateId(id, 'leave');
479
487
  return auditedOperation(AuditAction.UPDATE, 'leave', id, async () => {
480
- const leave = await patchOne(`/timeoff/leaves/${id}`, input);
488
+ const leave = await patchOne(endpointWithId(ENDPOINTS.leaves, id), input);
481
489
  return leave;
482
490
  });
483
491
  }
@@ -485,22 +493,18 @@ export async function updateLeave(id, input) {
485
493
  * Cancel a leave request
486
494
  */
487
495
  export async function cancelLeave(id) {
488
- if (!id || id <= 0) {
489
- throw new Error('Invalid leave ID. Please provide a positive number.');
490
- }
496
+ validateId(id, 'leave');
491
497
  return auditedOperation(AuditAction.DELETE, 'leave', id, async () => {
492
- await deleteOne(`/timeoff/leaves/${id}`);
498
+ await deleteOne(endpointWithId(ENDPOINTS.leaves, id));
493
499
  });
494
500
  }
495
501
  /**
496
502
  * Approve a leave request
497
503
  */
498
504
  export async function approveLeave(id, input) {
499
- if (!id || id <= 0) {
500
- throw new Error('Invalid leave ID. Please provide a positive number.');
501
- }
505
+ validateId(id, 'leave');
502
506
  return auditedOperation(AuditAction.APPROVE, 'leave', id, async () => {
503
- const leave = await postAction(`/timeoff/leaves/${id}/approve`, input || {});
507
+ const leave = await postAction(endpointWithAction(ENDPOINTS.leaves, id, 'approve'), input || {});
504
508
  return leave;
505
509
  });
506
510
  }
@@ -508,11 +512,9 @@ export async function approveLeave(id, input) {
508
512
  * Reject a leave request
509
513
  */
510
514
  export async function rejectLeave(id, input) {
511
- if (!id || id <= 0) {
512
- throw new Error('Invalid leave ID. Please provide a positive number.');
513
- }
515
+ validateId(id, 'leave');
514
516
  return auditedOperation(AuditAction.REJECT, 'leave', id, async () => {
515
- const leave = await postAction(`/timeoff/leaves/${id}/reject`, input || {});
517
+ const leave = await postAction(endpointWithAction(ENDPOINTS.leaves, id, 'reject'), input || {});
516
518
  return leave;
517
519
  });
518
520
  }
@@ -524,7 +526,7 @@ export async function rejectLeave(id, input) {
524
526
  */
525
527
  export async function createShift(input) {
526
528
  return auditedOperation(AuditAction.CREATE, 'shift', undefined, async () => {
527
- const shift = await postOne('/attendance/shifts', input);
529
+ const shift = await postOne(ENDPOINTS.shifts, input);
528
530
  return shift;
529
531
  });
530
532
  }
@@ -532,11 +534,9 @@ export async function createShift(input) {
532
534
  * Update a shift
533
535
  */
534
536
  export async function updateShift(id, input) {
535
- if (!id || id <= 0) {
536
- throw new Error('Invalid shift ID. Please provide a positive number.');
537
- }
537
+ validateId(id, 'shift');
538
538
  return auditedOperation(AuditAction.UPDATE, 'shift', id, async () => {
539
- const shift = await patchOne(`/attendance/shifts/${id}`, input);
539
+ const shift = await patchOne(endpointWithId(ENDPOINTS.shifts, id), input);
540
540
  return shift;
541
541
  });
542
542
  }
@@ -544,11 +544,9 @@ export async function updateShift(id, input) {
544
544
  * Delete a shift
545
545
  */
546
546
  export async function deleteShift(id) {
547
- if (!id || id <= 0) {
548
- throw new Error('Invalid shift ID. Please provide a positive number.');
549
- }
547
+ validateId(id, 'shift');
550
548
  return auditedOperation(AuditAction.DELETE, 'shift', id, async () => {
551
- await deleteOne(`/attendance/shifts/${id}`);
549
+ await deleteOne(endpointWithId(ENDPOINTS.shifts, id));
552
550
  });
553
551
  }
554
552
  // ============================================================================
@@ -559,17 +557,15 @@ export async function deleteShift(id) {
559
557
  */
560
558
  export async function listProjects(options) {
561
559
  const params = buildPaginationParams(options);
562
- const projects = await cached(CacheManager.key('projects', options), () => fetchList('/project_management/projects'), CACHE_TTL.default);
560
+ const projects = await cached(CacheManager.key('projects', options), () => fetchList(ENDPOINTS.projects), CACHE_TTL.default);
563
561
  return sliceForPagination(projects, params);
564
562
  }
565
563
  /**
566
564
  * Get a specific project by ID
567
565
  */
568
566
  export async function getProject(id) {
569
- if (!id || id <= 0) {
570
- throw new Error('Invalid project ID. Please provide a positive number.');
571
- }
572
- return cached(`project:${id}`, () => fetchOne(`/project_management/projects/${id}`), CACHE_TTL.default);
567
+ validateId(id, 'project');
568
+ return cached(`project:${id}`, () => fetchOne(endpointWithId(ENDPOINTS.projects, id)), CACHE_TTL.default);
573
569
  }
574
570
  /**
575
571
  * List project tasks
@@ -579,7 +575,7 @@ export async function listProjectTasks(projectId, options) {
579
575
  const queryParams = {};
580
576
  if (projectId)
581
577
  queryParams.project_ids = projectId;
582
- const tasks = await fetchList('/project_management/project_tasks', {
578
+ const tasks = await fetchList(ENDPOINTS.projectTasks, {
583
579
  params: queryParams,
584
580
  });
585
581
  return sliceForPagination(tasks, params);
@@ -588,10 +584,8 @@ export async function listProjectTasks(projectId, options) {
588
584
  * Get a specific project task by ID
589
585
  */
590
586
  export async function getProjectTask(id) {
591
- if (!id || id <= 0) {
592
- throw new Error('Invalid task ID. Please provide a positive number.');
593
- }
594
- return fetchOne(`/project_management/project_tasks/${id}`);
587
+ validateId(id, 'task');
588
+ return fetchOne(endpointWithId(ENDPOINTS.projectTasks, id));
595
589
  }
596
590
  /**
597
591
  * List project workers
@@ -601,7 +595,7 @@ export async function listProjectWorkers(projectId, options) {
601
595
  const queryParams = {};
602
596
  if (projectId)
603
597
  queryParams.project_ids = projectId;
604
- const workers = await fetchList('/project_management/project_workers', {
598
+ const workers = await fetchList(ENDPOINTS.projectWorkers, {
605
599
  params: queryParams,
606
600
  });
607
601
  return sliceForPagination(workers, params);
@@ -610,10 +604,8 @@ export async function listProjectWorkers(projectId, options) {
610
604
  * Get a specific project worker by ID
611
605
  */
612
606
  export async function getProjectWorker(id) {
613
- if (!id || id <= 0) {
614
- throw new Error('Invalid project worker ID. Please provide a positive number.');
615
- }
616
- return fetchOne(`/project_management/project_workers/${id}`);
607
+ validateId(id, 'project worker');
608
+ return fetchOne(endpointWithId(ENDPOINTS.projectWorkers, id));
617
609
  }
618
610
  /**
619
611
  * List time records
@@ -623,7 +615,7 @@ export async function listTimeRecords(projectWorkerId, options) {
623
615
  const queryParams = {};
624
616
  if (projectWorkerId)
625
617
  queryParams.project_workers_ids = projectWorkerId;
626
- const records = await fetchList('/project_management/time_records', {
618
+ const records = await fetchList(ENDPOINTS.timeRecords, {
627
619
  params: queryParams,
628
620
  });
629
621
  return sliceForPagination(records, params);
@@ -632,10 +624,8 @@ export async function listTimeRecords(projectWorkerId, options) {
632
624
  * Get a specific time record by ID
633
625
  */
634
626
  export async function getTimeRecord(id) {
635
- if (!id || id <= 0) {
636
- throw new Error('Invalid time record ID. Please provide a positive number.');
637
- }
638
- return fetchOne(`/project_management/time_records/${id}`);
627
+ validateId(id, 'time record');
628
+ return fetchOne(endpointWithId(ENDPOINTS.timeRecords, id));
639
629
  }
640
630
  // ============================================================================
641
631
  // Projects & Time Tracking - WRITE endpoints
@@ -645,7 +635,7 @@ export async function getTimeRecord(id) {
645
635
  */
646
636
  export async function createProject(input) {
647
637
  return auditedOperation(AuditAction.CREATE, 'project', undefined, async () => {
648
- const project = await postOne('/project_management/projects', input);
638
+ const project = await postOne(ENDPOINTS.projects, input);
649
639
  cache.invalidatePrefix('projects');
650
640
  return project;
651
641
  });
@@ -654,11 +644,9 @@ export async function createProject(input) {
654
644
  * Update a project
655
645
  */
656
646
  export async function updateProject(id, input) {
657
- if (!id || id <= 0) {
658
- throw new Error('Invalid project ID. Please provide a positive number.');
659
- }
647
+ validateId(id, 'project');
660
648
  return auditedOperation(AuditAction.UPDATE, 'project', id, async () => {
661
- const project = await patchOne(`/project_management/projects/${id}`, input);
649
+ const project = await patchOne(endpointWithId(ENDPOINTS.projects, id), input);
662
650
  cache.invalidate(`project:${id}`);
663
651
  cache.invalidatePrefix('projects');
664
652
  return project;
@@ -668,11 +656,9 @@ export async function updateProject(id, input) {
668
656
  * Delete a project
669
657
  */
670
658
  export async function deleteProject(id) {
671
- if (!id || id <= 0) {
672
- throw new Error('Invalid project ID. Please provide a positive number.');
673
- }
659
+ validateId(id, 'project');
674
660
  return auditedOperation(AuditAction.DELETE, 'project', id, async () => {
675
- await deleteOne(`/project_management/projects/${id}`);
661
+ await deleteOne(endpointWithId(ENDPOINTS.projects, id));
676
662
  cache.invalidate(`project:${id}`);
677
663
  cache.invalidatePrefix('projects');
678
664
  });
@@ -682,29 +668,25 @@ export async function deleteProject(id) {
682
668
  */
683
669
  export async function createProjectTask(input) {
684
670
  return auditedOperation(AuditAction.CREATE, 'project_task', undefined, async () => {
685
- return postOne('/project_management/project_tasks', input);
671
+ return postOne(ENDPOINTS.projectTasks, input);
686
672
  });
687
673
  }
688
674
  /**
689
675
  * Update a project task
690
676
  */
691
677
  export async function updateProjectTask(id, input) {
692
- if (!id || id <= 0) {
693
- throw new Error('Invalid task ID. Please provide a positive number.');
694
- }
678
+ validateId(id, 'task');
695
679
  return auditedOperation(AuditAction.UPDATE, 'project_task', id, async () => {
696
- return patchOne(`/project_management/project_tasks/${id}`, input);
680
+ return patchOne(endpointWithId(ENDPOINTS.projectTasks, id), input);
697
681
  });
698
682
  }
699
683
  /**
700
684
  * Delete a project task
701
685
  */
702
686
  export async function deleteProjectTask(id) {
703
- if (!id || id <= 0) {
704
- throw new Error('Invalid task ID. Please provide a positive number.');
705
- }
687
+ validateId(id, 'task');
706
688
  return auditedOperation(AuditAction.DELETE, 'project_task', id, async () => {
707
- await deleteOne(`/project_management/project_tasks/${id}`);
689
+ await deleteOne(endpointWithId(ENDPOINTS.projectTasks, id));
708
690
  });
709
691
  }
710
692
  /**
@@ -712,18 +694,16 @@ export async function deleteProjectTask(id) {
712
694
  */
713
695
  export async function assignProjectWorker(input) {
714
696
  return auditedOperation(AuditAction.ASSIGN, 'project_worker', undefined, async () => {
715
- return postOne('/project_management/project_workers', input);
697
+ return postOne(ENDPOINTS.projectWorkers, input);
716
698
  });
717
699
  }
718
700
  /**
719
701
  * Remove a worker from a project
720
702
  */
721
703
  export async function removeProjectWorker(id) {
722
- if (!id || id <= 0) {
723
- throw new Error('Invalid project worker ID. Please provide a positive number.');
724
- }
704
+ validateId(id, 'project worker');
725
705
  return auditedOperation(AuditAction.UNASSIGN, 'project_worker', id, async () => {
726
- await deleteOne(`/project_management/project_workers/${id}`);
706
+ await deleteOne(endpointWithId(ENDPOINTS.projectWorkers, id));
727
707
  });
728
708
  }
729
709
  /**
@@ -731,29 +711,25 @@ export async function removeProjectWorker(id) {
731
711
  */
732
712
  export async function createTimeRecord(input) {
733
713
  return auditedOperation(AuditAction.CREATE, 'time_record', undefined, async () => {
734
- return postOne('/project_management/time_records', input);
714
+ return postOne(ENDPOINTS.timeRecords, input);
735
715
  });
736
716
  }
737
717
  /**
738
718
  * Update a time record
739
719
  */
740
720
  export async function updateTimeRecord(id, input) {
741
- if (!id || id <= 0) {
742
- throw new Error('Invalid time record ID. Please provide a positive number.');
743
- }
721
+ validateId(id, 'time record');
744
722
  return auditedOperation(AuditAction.UPDATE, 'time_record', id, async () => {
745
- return patchOne(`/project_management/time_records/${id}`, input);
723
+ return patchOne(endpointWithId(ENDPOINTS.timeRecords, id), input);
746
724
  });
747
725
  }
748
726
  /**
749
727
  * Delete a time record
750
728
  */
751
729
  export async function deleteTimeRecord(id) {
752
- if (!id || id <= 0) {
753
- throw new Error('Invalid time record ID. Please provide a positive number.');
754
- }
730
+ validateId(id, 'time record');
755
731
  return auditedOperation(AuditAction.DELETE, 'time_record', id, async () => {
756
- await deleteOne(`/project_management/time_records/${id}`);
732
+ await deleteOne(endpointWithId(ENDPOINTS.timeRecords, id));
757
733
  });
758
734
  }
759
735
  // ============================================================================
@@ -764,17 +740,15 @@ export async function deleteTimeRecord(id) {
764
740
  */
765
741
  export async function listTrainings(options) {
766
742
  const params = buildPaginationParams(options);
767
- const trainings = await cached(CacheManager.key('trainings', options), () => fetchList('/trainings/trainings'), CACHE_TTL.default);
743
+ const trainings = await cached(CacheManager.key('trainings', options), () => fetchList(ENDPOINTS.trainings), CACHE_TTL.default);
768
744
  return sliceForPagination(trainings, params);
769
745
  }
770
746
  /**
771
747
  * Get a specific training by ID
772
748
  */
773
749
  export async function getTraining(id) {
774
- if (!id || id <= 0) {
775
- throw new Error('Invalid training ID. Please provide a positive number.');
776
- }
777
- return cached(`training:${id}`, () => fetchOne(`/trainings/trainings/${id}`), CACHE_TTL.default);
750
+ validateId(id, 'training');
751
+ return cached(`training:${id}`, () => fetchOne(endpointWithId(ENDPOINTS.trainings, id)), CACHE_TTL.default);
778
752
  }
779
753
  /**
780
754
  * List training sessions
@@ -784,17 +758,17 @@ export async function listTrainingSessions(trainingId, options) {
784
758
  const queryParams = {};
785
759
  if (trainingId)
786
760
  queryParams.training_id = trainingId;
787
- const sessions = await fetchList('/trainings/sessions', { params: queryParams });
761
+ const sessions = await fetchList(ENDPOINTS.trainingSessions, {
762
+ params: queryParams,
763
+ });
788
764
  return sliceForPagination(sessions, params);
789
765
  }
790
766
  /**
791
767
  * Get a specific training session by ID
792
768
  */
793
769
  export async function getTrainingSession(id) {
794
- if (!id || id <= 0) {
795
- throw new Error('Invalid session ID. Please provide a positive number.');
796
- }
797
- return fetchOne(`/trainings/sessions/${id}`);
770
+ validateId(id, 'session');
771
+ return fetchOne(endpointWithId(ENDPOINTS.trainingSessions, id));
798
772
  }
799
773
  /**
800
774
  * List training enrollments
@@ -804,7 +778,7 @@ export async function listTrainingEnrollments(trainingId, options) {
804
778
  const queryParams = {};
805
779
  if (trainingId)
806
780
  queryParams.training_id = trainingId;
807
- const enrollments = await fetchList('/trainings/memberships', {
781
+ const enrollments = await fetchList(ENDPOINTS.trainingMemberships, {
808
782
  params: queryParams,
809
783
  });
810
784
  return sliceForPagination(enrollments, params);
@@ -813,10 +787,8 @@ export async function listTrainingEnrollments(trainingId, options) {
813
787
  * Get a specific training enrollment by ID
814
788
  */
815
789
  export async function getTrainingEnrollment(id) {
816
- if (!id || id <= 0) {
817
- throw new Error('Invalid enrollment ID. Please provide a positive number.');
818
- }
819
- return fetchOne(`/trainings/memberships/${id}`);
790
+ validateId(id, 'enrollment');
791
+ return fetchOne(endpointWithId(ENDPOINTS.trainingMemberships, id));
820
792
  }
821
793
  // ============================================================================
822
794
  // Training & Development - WRITE endpoints
@@ -826,7 +798,7 @@ export async function getTrainingEnrollment(id) {
826
798
  */
827
799
  export async function createTraining(input) {
828
800
  return auditedOperation(AuditAction.CREATE, 'training', undefined, async () => {
829
- const training = await postOne('/trainings/trainings', input);
801
+ const training = await postOne(ENDPOINTS.trainings, input);
830
802
  cache.invalidatePrefix('trainings');
831
803
  return training;
832
804
  });
@@ -835,11 +807,9 @@ export async function createTraining(input) {
835
807
  * Update a training program
836
808
  */
837
809
  export async function updateTraining(id, input) {
838
- if (!id || id <= 0) {
839
- throw new Error('Invalid training ID. Please provide a positive number.');
840
- }
810
+ validateId(id, 'training');
841
811
  return auditedOperation(AuditAction.UPDATE, 'training', id, async () => {
842
- const training = await patchOne(`/trainings/trainings/${id}`, input);
812
+ const training = await patchOne(endpointWithId(ENDPOINTS.trainings, id), input);
843
813
  cache.invalidate(`training:${id}`);
844
814
  cache.invalidatePrefix('trainings');
845
815
  return training;
@@ -849,11 +819,9 @@ export async function updateTraining(id, input) {
849
819
  * Delete a training program
850
820
  */
851
821
  export async function deleteTraining(id) {
852
- if (!id || id <= 0) {
853
- throw new Error('Invalid training ID. Please provide a positive number.');
854
- }
822
+ validateId(id, 'training');
855
823
  return auditedOperation(AuditAction.DELETE, 'training', id, async () => {
856
- await deleteOne(`/trainings/trainings/${id}`);
824
+ await deleteOne(endpointWithId(ENDPOINTS.trainings, id));
857
825
  cache.invalidate(`training:${id}`);
858
826
  cache.invalidatePrefix('trainings');
859
827
  });
@@ -863,29 +831,25 @@ export async function deleteTraining(id) {
863
831
  */
864
832
  export async function createTrainingSession(input) {
865
833
  return auditedOperation(AuditAction.CREATE, 'training_session', undefined, async () => {
866
- return postOne('/trainings/sessions', input);
834
+ return postOne(ENDPOINTS.trainingSessions, input);
867
835
  });
868
836
  }
869
837
  /**
870
838
  * Update a training session
871
839
  */
872
840
  export async function updateTrainingSession(id, input) {
873
- if (!id || id <= 0) {
874
- throw new Error('Invalid session ID. Please provide a positive number.');
875
- }
841
+ validateId(id, 'session');
876
842
  return auditedOperation(AuditAction.UPDATE, 'training_session', id, async () => {
877
- return patchOne(`/trainings/sessions/${id}`, input);
843
+ return patchOne(endpointWithId(ENDPOINTS.trainingSessions, id), input);
878
844
  });
879
845
  }
880
846
  /**
881
847
  * Delete a training session
882
848
  */
883
849
  export async function deleteTrainingSession(id) {
884
- if (!id || id <= 0) {
885
- throw new Error('Invalid session ID. Please provide a positive number.');
886
- }
850
+ validateId(id, 'session');
887
851
  return auditedOperation(AuditAction.DELETE, 'training_session', id, async () => {
888
- await deleteOne(`/trainings/sessions/${id}`);
852
+ await deleteOne(endpointWithId(ENDPOINTS.trainingSessions, id));
889
853
  });
890
854
  }
891
855
  /**
@@ -893,18 +857,16 @@ export async function deleteTrainingSession(id) {
893
857
  */
894
858
  export async function enrollInTraining(input) {
895
859
  return auditedOperation(AuditAction.ASSIGN, 'training_enrollment', undefined, async () => {
896
- return postOne('/trainings/memberships', input);
860
+ return postOne(ENDPOINTS.trainingMemberships, input);
897
861
  });
898
862
  }
899
863
  /**
900
864
  * Remove enrollment from a training
901
865
  */
902
866
  export async function unenrollFromTraining(id) {
903
- if (!id || id <= 0) {
904
- throw new Error('Invalid enrollment ID. Please provide a positive number.');
905
- }
867
+ validateId(id, 'enrollment');
906
868
  return auditedOperation(AuditAction.UNASSIGN, 'training_enrollment', id, async () => {
907
- await deleteOne(`/trainings/memberships/${id}`);
869
+ await deleteOne(endpointWithId(ENDPOINTS.trainingMemberships, id));
908
870
  });
909
871
  }
910
872
  // ============================================================================
@@ -915,24 +877,22 @@ export async function unenrollFromTraining(id) {
915
877
  */
916
878
  export async function listWorkAreas(options) {
917
879
  const params = buildPaginationParams(options);
918
- const workAreas = await cached(CacheManager.key('work_areas', options), () => fetchList('/locations/work_areas'), CACHE_TTL.locations);
880
+ const workAreas = await cached(CacheManager.key('work_areas', options), () => fetchList(ENDPOINTS.workAreas), CACHE_TTL.locations);
919
881
  return sliceForPagination(workAreas, params);
920
882
  }
921
883
  /**
922
884
  * Get a specific work area by ID
923
885
  */
924
886
  export async function getWorkArea(id) {
925
- if (!id || id <= 0) {
926
- throw new Error('Invalid work area ID. Please provide a positive number.');
927
- }
928
- return cached(`work_area:${id}`, () => fetchOne(`/locations/work_areas/${id}`), CACHE_TTL.locations);
887
+ validateId(id, 'work area');
888
+ return cached(`work_area:${id}`, () => fetchOne(endpointWithId(ENDPOINTS.workAreas, id)), CACHE_TTL.locations);
929
889
  }
930
890
  /**
931
891
  * Create a work area
932
892
  */
933
893
  export async function createWorkArea(input) {
934
894
  return auditedOperation(AuditAction.CREATE, 'work_area', undefined, async () => {
935
- const workArea = await postOne('/locations/work_areas', input);
895
+ const workArea = await postOne(ENDPOINTS.workAreas, input);
936
896
  cache.invalidatePrefix('work_areas');
937
897
  return workArea;
938
898
  });
@@ -941,11 +901,9 @@ export async function createWorkArea(input) {
941
901
  * Update a work area
942
902
  */
943
903
  export async function updateWorkArea(id, input) {
944
- if (!id || id <= 0) {
945
- throw new Error('Invalid work area ID. Please provide a positive number.');
946
- }
904
+ validateId(id, 'work area');
947
905
  return auditedOperation(AuditAction.UPDATE, 'work_area', id, async () => {
948
- const workArea = await patchOne(`/locations/work_areas/${id}`, input);
906
+ const workArea = await patchOne(endpointWithId(ENDPOINTS.workAreas, id), input);
949
907
  cache.invalidate(`work_area:${id}`);
950
908
  cache.invalidatePrefix('work_areas');
951
909
  return workArea;
@@ -955,11 +913,9 @@ export async function updateWorkArea(id, input) {
955
913
  * Archive a work area
956
914
  */
957
915
  export async function archiveWorkArea(id) {
958
- if (!id || id <= 0) {
959
- throw new Error('Invalid work area ID. Please provide a positive number.');
960
- }
916
+ validateId(id, 'work area');
961
917
  return auditedOperation(AuditAction.ARCHIVE, 'work_area', id, async () => {
962
- const workArea = await postAction(`/locations/work_areas/${id}/archive`);
918
+ const workArea = await postAction(endpointWithAction(ENDPOINTS.workAreas, id, 'archive'));
963
919
  cache.invalidate(`work_area:${id}`);
964
920
  cache.invalidatePrefix('work_areas');
965
921
  return workArea;
@@ -969,11 +925,9 @@ export async function archiveWorkArea(id) {
969
925
  * Unarchive a work area
970
926
  */
971
927
  export async function unarchiveWorkArea(id) {
972
- if (!id || id <= 0) {
973
- throw new Error('Invalid work area ID. Please provide a positive number.');
974
- }
928
+ validateId(id, 'work area');
975
929
  return auditedOperation(AuditAction.UNARCHIVE, 'work_area', id, async () => {
976
- const workArea = await postAction(`/locations/work_areas/${id}/unarchive`);
930
+ const workArea = await postAction(endpointWithAction(ENDPOINTS.workAreas, id, 'unarchive'));
977
931
  cache.invalidate(`work_area:${id}`);
978
932
  cache.invalidatePrefix('work_areas');
979
933
  return workArea;
@@ -987,34 +941,30 @@ export async function unarchiveWorkArea(id) {
987
941
  */
988
942
  export async function listJobPostings(options) {
989
943
  const params = buildPaginationParams(options);
990
- const postings = await cached(CacheManager.key('job_postings', options), () => fetchList('/ats/job_postings'), CACHE_TTL.default);
944
+ const postings = await cached(CacheManager.key('job_postings', options), () => fetchList(ENDPOINTS.jobPostings), CACHE_TTL.default);
991
945
  return sliceForPagination(postings, params);
992
946
  }
993
947
  /**
994
948
  * Get a specific job posting by ID
995
949
  */
996
950
  export async function getJobPosting(id) {
997
- if (!id || id <= 0) {
998
- throw new Error('Invalid job posting ID. Please provide a positive number.');
999
- }
1000
- return cached(`job_posting:${id}`, () => fetchOne(`/ats/job_postings/${id}`), CACHE_TTL.default);
951
+ validateId(id, 'job posting');
952
+ return cached(`job_posting:${id}`, () => fetchOne(endpointWithId(ENDPOINTS.jobPostings, id)), CACHE_TTL.default);
1001
953
  }
1002
954
  /**
1003
955
  * List all candidates
1004
956
  */
1005
957
  export async function listCandidates(options) {
1006
958
  const params = buildPaginationParams(options);
1007
- const candidates = await fetchList('/ats/candidates');
959
+ const candidates = await fetchList(ENDPOINTS.candidates);
1008
960
  return sliceForPagination(candidates, params);
1009
961
  }
1010
962
  /**
1011
963
  * Get a specific candidate by ID
1012
964
  */
1013
965
  export async function getCandidate(id) {
1014
- if (!id || id <= 0) {
1015
- throw new Error('Invalid candidate ID. Please provide a positive number.');
1016
- }
1017
- return fetchOne(`/ats/candidates/${id}`);
966
+ validateId(id, 'candidate');
967
+ return fetchOne(endpointWithId(ENDPOINTS.candidates, id));
1018
968
  }
1019
969
  /**
1020
970
  * List all applications
@@ -1024,32 +974,30 @@ export async function listApplications(jobPostingId, options) {
1024
974
  const queryParams = {};
1025
975
  if (jobPostingId)
1026
976
  queryParams.job_posting_id = jobPostingId;
1027
- const applications = await fetchList('/ats/applications', { params: queryParams });
977
+ const applications = await fetchList(ENDPOINTS.applications, {
978
+ params: queryParams,
979
+ });
1028
980
  return sliceForPagination(applications, params);
1029
981
  }
1030
982
  /**
1031
983
  * Get a specific application by ID
1032
984
  */
1033
985
  export async function getApplication(id) {
1034
- if (!id || id <= 0) {
1035
- throw new Error('Invalid application ID. Please provide a positive number.');
1036
- }
1037
- return fetchOne(`/ats/applications/${id}`);
986
+ validateId(id, 'application');
987
+ return fetchOne(endpointWithId(ENDPOINTS.applications, id));
1038
988
  }
1039
989
  /**
1040
990
  * List all hiring stages
1041
991
  */
1042
992
  export async function listHiringStages() {
1043
- return cached('hiring_stages:all', () => fetchList('/ats/hiring_stages'), CACHE_TTL.default);
993
+ return cached('hiring_stages:all', () => fetchList(ENDPOINTS.hiringStages), CACHE_TTL.default);
1044
994
  }
1045
995
  /**
1046
996
  * Get a specific hiring stage by ID
1047
997
  */
1048
998
  export async function getHiringStage(id) {
1049
- if (!id || id <= 0) {
1050
- throw new Error('Invalid hiring stage ID. Please provide a positive number.');
1051
- }
1052
- return fetchOne(`/ats/hiring_stages/${id}`);
999
+ validateId(id, 'hiring stage');
1000
+ return fetchOne(endpointWithId(ENDPOINTS.hiringStages, id));
1053
1001
  }
1054
1002
  // ============================================================================
1055
1003
  // ATS (Recruiting) - WRITE endpoints
@@ -1059,7 +1007,7 @@ export async function getHiringStage(id) {
1059
1007
  */
1060
1008
  export async function createJobPosting(input) {
1061
1009
  return auditedOperation(AuditAction.CREATE, 'job_posting', undefined, async () => {
1062
- const posting = await postOne('/ats/job_postings', input);
1010
+ const posting = await postOne(ENDPOINTS.jobPostings, input);
1063
1011
  cache.invalidatePrefix('job_postings');
1064
1012
  return posting;
1065
1013
  });
@@ -1068,11 +1016,9 @@ export async function createJobPosting(input) {
1068
1016
  * Update a job posting
1069
1017
  */
1070
1018
  export async function updateJobPosting(id, input) {
1071
- if (!id || id <= 0) {
1072
- throw new Error('Invalid job posting ID. Please provide a positive number.');
1073
- }
1019
+ validateId(id, 'job posting');
1074
1020
  return auditedOperation(AuditAction.UPDATE, 'job_posting', id, async () => {
1075
- const posting = await patchOne(`/ats/job_postings/${id}`, input);
1021
+ const posting = await patchOne(endpointWithId(ENDPOINTS.jobPostings, id), input);
1076
1022
  cache.invalidate(`job_posting:${id}`);
1077
1023
  cache.invalidatePrefix('job_postings');
1078
1024
  return posting;
@@ -1082,11 +1028,9 @@ export async function updateJobPosting(id, input) {
1082
1028
  * Delete a job posting
1083
1029
  */
1084
1030
  export async function deleteJobPosting(id) {
1085
- if (!id || id <= 0) {
1086
- throw new Error('Invalid job posting ID. Please provide a positive number.');
1087
- }
1031
+ validateId(id, 'job posting');
1088
1032
  return auditedOperation(AuditAction.DELETE, 'job_posting', id, async () => {
1089
- await deleteOne(`/ats/job_postings/${id}`);
1033
+ await deleteOne(endpointWithId(ENDPOINTS.jobPostings, id));
1090
1034
  cache.invalidate(`job_posting:${id}`);
1091
1035
  cache.invalidatePrefix('job_postings');
1092
1036
  });
@@ -1096,29 +1040,25 @@ export async function deleteJobPosting(id) {
1096
1040
  */
1097
1041
  export async function createCandidate(input) {
1098
1042
  return auditedOperation(AuditAction.CREATE, 'candidate', undefined, async () => {
1099
- return postOne('/ats/candidates', input);
1043
+ return postOne(ENDPOINTS.candidates, input);
1100
1044
  });
1101
1045
  }
1102
1046
  /**
1103
1047
  * Update a candidate
1104
1048
  */
1105
1049
  export async function updateCandidate(id, input) {
1106
- if (!id || id <= 0) {
1107
- throw new Error('Invalid candidate ID. Please provide a positive number.');
1108
- }
1050
+ validateId(id, 'candidate');
1109
1051
  return auditedOperation(AuditAction.UPDATE, 'candidate', id, async () => {
1110
- return patchOne(`/ats/candidates/${id}`, input);
1052
+ return patchOne(endpointWithId(ENDPOINTS.candidates, id), input);
1111
1053
  });
1112
1054
  }
1113
1055
  /**
1114
1056
  * Delete a candidate
1115
1057
  */
1116
1058
  export async function deleteCandidate(id) {
1117
- if (!id || id <= 0) {
1118
- throw new Error('Invalid candidate ID. Please provide a positive number.');
1119
- }
1059
+ validateId(id, 'candidate');
1120
1060
  return auditedOperation(AuditAction.DELETE, 'candidate', id, async () => {
1121
- await deleteOne(`/ats/candidates/${id}`);
1061
+ await deleteOne(endpointWithId(ENDPOINTS.candidates, id));
1122
1062
  });
1123
1063
  }
1124
1064
  /**
@@ -1126,40 +1066,34 @@ export async function deleteCandidate(id) {
1126
1066
  */
1127
1067
  export async function createApplication(input) {
1128
1068
  return auditedOperation(AuditAction.CREATE, 'application', undefined, async () => {
1129
- return postOne('/ats/applications', input);
1069
+ return postOne(ENDPOINTS.applications, input);
1130
1070
  });
1131
1071
  }
1132
1072
  /**
1133
1073
  * Update an application
1134
1074
  */
1135
1075
  export async function updateApplication(id, input) {
1136
- if (!id || id <= 0) {
1137
- throw new Error('Invalid application ID. Please provide a positive number.');
1138
- }
1076
+ validateId(id, 'application');
1139
1077
  return auditedOperation(AuditAction.UPDATE, 'application', id, async () => {
1140
- return patchOne(`/ats/applications/${id}`, input);
1078
+ return patchOne(endpointWithId(ENDPOINTS.applications, id), input);
1141
1079
  });
1142
1080
  }
1143
1081
  /**
1144
1082
  * Delete an application
1145
1083
  */
1146
1084
  export async function deleteApplication(id) {
1147
- if (!id || id <= 0) {
1148
- throw new Error('Invalid application ID. Please provide a positive number.');
1149
- }
1085
+ validateId(id, 'application');
1150
1086
  return auditedOperation(AuditAction.DELETE, 'application', id, async () => {
1151
- await deleteOne(`/ats/applications/${id}`);
1087
+ await deleteOne(endpointWithId(ENDPOINTS.applications, id));
1152
1088
  });
1153
1089
  }
1154
1090
  /**
1155
1091
  * Advance an application to the next stage
1156
1092
  */
1157
1093
  export async function advanceApplication(id) {
1158
- if (!id || id <= 0) {
1159
- throw new Error('Invalid application ID. Please provide a positive number.');
1160
- }
1094
+ validateId(id, 'application');
1161
1095
  return auditedOperation(AuditAction.UPDATE, 'application', id, async () => {
1162
- return postAction(`/ats/applications/${id}/apply`);
1096
+ return postAction(endpointWithAction(ENDPOINTS.applications, id, 'apply'));
1163
1097
  });
1164
1098
  }
1165
1099
  // ============================================================================
@@ -1173,7 +1107,7 @@ export async function listPayrollSupplements(employeeId, options) {
1173
1107
  const queryParams = {};
1174
1108
  if (employeeId)
1175
1109
  queryParams.employee_id = employeeId;
1176
- const supplements = await fetchList('/payroll/supplements', {
1110
+ const supplements = await fetchList(ENDPOINTS.payrollSupplements, {
1177
1111
  params: queryParams,
1178
1112
  });
1179
1113
  return sliceForPagination(supplements, params);
@@ -1182,10 +1116,8 @@ export async function listPayrollSupplements(employeeId, options) {
1182
1116
  * Get a specific payroll supplement by ID
1183
1117
  */
1184
1118
  export async function getPayrollSupplement(id) {
1185
- if (!id || id <= 0) {
1186
- throw new Error('Invalid supplement ID. Please provide a positive number.');
1187
- }
1188
- return fetchOne(`/payroll/supplements/${id}`);
1119
+ validateId(id, 'supplement');
1120
+ return fetchOne(endpointWithId(ENDPOINTS.payrollSupplements, id));
1189
1121
  }
1190
1122
  /**
1191
1123
  * List tax identifiers
@@ -1195,7 +1127,7 @@ export async function listTaxIdentifiers(employeeId, options) {
1195
1127
  const queryParams = {};
1196
1128
  if (employeeId)
1197
1129
  queryParams.employee_id = employeeId;
1198
- const identifiers = await fetchList('/payroll_employees/identifiers', {
1130
+ const identifiers = await fetchList(ENDPOINTS.taxIdentifiers, {
1199
1131
  params: queryParams,
1200
1132
  });
1201
1133
  return sliceForPagination(identifiers, params);
@@ -1204,10 +1136,8 @@ export async function listTaxIdentifiers(employeeId, options) {
1204
1136
  * Get a specific tax identifier by ID
1205
1137
  */
1206
1138
  export async function getTaxIdentifier(id) {
1207
- if (!id || id <= 0) {
1208
- throw new Error('Invalid tax identifier ID. Please provide a positive number.');
1209
- }
1210
- return fetchOne(`/payroll_employees/identifiers/${id}`);
1139
+ validateId(id, 'tax identifier');
1140
+ return fetchOne(endpointWithId(ENDPOINTS.taxIdentifiers, id));
1211
1141
  }
1212
1142
  /**
1213
1143
  * List family situations
@@ -1217,7 +1147,7 @@ export async function listFamilySituations(employeeId, options) {
1217
1147
  const queryParams = {};
1218
1148
  if (employeeId)
1219
1149
  queryParams.employee_id = employeeId;
1220
- const situations = await fetchList('/payroll/family_situations', {
1150
+ const situations = await fetchList(ENDPOINTS.familySituations, {
1221
1151
  params: queryParams,
1222
1152
  });
1223
1153
  return sliceForPagination(situations, params);
@@ -1226,9 +1156,7 @@ export async function listFamilySituations(employeeId, options) {
1226
1156
  * Get a specific family situation by ID
1227
1157
  */
1228
1158
  export async function getFamilySituation(id) {
1229
- if (!id || id <= 0) {
1230
- throw new Error('Invalid family situation ID. Please provide a positive number.');
1231
- }
1232
- return fetchOne(`/payroll/family_situations/${id}`);
1159
+ validateId(id, 'family situation');
1160
+ return fetchOne(endpointWithId(ENDPOINTS.familySituations, id));
1233
1161
  }
1234
1162
  //# sourceMappingURL=api.js.map