@openinc/parse-server-opendash 4.1.8 → 4.2.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.
Files changed (57) hide show
  1. package/dist/features/openservice/init.js +4 -0
  2. package/dist/features/openservice/schedules/calendarSync/types/Event.d.ts +2 -0
  3. package/dist/features/openservice/serviceSchedules/acl/assignmentAcl.d.ts +30 -0
  4. package/dist/features/openservice/serviceSchedules/acl/assignmentAcl.js +85 -0
  5. package/dist/features/openservice/serviceSchedules/acl/index.d.ts +1 -0
  6. package/dist/features/openservice/serviceSchedules/acl/index.js +6 -0
  7. package/dist/features/openservice/serviceSchedules/calendarSync/functions/eventToPlan.d.ts +50 -0
  8. package/dist/features/openservice/serviceSchedules/calendarSync/functions/eventToPlan.js +115 -0
  9. package/dist/features/openservice/serviceSchedules/calendarSync/functions/outlookRecurrence.d.ts +59 -0
  10. package/dist/features/openservice/serviceSchedules/calendarSync/functions/outlookRecurrence.js +353 -0
  11. package/dist/features/openservice/serviceSchedules/calendarSync/functions/planToEvent.d.ts +27 -0
  12. package/dist/features/openservice/serviceSchedules/calendarSync/functions/planToEvent.js +145 -0
  13. package/dist/features/openservice/serviceSchedules/calendarSync/index.d.ts +8 -0
  14. package/dist/features/openservice/serviceSchedules/calendarSync/index.js +28 -0
  15. package/dist/features/openservice/serviceSchedules/calendarSync/service/ServiceCalendarSyncPoller.d.ts +54 -0
  16. package/dist/features/openservice/serviceSchedules/calendarSync/service/ServiceCalendarSyncPoller.js +248 -0
  17. package/dist/features/openservice/serviceSchedules/calendarSync/service/serviceCalendarMeta.d.ts +55 -0
  18. package/dist/features/openservice/serviceSchedules/calendarSync/service/serviceCalendarMeta.js +109 -0
  19. package/dist/features/openservice/serviceSchedules/calendarSync/service/syncPlanToCalendar.d.ts +26 -0
  20. package/dist/features/openservice/serviceSchedules/calendarSync/service/syncPlanToCalendar.js +122 -0
  21. package/dist/features/openservice/serviceSchedules/calendarSync/types/ServiceSyncTypes.d.ts +55 -0
  22. package/dist/features/openservice/serviceSchedules/calendarSync/types/ServiceSyncTypes.js +8 -0
  23. package/dist/features/openservice/serviceSchedules/initServiceSchedulesFeature.d.ts +10 -0
  24. package/dist/features/openservice/serviceSchedules/initServiceSchedulesFeature.js +39 -0
  25. package/dist/features/openservice/serviceTickets/alarmActions/functions/alarmTicketFields.d.ts +35 -0
  26. package/dist/features/openservice/serviceTickets/alarmActions/functions/alarmTicketFields.js +103 -0
  27. package/dist/features/openservice/serviceTickets/alarmActions/index.d.ts +7 -0
  28. package/dist/features/openservice/serviceTickets/alarmActions/index.js +16 -0
  29. package/dist/features/openservice/serviceTickets/alarmActions/service/createAlarmTicket.d.ts +33 -0
  30. package/dist/features/openservice/serviceTickets/alarmActions/service/createAlarmTicket.js +179 -0
  31. package/dist/features/openservice/serviceTickets/alarmActions/service/ensureAlarmTicketAction.d.ts +22 -0
  32. package/dist/features/openservice/serviceTickets/alarmActions/service/ensureAlarmTicketAction.js +127 -0
  33. package/dist/features/openservice/serviceTickets/alarmActions/types/AlarmTicketTypes.d.ts +80 -0
  34. package/dist/features/openservice/serviceTickets/alarmActions/types/AlarmTicketTypes.js +28 -0
  35. package/dist/features/permissions/types/Permissions.d.ts +1 -2
  36. package/dist/features/permissions/types/Permissions.js +8 -2
  37. package/dist/functions/openinc-openservice-alarm-create-ticket.d.ts +25 -0
  38. package/dist/functions/openinc-openservice-alarm-create-ticket.js +63 -0
  39. package/dist/functions/openinc-openservice-notify-contacts.d.ts +16 -0
  40. package/dist/functions/openinc-openservice-notify-contacts.js +82 -0
  41. package/dist/functions/openinc-openservice-schedule-calendar-sync.d.ts +15 -0
  42. package/dist/functions/openinc-openservice-schedule-calendar-sync.js +60 -0
  43. package/dist/hooks/Service_Schedule.js +89 -16
  44. package/dist/hooks/Service_Schedule_Template.js +28 -7
  45. package/dist/hooks/Service_Ticket.js +2 -2
  46. package/dist/types/Service_Schedule.d.ts +12 -0
  47. package/dist/types/Service_Schedule.js +21 -0
  48. package/dist/types/Service_Schedule_Execution.d.ts +6 -3
  49. package/dist/types/Service_Schedule_Execution.js +6 -0
  50. package/dist/types/Service_Schedule_Template.d.ts +7 -0
  51. package/dist/types/Service_Schedule_Template.js +12 -0
  52. package/package.json +5 -4
  53. package/schema/Service_Schedule.json +17 -0
  54. package/schema/Service_Schedule_Execution.json +5 -0
  55. package/schema/Service_Schedule_Template.json +9 -0
  56. package/dist/features/ruleset/applyRuleSet.d.ts +0 -41
  57. package/dist/features/ruleset/applyRuleSet.js +0 -81
@@ -5,6 +5,8 @@ const initIssuecategory_js_1 = require("./issuecategory/initIssuecategory.js");
5
5
  const initKanbanStates_js_1 = require("./kanbanState/initKanbanStates.js");
6
6
  const initMessages_js_1 = require("./messages/initMessages.js");
7
7
  const initSchedulesFeature_js_1 = require("./schedules/initSchedulesFeature.js");
8
+ const initServiceSchedulesFeature_js_1 = require("./serviceSchedules/initServiceSchedulesFeature.js");
9
+ const index_js_1 = require("./serviceTickets/alarmActions/index.js");
8
10
  const checkStatus_js_1 = require("./schedules/mappings/checkStatus.js");
9
11
  const reassignScheduleExecutions_js_1 = require("./schedules/mappings/reassignScheduleExecutions.js");
10
12
  const initTicket_js_1 = require("./ticket/initTicket.js");
@@ -20,5 +22,7 @@ async function init() {
20
22
  await (0, reassignScheduleExecutions_js_1.reassignScheduleExecutions)();
21
23
  await (0, checkStatus_js_1.checkStatus)();
22
24
  await (0, initSchedulesFeature_js_1.initSchedulesFeature)();
25
+ await (0, initServiceSchedulesFeature_js_1.initServiceSchedulesFeature)();
26
+ await (0, index_js_1.ensureAlarmTicketAction)();
23
27
  console.log("open.SERVICE feature initialized.");
24
28
  }
@@ -35,6 +35,8 @@ export type RecurrencePattern = {
35
35
  daysOfWeek?: Array<"Sunday" | "Monday" | "Tuesday" | "Wednesday" | "Thursday" | "Friday" | "Saturday">;
36
36
  firstDayOfWeek?: "Sunday" | "Monday" | "Tuesday" | "Wednesday" | "Thursday" | "Friday" | "Saturday";
37
37
  month?: number;
38
+ /** Day of the month for `absoluteMonthly` / `absoluteYearly` patterns. */
39
+ dayOfMonth?: number;
38
40
  index?: "first" | "second" | "third" | "fourth" | "last";
39
41
  };
40
42
  export type RecurrenceRange = {
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Marks a save this module made itself, so the `afterSave` that follows it does
3
+ * not start over. Without it every ACL write would trigger another pass — the
4
+ * second one is a no-op and would stop, but the round trip is pure waste.
5
+ */
6
+ export declare const ASSIGNMENT_ACL_CONTEXT_FLAG = "fromAssignmentAcl";
7
+ /** Which relations grant read access on this class. */
8
+ export interface AssignmentAclOptions {
9
+ /** Relation of `_Role`s that may read the record. */
10
+ roleRelations: string[];
11
+ /** Relation of `_User`s that may read the record. */
12
+ userRelations?: string[];
13
+ }
14
+ /**
15
+ * Who may see a maintenance plan (or a template): its creator, the roles and
16
+ * users assigned to it, and the administrators — nobody else.
17
+ *
18
+ * **Why this is rebuilt rather than amended.** Un-assigning somebody has to take
19
+ * their access away again, and an ACL carries no record of *why* a grant is
20
+ * there. Adding grants on every save would therefore be a one-way door: a role
21
+ * removed from the plan would keep reading it forever. The ACL is derived from
22
+ * the assignments instead, so it always says exactly what the assignments say.
23
+ *
24
+ * `od-admin` and `od-tenant-admin-<tenant>` are re-added by `defaultAclHandler`
25
+ * on the save that follows, so an assignment can never lock administrators out.
26
+ *
27
+ * Runs in `afterSave`, not `beforeSave`: relations are only queryable once the
28
+ * pending add/remove operations have been applied, which happens on save.
29
+ */
30
+ export declare function syncAssignmentAcl(object: Parse.Object, options: AssignmentAclOptions): Promise<void>;
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ASSIGNMENT_ACL_CONTEXT_FLAG = void 0;
7
+ exports.syncAssignmentAcl = syncAssignmentAcl;
8
+ const node_1 = __importDefault(require("parse/node"));
9
+ /**
10
+ * Marks a save this module made itself, so the `afterSave` that follows it does
11
+ * not start over. Without it every ACL write would trigger another pass — the
12
+ * second one is a no-op and would stop, but the round trip is pure waste.
13
+ */
14
+ exports.ASSIGNMENT_ACL_CONTEXT_FLAG = "fromAssignmentAcl";
15
+ /**
16
+ * Who may see a maintenance plan (or a template): its creator, the roles and
17
+ * users assigned to it, and the administrators — nobody else.
18
+ *
19
+ * **Why this is rebuilt rather than amended.** Un-assigning somebody has to take
20
+ * their access away again, and an ACL carries no record of *why* a grant is
21
+ * there. Adding grants on every save would therefore be a one-way door: a role
22
+ * removed from the plan would keep reading it forever. The ACL is derived from
23
+ * the assignments instead, so it always says exactly what the assignments say.
24
+ *
25
+ * `od-admin` and `od-tenant-admin-<tenant>` are re-added by `defaultAclHandler`
26
+ * on the save that follows, so an assignment can never lock administrators out.
27
+ *
28
+ * Runs in `afterSave`, not `beforeSave`: relations are only queryable once the
29
+ * pending add/remove operations have been applied, which happens on save.
30
+ */
31
+ async function syncAssignmentAcl(object, options) {
32
+ const acl = new node_1.default.ACL();
33
+ // Administrators. Mirrors `defaultAclHandler` so the ACL is already complete
34
+ // when it is written, rather than only after the next save.
35
+ acl.setRoleReadAccess("od-admin", true);
36
+ acl.setRoleWriteAccess("od-admin", true);
37
+ const tenant = object.get("tenant");
38
+ if (tenant) {
39
+ acl.setRoleReadAccess(`od-tenant-admin-${tenant.id}`, true);
40
+ acl.setRoleWriteAccess(`od-tenant-admin-${tenant.id}`, true);
41
+ }
42
+ // The creator keeps write — the record is theirs.
43
+ const creator = object.get("user");
44
+ if (creator?.id) {
45
+ acl.setReadAccess(creator.id, true);
46
+ acl.setWriteAccess(creator.id, true);
47
+ }
48
+ for (const relation of options.roleRelations) {
49
+ for (const role of await readRelation(object, relation)) {
50
+ const name = role.get("name");
51
+ // Roles are addressed by name in an ACL, not by id.
52
+ if (name)
53
+ acl.setRoleReadAccess(name, true);
54
+ }
55
+ }
56
+ for (const relation of options.userRelations ?? []) {
57
+ for (const user of await readRelation(object, relation)) {
58
+ if (user.id)
59
+ acl.setReadAccess(user.id, true);
60
+ }
61
+ }
62
+ if (sameAcl(object.getACL(), acl))
63
+ return;
64
+ object.setACL(acl);
65
+ await object.save(null, {
66
+ useMasterKey: true,
67
+ context: { [exports.ASSIGNMENT_ACL_CONTEXT_FLAG]: true },
68
+ });
69
+ }
70
+ /** The members of a relation, or an empty list when it has none. */
71
+ async function readRelation(object, field) {
72
+ try {
73
+ return await object.relation(field).query().find({ useMasterKey: true });
74
+ }
75
+ catch {
76
+ // A relation that was never written has no backing table yet.
77
+ return [];
78
+ }
79
+ }
80
+ /** Whether two ACLs grant exactly the same thing. */
81
+ function sameAcl(current, next) {
82
+ if (!current)
83
+ return false;
84
+ return JSON.stringify(current.toJSON()) === JSON.stringify(next.toJSON());
85
+ }
@@ -0,0 +1 @@
1
+ export { ASSIGNMENT_ACL_CONTEXT_FLAG, syncAssignmentAcl, type AssignmentAclOptions, } from "./assignmentAcl.js";
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.syncAssignmentAcl = exports.ASSIGNMENT_ACL_CONTEXT_FLAG = void 0;
4
+ var assignmentAcl_js_1 = require("./assignmentAcl.js");
5
+ Object.defineProperty(exports, "ASSIGNMENT_ACL_CONTEXT_FLAG", { enumerable: true, get: function () { return assignmentAcl_js_1.ASSIGNMENT_ACL_CONTEXT_FLAG; } });
6
+ Object.defineProperty(exports, "syncAssignmentAcl", { enumerable: true, get: function () { return assignmentAcl_js_1.syncAssignmentAcl; } });
@@ -0,0 +1,50 @@
1
+ import type { GraphEventResponse } from "../../../schedules/calendarSync/types/Event.js";
2
+ import type { ServiceCadence, ServiceNotificationRule } from "../types/ServiceSyncTypes.js";
3
+ /**
4
+ * The rule id the sync owns.
5
+ *
6
+ * An Outlook reminder becomes one notification rule on the plan, and it always
7
+ * gets **this** id: the sync may replace or remove its own rule on every
8
+ * inbound change, and must never touch the rules a user wrote in the plan
9
+ * editor. A stable id also keeps the scheduler's dedup key
10
+ * (`{scheduleId, ruleId, occurrence}`) stable, so re-syncing an unchanged event
11
+ * does not notify twice.
12
+ */
13
+ export declare const OUTLOOK_REMINDER_RULE_ID = "outlook-reminder";
14
+ /** What an Outlook event says about a maintenance plan. */
15
+ export type PlanFieldsFromEvent = {
16
+ /** Plan title — the subject, minus the machine prefix if there was one. */
17
+ title: string;
18
+ /** Machine name from the subject convention, if the subject used it. */
19
+ equipmentName?: string;
20
+ cadence: ServiceCadence;
21
+ /** The event's note, kept as the description of the plan's first step. */
22
+ description?: string;
23
+ /** The event's reminder, as the sync's own notification rule. */
24
+ notification?: ServiceNotificationRule;
25
+ };
26
+ /**
27
+ * Read a calendar event as maintenance plan fields.
28
+ *
29
+ * Pure — resolving the machine and writing the plan is the poller's job.
30
+ * Returns `undefined` for an event without a usable start, which is the one
31
+ * thing a plan cannot do without.
32
+ */
33
+ export declare function eventToPlanFields(event: GraphEventResponse): PlanFieldsFromEvent | undefined;
34
+ /**
35
+ * Split `"Flex 1" - Ölwechsel` into machine and title.
36
+ *
37
+ * Same convention as v1 (separator `" - "`, quotes optional), where the prefix
38
+ * named a `Maintenance_Source`; in v2 it names the machine. Unlike v1 the
39
+ * prefix is *removed* from the title — the machine is a field of its own here,
40
+ * and repeating it in the title is what made v1's plan lists unreadable.
41
+ */
42
+ export declare function parseSubject(subject: string | undefined): {
43
+ equipmentName?: string;
44
+ title: string;
45
+ };
46
+ /**
47
+ * Put the sync's rule into a plan's rule list, replacing an older one of its
48
+ * own and leaving every user-written rule untouched.
49
+ */
50
+ export declare function mergeReminderRule(existing: ServiceNotificationRule[] | undefined, rule: ServiceNotificationRule | undefined): ServiceNotificationRule[];
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OUTLOOK_REMINDER_RULE_ID = void 0;
4
+ exports.eventToPlanFields = eventToPlanFields;
5
+ exports.parseSubject = parseSubject;
6
+ exports.mergeReminderRule = mergeReminderRule;
7
+ const outlookRecurrence_js_1 = require("./outlookRecurrence.js");
8
+ /**
9
+ * The rule id the sync owns.
10
+ *
11
+ * An Outlook reminder becomes one notification rule on the plan, and it always
12
+ * gets **this** id: the sync may replace or remove its own rule on every
13
+ * inbound change, and must never touch the rules a user wrote in the plan
14
+ * editor. A stable id also keeps the scheduler's dedup key
15
+ * (`{scheduleId, ruleId, occurrence}`) stable, so re-syncing an unchanged event
16
+ * does not notify twice.
17
+ */
18
+ exports.OUTLOOK_REMINDER_RULE_ID = "outlook-reminder";
19
+ /**
20
+ * Read a calendar event as maintenance plan fields.
21
+ *
22
+ * Pure — resolving the machine and writing the plan is the poller's job.
23
+ * Returns `undefined` for an event without a usable start, which is the one
24
+ * thing a plan cannot do without.
25
+ */
26
+ function eventToPlanFields(event) {
27
+ const start = eventStart(event);
28
+ if (!start)
29
+ return undefined;
30
+ const { equipmentName, title } = parseSubject(event.subject);
31
+ return {
32
+ title: title || "Outlook",
33
+ equipmentName,
34
+ cadence: (0, outlookRecurrence_js_1.eventToCadence)(start, event.recurrence ?? undefined),
35
+ description: event.bodyPreview?.trim() || undefined,
36
+ notification: reminderToRule(event),
37
+ };
38
+ }
39
+ /**
40
+ * The start of the series as an absolute instant.
41
+ *
42
+ * Graph returns `dateTime` in the time zone named next to it, and this server
43
+ * asks for none — so in practice it is UTC and the time of day survives, which
44
+ * matters because the plan's `dtstart` is what the frontend shows. If the event
45
+ * ever arrives in another zone, the day is kept and the time falls back to
46
+ * midnight rather than being shifted by a guess.
47
+ */
48
+ function eventStart(event) {
49
+ const raw = event.start?.dateTime;
50
+ if (!raw)
51
+ return undefined;
52
+ const zone = event.start?.timeZone;
53
+ if (!zone || /^utc$/i.test(zone)) {
54
+ // Graph omits the offset; the trailing fraction can be 7 digits long.
55
+ const normalized = raw.endsWith("Z") ? raw : `${raw.slice(0, 23)}Z`;
56
+ const parsed = new Date(normalized);
57
+ if (!Number.isNaN(parsed.getTime()))
58
+ return parsed;
59
+ }
60
+ const dateOnly = new Date(`${raw.slice(0, 10)}T00:00:00.000Z`);
61
+ return Number.isNaN(dateOnly.getTime()) ? undefined : dateOnly;
62
+ }
63
+ /**
64
+ * Split `"Flex 1" - Ölwechsel` into machine and title.
65
+ *
66
+ * Same convention as v1 (separator `" - "`, quotes optional), where the prefix
67
+ * named a `Maintenance_Source`; in v2 it names the machine. Unlike v1 the
68
+ * prefix is *removed* from the title — the machine is a field of its own here,
69
+ * and repeating it in the title is what made v1's plan lists unreadable.
70
+ */
71
+ function parseSubject(subject) {
72
+ const value = (subject ?? "").trim();
73
+ const separator = value.indexOf(" - ");
74
+ if (separator === -1)
75
+ return { title: value };
76
+ const prefix = value
77
+ .slice(0, separator)
78
+ .trim()
79
+ .replace(/^"(.*)"$/, "$1")
80
+ .trim();
81
+ const rest = value.slice(separator + 3).trim();
82
+ if (!prefix || !rest)
83
+ return { title: value };
84
+ return { equipmentName: prefix, title: rest };
85
+ }
86
+ /**
87
+ * An Outlook reminder as a notification rule.
88
+ *
89
+ * Outlook counts minutes, a plan counts days — a maintenance date is a day, not
90
+ * a minute. Anything under a day therefore becomes "on the due date" instead of
91
+ * being rounded up to a day early.
92
+ */
93
+ function reminderToRule(event) {
94
+ if (!event.isReminderOn)
95
+ return undefined;
96
+ const minutes = event.reminderMinutesBeforeStart;
97
+ if (typeof minutes !== "number" || minutes < 0)
98
+ return undefined;
99
+ const offsetDays = Math.floor(minutes / (60 * 24));
100
+ return {
101
+ id: exports.OUTLOOK_REMINDER_RULE_ID,
102
+ trigger: offsetDays > 0 ? "before" : "due",
103
+ ...(offsetDays > 0 ? { offsetDays } : {}),
104
+ assignedRoles: true,
105
+ serviceProviders: true,
106
+ };
107
+ }
108
+ /**
109
+ * Put the sync's rule into a plan's rule list, replacing an older one of its
110
+ * own and leaving every user-written rule untouched.
111
+ */
112
+ function mergeReminderRule(existing, rule) {
113
+ const others = (existing ?? []).filter((entry) => entry.id !== exports.OUTLOOK_REMINDER_RULE_ID);
114
+ return rule ? [...others, rule] : others;
115
+ }
@@ -0,0 +1,59 @@
1
+ import type { Recurrence } from "../../../schedules/calendarSync/types/Event.js";
2
+ import type { ServiceCadence } from "../types/ServiceSyncTypes.js";
3
+ /** A recurrence rule taken apart into its parts. */
4
+ export type RRuleParts = {
5
+ freq?: "DAILY" | "WEEKLY" | "MONTHLY" | "YEARLY";
6
+ interval?: number;
7
+ byday?: string[];
8
+ bymonthday?: number[];
9
+ bymonth?: number[];
10
+ count?: number;
11
+ wkst?: string;
12
+ /**
13
+ * "The nth of the matching days" — `BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1` is
14
+ * "last workday of the month".
15
+ *
16
+ * Outlook has no equivalent: its `relativeMonthly` pattern takes *one*
17
+ * weekday and a position. With a single weekday the two agree (that is the
18
+ * ordinal `BYDAY` form); with several, the rule cannot be expressed — see
19
+ * {@link calendarUnsupportedReason}.
20
+ */
21
+ bysetpos?: number;
22
+ };
23
+ /**
24
+ * Read an RRULE string into its parts.
25
+ *
26
+ * Tolerant on purpose: an unknown key is dropped rather than throwing, because
27
+ * a rule that came from Outlook, from the editor, or from a hand-written import
28
+ * all end up here, and a single unexpected part must not stop a sync.
29
+ */
30
+ export declare function parseRRule(rrule: string | undefined): RRuleParts;
31
+ /** Write the parts back into an RRULE string, in the canonical part order. */
32
+ export declare function buildRRule(parts: RRuleParts): string;
33
+ /**
34
+ * An Outlook event's timing, as a v2 cadence.
35
+ *
36
+ * A non-recurring event becomes a one-off — the same shape the frontend's
37
+ * `oneOffCadence()` produces (`FREQ=DAILY;INTERVAL=1;COUNT=1`), so a plan
38
+ * created from a single Outlook appointment is indistinguishable from one
39
+ * created by hand.
40
+ */
41
+ export declare function eventToCadence(start: Date, recurrence?: Recurrence): ServiceCadence;
42
+ /**
43
+ * Why this cadence cannot be written to a calendar — `undefined` when it can.
44
+ *
45
+ * Outlook's recurrence vocabulary is a **subset** of RFC 5545, and the gap is
46
+ * not cosmetic: a rule we cannot express would have to be approximated, and an
47
+ * approximated maintenance date in someone's calendar is worse than no entry
48
+ * at all. The sync therefore leaves such plans alone instead of guessing (see
49
+ * `syncPlanToCalendar`).
50
+ */
51
+ export declare function calendarUnsupportedReason(cadence: ServiceCadence | undefined): string | undefined;
52
+ /**
53
+ * A v2 cadence as an Outlook recurrence — `undefined` for a one-off, which
54
+ * Graph expects as an event without a `recurrence` at all.
55
+ *
56
+ * `startDate` of the range is always the series start: Graph rejects a
57
+ * recurrence whose range starts on a different day than the event.
58
+ */
59
+ export declare function cadenceToRecurrence(cadence: ServiceCadence | undefined, start: Date): Recurrence | undefined;