slotlock 0.7.0 → 0.9.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.
Files changed (57) hide show
  1. package/CHANGELOG.md +281 -0
  2. package/agent-server.d.ts +25 -5
  3. package/agent-server.js +152 -7
  4. package/agent-store-backend.d.ts +6 -1
  5. package/agent-store-backend.js +861 -55
  6. package/application-api.d.ts +19 -1
  7. package/application-api.js +578 -21
  8. package/approvals.d.ts +2 -0
  9. package/approvals.js +12 -5
  10. package/assistant.d.ts +64 -9
  11. package/assistant.js +450 -82
  12. package/availability-store.d.ts +193 -0
  13. package/availability-store.js +558 -0
  14. package/booking-rules.d.ts +92 -5
  15. package/booking-rules.js +243 -15
  16. package/dashboard-assistant-ui.d.ts +21 -0
  17. package/dashboard-assistant-ui.js +539 -0
  18. package/dashboard-markdown.d.ts +2 -0
  19. package/dashboard-markdown.js +157 -0
  20. package/dashboard-settings-ui.d.ts +1 -0
  21. package/dashboard-settings-ui.js +78 -0
  22. package/dashboard.d.ts +84 -11
  23. package/dashboard.js +1654 -384
  24. package/ddl.d.ts +5 -1
  25. package/ddl.js +760 -9
  26. package/event-feed.d.ts +1 -1
  27. package/event-feed.js +18 -0
  28. package/holiday-import.d.ts +38 -0
  29. package/holiday-import.js +57 -0
  30. package/holidays.d.ts +38 -0
  31. package/holidays.js +150 -0
  32. package/hours.d.ts +125 -0
  33. package/hours.js +224 -0
  34. package/index.d.ts +14 -3
  35. package/index.js +10 -2
  36. package/interop.d.ts +25 -7
  37. package/interop.js +145 -17
  38. package/lifecycle.d.ts +31 -0
  39. package/lifecycle.js +115 -0
  40. package/maintenance.d.ts +19 -0
  41. package/maintenance.js +55 -1
  42. package/metrics.js +3 -0
  43. package/openapi.json +16237 -7795
  44. package/package.json +1 -1
  45. package/ranges.d.ts +28 -0
  46. package/ranges.js +135 -0
  47. package/resource-search.d.ts +19 -0
  48. package/resource-search.js +153 -0
  49. package/rest-api.js +7 -0
  50. package/rules.d.ts +2 -0
  51. package/rules.js +2 -0
  52. package/self-host.d.ts +1 -1
  53. package/self-host.js +221 -21
  54. package/slotlock.manifest.json +1 -1
  55. package/store.d.ts +367 -24
  56. package/store.js +1567 -97
  57. package/types.d.ts +198 -0
package/types.d.ts CHANGED
@@ -5,6 +5,13 @@ export interface Interval {
5
5
  start: Date;
6
6
  end: Date;
7
7
  }
8
+ /** Hours a resource without its own inherits, as the database resolves them (RFC 0004 B2). */
9
+ export interface SlotlockInheritedAvailability {
10
+ source: 'location' | 'tenant';
11
+ /** The location whose hours apply, when `source` is `location`. */
12
+ location?: string;
13
+ rules: WeeklyAvailabilityRule[];
14
+ }
8
15
  /** A schedulable resource (vehicle, room, worker, machine, or any other reservable asset). */
9
16
  export interface SlotlockResource {
10
17
  id: string;
@@ -22,6 +29,11 @@ export interface SlotlockResource {
22
29
  * uses the default (`SLOTLOCK_AVAILABILITY`, or the backend's `availabilityRules`).
23
30
  */
24
31
  availabilityRules?: WeeklyAvailabilityRule[];
32
+ /**
33
+ * The hours it inherits when it has none of its own (RFC 0004 B2): its location's, the nearest
34
+ * up the chain, else its tenant's. Absent when it has its own, or inherits the server's.
35
+ */
36
+ inheritedAvailability?: SlotlockInheritedAvailability;
25
37
  /**
26
38
  * `shared` resources are listed to and booked by agents; `private` ones are invisible to agents.
27
39
  * Fixed at creation; absent means shared.
@@ -89,6 +101,11 @@ export interface SlotlockApprovalPolicy {
89
101
  /** How long a person has to decide, before the booking's start less `leadMinutes`. */
90
102
  approvalTtlMinutes: number;
91
103
  leadMinutes: number;
104
+ /**
105
+ * It also holds the tenant's own bookings through the application API (bundles and pool
106
+ * bookings); without it, it holds only agents' (RFC 0004 B7).
107
+ */
108
+ includeApplication: boolean;
92
109
  createdAt: Date;
93
110
  updatedAt: Date;
94
111
  }
@@ -148,6 +165,12 @@ export interface SlotlockReservation {
148
165
  /** Monotonic command revision; legacy, hold, and shadow rows start at 1. */
149
166
  revision: number;
150
167
  source: string | null;
168
+ /** Whom to tell about it (RFC 0004 P3), when it names someone. */
169
+ contactRef?: string;
170
+ /** Present, and false, when no one should be notified about it. */
171
+ notify?: false;
172
+ /** Minutes before its start it is reminded, latest first, when it is. */
173
+ reminderMinutes?: number[];
151
174
  }
152
175
  export interface ExternalReservationCommand {
153
176
  tenantRef: string;
@@ -158,6 +181,34 @@ export interface ExternalReservationCommand {
158
181
  bufferAfterMs: number;
159
182
  revision: number;
160
183
  source?: string;
184
+ /**
185
+ * An admin's reason for moving the booking past its reschedule cutoff (RFC 0004 B5), kept on the
186
+ * feed. Without it, a move past the cutoff is refused.
187
+ */
188
+ overrideReason?: string;
189
+ /**
190
+ * The wall time the booking starts at in its resource's zone, `YYYY-MM-DDTHH:MM[:SS]` (RFC 0004
191
+ * B9): it must name `start` now, and if zone rules later move it, maintenance says so or follows
192
+ * it (`followZoneRules`). Left out, the booking keeps its instant only.
193
+ */
194
+ localStart?: string;
195
+ /**
196
+ * Whom to tell about it (RFC 0004 P3): a contact's reference (`putContact`), never the person.
197
+ * Put on the feed with each of its changes; a sender looks the person up by it.
198
+ */
199
+ contactRef?: string;
200
+ /** False: no sender should notify anyone about it (default true). On the feed only when false. */
201
+ notify?: boolean;
202
+ /**
203
+ * Minutes before its start to remind whom it names (RFC 0004 P3): up to five, each 0 to a year,
204
+ * said on the feed once each as `reminder.due` by maintenance, and again if the booking moves.
205
+ */
206
+ reminderMinutes?: number[];
207
+ }
208
+ /** What a cancellation records for the integrator to charge (RFC 0004 B5). Slotlock moves no money. */
209
+ export interface SlotlockRecordedFee {
210
+ amountMinor: number;
211
+ currency: string;
161
212
  }
162
213
  export type ExternalReservationResult = {
163
214
  ok: true;
@@ -166,6 +217,14 @@ export type ExternalReservationResult = {
166
217
  } | {
167
218
  ok: false;
168
219
  code: 'invalid_window';
220
+ }
221
+ /** `localStart` is not a wall time, or does not name `start` in the resource's zone. */
222
+ | {
223
+ ok: false;
224
+ code: 'invalid_local_start';
225
+ } | {
226
+ ok: false;
227
+ code: 'invalid_contact_ref' | 'invalid_notify' | 'invalid_reminders';
169
228
  } | {
170
229
  ok: false;
171
230
  code: 'invalid_buffer';
@@ -227,6 +286,13 @@ export type CancelExternalReservationResult = {
227
286
  ok: true;
228
287
  cancelled: boolean;
229
288
  revision: number;
289
+ cancellationFee?: SlotlockRecordedFee;
290
+ }
291
+ /** The booking's cancel cutoff has passed (RFC 0004 B5): an admin may override with a reason. */
292
+ | {
293
+ ok: false;
294
+ code: 'booking_rule_violated';
295
+ rule: 'cancel_cutoff';
230
296
  } | {
231
297
  ok: false;
232
298
  code: 'invalid_identity';
@@ -317,6 +383,21 @@ export interface SlotlockHold {
317
383
  source: string | null;
318
384
  /** The alternatives it was held with (`groupId` when acquired); absent when held alone. */
319
385
  groupId?: string;
386
+ /** Present when it waits for payment (RFC 0004 C3): it is confirmed only once paid. */
387
+ paymentRequired?: true;
388
+ /** The payment's reference at the integrator's processor, once paid. */
389
+ paymentRef?: string | null;
390
+ paidAt?: Date | null;
391
+ /** What the payment must be, when the hold names it. */
392
+ paymentAmount?: {
393
+ amountMinor: number;
394
+ currency: string;
395
+ } | null;
396
+ /** What was received, once paid and when the payer said. */
397
+ paidAmount?: {
398
+ amountMinor: number;
399
+ currency: string;
400
+ } | null;
320
401
  }
321
402
  /** One entry of a tenant's agenda (`listAgenda`): what occupies a resource, and from when. */
322
403
  export interface SlotlockAgendaEntry {
@@ -345,6 +426,11 @@ export type AcquireHoldResult =
345
426
  } | {
346
427
  ok: false;
347
428
  code: 'invalid_ttl';
429
+ }
430
+ /** `payment` without `paymentRequired`, or not an amount and an ISO 4217 currency. */
431
+ | {
432
+ ok: false;
433
+ code: 'invalid_payment';
348
434
  } | {
349
435
  ok: false;
350
436
  code: 'invalid_buffer';
@@ -412,6 +498,11 @@ export type ConfirmHoldResult = {
412
498
  ok: false;
413
499
  code: 'hold_expired';
414
500
  }
501
+ /** The hold waits for payment (RFC 0004 C3) and none is recorded yet (markHoldPaid). */
502
+ | {
503
+ ok: false;
504
+ code: 'payment_required';
505
+ }
415
506
  /** A cancellation of the hold's reference (its id) arrived first and retired it. */
416
507
  | {
417
508
  ok: false;
@@ -437,6 +528,71 @@ export type ReleaseHoldResult = {
437
528
  ok: true;
438
529
  released: boolean;
439
530
  };
531
+ /**
532
+ * A person a booking may name to be told about it (RFC 0004 P3), by the tenant's own reference: how
533
+ * to reach them, and which channels they agreed to. A sender reads it; Slotlock sends nothing.
534
+ */
535
+ export interface SlotlockContact {
536
+ ref: string;
537
+ name: string | null;
538
+ email: string | null;
539
+ /** E.164, for SMS. */
540
+ phone: string | null;
541
+ /** E.164, for WhatsApp. */
542
+ whatsapp: string | null;
543
+ /** Each channel the person said yes or no to; a channel left out has no answer recorded. */
544
+ consent: SlotlockContactConsent;
545
+ /** When each answer in `consent` was given (kept while it stands): the proof of it. */
546
+ consentAt: Partial<Record<keyof SlotlockContactConsent, Date>>;
547
+ /** BCP 47, for the language of what is sent. */
548
+ locale: string | null;
549
+ createdAt: Date;
550
+ updatedAt: Date;
551
+ }
552
+ export interface SlotlockContactConsent {
553
+ email?: boolean;
554
+ sms?: boolean;
555
+ whatsapp?: boolean;
556
+ }
557
+ export type PutContactResult = {
558
+ ok: true;
559
+ contact: SlotlockContact;
560
+ created: boolean;
561
+ } | {
562
+ ok: false;
563
+ code: 'invalid_contact_ref' | 'invalid_contact';
564
+ } | {
565
+ ok: false;
566
+ code: 'contact_limit_reached';
567
+ limit: number;
568
+ };
569
+ /** Recording a hold's payment (RFC 0004 C3); `idempotent` when it was recorded already. */
570
+ export type MarkHoldPaidResult = {
571
+ ok: true;
572
+ hold: SlotlockHold;
573
+ idempotent: boolean;
574
+ } | {
575
+ ok: false;
576
+ code: 'invalid_identity' | 'invalid_payment_ref' | 'invalid_payment';
577
+ }
578
+ /** Less than the hold's payment, in another currency, or not said when the hold names one. */
579
+ | {
580
+ ok: false;
581
+ code: 'payment_amount_mismatch';
582
+ } | {
583
+ ok: false;
584
+ code: 'hold_not_found' | 'hold_expired';
585
+ }
586
+ /** The hold does not wait for payment. */
587
+ | {
588
+ ok: false;
589
+ code: 'payment_not_required';
590
+ }
591
+ /** It was already paid by another payment. */
592
+ | {
593
+ ok: false;
594
+ code: 'payment_conflict';
595
+ };
440
596
  /** RFC 5545 TRANSP projection. Transparent events remain visible but never occupy a resource. */
441
597
  export type CalendarTransparency = 'opaque' | 'transparent';
442
598
  export type CalendarEventStatus = 'confirmed' | 'tentative';
@@ -543,6 +699,11 @@ export interface PutCalendarEventCommand extends CalendarEventContent {
543
699
  holdId: string;
544
700
  holderRef: string;
545
701
  };
702
+ /**
703
+ * False: no sender should notify anyone about it (RFC 0004 P3), and its reminders are not put on
704
+ * the feed. Default true; as sent with each revision.
705
+ */
706
+ notify?: boolean;
546
707
  }
547
708
  export type PutCalendarEventResult = {
548
709
  ok: true;
@@ -604,6 +765,11 @@ export type PutCalendarEventResult = {
604
765
  ok: false;
605
766
  code: 'hold_expired';
606
767
  }
768
+ /** `fromHold`'s hold waits for payment (RFC 0004 C3) and none is recorded yet. */
769
+ | {
770
+ ok: false;
771
+ code: 'payment_required';
772
+ }
607
773
  /** `fromHold`'s hold is on another resource, or does not cover the event. */
608
774
  | {
609
775
  ok: false;
@@ -644,6 +810,8 @@ export type CancelCalendarEventResult = {
644
810
  externalRef: string;
645
811
  revision: number;
646
812
  idempotent: boolean;
813
+ /** What a late cancellation costs, recorded for the integrator to charge (RFC 0004 B5). */
814
+ cancellationFee?: SlotlockRecordedFee;
647
815
  } | {
648
816
  ok: false;
649
817
  code: 'invalid_identity';
@@ -664,6 +832,12 @@ export type CancelCalendarEventResult = {
664
832
  ok: false;
665
833
  code: 'revision_conflict';
666
834
  currentRevision: number;
835
+ }
836
+ /** The event's cancel cutoff has passed (RFC 0004 B5). */
837
+ | {
838
+ ok: false;
839
+ code: 'booking_rule_violated';
840
+ rule: 'cancel_cutoff';
667
841
  };
668
842
  /** A place in the roll's order of due masters: the last one a batch examined. */
669
843
  export interface CalendarHorizonRollCursor {
@@ -763,6 +937,30 @@ export type CalendarCoverageResult = {
763
937
  code: 'coverage_limit_reached';
764
938
  limit: number;
765
939
  };
940
+ /**
941
+ * What a calendar that gives busy time but not its events says (Google freeBusy, Microsoft
942
+ * getSchedule; RFC 0004 C1): its coverage of the window, and the busy time in it, which replaces
943
+ * what the source said of that window before.
944
+ */
945
+ export interface SourceBusyCommand extends CalendarCoverageCommand {
946
+ /** At most SLOTLOCK_SOURCE_BUSY_MAX intervals; clipped to the window and merged. */
947
+ busy: Interval[];
948
+ }
949
+ export type SourceBusyResult = {
950
+ ok: true;
951
+ revision: number;
952
+ /** A revision already recorded: nothing changed. */
953
+ idempotent: boolean;
954
+ /** How many intervals are kept, merged and clipped. */
955
+ busy: number;
956
+ /** Bookings the source newly reports in the way of, each put on the feed as coverage.conflict. */
957
+ conflicts: number;
958
+ } | Exclude<CalendarCoverageResult, {
959
+ ok: true;
960
+ }> | {
961
+ ok: false;
962
+ code: 'invalid_busy';
963
+ };
766
964
  export interface CalendarCoverageAssessment {
767
965
  state: 'complete' | 'partial' | 'unknown';
768
966
  /** Required sources that are absent, stale, or do not fully cover the requested window. */