@typeb-digital/nucleus-sdk 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -6,6 +6,20 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). While the
7
7
  package is pre-`1.0.0`, **breaking changes may land in a minor version** (e.g. `0.0.x` → `0.1.0`).
8
8
 
9
+ ## [0.1.1] - 2026-06-22
10
+
11
+ ### Changed
12
+
13
+ - **Employees are read-only.** Nucleus owns employee writes (sourced via Google/HRIS
14
+ sync), so the scope system no longer accepts `create` / `update` / `delete` grants
15
+ for `employees`:
16
+ - `ScopeDeclaration` types `employees` as read-only — declaring a write action on it
17
+ is now a compile-time error.
18
+ - Removed `employees.update()` / `employees.delete()` from the SDK.
19
+
20
+ (The platform also rejects employee write grants at app-registration / group-permission
21
+ time, and the dashboard scope matrix shows `employees` as read-only.)
22
+
9
23
  ## [0.1.0] - 2026-06-22
10
24
 
11
25
  Action-typed scopes and write support. **This is a breaking release** — every consumer's
@@ -415,6 +415,30 @@ function transformJurisdiction(raw) {
415
415
  if ('country' in r) out['country'] = str(r['country']);
416
416
  return out;
417
417
  }
418
+ function transformJobLevel(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 ('code' in r) out['code'] = str(r['code']);
425
+ if ('department_id' in r) out['departmentId'] = str(r['department_id']);
426
+ if ('sort_order' in r) out['sortOrder'] = num(r['sort_order']) ?? 0;
427
+ return out;
428
+ }
429
+ function transformSalaryBand(raw) {
430
+ if (!raw || typeof raw !== 'object') return {};
431
+ const r = raw;
432
+ const out = {};
433
+ if ('id' in r) out['id'] = r['id'];
434
+ if ('job_level_id' in r) out['jobLevelId'] = str(r['job_level_id']);
435
+ if ('jurisdiction_id' in r) out['jurisdictionId'] = str(r['jurisdiction_id']);
436
+ if ('currency_code' in r) out['currencyCode'] = str(r['currency_code']);
437
+ if ('min_salary' in r) out['minSalary'] = num(r['min_salary']);
438
+ if ('max_salary' in r) out['maxSalary'] = num(r['max_salary']);
439
+ if ('target_salary' in r) out['targetSalary'] = num(r['target_salary']);
440
+ return out;
441
+ }
418
442
  function transformLeaveType(raw) {
419
443
  if (!raw || typeof raw !== 'object') return {};
420
444
  const r = raw;
@@ -566,6 +590,7 @@ class EmployeesAccessor {
566
590
  if (params?.employmentStatus) query['employmentStatus'] = params.employmentStatus;
567
591
  if (params?.page) query['page'] = params.page;
568
592
  if (params?.pageSize) query['pageSize'] = params.pageSize;
593
+ if (params?.updatedSince) query['updatedSince'] = params.updatedSince;
569
594
  if (params?.expand?.length) query['expand'] = params.expand.join(',');
570
595
  const result = await this.transport.getList('/api/v1/data/employees', query);
571
596
  if ('error' in result) return result;
@@ -583,23 +608,16 @@ class EmployeesAccessor {
583
608
  data: transformEmployee(result.data)
584
609
  };
585
610
  }
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
- }
601
611
  }
602
612
 
613
+ function transformMember(raw) {
614
+ return {
615
+ id: raw['id'],
616
+ projectId: raw['project_id'],
617
+ employeeId: raw['employee_id'],
618
+ role: raw['role'] ?? null
619
+ };
620
+ }
603
621
  class ProjectsAccessor {
604
622
  constructor(transport){
605
623
  this.transport = transport;
@@ -608,9 +626,11 @@ class ProjectsAccessor {
608
626
  const query = {};
609
627
  if (params?.search) query['search'] = params.search;
610
628
  if (params?.status) query['status'] = params.status;
629
+ if (params?.stage) query['stage'] = params.stage;
611
630
  if (params?.clientId) query['clientId'] = params.clientId;
612
631
  if (params?.page) query['page'] = params.page;
613
632
  if (params?.pageSize) query['pageSize'] = params.pageSize;
633
+ if (params?.updatedSince) query['updatedSince'] = params.updatedSince;
614
634
  if (params?.expand?.length) query['expand'] = params.expand.join(',');
615
635
  const result = await this.transport.getList('/api/v1/data/projects', query);
616
636
  if ('error' in result) return result;
@@ -649,6 +669,33 @@ class ProjectsAccessor {
649
669
  data: null
650
670
  };
651
671
  }
672
+ // ─── Team membership (the projects `team` bucket) ──────────────────────────
673
+ // Membership is the project↔employee join. Reads need `projects` read on `team`;
674
+ // add/remove need `projects` update on `team` (re-adding restores a soft-deleted row).
675
+ membersBase(projectId) {
676
+ return `/api/v1/data/project-memberships/projects/${projectId}/members`;
677
+ }
678
+ /** List a project's team members. Requires `projects` read on the `team` bucket. */ async listMembers(projectId) {
679
+ const result = await this.transport.get(this.membersBase(projectId));
680
+ if ('error' in result) return result;
681
+ return {
682
+ data: (result.data ?? []).map((r)=>transformMember(r))
683
+ };
684
+ }
685
+ /** Add a team member. Requires `projects` update on the `team` bucket. */ async addMember(projectId, data) {
686
+ const result = await this.transport.post(this.membersBase(projectId), toSnakeBody(data));
687
+ if ('error' in result) return result;
688
+ return {
689
+ data: transformMember(result.data)
690
+ };
691
+ }
692
+ /** Remove a team member. Requires `projects` update on the `team` bucket. */ async removeMember(projectId, employeeId) {
693
+ const result = await this.transport.del(`${this.membersBase(projectId)}/${employeeId}`);
694
+ if ('error' in result) return result;
695
+ return {
696
+ data: null
697
+ };
698
+ }
652
699
  }
653
700
 
654
701
  class ClientsAccessor {
@@ -662,6 +709,7 @@ class ClientsAccessor {
662
709
  if (params?.country) query['country'] = params.country;
663
710
  if (params?.page) query['page'] = params.page;
664
711
  if (params?.pageSize) query['pageSize'] = params.pageSize;
712
+ if (params?.updatedSince) query['updatedSince'] = params.updatedSince;
665
713
  if (params?.expand?.length) query['expand'] = params.expand.join(',');
666
714
  const result = await this.transport.getList('/api/v1/data/clients', query);
667
715
  if ('error' in result) return result;
@@ -711,6 +759,7 @@ class PartnersAccessor {
711
759
  if (params?.search) query['search'] = params.search;
712
760
  if (params?.page) query['page'] = params.page;
713
761
  if (params?.pageSize) query['pageSize'] = params.pageSize;
762
+ if (params?.updatedSince) query['updatedSince'] = params.updatedSince;
714
763
  if (params?.expand?.length) query['expand'] = params.expand.join(',');
715
764
  const result = await this.transport.getList('/api/v1/data/partners', query);
716
765
  if ('error' in result) return result;
@@ -760,6 +809,7 @@ class DepartmentsAccessor {
760
809
  if (params?.search) query['search'] = params.search;
761
810
  if (params?.page) query['page'] = params.page;
762
811
  if (params?.pageSize) query['pageSize'] = params.pageSize;
812
+ if (params?.updatedSince) query['updatedSince'] = params.updatedSince;
763
813
  const result = await this.transport.getList('/api/v1/data/departments', query);
764
814
  if ('error' in result) return result;
765
815
  return {
@@ -805,6 +855,7 @@ class ProjectTypesAccessor {
805
855
  const query = {};
806
856
  if (params?.page) query['page'] = params.page;
807
857
  if (params?.pageSize) query['pageSize'] = params.pageSize;
858
+ if (params?.updatedSince) query['updatedSince'] = params.updatedSince;
808
859
  const result = await this.transport.getList('/api/v1/data/project-types', query);
809
860
  if ('error' in result) return result;
810
861
  return {
@@ -850,6 +901,7 @@ class CurrenciesAccessor {
850
901
  const query = {};
851
902
  if (params?.page) query['page'] = params.page;
852
903
  if (params?.pageSize) query['pageSize'] = params.pageSize;
904
+ if (params?.updatedSince) query['updatedSince'] = params.updatedSince;
853
905
  const result = await this.transport.getList('/api/v1/data/currencies', query);
854
906
  if ('error' in result) return result;
855
907
  return {
@@ -899,6 +951,7 @@ class GenericRatesAccessor {
899
951
  if (params?.department) query['department'] = params.department;
900
952
  if (params?.page) query['page'] = params.page;
901
953
  if (params?.pageSize) query['pageSize'] = params.pageSize;
954
+ if (params?.updatedSince) query['updatedSince'] = params.updatedSince;
902
955
  const result = await this.transport.getList('/api/v1/data/generic-rates', query);
903
956
  if ('error' in result) return result;
904
957
  return {
@@ -945,6 +998,7 @@ class JurisdictionsAccessor {
945
998
  if (params?.search) query['search'] = params.search;
946
999
  if (params?.page) query['page'] = params.page;
947
1000
  if (params?.pageSize) query['pageSize'] = params.pageSize;
1001
+ if (params?.updatedSince) query['updatedSince'] = params.updatedSince;
948
1002
  const result = await this.transport.getList('/api/v1/data/jurisdictions', query);
949
1003
  if ('error' in result) return result;
950
1004
  return {
@@ -982,6 +1036,105 @@ class JurisdictionsAccessor {
982
1036
  }
983
1037
  }
984
1038
 
1039
+ /** JobLevelsAccessor — the company's career-ladder rungs (WS7). */ class JobLevelsAccessor {
1040
+ constructor(transport){
1041
+ this.transport = transport;
1042
+ }
1043
+ async list(params) {
1044
+ const query = {};
1045
+ if (params?.search) query['search'] = params.search;
1046
+ if (params?.departmentId) query['departmentId'] = params.departmentId;
1047
+ if (params?.page) query['page'] = params.page;
1048
+ if (params?.pageSize) query['pageSize'] = params.pageSize;
1049
+ if (params?.updatedSince) query['updatedSince'] = params.updatedSince;
1050
+ const result = await this.transport.getList('/api/v1/data/job-levels', query);
1051
+ if ('error' in result) return result;
1052
+ return {
1053
+ data: result.data.map((r)=>transformJobLevel(r)),
1054
+ meta: result.meta
1055
+ };
1056
+ }
1057
+ async getById(id) {
1058
+ const result = await this.transport.get(`/api/v1/data/job-levels/${id}`);
1059
+ if ('error' in result) return result;
1060
+ return {
1061
+ data: transformJobLevel(result.data)
1062
+ };
1063
+ }
1064
+ /** Create a job level. Requires `job-levels.create` scope. */ async create(data) {
1065
+ const result = await this.transport.post('/api/v1/data/job-levels', toSnakeBody(data));
1066
+ if ('error' in result) return result;
1067
+ return {
1068
+ data: transformJobLevel(result.data)
1069
+ };
1070
+ }
1071
+ /** Update a job level. Requires `job-levels.update` scope. */ async update(id, data) {
1072
+ const result = await this.transport.put(`/api/v1/data/job-levels/${id}`, toSnakeBody(data));
1073
+ if ('error' in result) return result;
1074
+ return {
1075
+ data: transformJobLevel(result.data)
1076
+ };
1077
+ }
1078
+ /** Soft-delete a job level. Requires `job-levels.delete` scope. */ async delete(id) {
1079
+ const result = await this.transport.del(`/api/v1/data/job-levels/${id}`);
1080
+ if ('error' in result) return result;
1081
+ return {
1082
+ data: null
1083
+ };
1084
+ }
1085
+ }
1086
+
1087
+ /**
1088
+ * SalaryBandsAccessor — per-jurisdiction pay ranges for a job level (WS7).
1089
+ * The `financials` bucket (min/max/target) is flagged sensitive server-side.
1090
+ */ class SalaryBandsAccessor {
1091
+ constructor(transport){
1092
+ this.transport = transport;
1093
+ }
1094
+ async list(params) {
1095
+ const query = {};
1096
+ if (params?.jobLevelId) query['jobLevelId'] = params.jobLevelId;
1097
+ if (params?.jurisdictionId) query['jurisdictionId'] = params.jurisdictionId;
1098
+ if (params?.page) query['page'] = params.page;
1099
+ if (params?.pageSize) query['pageSize'] = params.pageSize;
1100
+ if (params?.updatedSince) query['updatedSince'] = params.updatedSince;
1101
+ const result = await this.transport.getList('/api/v1/data/salary-bands', query);
1102
+ if ('error' in result) return result;
1103
+ return {
1104
+ data: result.data.map((r)=>transformSalaryBand(r)),
1105
+ meta: result.meta
1106
+ };
1107
+ }
1108
+ async getById(id) {
1109
+ const result = await this.transport.get(`/api/v1/data/salary-bands/${id}`);
1110
+ if ('error' in result) return result;
1111
+ return {
1112
+ data: transformSalaryBand(result.data)
1113
+ };
1114
+ }
1115
+ /** Create a salary band. Requires `salary-bands.create` scope. */ async create(data) {
1116
+ const result = await this.transport.post('/api/v1/data/salary-bands', toSnakeBody(data));
1117
+ if ('error' in result) return result;
1118
+ return {
1119
+ data: transformSalaryBand(result.data)
1120
+ };
1121
+ }
1122
+ /** Update a salary band. Requires `salary-bands.update` scope. */ async update(id, data) {
1123
+ const result = await this.transport.put(`/api/v1/data/salary-bands/${id}`, toSnakeBody(data));
1124
+ if ('error' in result) return result;
1125
+ return {
1126
+ data: transformSalaryBand(result.data)
1127
+ };
1128
+ }
1129
+ /** Soft-delete a salary band. Requires `salary-bands.delete` scope. */ async delete(id) {
1130
+ const result = await this.transport.del(`/api/v1/data/salary-bands/${id}`);
1131
+ if ('error' in result) return result;
1132
+ return {
1133
+ data: null
1134
+ };
1135
+ }
1136
+ }
1137
+
985
1138
  class LeaveTypesAccessor {
986
1139
  constructor(transport){
987
1140
  this.transport = transport;
@@ -991,6 +1144,7 @@ class LeaveTypesAccessor {
991
1144
  if (params?.search) query['search'] = params.search;
992
1145
  if (params?.page) query['page'] = params.page;
993
1146
  if (params?.pageSize) query['pageSize'] = params.pageSize;
1147
+ if (params?.updatedSince) query['updatedSince'] = params.updatedSince;
994
1148
  const result = await this.transport.getList('/api/v1/data/leave-types', query);
995
1149
  if ('error' in result) return result;
996
1150
  return {
@@ -1039,6 +1193,7 @@ class LeavesAccessor {
1039
1193
  if (params?.status) query['status'] = params.status;
1040
1194
  if (params?.page) query['page'] = params.page;
1041
1195
  if (params?.pageSize) query['pageSize'] = params.pageSize;
1196
+ if (params?.updatedSince) query['updatedSince'] = params.updatedSince;
1042
1197
  if (params?.expand?.length) query['expand'] = params.expand.join(',');
1043
1198
  const result = await this.transport.getList('/api/v1/data/leaves', query);
1044
1199
  if ('error' in result) return result;
@@ -1090,6 +1245,7 @@ class LeaveBalancesAccessor {
1090
1245
  if (params?.year) query['year'] = params.year;
1091
1246
  if (params?.page) query['page'] = params.page;
1092
1247
  if (params?.pageSize) query['pageSize'] = params.pageSize;
1248
+ if (params?.updatedSince) query['updatedSince'] = params.updatedSince;
1093
1249
  if (params?.expand?.length) query['expand'] = params.expand.join(',');
1094
1250
  const result = await this.transport.getList('/api/v1/data/leave-balances', query);
1095
1251
  if ('error' in result) return result;
@@ -1140,6 +1296,7 @@ class PoliciesAccessor {
1140
1296
  if (params?.category) query['category'] = params.category;
1141
1297
  if (params?.page) query['page'] = params.page;
1142
1298
  if (params?.pageSize) query['pageSize'] = params.pageSize;
1299
+ if (params?.updatedSince) query['updatedSince'] = params.updatedSince;
1143
1300
  const result = await this.transport.getList('/api/v1/data/policies', query);
1144
1301
  if ('error' in result) return result;
1145
1302
  return {
@@ -1187,6 +1344,7 @@ class PolicyAcknowledgementsAccessor {
1187
1344
  if (params?.employeeId) query['employeeId'] = params.employeeId;
1188
1345
  if (params?.page) query['page'] = params.page;
1189
1346
  if (params?.pageSize) query['pageSize'] = params.pageSize;
1347
+ if (params?.updatedSince) query['updatedSince'] = params.updatedSince;
1190
1348
  if (params?.expand?.length) query['expand'] = params.expand.join(',');
1191
1349
  const result = await this.transport.getList('/api/v1/data/policy-acknowledgements', query);
1192
1350
  if ('error' in result) return result;
@@ -1235,8 +1393,10 @@ class CalendarEventsAccessor {
1235
1393
  const query = {};
1236
1394
  if (params?.search) query['search'] = params.search;
1237
1395
  if (params?.type) query['type'] = params.type;
1396
+ if (params?.jurisdictionId) query['jurisdictionId'] = params.jurisdictionId;
1238
1397
  if (params?.page) query['page'] = params.page;
1239
1398
  if (params?.pageSize) query['pageSize'] = params.pageSize;
1399
+ if (params?.updatedSince) query['updatedSince'] = params.updatedSince;
1240
1400
  const result = await this.transport.getList('/api/v1/data/calendar-events', query);
1241
1401
  if ('error' in result) return result;
1242
1402
  return {
@@ -1284,6 +1444,7 @@ class TimesheetsAccessor {
1284
1444
  if (params?.projectId) query['projectId'] = params.projectId;
1285
1445
  if (params?.page) query['page'] = params.page;
1286
1446
  if (params?.pageSize) query['pageSize'] = params.pageSize;
1447
+ if (params?.updatedSince) query['updatedSince'] = params.updatedSince;
1287
1448
  if (params?.expand?.length) query['expand'] = params.expand.join(',');
1288
1449
  const result = await this.transport.getList('/api/v1/data/timesheets', query);
1289
1450
  if ('error' in result) return result;
@@ -1308,6 +1469,23 @@ class TimesheetsAccessor {
1308
1469
  data: transformTimesheet(result.data)
1309
1470
  };
1310
1471
  }
1472
+ /**
1473
+ * Bulk-create timesheet entries (partial accept). Requires `timesheets.create` scope.
1474
+ * Returns `{ created, rejected }` — every valid item is inserted; invalid items come back
1475
+ * with their input index + reason.
1476
+ */ async bulkCreate(items) {
1477
+ const result = await this.transport.post('/api/v1/data/timesheets/bulk', {
1478
+ items: items.map((i)=>toSnakeBody(i))
1479
+ });
1480
+ if ('error' in result) return result;
1481
+ const data = result.data;
1482
+ return {
1483
+ data: {
1484
+ created: data.created.map((r)=>transformTimesheet(r)),
1485
+ rejected: data.rejected
1486
+ }
1487
+ };
1488
+ }
1311
1489
  /** Update a timesheet entry. Requires `timesheets.update` scope. */ async update(id, data) {
1312
1490
  const result = await this.transport.put(`/api/v1/data/timesheets/${id}`, toSnakeBody(data));
1313
1491
  if ('error' in result) return result;
@@ -1324,6 +1502,98 @@ class TimesheetsAccessor {
1324
1502
  }
1325
1503
  }
1326
1504
 
1505
+ /**
1506
+ * CalendarAccessor — read workspace users' Google Calendar events (WS3).
1507
+ * Requires the app's `calendar:read` scope. Not part of the bucket/CRUD scope system.
1508
+ */ class CalendarAccessor {
1509
+ constructor(transport){
1510
+ this.transport = transport;
1511
+ }
1512
+ async listEvents(params) {
1513
+ const result = await this.transport.get('/api/v1/data/calendar/events', {
1514
+ employeeId: params.employeeId,
1515
+ timeMin: params.timeMin,
1516
+ timeMax: params.timeMax,
1517
+ q: params.q
1518
+ });
1519
+ if ('error' in result) return result;
1520
+ return {
1521
+ data: result.data
1522
+ };
1523
+ }
1524
+ }
1525
+
1526
+ /**
1527
+ * EmailAccessor — send Nucleus-mediated email (WS5). Requires the app's `email:send` scope.
1528
+ * In test mode the message is marked `[SANDBOX]` and external recipients are suppressed.
1529
+ */ class EmailAccessor {
1530
+ constructor(transport){
1531
+ this.transport = transport;
1532
+ }
1533
+ async send(input) {
1534
+ const result = await this.transport.post('/api/v1/data/comms/send', input);
1535
+ if ('error' in result) return result;
1536
+ return {
1537
+ data: result.data
1538
+ };
1539
+ }
1540
+ }
1541
+
1542
+ function transform(raw) {
1543
+ return {
1544
+ id: raw['id'],
1545
+ employeeId: raw['employee_id'],
1546
+ hourlyCostRate: raw['hourly_cost_rate'] ?? null,
1547
+ monthlyCostRate: raw['monthly_cost_rate'] ?? null,
1548
+ hourlyBillableRate: raw['hourly_billable_rate'] ?? null,
1549
+ monthlyBillableRate: raw['monthly_billable_rate'] ?? null,
1550
+ salary: raw['salary'] ?? null,
1551
+ currencyCode: raw['currency_code'],
1552
+ effectiveFrom: raw['effective_from'],
1553
+ effectiveTo: raw['effective_to'] ?? null
1554
+ };
1555
+ }
1556
+ /**
1557
+ * EmployeeCompensationAccessor — read/write an employee's effective-dated compensation (WS6).
1558
+ * Gated server-side by the `employee-compensation` scope (buckets: `core`, `rates`, `salary`).
1559
+ * `create` closes the prior open record and opens a new one (Nucleus = single comp authority).
1560
+ */ class EmployeeCompensationAccessor {
1561
+ constructor(transport){
1562
+ this.transport = transport;
1563
+ }
1564
+ base(employeeId) {
1565
+ return `/api/v1/data/employee-compensation/employees/${employeeId}/compensation`;
1566
+ }
1567
+ /** Full history, newest first. */ async list(employeeId) {
1568
+ const result = await this.transport.get(this.base(employeeId));
1569
+ if ('error' in result) return result;
1570
+ return {
1571
+ data: (result.data ?? []).map((r)=>transform(r))
1572
+ };
1573
+ }
1574
+ /** The current (open) record, or null. */ async current(employeeId) {
1575
+ const result = await this.transport.get(`${this.base(employeeId)}/current`);
1576
+ if ('error' in result) return result;
1577
+ return {
1578
+ data: result.data ? transform(result.data) : null
1579
+ };
1580
+ }
1581
+ /** Add a new effective-dated record (closes the prior open one). */ async create(employeeId, data) {
1582
+ const result = await this.transport.post(this.base(employeeId), toSnakeBody(data));
1583
+ if ('error' in result) return result;
1584
+ return {
1585
+ data: transform(result.data)
1586
+ };
1587
+ }
1588
+ /** Soft-delete a compensation record. */ async delete(employeeId, id) {
1589
+ const result = await this.transport.del(`${this.base(employeeId)}/${id}`);
1590
+ if ('error' in result) return result;
1591
+ return {
1592
+ data: null
1593
+ };
1594
+ }
1595
+ }
1596
+
1327
1597
  /**
1328
1598
  * AppsAccessor — wraps the self-service app info endpoint (§3.8, §6).
1329
1599
  * Calls GET /api/v1/platform/apps/me with the app token.
@@ -1478,6 +1748,8 @@ class AuthAccessor {
1478
1748
  this.currencies = new CurrenciesAccessor(transport);
1479
1749
  this.genericRates = new GenericRatesAccessor(transport);
1480
1750
  this.jurisdictions = new JurisdictionsAccessor(transport);
1751
+ this.jobLevels = new JobLevelsAccessor(transport);
1752
+ this.salaryBands = new SalaryBandsAccessor(transport);
1481
1753
  this.leaveTypes = new LeaveTypesAccessor(transport);
1482
1754
  this.leaves = new LeavesAccessor(transport);
1483
1755
  this.leaveBalances = new LeaveBalancesAccessor(transport);
@@ -1485,6 +1757,9 @@ class AuthAccessor {
1485
1757
  this.policyAcknowledgements = new PolicyAcknowledgementsAccessor(transport);
1486
1758
  this.calendarEvents = new CalendarEventsAccessor(transport);
1487
1759
  this.timesheets = new TimesheetsAccessor(transport);
1760
+ this.calendar = new CalendarAccessor(transport);
1761
+ this.email = new EmailAccessor(transport);
1762
+ this.employeeCompensation = new EmployeeCompensationAccessor(transport);
1488
1763
  this.apps = new AppsAccessor(transport);
1489
1764
  this.files = new FilesAccessor(transport);
1490
1765
  this.auth = new AuthAccessor(config.baseUrl);
@@ -1494,7 +1769,7 @@ class AuthAccessor {
1494
1769
  /**
1495
1770
  * Nucleus SDK — Type System
1496
1771
  *
1497
- * Bucket types are transcribed from docs/NUCLEUS_PIVOT.md §7 (Bucket Definitions).
1772
+ * Bucket types are transcribed from docs/ARCHITECTURE.md §7 (Bucket Definitions).
1498
1773
  * Each bucket type contains only that bucket's fields.
1499
1774
  * The scope-to-type inference intersects selected bucket types at compile time.
1500
1775
  */ // ─── Utility types ────────────────────────────────────────────────────────────