@typeb-digital/nucleus-sdk 0.0.6 → 0.1.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.
@@ -68,6 +68,44 @@ class NucleusTransport {
68
68
  return makeErrorResult('NETWORK_ERROR', String(err));
69
69
  }
70
70
  }
71
+ async put(path, body) {
72
+ try {
73
+ const envelope = await ofetch.$fetch(`${this.baseUrl}${path}`, {
74
+ method: 'PUT',
75
+ headers: this.headers,
76
+ body: JSON.stringify(body)
77
+ });
78
+ if (!envelope.success) return makeErrorResult('FORBIDDEN', envelope.error);
79
+ return {
80
+ data: envelope.data
81
+ };
82
+ } catch (err) {
83
+ if (err instanceof ofetch.FetchError) {
84
+ const errBody = err.data;
85
+ return makeErrorResult(statusToCode(err.status ?? 0, errBody?.error), err.message);
86
+ }
87
+ return makeErrorResult('NETWORK_ERROR', String(err));
88
+ }
89
+ }
90
+ async patch(path, body) {
91
+ try {
92
+ const envelope = await ofetch.$fetch(`${this.baseUrl}${path}`, {
93
+ method: 'PATCH',
94
+ headers: this.headers,
95
+ body: JSON.stringify(body)
96
+ });
97
+ if (!envelope.success) return makeErrorResult('FORBIDDEN', envelope.error);
98
+ return {
99
+ data: envelope.data
100
+ };
101
+ } catch (err) {
102
+ if (err instanceof ofetch.FetchError) {
103
+ const errBody = err.data;
104
+ return makeErrorResult(statusToCode(err.status ?? 0, errBody?.error), err.message);
105
+ }
106
+ return makeErrorResult('NETWORK_ERROR', String(err));
107
+ }
108
+ }
71
109
  async del(path, query) {
72
110
  try {
73
111
  const params = query ? Object.fromEntries(Object.entries(query).filter(([, v])=>v !== undefined)) : undefined;
@@ -124,7 +162,20 @@ class NucleusTransport {
124
162
  /**
125
163
  * Transform utilities — map snake_case Prisma API responses to camelCase SDK types.
126
164
  * Each function accepts `unknown` so it works regardless of which buckets were active.
127
- */ function str(v) {
165
+ */ /**
166
+ * Serialize a camelCase mutation input to the snake_case body the API expects.
167
+ * Shallow is sufficient — write bodies are flat. `undefined` keys are dropped;
168
+ * `null` is preserved (explicit clear).
169
+ */ function toSnakeBody(input) {
170
+ const out = {};
171
+ for (const [key, value] of Object.entries(input)){
172
+ if (value === undefined) continue;
173
+ const snake = key.replace(/[A-Z]/g, (m)=>`_${m.toLowerCase()}`);
174
+ out[snake] = value;
175
+ }
176
+ return out;
177
+ }
178
+ function str(v) {
128
179
  return typeof v === 'string' ? v : null;
129
180
  }
130
181
  function num(v) {
@@ -157,6 +208,7 @@ function transformCompensation(raw) {
157
208
  hourlyBillableRate: num(r['hourly_billable_rate']),
158
209
  monthlyCostRate: num(r['monthly_cost_rate']),
159
210
  monthlyBillableRate: num(r['monthly_billable_rate']),
211
+ salary: num(r['salary']),
160
212
  currencyCode: str(r['currency_code']) ?? 'USD',
161
213
  effectiveFrom: str(r['effective_from']) ?? ''
162
214
  };
@@ -168,6 +220,7 @@ function transformCompensationHistoryEntry(raw) {
168
220
  hourlyBillableRate: num(r['hourly_billable_rate']),
169
221
  monthlyCostRate: num(r['monthly_cost_rate']),
170
222
  monthlyBillableRate: num(r['monthly_billable_rate']),
223
+ salary: num(r['salary']),
171
224
  currencyCode: str(r['currency_code']) ?? 'USD',
172
225
  effectiveFrom: str(r['effective_from']) ?? '',
173
226
  effectiveTo: str(r['effective_to'])
@@ -187,6 +240,7 @@ function transformEmployee(raw) {
187
240
  if ('job_title' in r) out['jobTitle'] = str(r['job_title']);
188
241
  if ('department' in r) out['department'] = str(r['department']);
189
242
  if ('employment_status' in r) out['employmentStatus'] = str(r['employment_status']);
243
+ if ('employment_type' in r) out['employmentType'] = str(r['employment_type']);
190
244
  if ('is_external' in r) out['isExternal'] = bool(r['is_external']);
191
245
  // employment
192
246
  if ('start_date' in r) out['startDate'] = str(r['start_date']);
@@ -247,6 +301,11 @@ function transformProject(raw) {
247
301
  return entry;
248
302
  });
249
303
  }
304
+ // financials
305
+ if ('contract_value' in r) out['contractValue'] = num(r['contract_value']);
306
+ if ('budget' in r) out['budget'] = num(r['budget']);
307
+ if ('margin_target' in r) out['marginTarget'] = num(r['margin_target']);
308
+ if ('financials_currency' in r) out['financialsCurrency'] = str(r['financials_currency']);
250
309
  // integrations
251
310
  if ('source' in r) out['source'] = str(r['source']);
252
311
  if ('clockify_project_id' in r) out['clockifyProjectId'] = str(r['clockify_project_id']);
@@ -346,6 +405,155 @@ function transformGenericRate(raw) {
346
405
  if ('billable_rate' in r) out['billableRate'] = num(r['billable_rate']);
347
406
  return out;
348
407
  }
408
+ function transformJurisdiction(raw) {
409
+ if (!raw || typeof raw !== 'object') return {};
410
+ const r = raw;
411
+ const out = {};
412
+ if ('id' in r) out['id'] = r['id'];
413
+ if ('name' in r) out['name'] = str(r['name']);
414
+ if ('code' in r) out['code'] = str(r['code']);
415
+ if ('country' in r) out['country'] = str(r['country']);
416
+ return out;
417
+ }
418
+ function transformLeaveType(raw) {
419
+ if (!raw || typeof raw !== 'object') return {};
420
+ const r = raw;
421
+ const out = {};
422
+ if ('id' in r) out['id'] = r['id'];
423
+ if ('name' in r) out['name'] = str(r['name']);
424
+ if ('description' in r) out['description'] = str(r['description']);
425
+ if ('color' in r) out['color'] = str(r['color']);
426
+ if ('sort_order' in r) out['sortOrder'] = num(r['sort_order']) ?? 0;
427
+ return out;
428
+ }
429
+ function transformLeave(raw) {
430
+ if (!raw || typeof raw !== 'object') return {};
431
+ const r = raw;
432
+ const out = {};
433
+ // core
434
+ if ('id' in r) out['id'] = r['id'];
435
+ if ('employee_id' in r) out['employeeId'] = str(r['employee_id']);
436
+ if ('leave_type_id' in r) out['leaveTypeId'] = str(r['leave_type_id']);
437
+ if ('start_date' in r) out['startDate'] = str(r['start_date']);
438
+ if ('end_date' in r) out['endDate'] = str(r['end_date']);
439
+ if ('days' in r) out['days'] = num(r['days']);
440
+ if ('status' in r) out['status'] = str(r['status']);
441
+ if ('source' in r) out['source'] = str(r['source']);
442
+ // details
443
+ if ('reason' in r) out['reason'] = str(r['reason']);
444
+ // expand: employee / leaveType
445
+ if ('employee' in r) {
446
+ out['employee'] = r['employee'] ? transformEmployee(r['employee']) : null;
447
+ }
448
+ if ('leave_type' in r || 'leaveType' in r) {
449
+ const lt = r['leave_type'] ?? r['leaveType'];
450
+ out['leaveType'] = lt ? transformLeaveType(lt) : null;
451
+ }
452
+ return out;
453
+ }
454
+ function transformLeaveBalance(raw) {
455
+ if (!raw || typeof raw !== 'object') return {};
456
+ const r = raw;
457
+ const out = {};
458
+ // core
459
+ if ('id' in r) out['id'] = r['id'];
460
+ if ('employee_id' in r) out['employeeId'] = str(r['employee_id']);
461
+ if ('leave_type_id' in r) out['leaveTypeId'] = str(r['leave_type_id']);
462
+ if ('year' in r) out['year'] = num(r['year']);
463
+ if ('balance_days' in r) out['balanceDays'] = num(r['balance_days']);
464
+ // expand: employee / leaveType
465
+ if ('employee' in r) {
466
+ out['employee'] = r['employee'] ? transformEmployee(r['employee']) : null;
467
+ }
468
+ if ('leave_type' in r || 'leaveType' in r) {
469
+ const lt = r['leave_type'] ?? r['leaveType'];
470
+ out['leaveType'] = lt ? transformLeaveType(lt) : null;
471
+ }
472
+ return out;
473
+ }
474
+ function transformPolicy(raw) {
475
+ if (!raw || typeof raw !== 'object') return {};
476
+ const r = raw;
477
+ const out = {};
478
+ // core
479
+ if ('id' in r) out['id'] = r['id'];
480
+ if ('title' in r) out['title'] = str(r['title']);
481
+ if ('code' in r) out['code'] = str(r['code']);
482
+ if ('category' in r) out['category'] = str(r['category']);
483
+ if ('description' in r) out['description'] = str(r['description']);
484
+ if ('current_version' in r) out['currentVersion'] = num(r['current_version']) ?? 1;
485
+ if ('requires_acknowledgement' in r) {
486
+ out['requiresAcknowledgement'] = bool(r['requires_acknowledgement']);
487
+ }
488
+ if ('is_published' in r) out['isPublished'] = bool(r['is_published']);
489
+ // content
490
+ if ('body' in r) out['body'] = str(r['body']);
491
+ if ('file_key' in r) out['fileKey'] = str(r['file_key']);
492
+ return out;
493
+ }
494
+ function transformPolicyAcknowledgement(raw) {
495
+ if (!raw || typeof raw !== 'object') return {};
496
+ const r = raw;
497
+ const out = {};
498
+ // core
499
+ if ('id' in r) out['id'] = r['id'];
500
+ if ('policy_id' in r) out['policyId'] = str(r['policy_id']);
501
+ if ('employee_id' in r) out['employeeId'] = str(r['employee_id']);
502
+ if ('version' in r) out['version'] = num(r['version']);
503
+ if ('acknowledged_at' in r) out['acknowledgedAt'] = str(r['acknowledged_at']);
504
+ // expand: employee / policy
505
+ if ('employee' in r) {
506
+ out['employee'] = r['employee'] ? transformEmployee(r['employee']) : null;
507
+ }
508
+ if ('policy' in r) {
509
+ out['policy'] = r['policy'] ? transformPolicy(r['policy']) : null;
510
+ }
511
+ return out;
512
+ }
513
+ function transformCalendarEvent(raw) {
514
+ if (!raw || typeof raw !== 'object') return {};
515
+ const r = raw;
516
+ const out = {};
517
+ if ('id' in r) out['id'] = r['id'];
518
+ if ('title' in r) out['title'] = str(r['title']);
519
+ if ('description' in r) out['description'] = str(r['description']);
520
+ if ('start_date' in r) out['startDate'] = str(r['start_date']);
521
+ if ('end_date' in r) out['endDate'] = str(r['end_date']);
522
+ if ('all_day' in r) out['allDay'] = bool(r['all_day']);
523
+ if ('type' in r) out['type'] = str(r['type']);
524
+ if ('location' in r) out['location'] = str(r['location']);
525
+ if ('color' in r) out['color'] = str(r['color']);
526
+ if ('is_published' in r) out['isPublished'] = bool(r['is_published']);
527
+ return out;
528
+ }
529
+ function transformTimesheet(raw) {
530
+ if (!raw || typeof raw !== 'object') return {};
531
+ const r = raw;
532
+ const out = {};
533
+ // core
534
+ if ('id' in r) out['id'] = r['id'];
535
+ if ('employee_id' in r) out['employeeId'] = str(r['employee_id']);
536
+ if ('project_id' in r) out['projectId'] = str(r['project_id']);
537
+ if ('start_time' in r) out['startTime'] = str(r['start_time']);
538
+ if ('end_time' in r) out['endTime'] = str(r['end_time']);
539
+ if ('duration_minutes' in r) out['durationMinutes'] = num(r['duration_minutes']);
540
+ if ('billable' in r) out['billable'] = bool(r['billable']);
541
+ // details
542
+ if ('description' in r) out['description'] = str(r['description']);
543
+ if ('activity_name' in r) out['activityName'] = str(r['activity_name']);
544
+ // integrations
545
+ if ('source' in r) out['source'] = str(r['source']);
546
+ if ('kimai_id' in r) out['kimaiId'] = str(r['kimai_id']);
547
+ if ('kimai_activity_id' in r) out['kimaiActivityId'] = str(r['kimai_activity_id']);
548
+ // expand: employee / project
549
+ if ('employee' in r) {
550
+ out['employee'] = r['employee'] ? transformEmployee(r['employee']) : null;
551
+ }
552
+ if ('project' in r) {
553
+ out['project'] = r['project'] ? transformProject(r['project']) : null;
554
+ }
555
+ return out;
556
+ }
349
557
 
350
558
  class EmployeesAccessor {
351
559
  constructor(transport){
@@ -375,6 +583,21 @@ class EmployeesAccessor {
375
583
  data: transformEmployee(result.data)
376
584
  };
377
585
  }
586
+ // No create: the platform exposes no POST route for employees.
587
+ /** Update an employee (managerId / timezone overrides). Requires `employees.update` scope. */ async update(id, data) {
588
+ const result = await this.transport.patch(`/api/v1/data/employees/${id}`, toSnakeBody(data));
589
+ if ('error' in result) return result;
590
+ return {
591
+ data: transformEmployee(result.data)
592
+ };
593
+ }
594
+ /** Soft-delete an employee. Requires `employees.delete` scope. */ async delete(id) {
595
+ const result = await this.transport.del(`/api/v1/data/employees/${id}`);
596
+ if ('error' in result) return result;
597
+ return {
598
+ data: null
599
+ };
600
+ }
378
601
  }
379
602
 
380
603
  class ProjectsAccessor {
@@ -405,6 +628,27 @@ class ProjectsAccessor {
405
628
  data: transformProject(result.data)
406
629
  };
407
630
  }
631
+ /** Create a project. Requires `projects.create` scope. */ async create(data) {
632
+ const result = await this.transport.post('/api/v1/data/projects', toSnakeBody(data));
633
+ if ('error' in result) return result;
634
+ return {
635
+ data: transformProject(result.data)
636
+ };
637
+ }
638
+ /** Update a project. Requires `projects.update` scope. */ async update(id, data) {
639
+ const result = await this.transport.put(`/api/v1/data/projects/${id}`, toSnakeBody(data));
640
+ if ('error' in result) return result;
641
+ return {
642
+ data: transformProject(result.data)
643
+ };
644
+ }
645
+ /** Soft-delete a project. Requires `projects.delete` scope. */ async delete(id) {
646
+ const result = await this.transport.del(`/api/v1/data/projects/${id}`);
647
+ if ('error' in result) return result;
648
+ return {
649
+ data: null
650
+ };
651
+ }
408
652
  }
409
653
 
410
654
  class ClientsAccessor {
@@ -435,6 +679,27 @@ class ClientsAccessor {
435
679
  data: transformClient(result.data)
436
680
  };
437
681
  }
682
+ /** Create a client. Requires `clients.create` scope. */ async create(data) {
683
+ const result = await this.transport.post('/api/v1/data/clients', toSnakeBody(data));
684
+ if ('error' in result) return result;
685
+ return {
686
+ data: transformClient(result.data)
687
+ };
688
+ }
689
+ /** Update a client. Requires `clients.update` scope. */ async update(id, data) {
690
+ const result = await this.transport.put(`/api/v1/data/clients/${id}`, toSnakeBody(data));
691
+ if ('error' in result) return result;
692
+ return {
693
+ data: transformClient(result.data)
694
+ };
695
+ }
696
+ /** Soft-delete a client. Requires `clients.delete` scope. */ async delete(id) {
697
+ const result = await this.transport.del(`/api/v1/data/clients/${id}`);
698
+ if ('error' in result) return result;
699
+ return {
700
+ data: null
701
+ };
702
+ }
438
703
  }
439
704
 
440
705
  class PartnersAccessor {
@@ -463,6 +728,27 @@ class PartnersAccessor {
463
728
  data: transformPartner(result.data)
464
729
  };
465
730
  }
731
+ /** Create a partner. Requires `partners.create` scope. */ async create(data) {
732
+ const result = await this.transport.post('/api/v1/data/partners', toSnakeBody(data));
733
+ if ('error' in result) return result;
734
+ return {
735
+ data: transformPartner(result.data)
736
+ };
737
+ }
738
+ /** Update a partner. Requires `partners.update` scope. */ async update(id, data) {
739
+ const result = await this.transport.put(`/api/v1/data/partners/${id}`, toSnakeBody(data));
740
+ if ('error' in result) return result;
741
+ return {
742
+ data: transformPartner(result.data)
743
+ };
744
+ }
745
+ /** Soft-delete a partner. Requires `partners.delete` scope. */ async delete(id) {
746
+ const result = await this.transport.del(`/api/v1/data/partners/${id}`);
747
+ if ('error' in result) return result;
748
+ return {
749
+ data: null
750
+ };
751
+ }
466
752
  }
467
753
 
468
754
  class DepartmentsAccessor {
@@ -488,6 +774,27 @@ class DepartmentsAccessor {
488
774
  data: transformDepartment(result.data)
489
775
  };
490
776
  }
777
+ /** Create a department. Requires `departments.create` scope. */ async create(data) {
778
+ const result = await this.transport.post('/api/v1/data/departments', toSnakeBody(data));
779
+ if ('error' in result) return result;
780
+ return {
781
+ data: transformDepartment(result.data)
782
+ };
783
+ }
784
+ /** Update a department. Requires `departments.update` scope. */ async update(id, data) {
785
+ const result = await this.transport.put(`/api/v1/data/departments/${id}`, toSnakeBody(data));
786
+ if ('error' in result) return result;
787
+ return {
788
+ data: transformDepartment(result.data)
789
+ };
790
+ }
791
+ /** Soft-delete a department. Requires `departments.delete` scope. */ async delete(id) {
792
+ const result = await this.transport.del(`/api/v1/data/departments/${id}`);
793
+ if ('error' in result) return result;
794
+ return {
795
+ data: null
796
+ };
797
+ }
491
798
  }
492
799
 
493
800
  class ProjectTypesAccessor {
@@ -512,6 +819,27 @@ class ProjectTypesAccessor {
512
819
  data: transformProjectType(result.data)
513
820
  };
514
821
  }
822
+ /** Create a project type. Requires `projectTypes.create` scope. */ async create(data) {
823
+ const result = await this.transport.post('/api/v1/data/project-types', toSnakeBody(data));
824
+ if ('error' in result) return result;
825
+ return {
826
+ data: transformProjectType(result.data)
827
+ };
828
+ }
829
+ /** Update a project type. Requires `projectTypes.update` scope. */ async update(id, data) {
830
+ const result = await this.transport.put(`/api/v1/data/project-types/${id}`, toSnakeBody(data));
831
+ if ('error' in result) return result;
832
+ return {
833
+ data: transformProjectType(result.data)
834
+ };
835
+ }
836
+ /** Soft-delete a project type. Requires `projectTypes.delete` scope. */ async delete(id) {
837
+ const result = await this.transport.del(`/api/v1/data/project-types/${id}`);
838
+ if ('error' in result) return result;
839
+ return {
840
+ data: null
841
+ };
842
+ }
515
843
  }
516
844
 
517
845
  class CurrenciesAccessor {
@@ -536,6 +864,30 @@ class CurrenciesAccessor {
536
864
  data: transformCurrency(result.data)
537
865
  };
538
866
  }
867
+ /** Create a currency. Requires `currencies.create` scope. */ async create(data) {
868
+ const result = await this.transport.post('/api/v1/data/currencies', toSnakeBody(data));
869
+ if ('error' in result) return result;
870
+ return {
871
+ data: transformCurrency(result.data)
872
+ };
873
+ }
874
+ /**
875
+ * Update a currency (keyed by its ISO code; `code` itself is immutable).
876
+ * Requires `currencies.update` scope.
877
+ */ async update(code, data) {
878
+ const result = await this.transport.put(`/api/v1/data/currencies/${code}`, toSnakeBody(data));
879
+ if ('error' in result) return result;
880
+ return {
881
+ data: transformCurrency(result.data)
882
+ };
883
+ }
884
+ /** Soft-delete a currency (keyed by its ISO code). Requires `currencies.delete` scope. */ async delete(code) {
885
+ const result = await this.transport.del(`/api/v1/data/currencies/${code}`);
886
+ if ('error' in result) return result;
887
+ return {
888
+ data: null
889
+ };
890
+ }
539
891
  }
540
892
 
541
893
  class GenericRatesAccessor {
@@ -561,6 +913,415 @@ class GenericRatesAccessor {
561
913
  data: transformGenericRate(result.data)
562
914
  };
563
915
  }
916
+ /** Create a generic rate. Requires `genericRates.create` scope. */ async create(data) {
917
+ const result = await this.transport.post('/api/v1/data/generic-rates', toSnakeBody(data));
918
+ if ('error' in result) return result;
919
+ return {
920
+ data: transformGenericRate(result.data)
921
+ };
922
+ }
923
+ /** Update a generic rate. Requires `genericRates.update` scope. */ async update(id, data) {
924
+ const result = await this.transport.put(`/api/v1/data/generic-rates/${id}`, toSnakeBody(data));
925
+ if ('error' in result) return result;
926
+ return {
927
+ data: transformGenericRate(result.data)
928
+ };
929
+ }
930
+ /** Soft-delete a generic rate. Requires `genericRates.delete` scope. */ async delete(id) {
931
+ const result = await this.transport.del(`/api/v1/data/generic-rates/${id}`);
932
+ if ('error' in result) return result;
933
+ return {
934
+ data: null
935
+ };
936
+ }
937
+ }
938
+
939
+ class JurisdictionsAccessor {
940
+ constructor(transport){
941
+ this.transport = transport;
942
+ }
943
+ async list(params) {
944
+ const query = {};
945
+ if (params?.search) query['search'] = params.search;
946
+ if (params?.page) query['page'] = params.page;
947
+ if (params?.pageSize) query['pageSize'] = params.pageSize;
948
+ const result = await this.transport.getList('/api/v1/data/jurisdictions', query);
949
+ if ('error' in result) return result;
950
+ return {
951
+ data: result.data.map((r)=>transformJurisdiction(r)),
952
+ meta: result.meta
953
+ };
954
+ }
955
+ async getById(id) {
956
+ const result = await this.transport.get(`/api/v1/data/jurisdictions/${id}`);
957
+ if ('error' in result) return result;
958
+ return {
959
+ data: transformJurisdiction(result.data)
960
+ };
961
+ }
962
+ /** Create a jurisdiction. Requires `jurisdictions.create` scope. */ async create(data) {
963
+ const result = await this.transport.post('/api/v1/data/jurisdictions', toSnakeBody(data));
964
+ if ('error' in result) return result;
965
+ return {
966
+ data: transformJurisdiction(result.data)
967
+ };
968
+ }
969
+ /** Update a jurisdiction. Requires `jurisdictions.update` scope. */ async update(id, data) {
970
+ const result = await this.transport.put(`/api/v1/data/jurisdictions/${id}`, toSnakeBody(data));
971
+ if ('error' in result) return result;
972
+ return {
973
+ data: transformJurisdiction(result.data)
974
+ };
975
+ }
976
+ /** Soft-delete a jurisdiction. Requires `jurisdictions.delete` scope. */ async delete(id) {
977
+ const result = await this.transport.del(`/api/v1/data/jurisdictions/${id}`);
978
+ if ('error' in result) return result;
979
+ return {
980
+ data: null
981
+ };
982
+ }
983
+ }
984
+
985
+ class LeaveTypesAccessor {
986
+ constructor(transport){
987
+ this.transport = transport;
988
+ }
989
+ async list(params) {
990
+ const query = {};
991
+ if (params?.search) query['search'] = params.search;
992
+ if (params?.page) query['page'] = params.page;
993
+ if (params?.pageSize) query['pageSize'] = params.pageSize;
994
+ const result = await this.transport.getList('/api/v1/data/leave-types', query);
995
+ if ('error' in result) return result;
996
+ return {
997
+ data: result.data.map((r)=>transformLeaveType(r)),
998
+ meta: result.meta
999
+ };
1000
+ }
1001
+ async getById(id) {
1002
+ const result = await this.transport.get(`/api/v1/data/leave-types/${id}`);
1003
+ if ('error' in result) return result;
1004
+ return {
1005
+ data: transformLeaveType(result.data)
1006
+ };
1007
+ }
1008
+ /** Create a leave type. Requires `leaveTypes.create` scope. */ async create(data) {
1009
+ const result = await this.transport.post('/api/v1/data/leave-types', toSnakeBody(data));
1010
+ if ('error' in result) return result;
1011
+ return {
1012
+ data: transformLeaveType(result.data)
1013
+ };
1014
+ }
1015
+ /** Update a leave type. Requires `leaveTypes.update` scope. */ async update(id, data) {
1016
+ const result = await this.transport.put(`/api/v1/data/leave-types/${id}`, toSnakeBody(data));
1017
+ if ('error' in result) return result;
1018
+ return {
1019
+ data: transformLeaveType(result.data)
1020
+ };
1021
+ }
1022
+ /** Soft-delete a leave type. Requires `leaveTypes.delete` scope. */ async delete(id) {
1023
+ const result = await this.transport.del(`/api/v1/data/leave-types/${id}`);
1024
+ if ('error' in result) return result;
1025
+ return {
1026
+ data: null
1027
+ };
1028
+ }
1029
+ }
1030
+
1031
+ class LeavesAccessor {
1032
+ constructor(transport){
1033
+ this.transport = transport;
1034
+ }
1035
+ async list(params) {
1036
+ const query = {};
1037
+ if (params?.employeeId) query['employeeId'] = params.employeeId;
1038
+ if (params?.leaveTypeId) query['leaveTypeId'] = params.leaveTypeId;
1039
+ if (params?.status) query['status'] = params.status;
1040
+ if (params?.page) query['page'] = params.page;
1041
+ if (params?.pageSize) query['pageSize'] = params.pageSize;
1042
+ if (params?.expand?.length) query['expand'] = params.expand.join(',');
1043
+ const result = await this.transport.getList('/api/v1/data/leaves', query);
1044
+ if ('error' in result) return result;
1045
+ return {
1046
+ data: result.data.map((r)=>transformLeave(r)),
1047
+ meta: result.meta
1048
+ };
1049
+ }
1050
+ async getById(id, options) {
1051
+ const query = {};
1052
+ if (options?.expand?.length) query['expand'] = options.expand.join(',');
1053
+ const result = await this.transport.get(`/api/v1/data/leaves/${id}`, query);
1054
+ if ('error' in result) return result;
1055
+ return {
1056
+ data: transformLeave(result.data)
1057
+ };
1058
+ }
1059
+ /** Create a leave record. Requires `leaves.create` scope. */ async create(data) {
1060
+ const result = await this.transport.post('/api/v1/data/leaves', toSnakeBody(data));
1061
+ if ('error' in result) return result;
1062
+ return {
1063
+ data: transformLeave(result.data)
1064
+ };
1065
+ }
1066
+ /** Update a leave record. Requires `leaves.update` scope. */ async update(id, data) {
1067
+ const result = await this.transport.put(`/api/v1/data/leaves/${id}`, toSnakeBody(data));
1068
+ if ('error' in result) return result;
1069
+ return {
1070
+ data: transformLeave(result.data)
1071
+ };
1072
+ }
1073
+ /** Soft-delete a leave record. Requires `leaves.delete` scope. */ async delete(id) {
1074
+ const result = await this.transport.del(`/api/v1/data/leaves/${id}`);
1075
+ if ('error' in result) return result;
1076
+ return {
1077
+ data: null
1078
+ };
1079
+ }
1080
+ }
1081
+
1082
+ class LeaveBalancesAccessor {
1083
+ constructor(transport){
1084
+ this.transport = transport;
1085
+ }
1086
+ async list(params) {
1087
+ const query = {};
1088
+ if (params?.employeeId) query['employeeId'] = params.employeeId;
1089
+ if (params?.leaveTypeId) query['leaveTypeId'] = params.leaveTypeId;
1090
+ if (params?.year) query['year'] = params.year;
1091
+ if (params?.page) query['page'] = params.page;
1092
+ if (params?.pageSize) query['pageSize'] = params.pageSize;
1093
+ if (params?.expand?.length) query['expand'] = params.expand.join(',');
1094
+ const result = await this.transport.getList('/api/v1/data/leave-balances', query);
1095
+ if ('error' in result) return result;
1096
+ return {
1097
+ data: result.data.map((r)=>transformLeaveBalance(r)),
1098
+ meta: result.meta
1099
+ };
1100
+ }
1101
+ async getById(id, options) {
1102
+ const query = {};
1103
+ if (options?.expand?.length) query['expand'] = options.expand.join(',');
1104
+ const result = await this.transport.get(`/api/v1/data/leave-balances/${id}`, query);
1105
+ if ('error' in result) return result;
1106
+ return {
1107
+ data: transformLeaveBalance(result.data)
1108
+ };
1109
+ }
1110
+ /** Create a leave balance. Requires `leaveBalances.create` scope. */ async create(data) {
1111
+ const result = await this.transport.post('/api/v1/data/leave-balances', toSnakeBody(data));
1112
+ if ('error' in result) return result;
1113
+ return {
1114
+ data: transformLeaveBalance(result.data)
1115
+ };
1116
+ }
1117
+ /** Update a leave balance. Requires `leaveBalances.update` scope. */ async update(id, data) {
1118
+ const result = await this.transport.put(`/api/v1/data/leave-balances/${id}`, toSnakeBody(data));
1119
+ if ('error' in result) return result;
1120
+ return {
1121
+ data: transformLeaveBalance(result.data)
1122
+ };
1123
+ }
1124
+ /** Soft-delete a leave balance. Requires `leaveBalances.delete` scope. */ async delete(id) {
1125
+ const result = await this.transport.del(`/api/v1/data/leave-balances/${id}`);
1126
+ if ('error' in result) return result;
1127
+ return {
1128
+ data: null
1129
+ };
1130
+ }
1131
+ }
1132
+
1133
+ class PoliciesAccessor {
1134
+ constructor(transport){
1135
+ this.transport = transport;
1136
+ }
1137
+ async list(params) {
1138
+ const query = {};
1139
+ if (params?.search) query['search'] = params.search;
1140
+ if (params?.category) query['category'] = params.category;
1141
+ if (params?.page) query['page'] = params.page;
1142
+ if (params?.pageSize) query['pageSize'] = params.pageSize;
1143
+ const result = await this.transport.getList('/api/v1/data/policies', query);
1144
+ if ('error' in result) return result;
1145
+ return {
1146
+ data: result.data.map((r)=>transformPolicy(r)),
1147
+ meta: result.meta
1148
+ };
1149
+ }
1150
+ async getById(id) {
1151
+ const result = await this.transport.get(`/api/v1/data/policies/${id}`);
1152
+ if ('error' in result) return result;
1153
+ return {
1154
+ data: transformPolicy(result.data)
1155
+ };
1156
+ }
1157
+ /** Create a policy. Requires `policies.create` scope. */ async create(data) {
1158
+ const result = await this.transport.post('/api/v1/data/policies', toSnakeBody(data));
1159
+ if ('error' in result) return result;
1160
+ return {
1161
+ data: transformPolicy(result.data)
1162
+ };
1163
+ }
1164
+ /** Update a policy. Requires `policies.update` scope. */ async update(id, data) {
1165
+ const result = await this.transport.put(`/api/v1/data/policies/${id}`, toSnakeBody(data));
1166
+ if ('error' in result) return result;
1167
+ return {
1168
+ data: transformPolicy(result.data)
1169
+ };
1170
+ }
1171
+ /** Soft-delete a policy. Requires `policies.delete` scope. */ async delete(id) {
1172
+ const result = await this.transport.del(`/api/v1/data/policies/${id}`);
1173
+ if ('error' in result) return result;
1174
+ return {
1175
+ data: null
1176
+ };
1177
+ }
1178
+ }
1179
+
1180
+ class PolicyAcknowledgementsAccessor {
1181
+ constructor(transport){
1182
+ this.transport = transport;
1183
+ }
1184
+ async list(params) {
1185
+ const query = {};
1186
+ if (params?.policyId) query['policyId'] = params.policyId;
1187
+ if (params?.employeeId) query['employeeId'] = params.employeeId;
1188
+ if (params?.page) query['page'] = params.page;
1189
+ if (params?.pageSize) query['pageSize'] = params.pageSize;
1190
+ if (params?.expand?.length) query['expand'] = params.expand.join(',');
1191
+ const result = await this.transport.getList('/api/v1/data/policy-acknowledgements', query);
1192
+ if ('error' in result) return result;
1193
+ return {
1194
+ data: result.data.map((r)=>transformPolicyAcknowledgement(r)),
1195
+ meta: result.meta
1196
+ };
1197
+ }
1198
+ async getById(id, options) {
1199
+ const query = {};
1200
+ if (options?.expand?.length) query['expand'] = options.expand.join(',');
1201
+ const result = await this.transport.get(`/api/v1/data/policy-acknowledgements/${id}`, query);
1202
+ if ('error' in result) return result;
1203
+ return {
1204
+ data: transformPolicyAcknowledgement(result.data)
1205
+ };
1206
+ }
1207
+ /** Create a policy acknowledgement. Requires `policyAcknowledgements.create` scope. */ async create(data) {
1208
+ const result = await this.transport.post('/api/v1/data/policy-acknowledgements', toSnakeBody(data));
1209
+ if ('error' in result) return result;
1210
+ return {
1211
+ data: transformPolicyAcknowledgement(result.data)
1212
+ };
1213
+ }
1214
+ /** Update a policy acknowledgement. Requires `policyAcknowledgements.update` scope. */ async update(id, data) {
1215
+ const result = await this.transport.put(`/api/v1/data/policy-acknowledgements/${id}`, toSnakeBody(data));
1216
+ if ('error' in result) return result;
1217
+ return {
1218
+ data: transformPolicyAcknowledgement(result.data)
1219
+ };
1220
+ }
1221
+ /** Soft-delete a policy acknowledgement. Requires `policyAcknowledgements.delete` scope. */ async delete(id) {
1222
+ const result = await this.transport.del(`/api/v1/data/policy-acknowledgements/${id}`);
1223
+ if ('error' in result) return result;
1224
+ return {
1225
+ data: null
1226
+ };
1227
+ }
1228
+ }
1229
+
1230
+ class CalendarEventsAccessor {
1231
+ constructor(transport){
1232
+ this.transport = transport;
1233
+ }
1234
+ async list(params) {
1235
+ const query = {};
1236
+ if (params?.search) query['search'] = params.search;
1237
+ if (params?.type) query['type'] = params.type;
1238
+ if (params?.page) query['page'] = params.page;
1239
+ if (params?.pageSize) query['pageSize'] = params.pageSize;
1240
+ const result = await this.transport.getList('/api/v1/data/calendar-events', query);
1241
+ if ('error' in result) return result;
1242
+ return {
1243
+ data: result.data.map((r)=>transformCalendarEvent(r)),
1244
+ meta: result.meta
1245
+ };
1246
+ }
1247
+ async getById(id) {
1248
+ const result = await this.transport.get(`/api/v1/data/calendar-events/${id}`);
1249
+ if ('error' in result) return result;
1250
+ return {
1251
+ data: transformCalendarEvent(result.data)
1252
+ };
1253
+ }
1254
+ /** Create a calendar event. Requires `calendarEvents.create` scope. */ async create(data) {
1255
+ const result = await this.transport.post('/api/v1/data/calendar-events', toSnakeBody(data));
1256
+ if ('error' in result) return result;
1257
+ return {
1258
+ data: transformCalendarEvent(result.data)
1259
+ };
1260
+ }
1261
+ /** Update a calendar event. Requires `calendarEvents.update` scope. */ async update(id, data) {
1262
+ const result = await this.transport.put(`/api/v1/data/calendar-events/${id}`, toSnakeBody(data));
1263
+ if ('error' in result) return result;
1264
+ return {
1265
+ data: transformCalendarEvent(result.data)
1266
+ };
1267
+ }
1268
+ /** Soft-delete a calendar event. Requires `calendarEvents.delete` scope. */ async delete(id) {
1269
+ const result = await this.transport.del(`/api/v1/data/calendar-events/${id}`);
1270
+ if ('error' in result) return result;
1271
+ return {
1272
+ data: null
1273
+ };
1274
+ }
1275
+ }
1276
+
1277
+ class TimesheetsAccessor {
1278
+ constructor(transport){
1279
+ this.transport = transport;
1280
+ }
1281
+ async list(params) {
1282
+ const query = {};
1283
+ if (params?.employeeId) query['employeeId'] = params.employeeId;
1284
+ if (params?.projectId) query['projectId'] = params.projectId;
1285
+ if (params?.page) query['page'] = params.page;
1286
+ if (params?.pageSize) query['pageSize'] = params.pageSize;
1287
+ if (params?.expand?.length) query['expand'] = params.expand.join(',');
1288
+ const result = await this.transport.getList('/api/v1/data/timesheets', query);
1289
+ if ('error' in result) return result;
1290
+ return {
1291
+ data: result.data.map((r)=>transformTimesheet(r)),
1292
+ meta: result.meta
1293
+ };
1294
+ }
1295
+ async getById(id, options) {
1296
+ const query = {};
1297
+ if (options?.expand?.length) query['expand'] = options.expand.join(',');
1298
+ const result = await this.transport.get(`/api/v1/data/timesheets/${id}`, query);
1299
+ if ('error' in result) return result;
1300
+ return {
1301
+ data: transformTimesheet(result.data)
1302
+ };
1303
+ }
1304
+ /** Create a timesheet entry. Requires `timesheets.create` scope. */ async create(data) {
1305
+ const result = await this.transport.post('/api/v1/data/timesheets', toSnakeBody(data));
1306
+ if ('error' in result) return result;
1307
+ return {
1308
+ data: transformTimesheet(result.data)
1309
+ };
1310
+ }
1311
+ /** Update a timesheet entry. Requires `timesheets.update` scope. */ async update(id, data) {
1312
+ const result = await this.transport.put(`/api/v1/data/timesheets/${id}`, toSnakeBody(data));
1313
+ if ('error' in result) return result;
1314
+ return {
1315
+ data: transformTimesheet(result.data)
1316
+ };
1317
+ }
1318
+ /** Soft-delete a timesheet entry. Requires `timesheets.delete` scope. */ async delete(id) {
1319
+ const result = await this.transport.del(`/api/v1/data/timesheets/${id}`);
1320
+ if ('error' in result) return result;
1321
+ return {
1322
+ data: null
1323
+ };
1324
+ }
564
1325
  }
565
1326
 
566
1327
  /**
@@ -695,9 +1456,9 @@ class AuthAccessor {
695
1456
  * export const nucleus = new NucleusClient({
696
1457
  * token: process.env.NUCLEUS_TOKEN!,
697
1458
  * scopes: {
698
- * employees: ['identity', 'employment'],
699
- * projects: ['core'],
700
- * clients: ['identity'],
1459
+ * employees: { read: ['identity', 'employment'], update: ['contact'] },
1460
+ * projects: { read: ['core'] },
1461
+ * leaves: { read: ['core'], create: ['core'], delete: true },
701
1462
  * },
702
1463
  * });
703
1464
  * ```
@@ -716,6 +1477,14 @@ class AuthAccessor {
716
1477
  this.projectTypes = new ProjectTypesAccessor(transport);
717
1478
  this.currencies = new CurrenciesAccessor(transport);
718
1479
  this.genericRates = new GenericRatesAccessor(transport);
1480
+ this.jurisdictions = new JurisdictionsAccessor(transport);
1481
+ this.leaveTypes = new LeaveTypesAccessor(transport);
1482
+ this.leaves = new LeavesAccessor(transport);
1483
+ this.leaveBalances = new LeaveBalancesAccessor(transport);
1484
+ this.policies = new PoliciesAccessor(transport);
1485
+ this.policyAcknowledgements = new PolicyAcknowledgementsAccessor(transport);
1486
+ this.calendarEvents = new CalendarEventsAccessor(transport);
1487
+ this.timesheets = new TimesheetsAccessor(transport);
719
1488
  this.apps = new AppsAccessor(transport);
720
1489
  this.files = new FilesAccessor(transport);
721
1490
  this.auth = new AuthAccessor(config.baseUrl);