rotacloud 2.2.4 → 2.2.9

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.
@@ -1,4 +1,4 @@
1
- import { Account, Attendance, Auth, Availability, DailyBudgets, DailyRevenue, DayNote, DaysOff, Group, Leave, Role, Shift, TimeZone, Document, LeaveEmbargo, LeaveRequest, LeaveType, Location, Pin, Terminal, ToilAccrual, ToilAllowance, UserClockedIn, User, Settings, LogbookCategory, DayNoteV2, DayNoteV2QueryParameters } from './interfaces/index.js';
1
+ import { Account, Attendance, Auth, Availability, DailyBudgets, DailyRevenue, DayNote, DaysOff, Group, Leave, Role, Shift, TimeZone, Document, LeaveEmbargo, LeaveRequest, LeaveType, Location, Pin, Terminal, ToilAccrual, ToilAllowance, UserClockedIn, User, Settings, LogbookCategory, DayNoteV2, DayNoteV2QueryParameters, PartialUserV2 } from './interfaces/index.js';
2
2
  import { LogbookEntry, LogbookQueryParameters } from './interfaces/logbook.interface.js';
3
3
  import { Message } from './interfaces/message.interface.js';
4
4
  import { AttendanceQueryParams, AvailabilityQueryParams, DailyBudgetsQueryParams, DailyRevenueQueryParams, DayNotesQueryParams, DaysOffQueryParams, DocumentsQueryParams, GroupsQueryParams, LeaveEmbargoesQueryParams, LeaveQueryParams, LeaveRequestsQueryParams, LocationsQueryParams, RolesQueryParams, SettingsQueryParams, ShiftsQueryParams, TerminalsQueryParams, ToilAccrualsQueryParams, ToilAllowanceQueryParams, UsersQueryParams } from './interfaces/query-params/index.js';
@@ -59,5 +59,9 @@ export interface EndpointEntityMap extends Record<EndpointVersion, Record<string
59
59
  invoices: Endpoint<Invoice, InvoiceQueryParameters>;
60
60
  dayNotes: Endpoint<DayNoteV2, DayNoteV2QueryParameters, 'title' | 'message' | 'startDate' | 'endDate' | 'locations' | 'visibleToEmployees'>;
61
61
  'logbook/categories': Endpoint<LogbookCategory, undefined, Pick<LogbookCategory, 'name'>>;
62
+ users: Endpoint<User, undefined, {
63
+ users: RequirementsOf<PartialUserV2, 'firstName' | 'lastName' | 'roles'>[];
64
+ sendInvite?: boolean;
65
+ }>;
62
66
  };
63
67
  }
@@ -0,0 +1,112 @@
1
+ export interface Entitlement {
2
+ id: string;
3
+ }
4
+ export interface Plan {
5
+ id: string;
6
+ status: {
7
+ name: 'active' | 'in_trial';
8
+ };
9
+ }
10
+ export interface PlanExtended extends Plan {
11
+ trialEnds: string;
12
+ pricing: PlanPricing;
13
+ }
14
+ export interface Addon {
15
+ id: string;
16
+ status: {
17
+ name: 'active' | 'in_trial' | 'hidden';
18
+ };
19
+ }
20
+ export interface AddonExtended extends Addon {
21
+ trialEnds: string;
22
+ pricing: PlanPricing;
23
+ }
24
+ export interface PlanPricing {
25
+ itemPriceId: string;
26
+ price: number;
27
+ pricingModel: 'flat_fee' | 'per_unit' | 'tiered' | 'volume' | 'stairstep' | null;
28
+ tiers?: PricingTier;
29
+ currentTier?: PricingTier;
30
+ perUnitPrice?: number;
31
+ }
32
+ export interface PricingTier {
33
+ startingUnit: number;
34
+ endingUnit: number;
35
+ price: number;
36
+ }
37
+ export interface AccountSubscription {
38
+ status: 'in_trial' | 'active' | 'non_renewing' | 'paused' | 'cancelled';
39
+ dunningEnds?: string;
40
+ entitlements: Entitlement[];
41
+ plans: Plan[];
42
+ addons: Addon[];
43
+ }
44
+ export interface AccountSubscriptionExtended extends AccountSubscription {
45
+ billingFrequency: string;
46
+ billingEmail: string;
47
+ country: number;
48
+ subscriptionPaymentSourceId: string;
49
+ nextBillingAt: string;
50
+ trialEnds: string;
51
+ canRestartTrial: boolean;
52
+ totalPrice: number;
53
+ plans: PlanExtended[];
54
+ addons: AddonExtended[];
55
+ }
56
+ export interface SubscriptionUpdateReq {
57
+ paymentFrequency?: 'monthly' | 'yearly';
58
+ plans?: [{
59
+ planId: string;
60
+ trial?: boolean;
61
+ }];
62
+ addons?: [{
63
+ addonId: string;
64
+ trial?: boolean;
65
+ }];
66
+ subscriptionPaymentSourceId?: string;
67
+ billingEmail?: string;
68
+ country?: number;
69
+ }
70
+ export interface EstimatesRes {
71
+ owed: number;
72
+ totalPrice: number;
73
+ }
74
+ export interface ProductCatalogueItemPrice {
75
+ id: string;
76
+ currency: string;
77
+ pricingModel: 'stairstep' | 'perUnit';
78
+ unitPrice?: number;
79
+ stairStep?: {
80
+ startingUnit: number;
81
+ endingUnit?: number;
82
+ tierPrice: number;
83
+ };
84
+ billingPeriod: number;
85
+ billingPeriodUnit: 'monthly' | 'yearly';
86
+ }
87
+ export interface ProductCatalogueItem {
88
+ id: string;
89
+ name: string;
90
+ itemPrice: ProductCatalogueItemPrice[];
91
+ }
92
+ export interface ProductCatalogueRes {
93
+ plans: ProductCatalogueItem[];
94
+ addons: ProductCatalogueItem[];
95
+ }
96
+ export declare enum CancellationReason {
97
+ 'features' = 0,
98
+ 'value' = 1,
99
+ 'price' = 2,
100
+ 'not_using' = 3,
101
+ 'incompatible' = 4,
102
+ 'difficulty' = 5,
103
+ 'using_competitor' = 6,
104
+ 'sold' = 7,
105
+ 'temporary_closure' = 8,
106
+ 'closed' = 9
107
+ }
108
+ export interface CancelSubscriptionReq {
109
+ reason: CancellationReason;
110
+ message?: string;
111
+ detail?: string;
112
+ }
@@ -0,0 +1,13 @@
1
+ export var CancellationReason;
2
+ (function (CancellationReason) {
3
+ CancellationReason[CancellationReason["features"] = 0] = "features";
4
+ CancellationReason[CancellationReason["value"] = 1] = "value";
5
+ CancellationReason[CancellationReason["price"] = 2] = "price";
6
+ CancellationReason[CancellationReason["not_using"] = 3] = "not_using";
7
+ CancellationReason[CancellationReason["incompatible"] = 4] = "incompatible";
8
+ CancellationReason[CancellationReason["difficulty"] = 5] = "difficulty";
9
+ CancellationReason[CancellationReason["using_competitor"] = 6] = "using_competitor";
10
+ CancellationReason[CancellationReason["sold"] = 7] = "sold";
11
+ CancellationReason[CancellationReason["temporary_closure"] = 8] = "temporary_closure";
12
+ CancellationReason[CancellationReason["closed"] = 9] = "closed";
13
+ })(CancellationReason || (CancellationReason = {}));
@@ -34,6 +34,7 @@ export * from './time-zone.interface.js';
34
34
  export * from './toil-accrual.interface.js';
35
35
  export * from './toil-allowance.interface.js';
36
36
  export * from './user.interface.js';
37
+ export * from './user-v2.interface.js';
37
38
  export * from './users-clocked-in.interface.js';
38
39
  export * from './users-clocked-out.interface.js';
39
40
  export * from './document.interface.js';
@@ -34,6 +34,7 @@ export * from './time-zone.interface.js';
34
34
  export * from './toil-accrual.interface.js';
35
35
  export * from './toil-allowance.interface.js';
36
36
  export * from './user.interface.js';
37
+ export * from './user-v2.interface.js';
37
38
  export * from './users-clocked-in.interface.js';
38
39
  export * from './users-clocked-out.interface.js';
39
40
  export * from './document.interface.js';
@@ -1,7 +1,7 @@
1
1
  import { TerminalLocation } from './terminal.interface.js';
2
2
  export interface UserClockIn {
3
3
  method: string;
4
- shift: number;
4
+ shift: number | null;
5
5
  terminal?: number;
6
6
  user: number;
7
7
  photo?: string;
@@ -0,0 +1,26 @@
1
+ import { ManagerPermission } from './user.interface.js';
2
+ export interface CreateUserRequest extends PartialUserV2 {
3
+ permissions?: ManagerPermission[];
4
+ coverLocations?: number[];
5
+ managedLocations?: number[];
6
+ }
7
+ export interface CreateUserResponse extends PartialUserV2 {
8
+ id: number;
9
+ }
10
+ export interface PartialUserV2 {
11
+ firstName: string;
12
+ lastName: string;
13
+ locations: number[];
14
+ roles: UserRole[];
15
+ email: string | null;
16
+ level: 'admin' | 'employee' | 'manager';
17
+ salary: number;
18
+ salaryType: 'annual' | 'hourly';
19
+ }
20
+ export interface UserRole {
21
+ id: number;
22
+ perShift?: number;
23
+ perHour?: number;
24
+ payCode?: string;
25
+ isDefault?: boolean;
26
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,11 +1,11 @@
1
- import { Shift, TerminalLocation, UserBreak } from '../interfaces/index.js';
1
+ import { TerminalLocation, UserBreak } from '../interfaces/index.js';
2
2
  export interface UserClockedIn {
3
3
  user: number;
4
4
  location: number;
5
5
  role: number;
6
6
  in_time: number;
7
7
  minutes_late: number;
8
- shift: Pick<Shift, 'id' | 'start_time' | 'end_time' | 'minutes_break' | 'location' | 'role'>;
8
+ shift: number | null;
9
9
  in_method: string;
10
10
  in_location: TerminalLocation;
11
11
  in_device: string | null;
package/dist/main.d.ts CHANGED
@@ -239,4 +239,15 @@ export declare const createRotaCloudClient: (config: import("./interfaces/sdk-co
239
239
  endpointVersion: "v1";
240
240
  operations: ("get" | "delete" | "list" | "listAll" | "create" | "update")[];
241
241
  };
242
+ userV2: {
243
+ endpoint: "users";
244
+ endpointVersion: "v2";
245
+ operations: "create"[];
246
+ customOperations: {
247
+ create: ({ request, service }: import("./ops.js").OperationContext, userSpec: {
248
+ users: import("./utils.js").RequirementsOf<import("./interfaces/user-v2.interface.js").CreateUserRequest, "firstName" | "lastName" | "roles">[];
249
+ sendInvite?: boolean;
250
+ }) => import("./ops.js").RequestConfig<typeof userSpec, import("./interfaces/user-v2.interface.js").CreateUserResponse>;
251
+ };
252
+ };
242
253
  }>;
package/dist/ops.d.ts CHANGED
@@ -23,69 +23,82 @@ export type RequestConfig<RequestData, ResponseData> = AxiosRequestConfig<Reques
23
23
  *
24
24
  * This is intended as a convenient way of defining {@see OpFunction}s that can then be
25
25
  * "built" ready for the end user to call ({@see buildOp})
26
- * */
26
+ */
27
27
  export type OpDef<T, Param = any, Opts extends Partial<RequestOptions<any>> = RequestOptions<unknown>, Return = T> = ((ctx: OperationContext, param: Param, opts?: Opts) => RequestConfig<T, Return>) | ((ctx: OperationContext, param: Param, opts?: Opts) => AsyncIterable<T>) | ((ctx: OperationContext, param: Param, opts?: Opts) => Promise<T>);
28
28
  /** Operation function type to be called by the end user of the SDK.
29
29
  *
30
- * All methods on services follow this typing
31
- * */
32
- export type OpFunction<R = any, Param = undefined, Opts = RequestOptions<unknown>> = R extends AsyncIterable<infer U> | Promise<Iterable<infer U>> ? Param extends undefined ? {
33
- (query?: Param): R;
30
+ * All methods on services follow this typing.
31
+ */
32
+ export type OpFunction<Return = any, Param = undefined, Opts = RequestOptions<unknown>> = Return extends AsyncIterable<infer U> | Promise<Iterable<infer U>> ? Param extends undefined ? {
33
+ (query?: Param): Return;
34
34
  <F extends keyof U>(query: Param, options: {
35
35
  fields: F[];
36
- } & RequestOptions<U>): R extends AsyncIterable<U> ? Promise<AsyncIterable<Pick<U, F>>> : R extends Promise<Array<U>> ? Promise<Array<Pick<U, F>>> : Promise<Iterable<Pick<U, F>>>;
37
- (query: Param, options?: Opts): R;
36
+ } & RequestOptions<U>): Return extends AsyncIterable<U> ? Promise<AsyncIterable<Pick<U, F>>> : Return extends Promise<Array<U>> ? Promise<Array<Pick<U, F>>> : Promise<Iterable<Pick<U, F>>>;
37
+ (query: Param, options?: Opts): Return;
38
38
  } : Partial<Param> extends Param ? {
39
- (query?: Param): R;
39
+ (query?: Param): Return;
40
40
  <F extends keyof U>(query: Param, options: {
41
41
  fields: F[];
42
- } & RequestOptions<U>): R extends AsyncIterable<U> ? Promise<AsyncIterable<Pick<U, F>>> : R extends Promise<Array<U>> ? Promise<Array<Pick<U, F>>> : Promise<Iterable<Pick<U, F>>>;
43
- (query?: Param, options?: Opts): R;
42
+ } & RequestOptions<U>): Return extends AsyncIterable<U> ? Promise<AsyncIterable<Pick<U, F>>> : Return extends Promise<Array<U>> ? Promise<Array<Pick<U, F>>> : Promise<Iterable<Pick<U, F>>>;
43
+ (query?: Param, options?: Opts): Return;
44
44
  } : {
45
- (query: Param): R;
45
+ (query: Param): Return;
46
46
  <F extends keyof U>(query: Param, options: {
47
47
  fields: F[];
48
- } & RequestOptions<U>): R extends AsyncIterable<U> ? Promise<AsyncIterable<Pick<U, F>>> : R extends Promise<Array<U>> ? Promise<Array<Pick<U, F>>> : Promise<Iterable<Pick<U, F>>>;
49
- (query: Param, Options?: Opts): R;
48
+ } & RequestOptions<U>): Return extends AsyncIterable<U> ? Promise<AsyncIterable<Pick<U, F>>> : Return extends Promise<Array<U>> ? Promise<Array<Pick<U, F>>> : Promise<Iterable<Pick<U, F>>>;
49
+ (query: Param, Options?: Opts): Return;
50
50
  } : Param extends number ? {
51
- (id: Param): R;
52
- <F extends keyof Awaited<R>>(id: Param, options: {
51
+ (id: Param): Return;
52
+ <F extends keyof Awaited<Return>>(id: Param, options: {
53
53
  fields: F[];
54
54
  rawResponse: true;
55
- } & RequestOptions<Awaited<R>>): Promise<AxiosResponse<Pick<Awaited<R>, F>>>;
56
- <F extends keyof Awaited<R>>(id: Param, options: {
55
+ } & RequestOptions<Awaited<Return>>): Promise<AxiosResponse<Pick<Awaited<Return>, F>>>;
56
+ <F extends keyof Awaited<Return>>(id: Param, options: {
57
57
  fields: F[];
58
- } & RequestOptions<Awaited<R>>): Promise<Pick<Awaited<R>, F>>;
58
+ } & RequestOptions<Awaited<Return>>): Promise<Pick<Awaited<Return>, F>>;
59
59
  (id: Param, options?: {
60
60
  rawResponse: true;
61
- } & RequestOptions<Awaited<R>>): Promise<AxiosResponse<Awaited<R>>>;
62
- (id: Param, options?: RequestOptions<Awaited<R>>): R;
61
+ } & RequestOptions<Awaited<Return>>): Promise<AxiosResponse<Awaited<Return>>>;
62
+ (id: Param, options?: RequestOptions<Awaited<Return>>): Return;
63
+ } : Param extends number[] ? {
64
+ (ids: Param): Return;
65
+ <F extends keyof Awaited<Return>>(ids: Param, options: {
66
+ fields: F[];
67
+ rawResponse: true;
68
+ } & RequestOptions<Awaited<Return>>): Promise<AxiosResponse<Pick<Awaited<Return>, F>>>;
69
+ <F extends keyof Awaited<Return>>(ids: Param, options: {
70
+ fields: F[];
71
+ } & RequestOptions<Awaited<Return>>): Promise<Pick<Awaited<Return>, F>>;
72
+ (ids: Param, options?: {
73
+ rawResponse: true;
74
+ } & RequestOptions<Awaited<Return>>): Promise<AxiosResponse<Awaited<Return>>>;
75
+ (ids: Param, options?: RequestOptions<Awaited<Return>>): Return;
63
76
  } : Param extends undefined ? {
64
- (entity?: Param): R;
65
- <F extends keyof Awaited<R>>(entity: Param, options: {
77
+ (): Return;
78
+ <F extends keyof Awaited<Return>>(none: Param, options: {
66
79
  fields: F[];
67
80
  rawResponse: true;
68
- } & RequestOptions<Awaited<R>>): Promise<AxiosResponse<Pick<Awaited<R>, F>>>;
69
- <F extends keyof Awaited<R>>(entity: Param, options: {
81
+ } & RequestOptions<Awaited<Return>>): Promise<AxiosResponse<Pick<Awaited<Return>, F>>>;
82
+ <F extends keyof Awaited<Return>>(none: Param, options: {
70
83
  fields: F[];
71
- } & RequestOptions<Awaited<R>>): Promise<Pick<Awaited<R>, F>>;
72
- (entity?: Param, options?: {
84
+ } & RequestOptions<Awaited<Return>>): Promise<Pick<Awaited<Return>, F>>;
85
+ (none?: Param, options?: {
73
86
  rawResponse: true;
74
- } & RequestOptions<Awaited<R>>): Promise<AxiosResponse<Awaited<R>>>;
75
- (entity?: Param, options?: RequestOptions<Awaited<R>>): R;
87
+ } & RequestOptions<Awaited<Return>>): Promise<AxiosResponse<Awaited<Return>>>;
88
+ (none?: Param, options?: RequestOptions<Awaited<Return>>): Return;
76
89
  } : {
77
- (entity: Param): R;
78
- <F extends keyof Awaited<R>>(entity: Param, options: {
90
+ (entity: Param): Return;
91
+ <F extends keyof Awaited<Return>>(entity: Param, options: {
79
92
  fields: F[];
80
93
  rawResponse: true;
81
- } & RequestOptions<Awaited<R>>): Promise<AxiosResponse<Pick<Awaited<R>, F>>>;
82
- <F extends keyof Awaited<R>>(entity: Param, options: {
94
+ } & RequestOptions<Awaited<Return>>): Promise<AxiosResponse<Pick<Awaited<Return>, F>>>;
95
+ <F extends keyof Awaited<Return>>(entity: Param, options: {
83
96
  fields: F[];
84
- } & RequestOptions<Awaited<R>>): Promise<Pick<Awaited<R>, F>>;
97
+ } & RequestOptions<Awaited<Return>>): Promise<Pick<Awaited<Return>, F>>;
85
98
  (entity: Param, options?: {
86
99
  rawResponse: true;
87
- } & RequestOptions<Awaited<R>>): Promise<AxiosResponse<Awaited<R>>>;
88
- (entity: Param, options?: RequestOptions<Awaited<R>>): R;
100
+ } & RequestOptions<Awaited<Return>>): Promise<AxiosResponse<Awaited<Return>>>;
101
+ (entity: Param, options?: RequestOptions<Awaited<Return>>): Return;
89
102
  };
90
103
  /** Wrapper type for expressing the response returned by a v2 list op supported endpoint */
91
104
  interface PagedResponse<T> {
package/dist/service.d.ts CHANGED
@@ -10,6 +10,7 @@ import { ToilAllowanceQueryParams } from './interfaces/query-params/index.js';
10
10
  import { LogbookEntry, LogbookQueryParameters } from './interfaces/logbook.interface.js';
11
11
  import { Message } from './interfaces/message.interface.js';
12
12
  import { Invoice, InvoiceDownload } from './interfaces/invoice.interface.js';
13
+ import { CreateUserRequest, CreateUserResponse } from './interfaces/user-v2.interface.js';
13
14
  export type ServiceSpecification<CustomOp extends OpDef<unknown> = OpDef<any>> = {
14
15
  /** Operations allowed and usable for the endpoint */
15
16
  operations: Operation[];
@@ -282,4 +283,15 @@ export declare const SERVICES: {
282
283
  endpointVersion: "v1";
283
284
  operations: ("get" | "delete" | "list" | "listAll" | "create" | "update")[];
284
285
  };
286
+ userV2: {
287
+ endpoint: "users";
288
+ endpointVersion: "v2";
289
+ operations: "create"[];
290
+ customOperations: {
291
+ create: ({ request, service }: OperationContext, userSpec: {
292
+ users: RequirementsOf<CreateUserRequest, "firstName" | "lastName" | "roles">[];
293
+ sendInvite?: boolean;
294
+ }) => RequestConfig<typeof userSpec, CreateUserResponse>;
295
+ };
296
+ };
285
297
  };
package/dist/service.js CHANGED
@@ -381,4 +381,17 @@ export const SERVICES = {
381
381
  endpointVersion: 'v1',
382
382
  operations: ['create', 'get', 'list', 'listAll', 'update', 'delete'],
383
383
  },
384
+ userV2: {
385
+ endpoint: 'users',
386
+ endpointVersion: 'v2',
387
+ operations: ['create'],
388
+ customOperations: {
389
+ create: ({ request, service }, userSpec) => ({
390
+ ...request,
391
+ url: `${service.endpointVersion}/${service.endpoint}`,
392
+ method: 'POST',
393
+ data: userSpec,
394
+ }),
395
+ },
396
+ },
384
397
  };
package/dist/utils.d.ts CHANGED
@@ -23,12 +23,14 @@ export interface RequestOptions<T> {
23
23
  dryRun?: boolean;
24
24
  fields?: T extends Object ? (keyof T)[] : never;
25
25
  }
26
- /** Ensures the provided value resolves to `true` before continuing
27
- *
26
+ /** Ensures the provided value resolves to `true` before continuing.
28
27
  * If the value doesn't resolve to `true` then the provided `error` will be thrown
29
28
  *
30
29
  * Intended to be run in production and not removed on build
31
30
  *
31
+ * We don't use the `node:assert` package as this SDK needs to run in environments like
32
+ * web and mobile where the `node:assert` package may not be defined.
33
+ *
32
34
  * @param assertion assertion to verify
33
35
  * @param error error to throw. If `undefined` or a `string` then an {@link AssertionError}
34
36
  * will be thrown instead
package/dist/utils.js CHANGED
@@ -19,12 +19,14 @@ const DEFAULT_RETRY_STRATEGY_OPTIONS = {
19
19
  class AssertionError extends Error {
20
20
  name = AssertionError.prototype.name;
21
21
  }
22
- /** Ensures the provided value resolves to `true` before continuing
23
- *
22
+ /** Ensures the provided value resolves to `true` before continuing.
24
23
  * If the value doesn't resolve to `true` then the provided `error` will be thrown
25
24
  *
26
25
  * Intended to be run in production and not removed on build
27
26
  *
27
+ * We don't use the `node:assert` package as this SDK needs to run in environments like
28
+ * web and mobile where the `node:assert` package may not be defined.
29
+ *
28
30
  * @param assertion assertion to verify
29
31
  * @param error error to throw. If `undefined` or a `string` then an {@link AssertionError}
30
32
  * will be thrown instead
@@ -50,6 +52,7 @@ function toSearchParams(parameters) {
50
52
  }, []);
51
53
  return new URLSearchParams(reducedParams);
52
54
  }
55
+ /** Interceptor for {@link AxiosError}s mapping them into {@link SDKError} */
53
56
  function parseClientError(error) {
54
57
  const axiosErrorLocation = error.response || error.request;
55
58
  const apiErrorMessage = axiosErrorLocation.data?.message ?? axiosErrorLocation.data?.error;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rotacloud",
3
- "version": "2.2.4",
3
+ "version": "2.2.9",
4
4
  "description": "The RotaCloud SDK for the RotaCloud API",
5
5
  "type": "module",
6
6
  "engines": {
package/src/endpoint.ts CHANGED
@@ -27,6 +27,7 @@ import {
27
27
  LogbookCategory,
28
28
  DayNoteV2,
29
29
  DayNoteV2QueryParameters,
30
+ PartialUserV2,
30
31
  } from './interfaces/index.js';
31
32
  import { LogbookEntry, LogbookQueryParameters } from './interfaces/logbook.interface.js';
32
33
  import { Message } from './interfaces/message.interface.js';
@@ -128,5 +129,13 @@ export interface EndpointEntityMap extends Record<EndpointVersion, Record<string
128
129
  'title' | 'message' | 'startDate' | 'endDate' | 'locations' | 'visibleToEmployees'
129
130
  >;
130
131
  'logbook/categories': Endpoint<LogbookCategory, undefined, Pick<LogbookCategory, 'name'>>;
132
+ users: Endpoint<
133
+ User,
134
+ undefined,
135
+ {
136
+ users: RequirementsOf<PartialUserV2, 'firstName' | 'lastName' | 'roles'>[];
137
+ sendInvite?: boolean;
138
+ }
139
+ >;
131
140
  };
132
141
  }
@@ -0,0 +1,109 @@
1
+ export interface Entitlement {
2
+ id: string;
3
+ }
4
+
5
+ export interface Plan {
6
+ id: string;
7
+ status: { name: 'active' | 'in_trial' };
8
+ }
9
+ export interface PlanExtended extends Plan {
10
+ trialEnds: string;
11
+ pricing: PlanPricing;
12
+ }
13
+
14
+ export interface Addon {
15
+ id: string;
16
+ status: { name: 'active' | 'in_trial' | 'hidden' };
17
+ }
18
+ export interface AddonExtended extends Addon {
19
+ trialEnds: string;
20
+ pricing: PlanPricing;
21
+ }
22
+
23
+ export interface PlanPricing {
24
+ itemPriceId: string;
25
+ price: number;
26
+ pricingModel: 'flat_fee' | 'per_unit' | 'tiered' | 'volume' | 'stairstep' | null;
27
+ tiers?: PricingTier;
28
+ currentTier?: PricingTier;
29
+ perUnitPrice?: number;
30
+ }
31
+
32
+ export interface PricingTier {
33
+ startingUnit: number;
34
+ endingUnit: number;
35
+ price: number;
36
+ }
37
+
38
+ export interface AccountSubscription {
39
+ status: 'in_trial' | 'active' | 'non_renewing' | 'paused' | 'cancelled';
40
+ dunningEnds?: string;
41
+ entitlements: Entitlement[];
42
+ plans: Plan[];
43
+ addons: Addon[];
44
+ }
45
+
46
+ export interface AccountSubscriptionExtended extends AccountSubscription {
47
+ billingFrequency: string;
48
+ billingEmail: string;
49
+ country: number;
50
+ subscriptionPaymentSourceId: string;
51
+ nextBillingAt: string;
52
+ trialEnds: string;
53
+ canRestartTrial: boolean;
54
+ totalPrice: number;
55
+ plans: PlanExtended[];
56
+ addons: AddonExtended[];
57
+ }
58
+
59
+ export interface SubscriptionUpdateReq {
60
+ paymentFrequency?: 'monthly' | 'yearly';
61
+ plans?: [{ planId: string; trial?: boolean }];
62
+ addons?: [{ addonId: string; trial?: boolean }];
63
+ subscriptionPaymentSourceId?: string;
64
+ billingEmail?: string;
65
+ country?: number;
66
+ }
67
+
68
+ export interface EstimatesRes {
69
+ owed: number;
70
+ totalPrice: number;
71
+ }
72
+
73
+ export interface ProductCatalogueItemPrice {
74
+ id: string;
75
+ currency: string;
76
+ pricingModel: 'stairstep' | 'perUnit';
77
+ unitPrice?: number;
78
+ stairStep?: { startingUnit: number; endingUnit?: number; tierPrice: number };
79
+ billingPeriod: number;
80
+ billingPeriodUnit: 'monthly' | 'yearly';
81
+ }
82
+ export interface ProductCatalogueItem {
83
+ id: string;
84
+ name: string;
85
+ itemPrice: ProductCatalogueItemPrice[];
86
+ }
87
+
88
+ export interface ProductCatalogueRes {
89
+ plans: ProductCatalogueItem[];
90
+ addons: ProductCatalogueItem[];
91
+ }
92
+
93
+ export enum CancellationReason {
94
+ 'features',
95
+ 'value',
96
+ 'price',
97
+ 'not_using',
98
+ 'incompatible',
99
+ 'difficulty',
100
+ 'using_competitor',
101
+ 'sold',
102
+ 'temporary_closure',
103
+ 'closed',
104
+ }
105
+ export interface CancelSubscriptionReq {
106
+ reason: CancellationReason;
107
+ message?: string;
108
+ detail?: string;
109
+ }
@@ -34,6 +34,7 @@ export * from './time-zone.interface.js';
34
34
  export * from './toil-accrual.interface.js';
35
35
  export * from './toil-allowance.interface.js';
36
36
  export * from './user.interface.js';
37
+ export * from './user-v2.interface.js';
37
38
  export * from './users-clocked-in.interface.js';
38
39
  export * from './users-clocked-out.interface.js';
39
40
  export * from './document.interface.js';
@@ -2,7 +2,7 @@ import { TerminalLocation } from './terminal.interface.js';
2
2
 
3
3
  export interface UserClockIn {
4
4
  method: string;
5
- shift: number;
5
+ shift: number | null;
6
6
  terminal?: number;
7
7
  user: number;
8
8
  photo?: string;
@@ -0,0 +1,30 @@
1
+ import { ManagerPermission } from './user.interface.js';
2
+
3
+ export interface CreateUserRequest extends PartialUserV2 {
4
+ permissions?: ManagerPermission[];
5
+ coverLocations?: number[];
6
+ managedLocations?: number[];
7
+ }
8
+
9
+ export interface CreateUserResponse extends PartialUserV2 {
10
+ id: number;
11
+ }
12
+
13
+ export interface PartialUserV2 {
14
+ firstName: string;
15
+ lastName: string;
16
+ locations: number[];
17
+ roles: UserRole[];
18
+ email: string | null;
19
+ level: 'admin' | 'employee' | 'manager';
20
+ salary: number;
21
+ salaryType: 'annual' | 'hourly';
22
+ }
23
+
24
+ export interface UserRole {
25
+ id: number;
26
+ perShift?: number;
27
+ perHour?: number;
28
+ payCode?: string;
29
+ isDefault?: boolean;
30
+ }
@@ -1,4 +1,4 @@
1
- import { Shift, TerminalLocation, UserBreak } from '../interfaces/index.js';
1
+ import { TerminalLocation, UserBreak } from '../interfaces/index.js';
2
2
 
3
3
  export interface UserClockedIn {
4
4
  user: number;
@@ -6,7 +6,7 @@ export interface UserClockedIn {
6
6
  role: number;
7
7
  in_time: number;
8
8
  minutes_late: number;
9
- shift: Pick<Shift, 'id' | 'start_time' | 'end_time' | 'minutes_break' | 'location' | 'role'>;
9
+ shift: number | null;
10
10
  in_method: string;
11
11
  in_location: TerminalLocation;
12
12
  in_device: string | null;
package/src/ops.ts CHANGED
@@ -37,7 +37,7 @@ export type RequestConfig<RequestData, ResponseData> = AxiosRequestConfig<Reques
37
37
  *
38
38
  * This is intended as a convenient way of defining {@see OpFunction}s that can then be
39
39
  * "built" ready for the end user to call ({@see buildOp})
40
- * */
40
+ */
41
41
  export type OpDef<T, Param = any, Opts extends Partial<RequestOptions<any>> = RequestOptions<unknown>, Return = T> =
42
42
  | ((ctx: OperationContext, param: Param, opts?: Opts) => RequestConfig<T, Return>)
43
43
  | ((ctx: OperationContext, param: Param, opts?: Opts) => AsyncIterable<T>)
@@ -45,107 +45,130 @@ export type OpDef<T, Param = any, Opts extends Partial<RequestOptions<any>> = Re
45
45
 
46
46
  /** Operation function type to be called by the end user of the SDK.
47
47
  *
48
- * All methods on services follow this typing
49
- * */
50
- export type OpFunction<R = any, Param = undefined, Opts = RequestOptions<unknown>> = R extends
48
+ * All methods on services follow this typing.
49
+ */
50
+ export type OpFunction<Return = any, Param = undefined, Opts = RequestOptions<unknown>> = Return extends
51
51
  | AsyncIterable<infer U>
52
52
  | Promise<Iterable<infer U>>
53
53
  ? // List based op parameter names
54
54
  Param extends undefined
55
55
  ? {
56
- (query?: Param): R;
56
+ (query?: Param): Return;
57
57
  <F extends keyof U>(
58
58
  query: Param,
59
59
  options: { fields: F[] } & RequestOptions<U>,
60
- ): R extends AsyncIterable<U>
60
+ ): Return extends AsyncIterable<U>
61
61
  ? Promise<AsyncIterable<Pick<U, F>>>
62
- : R extends Promise<Array<U>>
62
+ : Return extends Promise<Array<U>>
63
63
  ? Promise<Array<Pick<U, F>>>
64
64
  : Promise<Iterable<Pick<U, F>>>;
65
- (query: Param, options?: Opts): R;
65
+ (query: Param, options?: Opts): Return;
66
66
  }
67
67
  : Partial<Param> extends Param
68
68
  ? {
69
- (query?: Param): R;
69
+ (query?: Param): Return;
70
70
  <F extends keyof U>(
71
71
  query: Param,
72
72
  options: { fields: F[] } & RequestOptions<U>,
73
- ): R extends AsyncIterable<U>
73
+ ): Return extends AsyncIterable<U>
74
74
  ? Promise<AsyncIterable<Pick<U, F>>>
75
- : R extends Promise<Array<U>>
75
+ : Return extends Promise<Array<U>>
76
76
  ? Promise<Array<Pick<U, F>>>
77
77
  : Promise<Iterable<Pick<U, F>>>;
78
- (query?: Param, options?: Opts): R;
78
+ (query?: Param, options?: Opts): Return;
79
79
  }
80
80
  : {
81
- (query: Param): R;
81
+ (query: Param): Return;
82
82
  <F extends keyof U>(
83
83
  query: Param,
84
84
  options: { fields: F[] } & RequestOptions<U>,
85
- ): R extends AsyncIterable<U>
85
+ ): Return extends AsyncIterable<U>
86
86
  ? Promise<AsyncIterable<Pick<U, F>>>
87
- : R extends Promise<Array<U>>
87
+ : Return extends Promise<Array<U>>
88
88
  ? Promise<Array<Pick<U, F>>>
89
89
  : Promise<Iterable<Pick<U, F>>>;
90
- (query: Param, Options?: Opts): R;
90
+ (query: Param, Options?: Opts): Return;
91
91
  }
92
- : Param extends number
92
+ : // ID based op parameters
93
+ Param extends number
93
94
  ? {
94
- (id: Param): R;
95
- <F extends keyof Awaited<R>>(
95
+ (id: Param): Return;
96
+ <F extends keyof Awaited<Return>>(
96
97
  id: Param,
97
- options: { fields: F[]; rawResponse: true } & RequestOptions<Awaited<R>>,
98
- ): Promise<AxiosResponse<Pick<Awaited<R>, F>>>;
99
- <F extends keyof Awaited<R>>(
98
+ options: { fields: F[]; rawResponse: true } & RequestOptions<Awaited<Return>>,
99
+ ): Promise<AxiosResponse<Pick<Awaited<Return>, F>>>;
100
+ <F extends keyof Awaited<Return>>(
100
101
  id: Param,
101
- options: { fields: F[] } & RequestOptions<Awaited<R>>,
102
- ): Promise<Pick<Awaited<R>, F>>;
102
+ options: { fields: F[] } & RequestOptions<Awaited<Return>>,
103
+ ): Promise<Pick<Awaited<Return>, F>>;
103
104
  (
104
105
  id: Param,
105
106
  options?: {
106
107
  rawResponse: true;
107
- } & RequestOptions<Awaited<R>>,
108
- ): Promise<AxiosResponse<Awaited<R>>>;
109
- (id: Param, options?: RequestOptions<Awaited<R>>): R;
108
+ } & RequestOptions<Awaited<Return>>,
109
+ ): Promise<AxiosResponse<Awaited<Return>>>;
110
+ (id: Param, options?: RequestOptions<Awaited<Return>>): Return;
110
111
  }
111
- : Param extends undefined
112
+ : Param extends number[]
112
113
  ? {
113
- (entity?: Param): R;
114
- <F extends keyof Awaited<R>>(
115
- entity: Param,
116
- options: { fields: F[]; rawResponse: true } & RequestOptions<Awaited<R>>,
117
- ): Promise<AxiosResponse<Pick<Awaited<R>, F>>>;
118
- <F extends keyof Awaited<R>>(
119
- entity: Param,
120
- options: { fields: F[] } & RequestOptions<Awaited<R>>,
121
- ): Promise<Pick<Awaited<R>, F>>;
114
+ (ids: Param): Return;
115
+ <F extends keyof Awaited<Return>>(
116
+ ids: Param,
117
+ options: { fields: F[]; rawResponse: true } & RequestOptions<Awaited<Return>>,
118
+ ): Promise<AxiosResponse<Pick<Awaited<Return>, F>>>;
119
+ <F extends keyof Awaited<Return>>(
120
+ ids: Param,
121
+ options: { fields: F[] } & RequestOptions<Awaited<Return>>,
122
+ ): Promise<Pick<Awaited<Return>, F>>;
122
123
  (
123
- entity?: Param,
124
+ ids: Param,
124
125
  options?: {
125
126
  rawResponse: true;
126
- } & RequestOptions<Awaited<R>>,
127
- ): Promise<AxiosResponse<Awaited<R>>>;
128
- (entity?: Param, options?: RequestOptions<Awaited<R>>): R;
127
+ } & RequestOptions<Awaited<Return>>,
128
+ ): Promise<AxiosResponse<Awaited<Return>>>;
129
+ (ids: Param, options?: RequestOptions<Awaited<Return>>): Return;
129
130
  }
130
- : // Entity based op parameter names
131
- {
132
- (entity: Param): R;
133
- <F extends keyof Awaited<R>>(
134
- entity: Param,
135
- options: { fields: F[]; rawResponse: true } & RequestOptions<Awaited<R>>,
136
- ): Promise<AxiosResponse<Pick<Awaited<R>, F>>>;
137
- <F extends keyof Awaited<R>>(
138
- entity: Param,
139
- options: { fields: F[] } & RequestOptions<Awaited<R>>,
140
- ): Promise<Pick<Awaited<R>, F>>;
141
- (
142
- entity: Param,
143
- options?: {
144
- rawResponse: true;
145
- } & RequestOptions<Awaited<R>>,
146
- ): Promise<AxiosResponse<Awaited<R>>>;
147
- (entity: Param, options?: RequestOptions<Awaited<R>>): R;
148
- };
131
+ : // Undefined based op parameters - ensures ops that don't require a
132
+ // param aren't forced to specify `undefined` but can if they need to specify
133
+ // options
134
+ Param extends undefined
135
+ ? {
136
+ (): Return;
137
+ <F extends keyof Awaited<Return>>(
138
+ none: Param,
139
+ options: { fields: F[]; rawResponse: true } & RequestOptions<Awaited<Return>>,
140
+ ): Promise<AxiosResponse<Pick<Awaited<Return>, F>>>;
141
+ <F extends keyof Awaited<Return>>(
142
+ none: Param,
143
+ options: { fields: F[] } & RequestOptions<Awaited<Return>>,
144
+ ): Promise<Pick<Awaited<Return>, F>>;
145
+ (
146
+ none?: Param,
147
+ options?: {
148
+ rawResponse: true;
149
+ } & RequestOptions<Awaited<Return>>,
150
+ ): Promise<AxiosResponse<Awaited<Return>>>;
151
+ (none?: Param, options?: RequestOptions<Awaited<Return>>): Return;
152
+ }
153
+ : // Entity based op parameter names
154
+ {
155
+ (entity: Param): Return;
156
+ <F extends keyof Awaited<Return>>(
157
+ entity: Param,
158
+ options: { fields: F[]; rawResponse: true } & RequestOptions<Awaited<Return>>,
159
+ ): Promise<AxiosResponse<Pick<Awaited<Return>, F>>>;
160
+ <F extends keyof Awaited<Return>>(
161
+ entity: Param,
162
+ options: { fields: F[] } & RequestOptions<Awaited<Return>>,
163
+ ): Promise<Pick<Awaited<Return>, F>>;
164
+ (
165
+ entity: Param,
166
+ options?: {
167
+ rawResponse: true;
168
+ } & RequestOptions<Awaited<Return>>,
169
+ ): Promise<AxiosResponse<Awaited<Return>>>;
170
+ (entity: Param, options?: RequestOptions<Awaited<Return>>): Return;
171
+ };
149
172
 
150
173
  /** Wrapper type for expressing the response returned by a v2 list op supported endpoint */
151
174
  interface PagedResponse<T> {
package/src/service.ts CHANGED
@@ -9,6 +9,7 @@ import {
9
9
  ShiftHistoryRecord,
10
10
  Terminal,
11
11
  ToilAllowance,
12
+ User,
12
13
  UserBreak,
13
14
  UserClockedIn,
14
15
  UserClockedOut,
@@ -34,6 +35,7 @@ import { ToilAllowanceQueryParams } from './interfaces/query-params/index.js';
34
35
  import { LogbookEntry, LogbookQueryParameters } from './interfaces/logbook.interface.js';
35
36
  import { Message } from './interfaces/message.interface.js';
36
37
  import { Invoice, InvoiceDownload } from './interfaces/invoice.interface.js';
38
+ import { CreateUserRequest, CreateUserResponse, PartialUserV2 } from './interfaces/user-v2.interface.js';
37
39
 
38
40
  export type ServiceSpecification<CustomOp extends OpDef<unknown> = OpDef<any>> = {
39
41
  /** Operations allowed and usable for the endpoint */
@@ -495,4 +497,23 @@ export const SERVICES = {
495
497
  endpointVersion: 'v1',
496
498
  operations: ['create', 'get', 'list', 'listAll', 'update', 'delete'],
497
499
  },
500
+ userV2: {
501
+ endpoint: 'users',
502
+ endpointVersion: 'v2',
503
+ operations: ['create'],
504
+ customOperations: {
505
+ create: (
506
+ { request, service },
507
+ userSpec: {
508
+ users: RequirementsOf<CreateUserRequest, 'firstName' | 'lastName' | 'roles'>[];
509
+ sendInvite?: boolean;
510
+ },
511
+ ): RequestConfig<typeof userSpec, CreateUserResponse> => ({
512
+ ...request,
513
+ url: `${service.endpointVersion}/${service.endpoint}`,
514
+ method: 'POST',
515
+ data: userSpec,
516
+ }),
517
+ },
518
+ },
498
519
  } satisfies Record<string, ServiceSpecification>;
package/src/utils.ts CHANGED
@@ -50,12 +50,14 @@ class AssertionError extends Error {
50
50
  override name = AssertionError.prototype.name;
51
51
  }
52
52
 
53
- /** Ensures the provided value resolves to `true` before continuing
54
- *
53
+ /** Ensures the provided value resolves to `true` before continuing.
55
54
  * If the value doesn't resolve to `true` then the provided `error` will be thrown
56
55
  *
57
56
  * Intended to be run in production and not removed on build
58
57
  *
58
+ * We don't use the `node:assert` package as this SDK needs to run in environments like
59
+ * web and mobile where the `node:assert` package may not be defined.
60
+ *
59
61
  * @param assertion assertion to verify
60
62
  * @param error error to throw. If `undefined` or a `string` then an {@link AssertionError}
61
63
  * will be thrown instead
@@ -81,6 +83,7 @@ function toSearchParams(parameters?: Record<string, QueryParameterValue>): URLSe
81
83
  return new URLSearchParams(reducedParams);
82
84
  }
83
85
 
86
+ /** Interceptor for {@link AxiosError}s mapping them into {@link SDKError} */
84
87
  function parseClientError(error: AxiosError): SDKError {
85
88
  const axiosErrorLocation = error.response || error.request;
86
89
  const apiErrorMessage = axiosErrorLocation.data?.message ?? axiosErrorLocation.data?.error;