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