recal-sdk 0.3.3 → 1.0.1
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/README.md +84 -803
- package/dist/client/client/client.gen.d.ts +3 -0
- package/dist/client/client/client.gen.d.ts.map +1 -0
- package/dist/client/client/client.gen.js +205 -0
- package/dist/client/client/client.gen.js.map +1 -0
- package/dist/client/client/index.d.ts +9 -0
- package/dist/client/client/index.d.ts.map +1 -0
- package/dist/client/client/index.js +7 -0
- package/dist/client/client/index.js.map +1 -0
- package/dist/client/client/types.gen.d.ts +125 -0
- package/dist/client/client/types.gen.d.ts.map +1 -0
- package/dist/client/client/types.gen.js +3 -0
- package/dist/client/client/types.gen.js.map +1 -0
- package/dist/client/client/utils.gen.d.ts +34 -0
- package/dist/client/client/utils.gen.d.ts.map +1 -0
- package/dist/client/client/utils.gen.js +232 -0
- package/dist/client/client/utils.gen.js.map +1 -0
- package/dist/client/client.gen.d.ts +13 -0
- package/dist/client/client.gen.d.ts.map +1 -0
- package/dist/client/client.gen.js +6 -0
- package/dist/client/client.gen.js.map +1 -0
- package/dist/client/core/auth.gen.d.ts +19 -0
- package/dist/client/core/auth.gen.d.ts.map +1 -0
- package/dist/client/core/auth.gen.js +15 -0
- package/dist/client/core/auth.gen.js.map +1 -0
- package/dist/client/core/bodySerializer.gen.d.ts +26 -0
- package/dist/client/core/bodySerializer.gen.d.ts.map +1 -0
- package/dist/client/core/bodySerializer.gen.js +58 -0
- package/dist/client/core/bodySerializer.gen.js.map +1 -0
- package/dist/client/core/params.gen.d.ts +34 -0
- package/dist/client/core/params.gen.d.ts.map +1 -0
- package/dist/client/core/params.gen.js +89 -0
- package/dist/client/core/params.gen.js.map +1 -0
- package/dist/client/core/pathSerializer.gen.d.ts +34 -0
- package/dist/client/core/pathSerializer.gen.d.ts.map +1 -0
- package/dist/client/core/pathSerializer.gen.js +115 -0
- package/dist/client/core/pathSerializer.gen.js.map +1 -0
- package/dist/client/core/queryKeySerializer.gen.d.ts +19 -0
- package/dist/client/core/queryKeySerializer.gen.d.ts.map +1 -0
- package/dist/client/core/queryKeySerializer.gen.js +100 -0
- package/dist/client/core/queryKeySerializer.gen.js.map +1 -0
- package/dist/client/core/serverSentEvents.gen.d.ts +72 -0
- package/dist/client/core/serverSentEvents.gen.d.ts.map +1 -0
- package/dist/client/core/serverSentEvents.gen.js +136 -0
- package/dist/client/core/serverSentEvents.gen.js.map +1 -0
- package/dist/client/core/types.gen.d.ts +79 -0
- package/dist/client/core/types.gen.d.ts.map +1 -0
- package/dist/client/core/types.gen.js +3 -0
- package/dist/client/core/types.gen.js.map +1 -0
- package/dist/client/core/utils.gen.d.ts +20 -0
- package/dist/client/core/utils.gen.d.ts.map +1 -0
- package/dist/client/core/utils.gen.js +88 -0
- package/dist/client/core/utils.gen.js.map +1 -0
- package/dist/client/index.d.ts +3 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +3 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/sdk.gen.d.ts +170 -0
- package/dist/client/sdk.gen.d.ts.map +1 -0
- package/dist/client/sdk.gen.js +884 -0
- package/dist/client/sdk.gen.js.map +1 -0
- package/dist/client/transformers.gen.d.ts +31 -0
- package/dist/client/transformers.gen.d.ts.map +1 -0
- package/dist/client/transformers.gen.js +263 -0
- package/dist/client/transformers.gen.js.map +1 -0
- package/dist/client/types.gen.d.ts +1765 -0
- package/dist/client/types.gen.d.ts.map +1 -0
- package/dist/client/types.gen.js +3 -0
- package/dist/client/types.gen.js.map +1 -0
- package/dist/client/zod.gen.d.ts +1457 -0
- package/dist/client/zod.gen.d.ts.map +1 -0
- package/dist/client/zod.gen.js +1218 -0
- package/dist/client/zod.gen.js.map +1 -0
- package/dist/index.d.ts +5 -33
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -55
- package/dist/index.js.map +1 -1
- package/dist/recal.d.ts +64 -0
- package/dist/recal.d.ts.map +1 -0
- package/dist/recal.js +81 -0
- package/dist/recal.js.map +1 -0
- package/dist/services/calendar.service.d.ts +55 -167
- package/dist/services/calendar.service.d.ts.map +1 -1
- package/dist/services/calendar.service.js +68 -301
- package/dist/services/calendar.service.js.map +1 -1
- package/dist/services/events.service.d.ts +167 -0
- package/dist/services/events.service.d.ts.map +1 -0
- package/dist/services/events.service.js +215 -0
- package/dist/services/events.service.js.map +1 -0
- package/dist/services/oauth.service.d.ts +120 -66
- package/dist/services/oauth.service.d.ts.map +1 -1
- package/dist/services/oauth.service.js +162 -151
- package/dist/services/oauth.service.js.map +1 -1
- package/dist/services/organizations.service.d.ts +137 -36
- package/dist/services/organizations.service.d.ts.map +1 -1
- package/dist/services/organizations.service.js +164 -90
- package/dist/services/organizations.service.js.map +1 -1
- package/dist/services/scheduling.service.d.ts +121 -58
- package/dist/services/scheduling.service.d.ts.map +1 -1
- package/dist/services/scheduling.service.js +98 -140
- package/dist/services/scheduling.service.js.map +1 -1
- package/dist/services/users.service.d.ts +87 -27
- package/dist/services/users.service.d.ts.map +1 -1
- package/dist/services/users.service.js +94 -69
- package/dist/services/users.service.js.map +1 -1
- package/dist/types.d.ts +68 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/response.d.ts +33 -0
- package/dist/utils/response.d.ts.map +1 -0
- package/dist/utils/response.js +54 -0
- package/dist/utils/response.js.map +1 -0
- package/package.json +5 -2
- package/dist/entities/organization.d.ts +0 -21
- package/dist/entities/organization.d.ts.map +0 -1
- package/dist/entities/organization.js +0 -32
- package/dist/entities/organization.js.map +0 -1
- package/dist/entities/user.d.ts +0 -23
- package/dist/entities/user.d.ts.map +0 -1
- package/dist/entities/user.js +0 -45
- package/dist/entities/user.js.map +0 -1
- package/dist/errors.d.ts +0 -22
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js +0 -36
- package/dist/errors.js.map +0 -1
- package/dist/typebox/calendar.tb.d.ts +0 -113
- package/dist/typebox/calendar.tb.d.ts.map +0 -1
- package/dist/typebox/calendar.tb.js +0 -92
- package/dist/typebox/calendar.tb.js.map +0 -1
- package/dist/typebox/oauth.tb.d.ts +0 -14
- package/dist/typebox/oauth.tb.d.ts.map +0 -1
- package/dist/typebox/oauth.tb.js +0 -16
- package/dist/typebox/oauth.tb.js.map +0 -1
- package/dist/typebox/organization.stripped.tb.d.ts +0 -6
- package/dist/typebox/organization.stripped.tb.d.ts.map +0 -1
- package/dist/typebox/organization.stripped.tb.js +0 -7
- package/dist/typebox/organization.stripped.tb.js.map +0 -1
- package/dist/typebox/organization.tb.d.ts +0 -10
- package/dist/typebox/organization.tb.d.ts.map +0 -1
- package/dist/typebox/organization.tb.js +0 -8
- package/dist/typebox/organization.tb.js.map +0 -1
- package/dist/typebox/scheduling.tb.d.ts +0 -107
- package/dist/typebox/scheduling.tb.d.ts.map +0 -1
- package/dist/typebox/scheduling.tb.js +0 -105
- package/dist/typebox/scheduling.tb.js.map +0 -1
- package/dist/typebox/timeString.tb.d.ts +0 -3
- package/dist/typebox/timeString.tb.d.ts.map +0 -1
- package/dist/typebox/timeString.tb.js +0 -7
- package/dist/typebox/timeString.tb.js.map +0 -1
- package/dist/typebox/user.stripped.tb.d.ts +0 -5
- package/dist/typebox/user.stripped.tb.d.ts.map +0 -1
- package/dist/typebox/user.stripped.tb.js +0 -6
- package/dist/typebox/user.stripped.tb.js.map +0 -1
- package/dist/typebox/user.tb.d.ts +0 -19
- package/dist/typebox/user.tb.d.ts.map +0 -1
- package/dist/typebox/user.tb.js +0 -10
- package/dist/typebox/user.tb.js.map +0 -1
- package/dist/types/calendar.types.d.ts +0 -124
- package/dist/types/calendar.types.d.ts.map +0 -1
- package/dist/types/calendar.types.js +0 -27
- package/dist/types/calendar.types.js.map +0 -1
- package/dist/types/internal.types.d.ts +0 -7
- package/dist/types/internal.types.d.ts.map +0 -1
- package/dist/types/internal.types.js +0 -2
- package/dist/types/internal.types.js.map +0 -1
- package/dist/types/oauth.types.d.ts +0 -22
- package/dist/types/oauth.types.d.ts.map +0 -1
- package/dist/types/oauth.types.js +0 -2
- package/dist/types/oauth.types.js.map +0 -1
- package/dist/types/organization.types.d.ts +0 -18
- package/dist/types/organization.types.d.ts.map +0 -1
- package/dist/types/organization.types.js +0 -2
- package/dist/types/organization.types.js.map +0 -1
- package/dist/types/scheduling.types.d.ts +0 -50
- package/dist/types/scheduling.types.d.ts.map +0 -1
- package/dist/types/scheduling.types.js +0 -2
- package/dist/types/scheduling.types.js.map +0 -1
- package/dist/types/user.types.d.ts +0 -11
- package/dist/types/user.types.d.ts.map +0 -1
- package/dist/types/user.types.js +0 -2
- package/dist/types/user.types.js.map +0 -1
- package/dist/utils/fetch.helper.d.ts +0 -62
- package/dist/utils/fetch.helper.d.ts.map +0 -1
- package/dist/utils/fetch.helper.js +0 -116
- package/dist/utils/fetch.helper.js.map +0 -1
- package/dist/utils/fetchErrorHandler.d.ts +0 -22
- package/dist/utils/fetchErrorHandler.d.ts.map +0 -1
- package/dist/utils/fetchErrorHandler.js +0 -35
- package/dist/utils/fetchErrorHandler.js.map +0 -1
- package/dist/utils/includes.helper.d.ts +0 -4
- package/dist/utils/includes.helper.d.ts.map +0 -1
- package/dist/utils/includes.helper.js +0 -10
- package/dist/utils/includes.helper.js.map +0 -1
- package/dist/utils/omit.d.ts +0 -2
- package/dist/utils/omit.d.ts.map +0 -1
- package/dist/utils/omit.js +0 -4
- package/dist/utils/omit.js.map +0 -1
package/README.md
CHANGED
|
@@ -3,15 +3,17 @@
|
|
|
3
3
|
A powerful, type-safe SDK for interacting with the Recal calendar API. Build sophisticated calendar integrations with support for Google and Microsoft calendar providers.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/recal-sdk)
|
|
6
|
+
[](https://github.com/recal-dev/recal-sdk-js/actions/workflows/tests.yml)
|
|
6
7
|
[](https://opensource.org/licenses/MIT)
|
|
7
8
|
[](https://www.typescriptlang.org/)
|
|
8
9
|
|
|
9
10
|
## Features
|
|
10
11
|
|
|
11
12
|
- **Multi-Provider Support**: Seamlessly work with Google Calendar and Microsoft Outlook
|
|
12
|
-
- **Type Safety**: Full TypeScript support with runtime validation
|
|
13
|
+
- **Type Safety**: Full TypeScript support with Zod runtime validation
|
|
14
|
+
- **Auto-Generated Client**: Powered by HeyAPI OpenAPI code generation
|
|
13
15
|
- **Rich Calendar Operations**: Events, busy queries, scheduling, and more
|
|
14
|
-
- **Organization Management**: Handle organizations and
|
|
16
|
+
- **Organization Management**: Handle organizations and user calendars
|
|
15
17
|
- **OAuth Integration**: Built-in OAuth flow support for calendar connections
|
|
16
18
|
- **Error Handling**: Comprehensive error types for robust applications
|
|
17
19
|
- **Modern Architecture**: Clean, testable service-based design
|
|
@@ -53,790 +55,36 @@ const recal = new RecalClient({
|
|
|
53
55
|
|
|
54
56
|
The SDK is organized into logical service modules:
|
|
55
57
|
|
|
56
|
-
- **`calendar`** -
|
|
58
|
+
- **`calendar`** - Calendar management and busy queries
|
|
59
|
+
- **`events`** - Event creation, updates, and queries
|
|
57
60
|
- **`scheduling`** - Availability and booking management
|
|
58
61
|
- **`users`** - User profile and settings
|
|
59
62
|
- **`organizations`** - Team and organization management
|
|
60
63
|
- **`oauth`** - Calendar provider authentication
|
|
61
64
|
|
|
62
|
-
|
|
65
|
+
## Documentation
|
|
63
66
|
|
|
64
|
-
|
|
67
|
+
- **API Reference**: [docs.recal.dev](https://docs.recal.dev) - Comprehensive guides and API schemas
|
|
68
|
+
- **Usage Examples**: [USAGE.md](./USAGE.md) - Detailed code examples for all services
|
|
69
|
+
- **OpenAPI Spec**: [api.recal.dev/v1/swagger](https://api.recal.dev/v1/swagger) - Interactive API documentation
|
|
65
70
|
|
|
66
|
-
##
|
|
71
|
+
## Configuration
|
|
67
72
|
|
|
68
|
-
|
|
69
|
-
>
|
|
70
|
-
> - Use the exported `Provider` enum for provider arguments.
|
|
71
|
-
> - Date/time fields in responses are parsed into `Date` objects at runtime.
|
|
72
|
-
>
|
|
73
|
-
> ```typescript
|
|
74
|
-
> import { Provider } from 'recal-sdk'
|
|
75
|
-
> ```
|
|
73
|
+
### Environment Variables
|
|
76
74
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
#### List Calendars
|
|
80
|
-
|
|
81
|
-
```typescript
|
|
82
|
-
// Get all calendars for a user
|
|
83
|
-
const calendars = await recal.calendar.listCalendars('user_id')
|
|
84
|
-
|
|
85
|
-
// Or filter by provider
|
|
86
|
-
const googleCalendars = await recal.calendar.listCalendars(
|
|
87
|
-
'user_id',
|
|
88
|
-
'google' // optional: filter by provider
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
// Or filter by multiple providers
|
|
92
|
-
const multiProviderCalendars = await recal.calendar.listCalendars(
|
|
93
|
-
'user_id',
|
|
94
|
-
['google', 'microsoft'] // optional: filter by multiple providers
|
|
95
|
-
)
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
#### Get Busy Information
|
|
99
|
-
|
|
100
|
-
```typescript
|
|
101
|
-
// Get user's availability (simplest form)
|
|
102
|
-
const busy = await recal.calendar.getBusy(
|
|
103
|
-
'user_id',
|
|
104
|
-
new Date('2024-01-01'),
|
|
105
|
-
new Date('2024-01-07')
|
|
106
|
-
)
|
|
107
|
-
|
|
108
|
-
// Or with optional filters
|
|
109
|
-
const busyFiltered = await recal.calendar.getBusy(
|
|
110
|
-
'user_id',
|
|
111
|
-
new Date('2024-01-01'),
|
|
112
|
-
new Date('2024-01-07'),
|
|
113
|
-
{
|
|
114
|
-
provider: 'google', // optional: filter by provider
|
|
115
|
-
calendarIds: ["calendar_id"] // optional: filter by calendarIds
|
|
116
|
-
timeZone: 'America/New_York', // optional: timezone
|
|
117
|
-
}
|
|
118
|
-
)
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
#### List Events
|
|
122
|
-
|
|
123
|
-
```typescript
|
|
124
|
-
// Get all events in a date range (simplest form)
|
|
125
|
-
const events = await recal.calendar.getEvents(
|
|
126
|
-
'user_id',
|
|
127
|
-
new Date('2024-01-01'),
|
|
128
|
-
new Date('2024-01-31')
|
|
129
|
-
)
|
|
130
|
-
|
|
131
|
-
// Or with optional filters
|
|
132
|
-
const eventsFiltered = await recal.calendar.getEvents(
|
|
133
|
-
'user_id',
|
|
134
|
-
new Date('2024-01-01'),
|
|
135
|
-
new Date('2024-01-31'),
|
|
136
|
-
{
|
|
137
|
-
provider: 'google', // optional: filter by provider
|
|
138
|
-
calendarIds: ["calendar_id"] // optional: filter by calendarIds
|
|
139
|
-
timeZone: 'Europe/London' // optional: timezone
|
|
140
|
-
}
|
|
141
|
-
)
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
#### Create Event
|
|
145
|
-
|
|
146
|
-
```typescript
|
|
147
|
-
// Create a new event (without optional timezone)
|
|
148
|
-
const event = await recal.calendar.createEvent({
|
|
149
|
-
userId: 'user_id',
|
|
150
|
-
provider: 'google',
|
|
151
|
-
calendarId: 'calendar_id',
|
|
152
|
-
event: {
|
|
153
|
-
subject: 'Team Meeting',
|
|
154
|
-
description: 'Weekly sync',
|
|
155
|
-
start: new Date('2024-01-15T10:00:00Z'),
|
|
156
|
-
end: new Date('2024-01-15T11:00:00Z'),
|
|
157
|
-
attendees: [
|
|
158
|
-
{ email: 'colleague@company.com' }
|
|
159
|
-
]
|
|
160
|
-
}
|
|
161
|
-
})
|
|
162
|
-
|
|
163
|
-
// Or with timezone option
|
|
164
|
-
const eventWithTZ = await recal.calendar.createEvent({
|
|
165
|
-
userId: 'user_id',
|
|
166
|
-
provider: 'google',
|
|
167
|
-
calendarId: 'calendar_id',
|
|
168
|
-
event: {
|
|
169
|
-
subject: 'Team Meeting',
|
|
170
|
-
description: 'Weekly sync',
|
|
171
|
-
start: new Date('2024-01-15T10:00:00Z'),
|
|
172
|
-
end: new Date('2024-01-15T11:00:00Z'),
|
|
173
|
-
attendees: [
|
|
174
|
-
{ email: 'colleague@company.com' }
|
|
175
|
-
]
|
|
176
|
-
},
|
|
177
|
-
options: { timeZone: 'Europe/Berlin' } // optional
|
|
178
|
-
})
|
|
179
|
-
```
|
|
180
|
-
#### Get Event
|
|
181
|
-
|
|
182
|
-
```typescript
|
|
183
|
-
// Get an existing event
|
|
184
|
-
const event = await recal.calendar.getEvent({
|
|
185
|
-
userId: 'user_id',
|
|
186
|
-
provider: 'google',
|
|
187
|
-
calendarId: 'calendar_id',
|
|
188
|
-
eventId: 'event_id',
|
|
189
|
-
options: { timeZone: 'Europe/Berlin' } // optional
|
|
190
|
-
})
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
#### Update Event
|
|
194
|
-
|
|
195
|
-
```typescript
|
|
196
|
-
// Update an existing event (simplest form)
|
|
197
|
-
const updated = await recal.calendar.updateEvent({
|
|
198
|
-
userId: 'user_id',
|
|
199
|
-
provider: 'google',
|
|
200
|
-
calendarId: 'calendar_id',
|
|
201
|
-
eventId: 'event_id',
|
|
202
|
-
event: {
|
|
203
|
-
subject: 'Updated Meeting Title',
|
|
204
|
-
start: new Date('2024-01-15T14:00:00Z'),
|
|
205
|
-
end: new Date('2024-01-15T15:00:00Z')
|
|
206
|
-
}
|
|
207
|
-
})
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
```typescript
|
|
211
|
-
// or with more options
|
|
212
|
-
const updated = await recal.calendar.updateEvent({
|
|
213
|
-
userId: 'user_id',
|
|
214
|
-
provider: 'google',
|
|
215
|
-
calendarId: 'calendar_id',
|
|
216
|
-
eventId: 'event_id',
|
|
217
|
-
event: {
|
|
218
|
-
subject: 'Updated Meeting title',
|
|
219
|
-
description: 'Updated description',
|
|
220
|
-
start: new Date('2024-01-15T11:00:00Z'),
|
|
221
|
-
end: new Date('2024-01-15T12:00:00Z'),
|
|
222
|
-
attendees: [
|
|
223
|
-
{ email: 'colleague@company.com' }
|
|
224
|
-
]
|
|
225
|
-
},
|
|
226
|
-
options: { timeZone: 'Europe/Berlin' } // optional
|
|
227
|
-
})
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
#### Delete Event
|
|
231
|
-
|
|
232
|
-
```typescript
|
|
233
|
-
// Delete an event
|
|
234
|
-
await recal.calendar.deleteEvent({
|
|
235
|
-
userId: 'user_id',
|
|
236
|
-
provider: 'google',
|
|
237
|
-
calendarId: 'calendar_id',
|
|
238
|
-
eventId: 'event_id'
|
|
239
|
-
})
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
#### Cross-Calendar Operations (Meta Events)
|
|
243
|
-
|
|
244
|
-
Meta events allow you to work with events across multiple calendar providers:
|
|
245
|
-
|
|
246
|
-
```typescript
|
|
247
|
-
// Create event across all connected calendars (default behavior)
|
|
248
|
-
const metaEvent = await recal.calendar.createEventByMetaId(
|
|
249
|
-
'user_id',
|
|
250
|
-
{
|
|
251
|
-
subject: 'Cross-platform meeting',
|
|
252
|
-
start: new Date('2024-01-20T15:00:00Z'),
|
|
253
|
-
end: new Date('2024-01-20T16:00:00Z')
|
|
254
|
-
}
|
|
255
|
-
)
|
|
256
|
-
|
|
257
|
-
// Or specify which providers and timezone to use
|
|
258
|
-
const metaEventSpecific = await recal.calendar.createEventByMetaId(
|
|
259
|
-
'user_id',
|
|
260
|
-
{
|
|
261
|
-
subject: 'Cross-platform meeting',
|
|
262
|
-
start: new Date('2024-01-20T15:00:00Z'),
|
|
263
|
-
end: new Date('2024-01-20T16:00:00Z')
|
|
264
|
-
},
|
|
265
|
-
{
|
|
266
|
-
provider: ['google', 'microsoft'], // Create on specific providers
|
|
267
|
-
timeZone: 'Europe/Berlin' // optional
|
|
268
|
-
}
|
|
269
|
-
)
|
|
270
|
-
|
|
271
|
-
// Get event across all connected calendars (default behavior)
|
|
272
|
-
const metaEventGet = await recal.calendar.getEventByMetaId(
|
|
273
|
-
'user_id',
|
|
274
|
-
metaEvent.metaId
|
|
275
|
-
)
|
|
276
|
-
|
|
277
|
-
// Update across all calendars using meta ID
|
|
278
|
-
await recal.calendar.updateEventByMetaId(
|
|
279
|
-
'user_id',
|
|
280
|
-
metaEvent.metaId,
|
|
281
|
-
{ subject: 'Updated title' }
|
|
282
|
-
)
|
|
283
|
-
|
|
284
|
-
// Delete from all calendars
|
|
285
|
-
await recal.calendar.deleteEventByMetaId(
|
|
286
|
-
'user_id',
|
|
287
|
-
metaEvent.metaId
|
|
288
|
-
)
|
|
289
|
-
```
|
|
290
|
-
|
|
291
|
-
### Scheduling Service
|
|
292
|
-
|
|
293
|
-
#### Get User Availability (Basic)
|
|
75
|
+
- **`RECAL_TOKEN`** (required) - Your Recal API token (must start with "recal_")
|
|
76
|
+
- **`RECAL_URL`** (optional) - API base URL (defaults to `https://api.recal.dev`)
|
|
294
77
|
|
|
295
78
|
```typescript
|
|
296
|
-
//
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
new Date('2024-01-15'),
|
|
300
|
-
new Date('2024-01-20'),
|
|
301
|
-
{
|
|
302
|
-
slotDuration: 30 // Only required: slot duration in minutes
|
|
303
|
-
}
|
|
304
|
-
)
|
|
305
|
-
|
|
306
|
-
// Or with more options
|
|
307
|
-
const availabilityDetailed = await recal.scheduling.user(
|
|
308
|
-
'user_id',
|
|
309
|
-
new Date('2024-01-15'),
|
|
310
|
-
new Date('2024-01-20'),
|
|
311
|
-
{
|
|
312
|
-
slotDuration: 30, // Duration of each slot in minutes
|
|
313
|
-
padding: 0, // Padding between slots
|
|
314
|
-
earliestTimeEachDay: '09:00', // Format: HH:mm
|
|
315
|
-
latestTimeEachDay: '17:00', // Format: HH:mm
|
|
316
|
-
provider: 'google', // optional: filter by provider
|
|
317
|
-
timeZone: 'America/New_York' // optional
|
|
318
|
-
}
|
|
319
|
-
)
|
|
320
|
-
```
|
|
321
|
-
|
|
322
|
-
#### Allow Overlapping Events
|
|
323
|
-
|
|
324
|
-
```typescript
|
|
325
|
-
// Find availability even when user already has events scheduled
|
|
326
|
-
const availabilityWithOverlaps = await recal.scheduling.user(
|
|
327
|
-
'user_id',
|
|
328
|
-
new Date('2024-01-15'),
|
|
329
|
-
new Date('2024-01-20'),
|
|
330
|
-
{
|
|
331
|
-
slotDuration: 30,
|
|
332
|
-
maxOverlaps: 2, // Allow up to 2 overlapping events per slot
|
|
333
|
-
timeZone: 'America/New_York'
|
|
334
|
-
}
|
|
335
|
-
)
|
|
336
|
-
|
|
337
|
-
// maxOverlaps: 2, means 1 slot is still available even if the user has 2 existing events at the same time.
|
|
338
|
-
// This enables booking a n concurrent events - useful for optional meetings,
|
|
339
|
-
// tentative invites, or users who can handle multiple events simultaneous, like a team that has just one calendar.
|
|
340
|
-
```
|
|
341
|
-
|
|
342
|
-
#### Get User Availability (Advanced)
|
|
343
|
-
|
|
344
|
-
```typescript
|
|
345
|
-
// Find available time slots with custom schedules
|
|
346
|
-
const schedules = [
|
|
347
|
-
{
|
|
348
|
-
days: ['monday'], // Monday
|
|
349
|
-
start: '09:00',
|
|
350
|
-
end: '17:00'
|
|
351
|
-
},
|
|
352
|
-
// ... more schedule rules
|
|
353
|
-
]
|
|
354
|
-
|
|
355
|
-
// Minimal config
|
|
356
|
-
const availability = await recal.scheduling.userAdvanced(
|
|
357
|
-
'user_id',
|
|
358
|
-
schedules,
|
|
359
|
-
new Date('2024-01-15'),
|
|
360
|
-
new Date('2024-01-20'),
|
|
361
|
-
{ slotDuration: 30 } // Only required option
|
|
362
|
-
)
|
|
363
|
-
|
|
364
|
-
// Or with more options
|
|
365
|
-
const availabilityDetailed = await recal.scheduling.userAdvanced(
|
|
366
|
-
'user_id',
|
|
367
|
-
schedules,
|
|
368
|
-
new Date('2024-01-15'),
|
|
369
|
-
new Date('2024-01-20'),
|
|
370
|
-
{
|
|
371
|
-
slotDuration: 30,
|
|
372
|
-
padding: 15,
|
|
373
|
-
provider: 'google', // optional
|
|
374
|
-
timeZone: 'America/New_York' // optional
|
|
375
|
-
}
|
|
376
|
-
)
|
|
377
|
-
```
|
|
378
|
-
|
|
379
|
-
#### Get Organization-Wide Availability
|
|
380
|
-
|
|
381
|
-
```typescript
|
|
382
|
-
// Find organization-wide available time slots (minimal)
|
|
383
|
-
const orgAvailability = await recal.scheduling.organization(
|
|
384
|
-
'org-slug',
|
|
385
|
-
new Date('2024-01-15'),
|
|
386
|
-
new Date('2024-01-20'),
|
|
387
|
-
{ slotDuration: 60 } // Only required option
|
|
388
|
-
)
|
|
389
|
-
|
|
390
|
-
// Or with constraints
|
|
391
|
-
const orgAvailabilityConstrained = await recal.scheduling.organization(
|
|
392
|
-
'org-slug',
|
|
393
|
-
new Date('2024-01-15'),
|
|
394
|
-
new Date('2024-01-20'),
|
|
395
|
-
{
|
|
396
|
-
slotDuration: 60,
|
|
397
|
-
padding: 0,
|
|
398
|
-
earliestTimeEachDay: '09:00',
|
|
399
|
-
latestTimeEachDay: '17:00',
|
|
400
|
-
provider: ['google', 'microsoft'], // optional
|
|
401
|
-
timeZone: 'America/New_York' // optional
|
|
402
|
-
}
|
|
403
|
-
)
|
|
404
|
-
```
|
|
405
|
-
|
|
406
|
-
### Users Service
|
|
407
|
-
|
|
408
|
-
#### Get User
|
|
409
|
-
|
|
410
|
-
```typescript
|
|
411
|
-
// Get user information (basic)
|
|
412
|
-
const user = await recal.users.get('user_id', {})
|
|
413
|
-
|
|
414
|
-
// Or with additional data
|
|
415
|
-
const userWithDetails = await recal.users.get('user_id', {
|
|
416
|
-
includeOrgs: true, // Include organizations
|
|
417
|
-
includeOAuth: true // Include OAuth connections
|
|
418
|
-
})
|
|
419
|
-
console.log(user.id)
|
|
420
|
-
```
|
|
421
|
-
|
|
422
|
-
#### List All Users
|
|
423
|
-
|
|
424
|
-
```typescript
|
|
425
|
-
// Get all users
|
|
426
|
-
const users = await recal.users.listAll()
|
|
427
|
-
```
|
|
428
|
-
|
|
429
|
-
#### Create User
|
|
430
|
-
|
|
431
|
-
```typescript
|
|
432
|
-
// Create a new user (without organizations)
|
|
433
|
-
const user = await recal.users.create('user_id')
|
|
434
|
-
|
|
435
|
-
// Or with organization memberships
|
|
436
|
-
const userWithOrgs = await recal.users.create(
|
|
437
|
-
'user_id',
|
|
438
|
-
['org-slug-1', 'org-slug-2'] // optional: organization slugs
|
|
439
|
-
)
|
|
440
|
-
```
|
|
441
|
-
|
|
442
|
-
#### Update User
|
|
443
|
-
|
|
444
|
-
```typescript
|
|
445
|
-
// Update user ID
|
|
446
|
-
const updatedUser = await recal.users.update('old_user_id', {
|
|
447
|
-
id: 'new_user_id'
|
|
448
|
-
})
|
|
449
|
-
```
|
|
450
|
-
|
|
451
|
-
#### Delete User
|
|
452
|
-
|
|
453
|
-
```typescript
|
|
454
|
-
// Delete a user
|
|
455
|
-
const deletedUser = await recal.users.delete('user_id')
|
|
456
|
-
```
|
|
457
|
-
|
|
458
|
-
### Organizations Service
|
|
459
|
-
|
|
460
|
-
#### Get Organization
|
|
461
|
-
|
|
462
|
-
```typescript
|
|
463
|
-
// Get organization by slug
|
|
464
|
-
const org = await recal.organizations.get('acme-corp')
|
|
465
|
-
```
|
|
466
|
-
|
|
467
|
-
#### List All Organizations
|
|
468
|
-
|
|
469
|
-
```typescript
|
|
470
|
-
// Get all organizations
|
|
471
|
-
const orgs = await recal.organizations.listAll()
|
|
472
|
-
|
|
473
|
-
// Get organizations for a specific user
|
|
474
|
-
const userOrgs = await recal.organizations.listAllFromUser('user_id')
|
|
475
|
-
```
|
|
476
|
-
|
|
477
|
-
#### Create Organization
|
|
478
|
-
|
|
479
|
-
```typescript
|
|
480
|
-
// Create a new organization
|
|
481
|
-
const org = await recal.organizations.create(
|
|
482
|
-
'acme-corp', // slug
|
|
483
|
-
'Acme Corporation' // name
|
|
484
|
-
)
|
|
485
|
-
```
|
|
486
|
-
|
|
487
|
-
#### Update Organization
|
|
488
|
-
|
|
489
|
-
```typescript
|
|
490
|
-
// Update organization
|
|
491
|
-
const updated = await recal.organizations.update('acme-corp', {
|
|
492
|
-
slug: 'new-slug',
|
|
493
|
-
name: 'New Name'
|
|
494
|
-
})
|
|
495
|
-
```
|
|
496
|
-
|
|
497
|
-
#### Manage Members
|
|
498
|
-
|
|
499
|
-
```typescript
|
|
500
|
-
// Get all members
|
|
501
|
-
const members = await recal.organizations.getMembers('acme-corp')
|
|
502
|
-
|
|
503
|
-
// Add members
|
|
504
|
-
await recal.organizations.addMembers(
|
|
505
|
-
'acme-corp',
|
|
506
|
-
['user_id_1', 'user_id_2']
|
|
507
|
-
)
|
|
508
|
-
|
|
509
|
-
// Remove members
|
|
510
|
-
await recal.organizations.removeMembers(
|
|
511
|
-
'acme-corp',
|
|
512
|
-
['user_id_1', 'user_id_2']
|
|
513
|
-
)
|
|
514
|
-
```
|
|
515
|
-
|
|
516
|
-
#### Organization-Wide Busy
|
|
517
|
-
|
|
518
|
-
```typescript
|
|
519
|
-
// Get team availability (simplest form)
|
|
520
|
-
const teamBusy = await recal.calendar.getOrgWideBusy(
|
|
521
|
-
'acme-corp',
|
|
522
|
-
new Date('2024-01-15'),
|
|
523
|
-
new Date('2024-01-20'),
|
|
524
|
-
true // primaryOnly: only check primary calendars
|
|
525
|
-
)
|
|
526
|
-
|
|
527
|
-
// Or with optional filters
|
|
528
|
-
const teamBusyFiltered = await recal.calendar.getOrgWideBusy(
|
|
529
|
-
'acme-corp',
|
|
530
|
-
new Date('2024-01-15'),
|
|
531
|
-
new Date('2024-01-20'),
|
|
532
|
-
true, // primaryOnly: only check primary calendars
|
|
533
|
-
{
|
|
534
|
-
provider: 'google', // optional: filter by provider
|
|
535
|
-
timeZone: 'America/New_York' // optional
|
|
536
|
-
}
|
|
537
|
-
)
|
|
538
|
-
```
|
|
539
|
-
|
|
540
|
-
### OAuth Service
|
|
541
|
-
|
|
542
|
-
#### Get OAuth Link
|
|
543
|
-
|
|
544
|
-
```typescript
|
|
545
|
-
// Get OAuth authorization URL (with defaults)
|
|
546
|
-
const link = await recal.oauth.getLink(
|
|
547
|
-
'user_id',
|
|
548
|
-
'google'
|
|
549
|
-
)
|
|
550
|
-
|
|
551
|
-
// Or with custom options
|
|
552
|
-
const linkWithOptions = await recal.oauth.getLink(
|
|
553
|
-
'user_id',
|
|
554
|
-
'google',
|
|
555
|
-
{
|
|
556
|
-
scope: ['edit'], // 'edit' or 'free-busy' (for OAuth scopes)
|
|
557
|
-
accessType: 'offline', // 'offline' or 'online'
|
|
558
|
-
redirectUrl: 'https://app.example.com/callback' // optional
|
|
559
|
-
}
|
|
560
|
-
)
|
|
561
|
-
console.log(link.url) // Use this URL to redirect user
|
|
562
|
-
```
|
|
563
|
-
|
|
564
|
-
#### Get Multiple OAuth Links
|
|
565
|
-
|
|
566
|
-
```typescript
|
|
567
|
-
// Get OAuth URLs for all providers (simplest)
|
|
568
|
-
const links = await recal.oauth.getBulkLinks('user_id')
|
|
569
|
-
|
|
570
|
-
// Or with specific providers and options
|
|
571
|
-
const linksFiltered = await recal.oauth.getBulkLinks(
|
|
572
|
-
'user_id',
|
|
573
|
-
{
|
|
574
|
-
provider: ['google', 'microsoft'],
|
|
575
|
-
scope: ['edit'],
|
|
576
|
-
accessType: 'offline'
|
|
577
|
-
}
|
|
578
|
-
)
|
|
579
|
-
```
|
|
580
|
-
|
|
581
|
-
#### Manage OAuth Connections
|
|
582
|
-
|
|
583
|
-
```typescript
|
|
584
|
-
// Get all OAuth connections for a user
|
|
585
|
-
const connections = await recal.oauth.getAllConnections(
|
|
586
|
-
'user_id',
|
|
587
|
-
true // redacted (default: true)
|
|
588
|
-
)
|
|
589
|
-
|
|
590
|
-
// Get specific provider connection
|
|
591
|
-
const googleConnection = await recal.oauth.getConnection(
|
|
592
|
-
'user_id',
|
|
593
|
-
'google',
|
|
594
|
-
false // redacted
|
|
595
|
-
)
|
|
596
|
-
|
|
597
|
-
// Set OAuth tokens manually
|
|
598
|
-
const connection = await recal.oauth.setConnection(
|
|
599
|
-
'user_id',
|
|
600
|
-
'google',
|
|
601
|
-
{
|
|
602
|
-
accessToken: 'access_token',
|
|
603
|
-
refreshToken: 'refresh_token', // optional
|
|
604
|
-
scope: ['edit'],
|
|
605
|
-
expiresAt: new Date('2024-12-31'), // optional
|
|
606
|
-
email: 'user@example.com' // optional
|
|
607
|
-
}
|
|
608
|
-
)
|
|
609
|
-
|
|
610
|
-
// Disconnect a provider
|
|
611
|
-
await recal.oauth.disconnect('user_id', 'google')
|
|
612
|
-
|
|
613
|
-
// Get a fresh access token (useful when you need to make direct API calls)
|
|
614
|
-
const tokenData = await recal.oauth.getFreshAccessToken('user_id', 'google')
|
|
615
|
-
console.log(tokenData.accessToken)
|
|
616
|
-
```
|
|
617
|
-
|
|
618
|
-
#### Verify OAuth Callback
|
|
619
|
-
|
|
620
|
-
```typescript
|
|
621
|
-
// Verify OAuth code from callback
|
|
622
|
-
const result = await recal.oauth.verify(
|
|
623
|
-
'google',
|
|
624
|
-
'auth_code_from_callback',
|
|
625
|
-
['edit'], // 'edit' or 'free-busy' - single scope, not array
|
|
626
|
-
'state_parameter',
|
|
627
|
-
'https://app.example.com/callback' // optional
|
|
628
|
-
)
|
|
629
|
-
```
|
|
630
|
-
|
|
631
|
-
## Advanced Usage
|
|
632
|
-
|
|
633
|
-
### Error Handling
|
|
634
|
-
|
|
635
|
-
The SDK provides specific error types for different scenarios:
|
|
636
|
-
|
|
637
|
-
```typescript
|
|
638
|
-
import {
|
|
639
|
-
UserNotFoundError,
|
|
640
|
-
EventNotFoundError,
|
|
641
|
-
OAuthConnectionNotFoundError,
|
|
642
|
-
OrganizationNotFoundError
|
|
643
|
-
} from 'recal-sdk'
|
|
644
|
-
|
|
645
|
-
try {
|
|
646
|
-
const event = await recal.calendar.getEvent({
|
|
647
|
-
userId: 'user_id',
|
|
648
|
-
provider: 'google',
|
|
649
|
-
calendarId: 'calendar_id',
|
|
650
|
-
eventId: 'event_id'
|
|
651
|
-
})
|
|
652
|
-
} catch (error) {
|
|
653
|
-
if (error instanceof UserNotFoundError) {
|
|
654
|
-
console.log('User does not exist:', error.userId)
|
|
655
|
-
} else if (error instanceof EventNotFoundError) {
|
|
656
|
-
console.log('Event not found:', error.eventId)
|
|
657
|
-
} else if (error instanceof OAuthConnectionNotFoundError) {
|
|
658
|
-
console.log('Calendar not connected:', error.provider)
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
```
|
|
662
|
-
|
|
663
|
-
### Batch Operations
|
|
664
|
-
|
|
665
|
-
```typescript
|
|
666
|
-
// Process multiple users' calendars (simplest form)
|
|
667
|
-
const userIds = ['user1', 'user2', 'user3']
|
|
668
|
-
const allEvents = await Promise.all(
|
|
669
|
-
userIds.map(userId =>
|
|
670
|
-
recal.calendar.getEvents(
|
|
671
|
-
userId,
|
|
672
|
-
new Date('2024-01-01'),
|
|
673
|
-
new Date('2024-01-31')
|
|
674
|
-
)
|
|
675
|
-
)
|
|
676
|
-
)
|
|
677
|
-
```
|
|
678
|
-
|
|
679
|
-
### Working with Multiple Providers
|
|
680
|
-
|
|
681
|
-
```typescript
|
|
682
|
-
// Get all busy data (without filtering)
|
|
683
|
-
const startDate = new Date('2024-01-01')
|
|
684
|
-
const endDate = new Date('2024-01-31')
|
|
685
|
-
|
|
686
|
-
const allBusy = await recal.calendar.getBusy(
|
|
687
|
-
'user_id',
|
|
688
|
-
startDate,
|
|
689
|
-
endDate
|
|
690
|
-
)
|
|
691
|
-
|
|
692
|
-
// Or aggregate by specific providers
|
|
693
|
-
const providers: Provider[] = ['google', 'microsoft']
|
|
694
|
-
const busyTimes = await Promise.all(
|
|
695
|
-
providers.map(provider =>
|
|
696
|
-
recal.calendar.getBusy(
|
|
697
|
-
'user_id',
|
|
698
|
-
startDate,
|
|
699
|
-
endDate,
|
|
700
|
-
{ provider } // filter by specific provider
|
|
701
|
-
)
|
|
702
|
-
)
|
|
703
|
-
)
|
|
704
|
-
|
|
705
|
-
// Process the busy times as needed for your application
|
|
706
|
-
// Each element is Busy = TimeRange[]; flatten into a single array of TimeRange
|
|
707
|
-
const allBusyPeriods = busyTimes.flat()
|
|
708
|
-
```
|
|
709
|
-
|
|
710
|
-
### Custom Request Configuration
|
|
79
|
+
// Using environment variables
|
|
80
|
+
// Set RECAL_TOKEN in your .env file
|
|
81
|
+
const recal = new RecalClient()
|
|
711
82
|
|
|
712
|
-
|
|
713
|
-
// Use custom base URL
|
|
83
|
+
// Or provide explicitly
|
|
714
84
|
const recal = new RecalClient({
|
|
715
|
-
token:
|
|
716
|
-
url:
|
|
717
|
-
})
|
|
718
|
-
```
|
|
719
|
-
|
|
720
|
-
## Examples
|
|
721
|
-
|
|
722
|
-
### Building a Booking System
|
|
723
|
-
|
|
724
|
-
```typescript
|
|
725
|
-
// 1. Check availability
|
|
726
|
-
const availability = await recal.scheduling.user(
|
|
727
|
-
'consultant_id',
|
|
728
|
-
new Date(),
|
|
729
|
-
new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // Next 7 days
|
|
730
|
-
{
|
|
731
|
-
slotDuration: 60, // 60-minute slots
|
|
732
|
-
padding: 15, // 15-minute padding between slots
|
|
733
|
-
earliestTimeEachDay: '09:00',
|
|
734
|
-
latestTimeEachDay: '17:00',
|
|
735
|
-
timeZone: 'America/New_York'
|
|
736
|
-
}
|
|
737
|
-
)
|
|
738
|
-
|
|
739
|
-
// 2. Display available slots to user
|
|
740
|
-
const availableSlots = availability.availableSlots // Already filtered for availability
|
|
741
|
-
|
|
742
|
-
// 3. User selects a slot and provides their information
|
|
743
|
-
const selectedSlot = availableSlots[0] // Example: first available slot
|
|
744
|
-
const clientName = 'John Doe'
|
|
745
|
-
const clientEmail = 'john@example.com'
|
|
746
|
-
|
|
747
|
-
// 4. Create an event for the selected slot (using calendar service)
|
|
748
|
-
const booking = await recal.calendar.createEvent({
|
|
749
|
-
userId: 'consultant_id',
|
|
750
|
-
provider: 'google',
|
|
751
|
-
calendarId: 'primary',
|
|
752
|
-
event: {
|
|
753
|
-
subject: 'Consultation with ' + clientName,
|
|
754
|
-
description: 'Initial consultation',
|
|
755
|
-
start: selectedSlot.start,
|
|
756
|
-
end: selectedSlot.end,
|
|
757
|
-
attendees: [{ email: clientEmail }]
|
|
758
|
-
}
|
|
85
|
+
token: "recal_xyz",
|
|
86
|
+
url: "https://api.recal.dev" // optional
|
|
759
87
|
})
|
|
760
|
-
|
|
761
|
-
// 5. Send confirmation
|
|
762
|
-
console.log('Booking confirmed:', booking.id)
|
|
763
|
-
```
|
|
764
|
-
|
|
765
|
-
### Syncing Calendars
|
|
766
|
-
|
|
767
|
-
```typescript
|
|
768
|
-
// Sync events between providers
|
|
769
|
-
async function syncCalendars(userId: string) {
|
|
770
|
-
// Get all events from all providers
|
|
771
|
-
const allEvents = await recal.calendar.getEvents(
|
|
772
|
-
userId,
|
|
773
|
-
new Date(),
|
|
774
|
-
new Date(Date.now() + 30 * 24 * 60 * 60 * 1000)
|
|
775
|
-
)
|
|
776
|
-
|
|
777
|
-
// Or get events from Google only
|
|
778
|
-
const googleEvents = await recal.calendar.getEvents(
|
|
779
|
-
userId,
|
|
780
|
-
new Date(),
|
|
781
|
-
new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
|
|
782
|
-
{ provider: 'google' }
|
|
783
|
-
)
|
|
784
|
-
|
|
785
|
-
// Copy to Microsoft calendar
|
|
786
|
-
for (const event of googleEvents) {
|
|
787
|
-
if (!event.metaId) { // Not already synced
|
|
788
|
-
await recal.calendar.createEvent({
|
|
789
|
-
userId,
|
|
790
|
-
provider: 'microsoft',
|
|
791
|
-
calendarId: 'primary',
|
|
792
|
-
event: {
|
|
793
|
-
subject: event.subject,
|
|
794
|
-
description: event.description,
|
|
795
|
-
start: event.start,
|
|
796
|
-
end: event.end,
|
|
797
|
-
attendees: event.attendees
|
|
798
|
-
}
|
|
799
|
-
})
|
|
800
|
-
}
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
```
|
|
804
|
-
|
|
805
|
-
### Team Scheduling
|
|
806
|
-
|
|
807
|
-
```typescript
|
|
808
|
-
// Find time when entire team is available
|
|
809
|
-
async function findTeamSlot(
|
|
810
|
-
orgSlug: string,
|
|
811
|
-
duration: number,
|
|
812
|
-
startDate: Date,
|
|
813
|
-
endDate: Date
|
|
814
|
-
) {
|
|
815
|
-
// Option 1: Get raw busy times for manual processing
|
|
816
|
-
const busyTimes = await recal.calendar.getOrgWideBusy(
|
|
817
|
-
orgSlug,
|
|
818
|
-
startDate,
|
|
819
|
-
endDate,
|
|
820
|
-
true // Only check primary calendars
|
|
821
|
-
)
|
|
822
|
-
// Process busyTimes array to find gaps for your needs
|
|
823
|
-
|
|
824
|
-
// Option 2: Use the scheduling service (recommended)
|
|
825
|
-
const availability = await recal.scheduling.getOrgWideAvailability(
|
|
826
|
-
orgSlug,
|
|
827
|
-
startDate,
|
|
828
|
-
endDate,
|
|
829
|
-
{
|
|
830
|
-
slotDuration: duration,
|
|
831
|
-
padding: 0,
|
|
832
|
-
earliestTimeEachDay: '09:00',
|
|
833
|
-
latestTimeEachDay: '17:00'
|
|
834
|
-
}
|
|
835
|
-
)
|
|
836
|
-
|
|
837
|
-
// Returns ready-to-use available time slots
|
|
838
|
-
return availability.availableSlots
|
|
839
|
-
}
|
|
840
88
|
```
|
|
841
89
|
|
|
842
90
|
## SDK Development
|
|
@@ -868,39 +116,49 @@ bun run build
|
|
|
868
116
|
|
|
869
117
|
```
|
|
870
118
|
src/
|
|
871
|
-
├──
|
|
872
|
-
├──
|
|
119
|
+
├── client/ # Auto-generated HeyAPI SDK (DO NOT EDIT)
|
|
120
|
+
│ ├── client.gen.ts # HTTP client
|
|
121
|
+
│ ├── sdk.gen.ts # Generated SDK functions
|
|
122
|
+
│ ├── types.gen.ts # Generated TypeScript types
|
|
123
|
+
│ ├── zod.gen.ts # Zod validation schemas
|
|
124
|
+
│ └── core/ # Core utilities
|
|
125
|
+
├── services/ # Service wrapper implementations
|
|
873
126
|
│ ├── calendar.service.ts
|
|
874
|
-
│ ├──
|
|
875
|
-
│ ├──
|
|
127
|
+
│ ├── events.service.ts
|
|
128
|
+
│ ├── oauth.service.ts
|
|
876
129
|
│ ├── organizations.service.ts
|
|
877
|
-
│
|
|
878
|
-
|
|
879
|
-
│ ├── user.ts
|
|
880
|
-
│ └── organization.ts
|
|
881
|
-
├── types/ # TypeScript type definitions
|
|
882
|
-
│ ├── calendar.types.ts
|
|
883
|
-
│ ├── scheduling.types.ts
|
|
884
|
-
│ ├── internal.types.ts
|
|
885
|
-
│ └── oauth.types.ts
|
|
886
|
-
├── typebox/ # Runtime validation schemas (auto-generated)
|
|
887
|
-
│ ├── calendar.tb.ts
|
|
888
|
-
│ ├── scheduling.tb.ts
|
|
889
|
-
│ ├── oauth.tb.ts
|
|
890
|
-
│ ├── organization.tb.ts
|
|
891
|
-
│ ├── user.tb.ts
|
|
892
|
-
│ ├── timeString.tb.ts
|
|
893
|
-
│ ├── organization.stripped.tb.ts
|
|
894
|
-
│ └── user.stripped.tb.ts
|
|
130
|
+
│ ├── scheduling.service.ts
|
|
131
|
+
│ └── users.service.ts
|
|
895
132
|
├── utils/ # Helper utilities
|
|
896
|
-
│ ├──
|
|
897
|
-
│
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
└──
|
|
133
|
+
│ ├── functionize.ts # Lazy evaluation helper
|
|
134
|
+
│ └── response.ts # Response unwrapper
|
|
135
|
+
├── errors.ts # Custom error classes
|
|
136
|
+
├── index.ts # Main exports
|
|
137
|
+
├── recal.ts # Recal client class
|
|
138
|
+
└── types.ts # Type re-exports
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
> **Note**: All files in `client/` with `*.gen.ts` suffix are auto-generated from the OpenAPI specification and should not be edited manually. Use `bun run generate` to regenerate them.
|
|
142
|
+
|
|
143
|
+
### Code Generation
|
|
144
|
+
|
|
145
|
+
This SDK uses [HeyAPI's OpenAPI TypeScript generator](https://heyapi.vercel.app/) to create type-safe client code from the Recal API OpenAPI specification.
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
# Regenerate client code from OpenAPI spec
|
|
149
|
+
bun run generate
|
|
902
150
|
```
|
|
903
151
|
|
|
152
|
+
The generation process:
|
|
153
|
+
1. Fetches the latest OpenAPI spec from the Recal API
|
|
154
|
+
2. Generates TypeScript types, Zod schemas, and SDK functions
|
|
155
|
+
3. Creates all `*.gen.ts` files in `src/client/`
|
|
156
|
+
|
|
157
|
+
**When to regenerate:**
|
|
158
|
+
- After API updates or new endpoint additions
|
|
159
|
+
- When types or schemas need to be refreshed
|
|
160
|
+
- Before major version releases
|
|
161
|
+
|
|
904
162
|
### Code Style
|
|
905
163
|
|
|
906
164
|
This project uses Biome for formatting and linting:
|
|
@@ -918,6 +176,19 @@ bun run check:fix
|
|
|
918
176
|
|
|
919
177
|
### Testing
|
|
920
178
|
|
|
179
|
+
The SDK includes comprehensive integration tests for all services.
|
|
180
|
+
|
|
181
|
+
**Required Environment Variables:**
|
|
182
|
+
```bash
|
|
183
|
+
RECAL_TOKEN=recal_xxx # Required for all tests
|
|
184
|
+
RECAL_URL=https://api.recal.dev # Optional, defaults to production
|
|
185
|
+
|
|
186
|
+
# Optional (for OAuth integration tests)
|
|
187
|
+
GOOGLE_ACCESS_TOKEN=xxx
|
|
188
|
+
GOOGLE_REFRESH_TOKEN=xxx
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
**Run Tests:**
|
|
921
192
|
```bash
|
|
922
193
|
# Run all tests
|
|
923
194
|
bun test
|
|
@@ -929,9 +200,11 @@ bun test tests/integrations/users.test.ts
|
|
|
929
200
|
bun test --coverage
|
|
930
201
|
```
|
|
931
202
|
|
|
203
|
+
> **Note**: Tests without OAuth tokens will skip OAuth-dependent tests automatically.
|
|
204
|
+
|
|
932
205
|
## Contributing
|
|
933
206
|
|
|
934
|
-
We welcome contributions!
|
|
207
|
+
We welcome contributions! Here's how to get started:
|
|
935
208
|
|
|
936
209
|
### Development Workflow
|
|
937
210
|
|
|
@@ -943,6 +216,14 @@ We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) f
|
|
|
943
216
|
6. Push to your branch (`git push origin feature/amazing-feature`)
|
|
944
217
|
7. Open a Pull Request
|
|
945
218
|
|
|
219
|
+
### Guidelines
|
|
220
|
+
|
|
221
|
+
- Follow the existing code style (enforced by Biome)
|
|
222
|
+
- Add tests for new features
|
|
223
|
+
- Update documentation as needed
|
|
224
|
+
- Keep commits focused and descriptive
|
|
225
|
+
- Do not edit `*.gen.ts` files (auto-generated)
|
|
226
|
+
|
|
946
227
|
### Reporting Issues
|
|
947
228
|
|
|
948
229
|
Found a bug or have a feature request? Please [open an issue](https://github.com/recal-dev/recal-sdk-js/issues) with:
|
|
@@ -955,9 +236,9 @@ Found a bug or have a feature request? Please [open an issue](https://github.com
|
|
|
955
236
|
## Support
|
|
956
237
|
|
|
957
238
|
- **Documentation**: [https://docs.recal.dev](https://docs.recal.dev)
|
|
958
|
-
- **API Reference**: [https://api.recal.dev/
|
|
239
|
+
- **API Reference**: [https://api.recal.dev/v1/swagger](https://api.recal.dev/v1/swagger)
|
|
959
240
|
- **Email**: team@recal.dev
|
|
960
|
-
- **
|
|
241
|
+
- **Slack**: [Join our community](https://join.slack.com/t/recal-workspace/shared_invite/zt-394r5j23n-WDr_tz753Cf8lQljrK_0Mw)
|
|
961
242
|
|
|
962
243
|
## License
|
|
963
244
|
|