@axium/calendar 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,461 @@
1
+ import { type UserInternal } from '@axium/core';
2
+ import type { WithRequired } from 'utilium';
3
+ import * as z from 'zod';
4
+ export declare function dayOfYear(date: Date): number;
5
+ /**
6
+ *
7
+ * @param date
8
+ * @param baseOnWeeks Whether to use day-based (w1 = 1-7, w2 = 8-14), or week-based (Sat-Sun)
9
+ * @returns
10
+ */
11
+ export declare function weekOfYear(date: Date, baseOnWeeks?: boolean): number;
12
+ export declare function weekDaysFor(date: Date): Date[];
13
+ /**
14
+ * Converts a date to the format expected by `<input type="datetime-local">`
15
+ */
16
+ export declare function dateToInputValue(date?: Date): string | null;
17
+ /**
18
+ * @todo this feels like a mess:
19
+ * - Want to support "external" attendees where no associated user exists (no userId), email makes sense for the "key"
20
+ * - For users, we want to use the userId as the "key"
21
+ * - Need to check for uniqueness server-side
22
+ */
23
+ export declare const AttendeeInit: z.ZodObject<{
24
+ email: z.ZodEmail;
25
+ userId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
26
+ name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
27
+ }, z.core.$strip>;
28
+ export interface AttendeeInit extends z.infer<typeof AttendeeInit> {
29
+ }
30
+ export declare const AttendeeStatus: z.ZodLiteral<"needs-action" | "accepted" | "declined" | "tentative">;
31
+ export type AttendeeStatus = z.infer<typeof AttendeeStatus>;
32
+ export declare const Attendee: z.ZodObject<{
33
+ email: z.ZodEmail;
34
+ userId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
35
+ name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
36
+ eventId: z.ZodUUID;
37
+ status: z.ZodLiteral<"needs-action" | "accepted" | "declined" | "tentative">;
38
+ role: z.ZodOptional<z.ZodNullable<z.ZodString>>;
39
+ }, z.core.$strip>;
40
+ export interface Attendee extends z.infer<typeof Attendee> {
41
+ }
42
+ export declare const EventFilter: z.ZodObject<{
43
+ start: z.ZodCoercedDate<unknown>;
44
+ end: z.ZodCoercedDate<unknown>;
45
+ }, z.core.$strip>;
46
+ export interface EventFilter extends z.infer<typeof EventFilter> {
47
+ }
48
+ export declare function getSpanFilter(span: 'week' | 'month', at: Date): EventFilter;
49
+ export declare const EventData: z.ZodObject<{
50
+ calId: z.ZodUUID;
51
+ summary: z.ZodString;
52
+ location: z.ZodOptional<z.ZodNullable<z.ZodString>>;
53
+ start: z.ZodCoercedDate<unknown>;
54
+ end: z.ZodCoercedDate<unknown>;
55
+ isAllDay: z.ZodCoercedBoolean<unknown>;
56
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
57
+ recurrence: z.ZodOptional<z.ZodNullable<z.ZodString>>;
58
+ recurrenceExcludes: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString>>>;
59
+ recurrenceId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
60
+ }, z.core.$strip>;
61
+ export interface EventData extends z.infer<typeof EventData> {
62
+ }
63
+ export declare const EventInit: z.ZodObject<{
64
+ calId: z.ZodUUID;
65
+ summary: z.ZodString;
66
+ location: z.ZodOptional<z.ZodNullable<z.ZodString>>;
67
+ start: z.ZodCoercedDate<unknown>;
68
+ end: z.ZodCoercedDate<unknown>;
69
+ isAllDay: z.ZodCoercedBoolean<unknown>;
70
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
71
+ recurrence: z.ZodOptional<z.ZodNullable<z.ZodString>>;
72
+ recurrenceExcludes: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString>>>;
73
+ recurrenceId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
74
+ attendees: z.ZodArray<z.ZodObject<{
75
+ email: z.ZodEmail;
76
+ userId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
77
+ name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
78
+ }, z.core.$strip>>;
79
+ sendEmails: z.ZodBoolean;
80
+ recurrenceUpdateAll: z.ZodOptional<z.ZodBoolean>;
81
+ }, z.core.$strip>;
82
+ export interface EventInit extends z.infer<typeof EventInit> {
83
+ }
84
+ export declare const Event: z.ZodObject<{
85
+ calId: z.ZodUUID;
86
+ summary: z.ZodString;
87
+ location: z.ZodOptional<z.ZodNullable<z.ZodString>>;
88
+ start: z.ZodCoercedDate<unknown>;
89
+ end: z.ZodCoercedDate<unknown>;
90
+ isAllDay: z.ZodCoercedBoolean<unknown>;
91
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
92
+ recurrence: z.ZodOptional<z.ZodNullable<z.ZodString>>;
93
+ recurrenceExcludes: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString>>>;
94
+ recurrenceId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
95
+ id: z.ZodUUID;
96
+ created: z.ZodCoercedDate<unknown>;
97
+ attendees: z.ZodArray<z.ZodObject<{
98
+ email: z.ZodEmail;
99
+ userId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
100
+ name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
101
+ eventId: z.ZodUUID;
102
+ status: z.ZodLiteral<"needs-action" | "accepted" | "declined" | "tentative">;
103
+ role: z.ZodOptional<z.ZodNullable<z.ZodString>>;
104
+ }, z.core.$strip>>;
105
+ }, z.core.$strip>;
106
+ export interface Event extends z.infer<typeof Event> {
107
+ calendar?: Calendar;
108
+ }
109
+ export declare function formatEventTimes(event: Event): string;
110
+ export declare const CalendarInit: z.ZodObject<{
111
+ name: z.ZodString;
112
+ }, z.core.$strip>;
113
+ export interface CalendarInit extends z.infer<typeof CalendarInit> {
114
+ }
115
+ export declare const Calendar: z.ZodObject<{
116
+ name: z.ZodString;
117
+ id: z.ZodUUID;
118
+ userId: z.ZodUUID;
119
+ created: z.ZodCoercedDate<unknown>;
120
+ acl: z.ZodOptional<z.ZodArray<z.ZodObject<{
121
+ itemId: z.ZodUUID;
122
+ userId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
123
+ role: z.ZodOptional<z.ZodNullable<z.ZodString>>;
124
+ tag: z.ZodOptional<z.ZodNullable<z.ZodString>>;
125
+ user: z.ZodOptional<z.ZodNullable<z.ZodObject<{
126
+ id: z.ZodUUID;
127
+ name: z.ZodString;
128
+ email: z.ZodOptional<z.ZodEmail>;
129
+ emailVerified: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodCoercedDate<unknown>>>>;
130
+ image: z.ZodOptional<z.ZodNullable<z.ZodURL>>;
131
+ preferences: z.ZodOptional<z.ZodLazy<z.ZodObject<{
132
+ debug: z.ZodDefault<z.ZodBoolean>;
133
+ }, z.core.$strip>>>;
134
+ roles: z.ZodArray<z.ZodString>;
135
+ tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
136
+ registeredAt: z.ZodCoercedDate<unknown>;
137
+ isAdmin: z.ZodOptional<z.ZodBoolean>;
138
+ isSuspended: z.ZodOptional<z.ZodBoolean>;
139
+ }, z.core.$strip>>>;
140
+ createdAt: z.ZodCoercedDate<unknown>;
141
+ }, z.core.$catchall<z.ZodBoolean>>>>;
142
+ }, z.core.$strip>;
143
+ export interface Calendar extends z.infer<typeof Calendar> {
144
+ }
145
+ export interface CalendarPermissionsInfo {
146
+ icon: string;
147
+ list: string;
148
+ perms: Record<string, boolean>;
149
+ }
150
+ export declare function getCalPermissionsInfo(cal: WithRequired<Calendar, 'acl'>, user: Pick<UserInternal, 'id' | 'roles' | 'tags'>): CalendarPermissionsInfo;
151
+ declare const CalendarAPI: {
152
+ readonly 'users/:id/calendars': {
153
+ readonly PUT: readonly [z.ZodObject<{
154
+ name: z.ZodString;
155
+ }, z.core.$strip>, z.ZodObject<{
156
+ name: z.ZodString;
157
+ id: z.ZodUUID;
158
+ userId: z.ZodUUID;
159
+ created: z.ZodCoercedDate<unknown>;
160
+ acl: z.ZodOptional<z.ZodArray<z.ZodObject<{
161
+ itemId: z.ZodUUID;
162
+ userId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
163
+ role: z.ZodOptional<z.ZodNullable<z.ZodString>>;
164
+ tag: z.ZodOptional<z.ZodNullable<z.ZodString>>;
165
+ user: z.ZodOptional<z.ZodNullable<z.ZodObject<{
166
+ id: z.ZodUUID;
167
+ name: z.ZodString;
168
+ email: z.ZodOptional<z.ZodEmail>;
169
+ emailVerified: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodCoercedDate<unknown>>>>;
170
+ image: z.ZodOptional<z.ZodNullable<z.ZodURL>>;
171
+ preferences: z.ZodOptional<z.ZodLazy<z.ZodObject<{
172
+ debug: z.ZodDefault<z.ZodBoolean>;
173
+ }, z.core.$strip>>>;
174
+ roles: z.ZodArray<z.ZodString>;
175
+ tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
176
+ registeredAt: z.ZodCoercedDate<unknown>;
177
+ isAdmin: z.ZodOptional<z.ZodBoolean>;
178
+ isSuspended: z.ZodOptional<z.ZodBoolean>;
179
+ }, z.core.$strip>>>;
180
+ createdAt: z.ZodCoercedDate<unknown>;
181
+ }, z.core.$catchall<z.ZodBoolean>>>>;
182
+ }, z.core.$strip>];
183
+ readonly GET: z.ZodArray<z.ZodObject<{
184
+ name: z.ZodString;
185
+ id: z.ZodUUID;
186
+ userId: z.ZodUUID;
187
+ created: z.ZodCoercedDate<unknown>;
188
+ acl: z.ZodNonOptional<z.ZodOptional<z.ZodArray<z.ZodObject<{
189
+ itemId: z.ZodUUID;
190
+ userId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
191
+ role: z.ZodOptional<z.ZodNullable<z.ZodString>>;
192
+ tag: z.ZodOptional<z.ZodNullable<z.ZodString>>;
193
+ user: z.ZodOptional<z.ZodNullable<z.ZodObject<{
194
+ id: z.ZodUUID;
195
+ name: z.ZodString;
196
+ email: z.ZodOptional<z.ZodEmail>;
197
+ emailVerified: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodCoercedDate<unknown>>>>;
198
+ image: z.ZodOptional<z.ZodNullable<z.ZodURL>>;
199
+ preferences: z.ZodOptional<z.ZodLazy<z.ZodObject<{
200
+ debug: z.ZodDefault<z.ZodBoolean>;
201
+ }, z.core.$strip>>>;
202
+ roles: z.ZodArray<z.ZodString>;
203
+ tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
204
+ registeredAt: z.ZodCoercedDate<unknown>;
205
+ isAdmin: z.ZodOptional<z.ZodBoolean>;
206
+ isSuspended: z.ZodOptional<z.ZodBoolean>;
207
+ }, z.core.$strip>>>;
208
+ createdAt: z.ZodCoercedDate<unknown>;
209
+ }, z.core.$catchall<z.ZodBoolean>>>>>;
210
+ }, z.core.$strip>>;
211
+ };
212
+ readonly 'calendars/:id': {
213
+ readonly GET: z.ZodObject<{
214
+ name: z.ZodString;
215
+ id: z.ZodUUID;
216
+ userId: z.ZodUUID;
217
+ created: z.ZodCoercedDate<unknown>;
218
+ acl: z.ZodNonOptional<z.ZodOptional<z.ZodArray<z.ZodObject<{
219
+ itemId: z.ZodUUID;
220
+ userId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
221
+ role: z.ZodOptional<z.ZodNullable<z.ZodString>>;
222
+ tag: z.ZodOptional<z.ZodNullable<z.ZodString>>;
223
+ user: z.ZodOptional<z.ZodNullable<z.ZodObject<{
224
+ id: z.ZodUUID;
225
+ name: z.ZodString;
226
+ email: z.ZodOptional<z.ZodEmail>;
227
+ emailVerified: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodCoercedDate<unknown>>>>;
228
+ image: z.ZodOptional<z.ZodNullable<z.ZodURL>>;
229
+ preferences: z.ZodOptional<z.ZodLazy<z.ZodObject<{
230
+ debug: z.ZodDefault<z.ZodBoolean>;
231
+ }, z.core.$strip>>>;
232
+ roles: z.ZodArray<z.ZodString>;
233
+ tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
234
+ registeredAt: z.ZodCoercedDate<unknown>;
235
+ isAdmin: z.ZodOptional<z.ZodBoolean>;
236
+ isSuspended: z.ZodOptional<z.ZodBoolean>;
237
+ }, z.core.$strip>>>;
238
+ createdAt: z.ZodCoercedDate<unknown>;
239
+ }, z.core.$catchall<z.ZodBoolean>>>>>;
240
+ }, z.core.$strip>;
241
+ readonly PATCH: readonly [z.ZodObject<{
242
+ name: z.ZodString;
243
+ }, z.core.$strip>, z.ZodObject<{
244
+ name: z.ZodString;
245
+ id: z.ZodUUID;
246
+ userId: z.ZodUUID;
247
+ created: z.ZodCoercedDate<unknown>;
248
+ acl: z.ZodOptional<z.ZodArray<z.ZodObject<{
249
+ itemId: z.ZodUUID;
250
+ userId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
251
+ role: z.ZodOptional<z.ZodNullable<z.ZodString>>;
252
+ tag: z.ZodOptional<z.ZodNullable<z.ZodString>>;
253
+ user: z.ZodOptional<z.ZodNullable<z.ZodObject<{
254
+ id: z.ZodUUID;
255
+ name: z.ZodString;
256
+ email: z.ZodOptional<z.ZodEmail>;
257
+ emailVerified: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodCoercedDate<unknown>>>>;
258
+ image: z.ZodOptional<z.ZodNullable<z.ZodURL>>;
259
+ preferences: z.ZodOptional<z.ZodLazy<z.ZodObject<{
260
+ debug: z.ZodDefault<z.ZodBoolean>;
261
+ }, z.core.$strip>>>;
262
+ roles: z.ZodArray<z.ZodString>;
263
+ tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
264
+ registeredAt: z.ZodCoercedDate<unknown>;
265
+ isAdmin: z.ZodOptional<z.ZodBoolean>;
266
+ isSuspended: z.ZodOptional<z.ZodBoolean>;
267
+ }, z.core.$strip>>>;
268
+ createdAt: z.ZodCoercedDate<unknown>;
269
+ }, z.core.$catchall<z.ZodBoolean>>>>;
270
+ }, z.core.$strip>];
271
+ readonly DELETE: z.ZodObject<{
272
+ name: z.ZodString;
273
+ id: z.ZodUUID;
274
+ userId: z.ZodUUID;
275
+ created: z.ZodCoercedDate<unknown>;
276
+ acl: z.ZodOptional<z.ZodArray<z.ZodObject<{
277
+ itemId: z.ZodUUID;
278
+ userId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
279
+ role: z.ZodOptional<z.ZodNullable<z.ZodString>>;
280
+ tag: z.ZodOptional<z.ZodNullable<z.ZodString>>;
281
+ user: z.ZodOptional<z.ZodNullable<z.ZodObject<{
282
+ id: z.ZodUUID;
283
+ name: z.ZodString;
284
+ email: z.ZodOptional<z.ZodEmail>;
285
+ emailVerified: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodCoercedDate<unknown>>>>;
286
+ image: z.ZodOptional<z.ZodNullable<z.ZodURL>>;
287
+ preferences: z.ZodOptional<z.ZodLazy<z.ZodObject<{
288
+ debug: z.ZodDefault<z.ZodBoolean>;
289
+ }, z.core.$strip>>>;
290
+ roles: z.ZodArray<z.ZodString>;
291
+ tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
292
+ registeredAt: z.ZodCoercedDate<unknown>;
293
+ isAdmin: z.ZodOptional<z.ZodBoolean>;
294
+ isSuspended: z.ZodOptional<z.ZodBoolean>;
295
+ }, z.core.$strip>>>;
296
+ createdAt: z.ZodCoercedDate<unknown>;
297
+ }, z.core.$catchall<z.ZodBoolean>>>>;
298
+ }, z.core.$strip>;
299
+ };
300
+ readonly 'calendars/:id/events': {
301
+ readonly GET: readonly [z.ZodObject<{
302
+ start: z.ZodCoercedDate<unknown>;
303
+ end: z.ZodCoercedDate<unknown>;
304
+ }, z.core.$strip>, z.ZodArray<z.ZodObject<{
305
+ calId: z.ZodUUID;
306
+ summary: z.ZodString;
307
+ location: z.ZodOptional<z.ZodNullable<z.ZodString>>;
308
+ start: z.ZodCoercedDate<unknown>;
309
+ end: z.ZodCoercedDate<unknown>;
310
+ isAllDay: z.ZodCoercedBoolean<unknown>;
311
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
312
+ recurrence: z.ZodOptional<z.ZodNullable<z.ZodString>>;
313
+ recurrenceExcludes: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString>>>;
314
+ recurrenceId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
315
+ id: z.ZodUUID;
316
+ created: z.ZodCoercedDate<unknown>;
317
+ attendees: z.ZodArray<z.ZodObject<{
318
+ email: z.ZodEmail;
319
+ userId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
320
+ name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
321
+ eventId: z.ZodUUID;
322
+ status: z.ZodLiteral<"needs-action" | "accepted" | "declined" | "tentative">;
323
+ role: z.ZodOptional<z.ZodNullable<z.ZodString>>;
324
+ }, z.core.$strip>>;
325
+ }, z.core.$strip>>];
326
+ readonly PUT: readonly [z.ZodObject<{
327
+ calId: z.ZodUUID;
328
+ summary: z.ZodString;
329
+ location: z.ZodOptional<z.ZodNullable<z.ZodString>>;
330
+ start: z.ZodCoercedDate<unknown>;
331
+ end: z.ZodCoercedDate<unknown>;
332
+ isAllDay: z.ZodCoercedBoolean<unknown>;
333
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
334
+ recurrence: z.ZodOptional<z.ZodNullable<z.ZodString>>;
335
+ recurrenceExcludes: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString>>>;
336
+ recurrenceId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
337
+ attendees: z.ZodArray<z.ZodObject<{
338
+ email: z.ZodEmail;
339
+ userId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
340
+ name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
341
+ }, z.core.$strip>>;
342
+ sendEmails: z.ZodBoolean;
343
+ recurrenceUpdateAll: z.ZodOptional<z.ZodBoolean>;
344
+ }, z.core.$strip>, z.ZodObject<{
345
+ calId: z.ZodUUID;
346
+ summary: z.ZodString;
347
+ location: z.ZodOptional<z.ZodNullable<z.ZodString>>;
348
+ start: z.ZodCoercedDate<unknown>;
349
+ end: z.ZodCoercedDate<unknown>;
350
+ isAllDay: z.ZodCoercedBoolean<unknown>;
351
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
352
+ recurrence: z.ZodOptional<z.ZodNullable<z.ZodString>>;
353
+ recurrenceExcludes: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString>>>;
354
+ recurrenceId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
355
+ id: z.ZodUUID;
356
+ created: z.ZodCoercedDate<unknown>;
357
+ attendees: z.ZodArray<z.ZodObject<{
358
+ email: z.ZodEmail;
359
+ userId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
360
+ name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
361
+ eventId: z.ZodUUID;
362
+ status: z.ZodLiteral<"needs-action" | "accepted" | "declined" | "tentative">;
363
+ role: z.ZodOptional<z.ZodNullable<z.ZodString>>;
364
+ }, z.core.$strip>>;
365
+ }, z.core.$strip>];
366
+ };
367
+ readonly 'events/:id': {
368
+ readonly GET: z.ZodObject<{
369
+ calId: z.ZodUUID;
370
+ summary: z.ZodString;
371
+ location: z.ZodOptional<z.ZodNullable<z.ZodString>>;
372
+ start: z.ZodCoercedDate<unknown>;
373
+ end: z.ZodCoercedDate<unknown>;
374
+ isAllDay: z.ZodCoercedBoolean<unknown>;
375
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
376
+ recurrence: z.ZodOptional<z.ZodNullable<z.ZodString>>;
377
+ recurrenceExcludes: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString>>>;
378
+ recurrenceId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
379
+ id: z.ZodUUID;
380
+ created: z.ZodCoercedDate<unknown>;
381
+ attendees: z.ZodArray<z.ZodObject<{
382
+ email: z.ZodEmail;
383
+ userId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
384
+ name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
385
+ eventId: z.ZodUUID;
386
+ status: z.ZodLiteral<"needs-action" | "accepted" | "declined" | "tentative">;
387
+ role: z.ZodOptional<z.ZodNullable<z.ZodString>>;
388
+ }, z.core.$strip>>;
389
+ }, z.core.$strip>;
390
+ readonly PATCH: readonly [z.ZodObject<{
391
+ calId: z.ZodUUID;
392
+ summary: z.ZodString;
393
+ location: z.ZodOptional<z.ZodNullable<z.ZodString>>;
394
+ start: z.ZodCoercedDate<unknown>;
395
+ end: z.ZodCoercedDate<unknown>;
396
+ isAllDay: z.ZodCoercedBoolean<unknown>;
397
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
398
+ recurrence: z.ZodOptional<z.ZodNullable<z.ZodString>>;
399
+ recurrenceExcludes: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString>>>;
400
+ recurrenceId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
401
+ attendees: z.ZodArray<z.ZodObject<{
402
+ email: z.ZodEmail;
403
+ userId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
404
+ name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
405
+ }, z.core.$strip>>;
406
+ sendEmails: z.ZodBoolean;
407
+ recurrenceUpdateAll: z.ZodOptional<z.ZodBoolean>;
408
+ }, z.core.$strip>, z.ZodObject<{
409
+ calId: z.ZodUUID;
410
+ summary: z.ZodString;
411
+ location: z.ZodOptional<z.ZodNullable<z.ZodString>>;
412
+ start: z.ZodCoercedDate<unknown>;
413
+ end: z.ZodCoercedDate<unknown>;
414
+ isAllDay: z.ZodCoercedBoolean<unknown>;
415
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
416
+ recurrence: z.ZodOptional<z.ZodNullable<z.ZodString>>;
417
+ recurrenceExcludes: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString>>>;
418
+ recurrenceId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
419
+ id: z.ZodUUID;
420
+ created: z.ZodCoercedDate<unknown>;
421
+ attendees: z.ZodArray<z.ZodObject<{
422
+ email: z.ZodEmail;
423
+ userId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
424
+ name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
425
+ eventId: z.ZodUUID;
426
+ status: z.ZodLiteral<"needs-action" | "accepted" | "declined" | "tentative">;
427
+ role: z.ZodOptional<z.ZodNullable<z.ZodString>>;
428
+ }, z.core.$strip>>;
429
+ }, z.core.$strip>];
430
+ readonly DELETE: z.ZodObject<{
431
+ calId: z.ZodUUID;
432
+ summary: z.ZodString;
433
+ location: z.ZodOptional<z.ZodNullable<z.ZodString>>;
434
+ start: z.ZodCoercedDate<unknown>;
435
+ end: z.ZodCoercedDate<unknown>;
436
+ isAllDay: z.ZodCoercedBoolean<unknown>;
437
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
438
+ recurrence: z.ZodOptional<z.ZodNullable<z.ZodString>>;
439
+ recurrenceExcludes: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString>>>;
440
+ recurrenceId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
441
+ id: z.ZodUUID;
442
+ created: z.ZodCoercedDate<unknown>;
443
+ attendees: z.ZodArray<z.ZodObject<{
444
+ email: z.ZodEmail;
445
+ userId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
446
+ name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
447
+ eventId: z.ZodUUID;
448
+ status: z.ZodLiteral<"needs-action" | "accepted" | "declined" | "tentative">;
449
+ role: z.ZodOptional<z.ZodNullable<z.ZodString>>;
450
+ }, z.core.$strip>>;
451
+ }, z.core.$strip>;
452
+ };
453
+ };
454
+ type CalendarAPI = typeof CalendarAPI;
455
+ declare module '@axium/core/api' {
456
+ interface $API extends CalendarAPI {
457
+ }
458
+ }
459
+ export declare function eventToICS(event: Event): string;
460
+ export declare function eventFromICS(ics: string): Event;
461
+ export {};
package/dist/common.js ADDED
@@ -0,0 +1,194 @@
1
+ import { $API, AccessControl, pickPermissions } from '@axium/core';
2
+ import * as z from 'zod';
3
+ import $pkg from '../package.json' with { type: 'json' };
4
+ export function dayOfYear(date) {
5
+ const yearStart = new Date(date.getFullYear(), 0, 1);
6
+ return Math.round((date.getTime() - yearStart.getTime()) / 86400000 + 1);
7
+ }
8
+ /**
9
+ *
10
+ * @param date
11
+ * @param baseOnWeeks Whether to use day-based (w1 = 1-7, w2 = 8-14), or week-based (Sat-Sun)
12
+ * @returns
13
+ */
14
+ export function weekOfYear(date, baseOnWeeks = false) {
15
+ let day = dayOfYear(date);
16
+ if (baseOnWeeks) {
17
+ day += new Date(date.getFullYear(), 0, 1).getDay();
18
+ }
19
+ const week = Math.ceil(day / 7);
20
+ return week > 52 ? week % 52 : week;
21
+ }
22
+ export function weekDaysFor(date) {
23
+ const days = [];
24
+ for (let i = 0; i < 7; i++) {
25
+ days.push(new Date(date.getFullYear(), date.getMonth(), date.getDate() - date.getDay() + i, 0, 0, 0, 0));
26
+ }
27
+ return days;
28
+ }
29
+ /**
30
+ * Converts a date to the format expected by `<input type="datetime-local">`
31
+ */
32
+ export function dateToInputValue(date) {
33
+ if (!date?.toISOString)
34
+ return null;
35
+ const offset = date.getTimezoneOffset() * 60_000;
36
+ const localDate = new Date(date.getTime() - offset);
37
+ return localDate.toISOString().slice(0, -1);
38
+ }
39
+ /**
40
+ * @todo this feels like a mess:
41
+ * - Want to support "external" attendees where no associated user exists (no userId), email makes sense for the "key"
42
+ * - For users, we want to use the userId as the "key"
43
+ * - Need to check for uniqueness server-side
44
+ */
45
+ export const AttendeeInit = z.object({
46
+ email: z.email(),
47
+ userId: z.uuid().nullish(),
48
+ name: z.string().nullish(),
49
+ });
50
+ export const AttendeeStatus = z.literal(['needs-action', 'accepted', 'declined', 'tentative']);
51
+ export const Attendee = AttendeeInit.extend({
52
+ eventId: z.uuid(),
53
+ status: AttendeeStatus,
54
+ role: z.string().nullish(),
55
+ });
56
+ export const EventFilter = z.object({
57
+ start: z.coerce.date(),
58
+ end: z.coerce.date(),
59
+ });
60
+ export function getSpanFilter(span, at) {
61
+ switch (span) {
62
+ case 'week': {
63
+ const startDay = at.getDate() - at.getDay();
64
+ return {
65
+ start: new Date(at.getFullYear(), at.getMonth(), startDay),
66
+ end: new Date(at.getFullYear(), at.getMonth(), startDay + 7),
67
+ };
68
+ }
69
+ case 'month':
70
+ return {
71
+ start: new Date(at.getFullYear(), at.getMonth(), 1),
72
+ end: new Date(at.getFullYear(), at.getMonth() + 1, 0),
73
+ };
74
+ }
75
+ }
76
+ export const EventData = z.object({
77
+ calId: z.uuid(),
78
+ summary: z.string().max(250),
79
+ location: z.string().max(250).nullish(),
80
+ start: z.coerce.date(),
81
+ end: z.coerce.date(),
82
+ isAllDay: z.coerce.boolean(),
83
+ description: z.string().max(2000).nullish(),
84
+ // note: recurrences are not support yet
85
+ recurrence: z.string().max(1000).nullish(),
86
+ recurrenceExcludes: z.string().max(100).array().max(100).nullish(),
87
+ recurrenceId: z.uuid().nullish(),
88
+ });
89
+ export const EventInit = EventData.extend({
90
+ attendees: AttendeeInit.array().max(100),
91
+ sendEmails: z.boolean(),
92
+ /** Whether to update all recurring events (true) or just subsequent ones (false). Only valid when changing a recurring event. */
93
+ recurrenceUpdateAll: z.boolean().optional(),
94
+ });
95
+ export const Event = EventData.extend({
96
+ id: z.uuid(),
97
+ created: z.coerce.date(),
98
+ attendees: Attendee.array(),
99
+ });
100
+ const format = {
101
+ hour: '2-digit',
102
+ minute: '2-digit',
103
+ };
104
+ export function formatEventTimes(event) {
105
+ return `${event.start.toLocaleTimeString('default', format)} - ${event.end.toLocaleTimeString('default', format)}`;
106
+ }
107
+ export const CalendarInit = z.object({
108
+ name: z.string(),
109
+ });
110
+ export const Calendar = CalendarInit.extend({
111
+ id: z.uuid(),
112
+ userId: z.uuid(),
113
+ created: z.coerce.date(),
114
+ acl: AccessControl.array().optional(),
115
+ });
116
+ export function getCalPermissionsInfo(cal, user) {
117
+ const ac = cal.acl.find(ac => ac.userId == user.id ||
118
+ (ac.role && user.roles.includes(ac.role)) ||
119
+ (ac.tag && user.tags.includes(ac.tag)) ||
120
+ (!ac.userId && !ac.role && !ac.tag));
121
+ if (!ac)
122
+ throw new Error('No ACL entry available to show permissions');
123
+ const perms = pickPermissions(ac);
124
+ return {
125
+ list: Object.entries(perms)
126
+ .filter(([, v]) => v)
127
+ .map(([k]) => k)
128
+ .join(', '),
129
+ icon: ac.manage ? 'user-gear' : ac.edit ? 'user-pen' : ac.read ? 'user' : 'user-lock',
130
+ perms,
131
+ };
132
+ }
133
+ const CalendarAPI = {
134
+ 'users/:id/calendars': {
135
+ PUT: [CalendarInit, Calendar],
136
+ GET: Calendar.required({ acl: true }).array(),
137
+ },
138
+ 'calendars/:id': {
139
+ GET: Calendar.required({ acl: true }),
140
+ PATCH: [CalendarInit, Calendar],
141
+ DELETE: Calendar,
142
+ },
143
+ 'calendars/:id/events': {
144
+ GET: [EventFilter, Event.array()],
145
+ PUT: [EventInit, Event],
146
+ },
147
+ 'events/:id': {
148
+ GET: Event,
149
+ PATCH: [EventInit, Event],
150
+ DELETE: Event,
151
+ },
152
+ };
153
+ Object.assign($API, CalendarAPI);
154
+ function formatDate(date) {
155
+ return date.toUTCString().replaceAll('-', '').replaceAll(':', '');
156
+ }
157
+ export function eventToICS(event) {
158
+ const lines = [
159
+ 'BEGIN:VCALENDAR',
160
+ 'VERSION:2.0',
161
+ `PRODID:-//Axium//Calendar ${$pkg.version}//EN`,
162
+ 'BEGIN:VEVENT',
163
+ 'UID:' + event.id,
164
+ 'DTSTAMP:' + formatDate(new Date()),
165
+ 'DTSTART:' + formatDate(event.start),
166
+ 'DTEND:' + formatDate(event.end),
167
+ 'SUMMARY:' + event.summary,
168
+ ];
169
+ if (event.description)
170
+ lines.push('DESCRIPTION:' + event.description);
171
+ if (event.location)
172
+ lines.push('LOCATION:' + event.location);
173
+ if (event.attendees) {
174
+ for (const attendee of event.attendees) {
175
+ let line = 'ATTENDEE';
176
+ if (attendee.status)
177
+ line += ';PARTSTAT=' + attendee.status.toUpperCase();
178
+ if (attendee.name)
179
+ line += ';CN=' + attendee.name;
180
+ if (attendee.role)
181
+ line += ';ROLE=' + attendee.role;
182
+ line += ':mailto:' + attendee.email;
183
+ lines.push(line);
184
+ }
185
+ }
186
+ if (event.recurrence)
187
+ lines.push('RRULE:' + event.recurrence);
188
+ lines.push('END:VEVENT');
189
+ lines.push('END:VCALENDAR');
190
+ return lines.join('\r\n');
191
+ }
192
+ export function eventFromICS(ics) {
193
+ throw 'eventFromICS not implemented yet';
194
+ }
@@ -0,0 +1,3 @@
1
+ import './common.js';
2
+ import './server.js';
3
+ export declare function statusText(): Promise<string>;
package/dist/hooks.js ADDED
@@ -0,0 +1,7 @@
1
+ import { count } from '@axium/server/database';
2
+ import './common.js';
3
+ import './server.js';
4
+ export async function statusText() {
5
+ const { calendars, events } = await count('calendars', 'events');
6
+ return `${calendars} calendars containing ${events} events`;
7
+ }
@@ -0,0 +1,5 @@
1
+ import type schema from '../db.json';
2
+ declare module '@axium/server/database' {
3
+ interface Schema extends FromSchemaFile<typeof schema> {
4
+ }
5
+ }