@things-factory/calendar 10.1.234 → 10.1.241

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 (49) hide show
  1. package/dist-client/pages/calendar/calendar-days-off-page.d.ts +43 -0
  2. package/dist-client/pages/calendar/calendar-days-off-page.js +161 -0
  3. package/dist-client/pages/calendar/calendar-days-off-page.js.map +1 -0
  4. package/dist-client/pages/calendar/calendar-list-page.js +33 -5
  5. package/dist-client/pages/calendar/calendar-list-page.js.map +1 -1
  6. package/dist-client/pages/event/event-list-page.d.ts +4 -0
  7. package/dist-client/pages/event/event-list-page.js +60 -5
  8. package/dist-client/pages/event/event-list-page.js.map +1 -1
  9. package/dist-client/pages/sure-to-delete.d.ts +1 -0
  10. package/dist-client/pages/sure-to-delete.js +10 -0
  11. package/dist-client/pages/sure-to-delete.js.map +1 -0
  12. package/dist-client/route.d.ts +1 -1
  13. package/dist-client/route.js +3 -0
  14. package/dist-client/route.js.map +1 -1
  15. package/dist-client/tsconfig.tsbuildinfo +1 -1
  16. package/dist-server/service/calendar/calendar-service.js +14 -1
  17. package/dist-server/service/calendar/calendar-service.js.map +1 -1
  18. package/dist-server/service/calendar/calendar-type.d.ts +24 -1
  19. package/dist-server/service/calendar/calendar-type.js +85 -1
  20. package/dist-server/service/calendar/calendar-type.js.map +1 -1
  21. package/dist-server/service/calendar/calendar.d.ts +7 -0
  22. package/dist-server/service/calendar/calendar.js +22 -1
  23. package/dist-server/service/calendar/calendar.js.map +1 -1
  24. package/dist-server/service/calendar/index.d.ts +2 -1
  25. package/dist-server/service/calendar/index.js +2 -1
  26. package/dist-server/service/calendar/index.js.map +1 -1
  27. package/dist-server/service/calendar/working-days-resolver.d.ts +8 -0
  28. package/dist-server/service/calendar/working-days-resolver.js +92 -0
  29. package/dist-server/service/calendar/working-days-resolver.js.map +1 -0
  30. package/dist-server/service/calendar/working-days.d.ts +49 -0
  31. package/dist-server/service/calendar/working-days.js +185 -0
  32. package/dist-server/service/calendar/working-days.js.map +1 -0
  33. package/dist-server/service/event/event-query.js +1 -1
  34. package/dist-server/service/event/event-query.js.map +1 -1
  35. package/dist-server/service/event-series/recurrence.d.ts +2 -0
  36. package/dist-server/service/event-series/recurrence.js +1 -0
  37. package/dist-server/service/event-series/recurrence.js.map +1 -1
  38. package/dist-server/service/index.d.ts +3 -2
  39. package/dist-server/service/index.js +1 -0
  40. package/dist-server/service/index.js.map +1 -1
  41. package/dist-server/tsconfig.tsbuildinfo +1 -1
  42. package/package.json +4 -3
  43. package/things-factory.config.js +4 -0
  44. package/translations/en.json +23 -1
  45. package/translations/es.json +23 -1
  46. package/translations/ja.json +23 -1
  47. package/translations/ko.json +23 -1
  48. package/translations/ms.json +23 -1
  49. package/translations/zh.json +23 -1
@@ -0,0 +1,185 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DAYS_OFF_MAX_SPAN = exports.PUBLIC_HOLIDAYS_NAME = exports.WORKING_DAYS_MAX_SPAN = exports.WORK_WEEKDAYS = void 0;
4
+ exports.daysOffBetween = daysOffBetween;
5
+ exports.workingDaysBetween = workingDaysBetween;
6
+ exports.savePublicHolidays = savePublicHolidays;
7
+ exports.daysOffOfCalendar = daysOffOfCalendar;
8
+ exports.saveDaysOff = saveDaysOff;
9
+ const typeorm_1 = require("typeorm");
10
+ const shell_1 = require("@things-factory/shell");
11
+ const event_1 = require("../event/event");
12
+ const event_rules_1 = require("../event/event-rules");
13
+ const recurrence_1 = require("../event-series/recurrence");
14
+ const calendar_1 = require("./calendar");
15
+ /**
16
+ * **The working days of a domain** (ADR-0179): its working weekdays, less every day an event of a days-off or a public
17
+ * holiday calendar covers. The one answer every module asks - a leave request, a grid of leave, a screen.
18
+ *
19
+ * The working weekdays are Monday to Friday. A domain that runs work shifts will read them from its shifts (ADR-0179 미결).
20
+ */
21
+ exports.WORK_WEEKDAYS = [1, 2, 3, 4, 5];
22
+ /** The longest span asked at once. */
23
+ exports.WORKING_DAYS_MAX_SPAN = 400;
24
+ /** The name the public holiday calendar is made with; the domain may rename it. */
25
+ exports.PUBLIC_HOLIDAYS_NAME = '국가 공휴일';
26
+ const DAY = /^\d{4}-\d{2}-\d{2}$/;
27
+ const dayAfter = (day) => new Date(Date.parse(`${day}T00:00:00Z`) + 86400000).toISOString().slice(0, 10);
28
+ const weekdayOf = (day) => new Date(`${day}T00:00:00Z`).getUTCDay();
29
+ function checkDay(day, field) {
30
+ const at = new Date(`${day}T00:00:00Z`);
31
+ /* A day past the month's end (08-32) is no date at all - toISOString would throw rather than refuse. */
32
+ if (!DAY.test(day ?? '') || isNaN(at.getTime()) || at.toISOString().slice(0, 10) !== day)
33
+ throw new shell_1.Refusal('working-days-day-invalid', { field, value: String(day ?? '') }, `${field}: YYYY-MM-DD`);
34
+ return day;
35
+ }
36
+ /** The days off from `from` to `to` (both kept), each with the name of what makes it one. */
37
+ async function daysOffBetween(tx, domain, from, to) {
38
+ checkDay(from, 'from');
39
+ checkDay(to, 'to');
40
+ if (to < from)
41
+ throw new shell_1.Refusal('working-days-range-reversed', { from, to }, 'to: on or after from');
42
+ const span = (Date.parse(to) - Date.parse(from)) / 86400000 + 1;
43
+ if (span > exports.WORKING_DAYS_MAX_SPAN)
44
+ throw new shell_1.Refusal('working-days-range-too-long', { max: exports.WORKING_DAYS_MAX_SPAN }, `at most ${exports.WORKING_DAYS_MAX_SPAN} days at once`);
45
+ const timezone = domain.timezone;
46
+ (0, shell_1.calendarDay)(timezone);
47
+ const calendars = await tx.getRepository(calendar_1.Calendar).find({ where: { domain: { id: domain.id }, active: true, kind: (0, typeorm_1.In)([calendar_1.CalendarKind.DAYS_OFF, calendar_1.CalendarKind.PUBLIC_HOLIDAYS]) } });
48
+ const off = new Map();
49
+ if (!calendars.length)
50
+ return off;
51
+ const events = await tx.getRepository(event_1.Event).find({
52
+ where: {
53
+ domain: { id: domain.id },
54
+ calendar: { id: (0, typeorm_1.In)(calendars.map(one => one.id)) },
55
+ status: (0, typeorm_1.Not)(event_rules_1.EventStatus.CANCELLED),
56
+ startAt: (0, typeorm_1.LessThan)((0, recurrence_1.instantOf)(dayAfter(to), 0, 0, 0, timezone)),
57
+ endAt: (0, typeorm_1.MoreThan)((0, recurrence_1.instantOf)(from, 0, 0, 0, timezone))
58
+ },
59
+ order: { startAt: 'ASC' }
60
+ });
61
+ for (const event of events) {
62
+ const last = (0, shell_1.calendarDay)(timezone, new Date(event.endAt.getTime() - 1));
63
+ for (let day = (0, shell_1.calendarDay)(timezone, event.startAt); day <= last; day = dayAfter(day))
64
+ if (day >= from && day <= to && !off.has(day))
65
+ off.set(day, event.name);
66
+ }
67
+ return off;
68
+ }
69
+ /** The working days from `from` to `to` (both kept), in order. */
70
+ async function workingDaysBetween(tx, domain, from, to) {
71
+ const off = await daysOffBetween(tx, domain, from, to);
72
+ const days = [];
73
+ for (let day = from; day <= to; day = dayAfter(day))
74
+ if (exports.WORK_WEEKDAYS.includes(weekdayOf(day)) && !off.has(day))
75
+ days.push(day);
76
+ return days;
77
+ }
78
+ /**
79
+ * A year's public holidays as the import gives them: the domain's public holiday calendar (made when there is none)
80
+ * holds exactly these for the year - a day the import no longer gives is taken away. The number kept.
81
+ */
82
+ async function savePublicHolidays(tx, domain, user, year, days) {
83
+ if (!Number.isInteger(year) || year < 1900 || year > 2999)
84
+ throw new shell_1.Refusal('public-holidays-year-invalid', { value: String(year) }, 'year: a year such as 2026');
85
+ const given = new Map();
86
+ for (const one of days) {
87
+ checkDay(one.day, 'day');
88
+ if (!one.day.startsWith(`${year}-`))
89
+ throw new shell_1.Refusal('public-holidays-day-outside-year', { day: one.day, year }, `${one.day} is not in ${year}`);
90
+ const name = one.name?.trim();
91
+ if (!name)
92
+ throw new shell_1.Refusal('public-holidays-name-required', { day: one.day }, `${one.day} needs its name`);
93
+ given.set(`${one.day} ${name}`, { day: one.day, name });
94
+ }
95
+ const timezone = domain.timezone;
96
+ (0, shell_1.calendarDay)(timezone);
97
+ const calendars = tx.getRepository(calendar_1.Calendar);
98
+ let calendar = await calendars.findOne({ where: { domain: { id: domain.id }, kind: calendar_1.CalendarKind.PUBLIC_HOLIDAYS } });
99
+ if (!calendar) {
100
+ const taken = await calendars.findOne({ where: { domain: { id: domain.id }, name: exports.PUBLIC_HOLIDAYS_NAME } });
101
+ if (taken)
102
+ throw new shell_1.Refusal('calendar-name-duplicate', { name: exports.PUBLIC_HOLIDAYS_NAME }, 'Another calendar of this domain has that name');
103
+ calendar = await calendars.save(calendars.create({ domain, name: exports.PUBLIC_HOLIDAYS_NAME, kind: calendar_1.CalendarKind.PUBLIC_HOLIDAYS, active: true, creator: user, updater: user }));
104
+ }
105
+ const events = tx.getRepository(event_1.Event);
106
+ const kept = await events.find({
107
+ where: { domain: { id: domain.id }, calendar: { id: calendar.id }, startAt: (0, typeorm_1.MoreThan)(new Date((0, recurrence_1.instantOf)(`${year}-01-01`, 0, 0, 0, timezone).getTime() - 1)) }
108
+ });
109
+ const inYear = kept.filter(one => (0, shell_1.calendarDay)(timezone, one.startAt).startsWith(`${year}-`));
110
+ const have = new Set();
111
+ const gone = [];
112
+ for (const one of inYear) {
113
+ const key = `${(0, shell_1.calendarDay)(timezone, one.startAt)} ${one.name}`;
114
+ if (given.has(key) && !have.has(key))
115
+ have.add(key);
116
+ else
117
+ gone.push(one.id);
118
+ }
119
+ if (gone.length)
120
+ await events.softDelete(gone);
121
+ for (const [key, one] of given) {
122
+ if (have.has(key))
123
+ continue;
124
+ const startAt = (0, recurrence_1.instantOf)(one.day, 0, 0, 0, timezone);
125
+ await events.save(events.create({ domain, calendar, name: one.name, scheduledAt: startAt, startAt, endAt: (0, recurrence_1.instantOf)(dayAfter(one.day), 0, 0, 0, timezone), status: event_rules_1.EventStatus.SCHEDULED, creator: user, updater: user }));
126
+ }
127
+ return given.size;
128
+ }
129
+ /** The longest day off written as one row - a summer break, not a season. */
130
+ exports.DAYS_OFF_MAX_SPAN = 31;
131
+ async function daysOffCalendar(tx, domain, calendarId) {
132
+ const calendar = await tx.getRepository(calendar_1.Calendar).findOne({ where: { domain: { id: domain.id }, id: calendarId } });
133
+ if (!calendar)
134
+ throw new shell_1.Refusal('calendar-not-found', {}, 'No calendar of this domain by that id');
135
+ return calendar;
136
+ }
137
+ /** The days off a calendar holds, as the rows a person writes them - earliest first. */
138
+ async function daysOffOfCalendar(tx, domain, calendarId) {
139
+ const calendar = await daysOffCalendar(tx, domain, calendarId);
140
+ const timezone = domain.timezone;
141
+ (0, shell_1.calendarDay)(timezone);
142
+ const events = await tx.getRepository(event_1.Event).find({ where: { domain: { id: domain.id }, calendar: { id: calendar.id } }, order: { startAt: 'ASC' } });
143
+ return events.map(one => ({ id: one.id, name: one.name, from: (0, shell_1.calendarDay)(timezone, one.startAt), to: (0, shell_1.calendarDay)(timezone, new Date(one.endAt.getTime() - 1)) }));
144
+ }
145
+ /**
146
+ * A company's days off written by hand (ADR-0179 결정 2): rows added, changed or taken away on a days-off calendar - the
147
+ * public holiday calendar is the import's, and written only by it. Each row is whole days from one day to another in the
148
+ * domain's days; refused by the field it is in. The number of rows the calendar keeps.
149
+ */
150
+ async function saveDaysOff(tx, domain, user, calendarId, patches) {
151
+ const calendar = await daysOffCalendar(tx, domain, calendarId);
152
+ if (calendar.kind !== calendar_1.CalendarKind.DAYS_OFF)
153
+ throw new shell_1.Refusal('calendar-days-off-only', { name: calendar.name }, 'Days off are written on a days-off calendar');
154
+ const timezone = domain.timezone;
155
+ (0, shell_1.calendarDay)(timezone);
156
+ const events = tx.getRepository(event_1.Event);
157
+ for (const patch of patches) {
158
+ const flag = String(patch.cuFlag ?? '').toUpperCase();
159
+ const stored = patch.id ? await events.findOne({ where: { domain: { id: domain.id }, calendar: { id: calendar.id }, id: patch.id } }) : null;
160
+ if (patch.id && !stored)
161
+ throw new shell_1.Refusal('event-not-found', {}, 'No day off of this calendar by that id');
162
+ if (flag === '-') {
163
+ await events.softDelete({ id: stored.id });
164
+ continue;
165
+ }
166
+ const before = stored ? { name: stored.name, from: (0, shell_1.calendarDay)(timezone, stored.startAt), to: (0, shell_1.calendarDay)(timezone, new Date(stored.endAt.getTime() - 1)) } : { name: '', from: '', to: '' };
167
+ const name = (patch.name ?? before.name)?.trim();
168
+ if (!name)
169
+ throw new shell_1.Refusal('days-off-name-required', {}, 'name: what the day off is');
170
+ const from = checkDay(patch.from ?? before.from, 'from');
171
+ const to = checkDay(patch.to ?? (stored ? before.to : from) ?? from, 'to');
172
+ if (to < from)
173
+ throw new shell_1.Refusal('working-days-range-reversed', { from, to }, 'to: on or after from');
174
+ if ((Date.parse(to) - Date.parse(from)) / 86400000 + 1 > exports.DAYS_OFF_MAX_SPAN)
175
+ throw new shell_1.Refusal('days-off-too-long', { max: exports.DAYS_OFF_MAX_SPAN }, `at most ${exports.DAYS_OFF_MAX_SPAN} days in a row`);
176
+ const startAt = (0, recurrence_1.instantOf)(from, 0, 0, 0, timezone);
177
+ const endAt = (0, recurrence_1.instantOf)(dayAfter(to), 0, 0, 0, timezone);
178
+ if (stored)
179
+ await events.save({ ...stored, name, scheduledAt: startAt, startAt, endAt, updater: user });
180
+ else
181
+ await events.save(events.create({ domain, calendar, name, scheduledAt: startAt, startAt, endAt, status: event_rules_1.EventStatus.SCHEDULED, creator: user, updater: user }));
182
+ }
183
+ return events.count({ where: { domain: { id: domain.id }, calendar: { id: calendar.id } } });
184
+ }
185
+ //# sourceMappingURL=working-days.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"working-days.js","sourceRoot":"","sources":["../../../server/service/calendar/working-days.ts"],"names":[],"mappings":"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","sourcesContent":["import { EntityManager, In, LessThan, MoreThan, Not } from 'typeorm'\n\nimport { User } from '@things-factory/auth-base'\nimport { calendarDay, Domain, Refusal } from '@things-factory/shell'\n\nimport { Event } from '../event/event'\nimport { EventStatus } from '../event/event-rules'\nimport { instantOf } from '../event-series/recurrence'\nimport { Calendar, CalendarKind } from './calendar'\n\n/**\n * **The working days of a domain** (ADR-0179): its working weekdays, less every day an event of a days-off or a public\n * holiday calendar covers. The one answer every module asks - a leave request, a grid of leave, a screen.\n *\n * The working weekdays are Monday to Friday. A domain that runs work shifts will read them from its shifts (ADR-0179 미결).\n */\nexport const WORK_WEEKDAYS = [1, 2, 3, 4, 5]\n\n/** The longest span asked at once. */\nexport const WORKING_DAYS_MAX_SPAN = 400\n\n/** The name the public holiday calendar is made with; the domain may rename it. */\nexport const PUBLIC_HOLIDAYS_NAME = '국가 공휴일'\n\nconst DAY = /^\\d{4}-\\d{2}-\\d{2}$/\nconst dayAfter = (day: string) => new Date(Date.parse(`${day}T00:00:00Z`) + 86400000).toISOString().slice(0, 10)\nconst weekdayOf = (day: string) => new Date(`${day}T00:00:00Z`).getUTCDay()\n\nfunction checkDay(day: string, field: string): string {\n const at = new Date(`${day}T00:00:00Z`)\n /* A day past the month's end (08-32) is no date at all - toISOString would throw rather than refuse. */\n if (!DAY.test(day ?? '') || isNaN(at.getTime()) || at.toISOString().slice(0, 10) !== day)\n throw new Refusal('working-days-day-invalid', { field, value: String(day ?? '') }, `${field}: YYYY-MM-DD`)\n return day\n}\n\n/** The days off from `from` to `to` (both kept), each with the name of what makes it one. */\nexport async function daysOffBetween(tx: EntityManager, domain: Domain, from: string, to: string): Promise<Map<string, string>> {\n checkDay(from, 'from')\n checkDay(to, 'to')\n if (to < from) throw new Refusal('working-days-range-reversed', { from, to }, 'to: on or after from')\n const span = (Date.parse(to) - Date.parse(from)) / 86400000 + 1\n if (span > WORKING_DAYS_MAX_SPAN) throw new Refusal('working-days-range-too-long', { max: WORKING_DAYS_MAX_SPAN }, `at most ${WORKING_DAYS_MAX_SPAN} days at once`)\n const timezone = domain.timezone\n calendarDay(timezone)\n const calendars = await tx.getRepository(Calendar).find({ where: { domain: { id: domain.id }, active: true, kind: In([CalendarKind.DAYS_OFF, CalendarKind.PUBLIC_HOLIDAYS]) } })\n const off = new Map<string, string>()\n if (!calendars.length) return off\n const events = await tx.getRepository(Event).find({\n where: {\n domain: { id: domain.id },\n calendar: { id: In(calendars.map(one => one.id)) },\n status: Not(EventStatus.CANCELLED),\n startAt: LessThan(instantOf(dayAfter(to), 0, 0, 0, timezone)),\n endAt: MoreThan(instantOf(from, 0, 0, 0, timezone))\n } as any,\n order: { startAt: 'ASC' }\n })\n for (const event of events) {\n const last = calendarDay(timezone, new Date(event.endAt.getTime() - 1))\n for (let day = calendarDay(timezone, event.startAt); day <= last; day = dayAfter(day)) if (day >= from && day <= to && !off.has(day)) off.set(day, event.name)\n }\n return off\n}\n\n/** The working days from `from` to `to` (both kept), in order. */\nexport async function workingDaysBetween(tx: EntityManager, domain: Domain, from: string, to: string): Promise<string[]> {\n const off = await daysOffBetween(tx, domain, from, to)\n const days: string[] = []\n for (let day = from; day <= to; day = dayAfter(day)) if (WORK_WEEKDAYS.includes(weekdayOf(day)) && !off.has(day)) days.push(day)\n return days\n}\n\n/**\n * A year's public holidays as the import gives them: the domain's public holiday calendar (made when there is none)\n * holds exactly these for the year - a day the import no longer gives is taken away. The number kept.\n */\nexport async function savePublicHolidays(tx: EntityManager, domain: Domain, user: User, year: number, days: readonly { day: string; name: string }[]): Promise<number> {\n if (!Number.isInteger(year) || year < 1900 || year > 2999) throw new Refusal('public-holidays-year-invalid', { value: String(year) }, 'year: a year such as 2026')\n const given = new Map<string, { day: string; name: string }>()\n for (const one of days) {\n checkDay(one.day, 'day')\n if (!one.day.startsWith(`${year}-`)) throw new Refusal('public-holidays-day-outside-year', { day: one.day, year }, `${one.day} is not in ${year}`)\n const name = one.name?.trim()\n if (!name) throw new Refusal('public-holidays-name-required', { day: one.day }, `${one.day} needs its name`)\n given.set(`${one.day} ${name}`, { day: one.day, name })\n }\n const timezone = domain.timezone\n calendarDay(timezone)\n\n const calendars = tx.getRepository(Calendar)\n let calendar = await calendars.findOne({ where: { domain: { id: domain.id }, kind: CalendarKind.PUBLIC_HOLIDAYS } })\n if (!calendar) {\n const taken = await calendars.findOne({ where: { domain: { id: domain.id }, name: PUBLIC_HOLIDAYS_NAME } })\n if (taken) throw new Refusal('calendar-name-duplicate', { name: PUBLIC_HOLIDAYS_NAME }, 'Another calendar of this domain has that name')\n calendar = await calendars.save(calendars.create({ domain, name: PUBLIC_HOLIDAYS_NAME, kind: CalendarKind.PUBLIC_HOLIDAYS, active: true, creator: user, updater: user }))\n }\n\n const events = tx.getRepository(Event)\n const kept = await events.find({\n where: { domain: { id: domain.id }, calendar: { id: calendar.id }, startAt: MoreThan(new Date(instantOf(`${year}-01-01`, 0, 0, 0, timezone).getTime() - 1)) } as any\n })\n const inYear = kept.filter(one => calendarDay(timezone, one.startAt).startsWith(`${year}-`))\n const have = new Set<string>()\n const gone: string[] = []\n for (const one of inYear) {\n const key = `${calendarDay(timezone, one.startAt)} ${one.name}`\n if (given.has(key) && !have.has(key)) have.add(key)\n else gone.push(one.id)\n }\n if (gone.length) await events.softDelete(gone)\n for (const [key, one] of given) {\n if (have.has(key)) continue\n const startAt = instantOf(one.day, 0, 0, 0, timezone)\n await events.save(events.create({ domain, calendar, name: one.name, scheduledAt: startAt, startAt, endAt: instantOf(dayAfter(one.day), 0, 0, 0, timezone), status: EventStatus.SCHEDULED, creator: user, updater: user } as any))\n }\n return given.size\n}\n\n/** The longest day off written as one row - a summer break, not a season. */\nexport const DAYS_OFF_MAX_SPAN = 31\n\n/** One row of a days-off calendar: a day off from one day to another, both kept, in the domain's days. */\nexport interface DayOffRow {\n id: string\n name: string\n from: string\n to: string\n}\n\nasync function daysOffCalendar(tx: EntityManager, domain: Domain, calendarId: string): Promise<Calendar> {\n const calendar = await tx.getRepository(Calendar).findOne({ where: { domain: { id: domain.id }, id: calendarId } })\n if (!calendar) throw new Refusal('calendar-not-found', {}, 'No calendar of this domain by that id')\n return calendar\n}\n\n/** The days off a calendar holds, as the rows a person writes them - earliest first. */\nexport async function daysOffOfCalendar(tx: EntityManager, domain: Domain, calendarId: string): Promise<DayOffRow[]> {\n const calendar = await daysOffCalendar(tx, domain, calendarId)\n const timezone = domain.timezone\n calendarDay(timezone)\n const events = await tx.getRepository(Event).find({ where: { domain: { id: domain.id }, calendar: { id: calendar.id } } as any, order: { startAt: 'ASC' } })\n return events.map(one => ({ id: one.id, name: one.name, from: calendarDay(timezone, one.startAt), to: calendarDay(timezone, new Date(one.endAt.getTime() - 1)) }))\n}\n\n/**\n * A company's days off written by hand (ADR-0179 결정 2): rows added, changed or taken away on a days-off calendar - the\n * public holiday calendar is the import's, and written only by it. Each row is whole days from one day to another in the\n * domain's days; refused by the field it is in. The number of rows the calendar keeps.\n */\nexport async function saveDaysOff(\n tx: EntityManager,\n domain: Domain,\n user: User,\n calendarId: string,\n patches: readonly { id?: string; name?: string; from?: string; to?: string; cuFlag: string }[]\n): Promise<number> {\n const calendar = await daysOffCalendar(tx, domain, calendarId)\n if (calendar.kind !== CalendarKind.DAYS_OFF) throw new Refusal('calendar-days-off-only', { name: calendar.name }, 'Days off are written on a days-off calendar')\n const timezone = domain.timezone\n calendarDay(timezone)\n const events = tx.getRepository(Event)\n\n for (const patch of patches) {\n const flag = String(patch.cuFlag ?? '').toUpperCase()\n const stored = patch.id ? await events.findOne({ where: { domain: { id: domain.id }, calendar: { id: calendar.id }, id: patch.id } as any }) : null\n if (patch.id && !stored) throw new Refusal('event-not-found', {}, 'No day off of this calendar by that id')\n if (flag === '-') {\n await events.softDelete({ id: stored!.id })\n continue\n }\n const before = stored ? { name: stored.name, from: calendarDay(timezone, stored.startAt), to: calendarDay(timezone, new Date(stored.endAt.getTime() - 1)) } : { name: '', from: '', to: '' }\n const name = (patch.name ?? before.name)?.trim()\n if (!name) throw new Refusal('days-off-name-required', {}, 'name: what the day off is')\n const from = checkDay(patch.from ?? before.from, 'from')\n const to = checkDay(patch.to ?? (stored ? before.to : from) ?? from, 'to')\n if (to < from) throw new Refusal('working-days-range-reversed', { from, to }, 'to: on or after from')\n if ((Date.parse(to) - Date.parse(from)) / 86400000 + 1 > DAYS_OFF_MAX_SPAN)\n throw new Refusal('days-off-too-long', { max: DAYS_OFF_MAX_SPAN }, `at most ${DAYS_OFF_MAX_SPAN} days in a row`)\n const startAt = instantOf(from, 0, 0, 0, timezone)\n const endAt = instantOf(dayAfter(to), 0, 0, 0, timezone)\n if (stored) await events.save({ ...stored, name, scheduledAt: startAt, startAt, endAt, updater: user })\n else await events.save(events.create({ domain, calendar, name, scheduledAt: startAt, startAt, endAt, status: EventStatus.SCHEDULED, creator: user, updater: user } as any))\n }\n return events.count({ where: { domain: { id: domain.id }, calendar: { id: calendar.id } } as any })\n}\n\n"]}
@@ -35,7 +35,7 @@ let EventQuery = class EventQuery {
35
35
  });
36
36
  }
37
37
  /*
38
- * The one door of this module that asks for no calendar privilege: it reads only the caller's own - the events they
38
+ * A door of this module that asks for no calendar privilege (daysOff is the other): it reads only the caller's own - the events they
39
39
  * are named on or responsible for - as a person's work calendar does with their to-dos (worklist activityEvents).
40
40
  */
41
41
  myEvents(from, to, context) {
@@ -1 +1 @@
1
- {"version":3,"file":"event-query.js","sourceRoot":"","sources":["../../../server/service/event/event-query.ts"],"names":[],"mappings":"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","sourcesContent":["import { Arg, Args, Ctx, Directive, FieldResolver, Query, Resolver, Root, UseMiddleware } from 'type-graphql'\nimport { LessThan, MoreThan } from 'typeorm'\n\nimport { User } from '@things-factory/auth-base'\nimport { Domain, getQueryBuilderFromListParams, getRepository, ListParam, Refusal, SpeakRefusals } from '@things-factory/shell'\n\nimport { Attendee } from '../attendee/attendee'\nimport { Calendar } from '../calendar/calendar'\nimport { EventSeries } from '../event-series/event-series'\nimport { Event } from './event'\nimport { EventList } from './event-type'\nimport { myEventsOf } from './event-service'\n\n@UseMiddleware(SpeakRefusals)\n@Resolver(Event)\nexport class EventQuery {\n @Directive('@privilege(category: \"calendar\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => Event, { nullable: true, description: 'To fetch an event' })\n event(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<Event> {\n return getRepository(Event).findOne({ where: { domain: { id: context.state.domain.id }, id } })\n }\n\n @Directive('@privilege(category: \"calendar\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => EventList, { description: 'To fetch a page of events' })\n async events(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<EventList> {\n const queryBuilder = getQueryBuilderFromListParams({\n domain: context.state.domain,\n params,\n repository: getRepository(Event),\n searchables: ['name', 'description', 'location']\n })\n const [items, total] = await queryBuilder.getManyAndCount()\n return { items, total }\n }\n\n @Directive('@privilege(category: \"calendar\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => [Event], { description: 'To fetch the events that overlap a time range, earliest first' })\n eventsBetween(\n @Arg('from') from: Date,\n @Arg('to') to: Date,\n @Arg('calendarId', { nullable: true }) calendarId: string,\n @Ctx() context: ResolverContext\n ): Promise<Event[]> {\n if (!(to > from)) throw new Refusal('event-range-invalid', {}, 'The range ends after it starts')\n return getRepository(Event).find({\n where: { domain: { id: context.state.domain.id }, startAt: LessThan(to), endAt: MoreThan(from), ...(calendarId ? { calendar: { id: calendarId } } : {}) },\n order: { startAt: 'ASC' }\n })\n }\n\n /*\n * The one door of this module that asks for no calendar privilege: it reads only the caller's own - the events they\n * are named on or responsible for - as a person's work calendar does with their to-dos (worklist activityEvents).\n */\n @Query(returns => [Event], { description: \"The caller's own events that overlap a time range - those they are named on or responsible for, earliest first\" })\n myEvents(@Arg('from') from: Date, @Arg('to') to: Date, @Ctx() context: ResolverContext): Promise<Event[]> {\n return myEventsOf(getRepository(Event).manager, context.state.domain, context.state.user, from, to)\n }\n\n @FieldResolver(type => [Attendee], { description: 'The people named on the event' })\n attendees(@Root() event: Event): Promise<Attendee[]> {\n return getRepository(Attendee).find({ where: { event: { id: event.id } }, order: { createdAt: 'ASC' } })\n }\n\n @FieldResolver(type => EventSeries, { nullable: true })\n series(@Root() event: Event): Promise<EventSeries> {\n return event.seriesId ? getRepository(EventSeries).findOne({ where: { id: event.seriesId }, withDeleted: true }) : null\n }\n\n @FieldResolver(type => Calendar, { nullable: true })\n calendar(@Root() event: Event): Promise<Calendar> {\n return event.calendarId ? getRepository(Calendar).findOneBy({ id: event.calendarId }) : null\n }\n\n @FieldResolver(type => User, { nullable: true })\n organizer(@Root() event: Event): Promise<User> {\n return event.organizerId ? getRepository(User).findOneBy({ id: event.organizerId }) : null\n }\n\n @FieldResolver(type => Domain)\n domain(@Root() event: Event): Promise<Domain> {\n return getRepository(Domain).findOneBy({ id: event.domainId })\n }\n\n @FieldResolver(type => User)\n updater(@Root() event: Event): Promise<User> {\n return event.updaterId ? getRepository(User).findOneBy({ id: event.updaterId }) : null\n }\n\n @FieldResolver(type => User)\n creator(@Root() event: Event): Promise<User> {\n return event.creatorId ? getRepository(User).findOneBy({ id: event.creatorId }) : null\n }\n}\n"]}
1
+ {"version":3,"file":"event-query.js","sourceRoot":"","sources":["../../../server/service/event/event-query.ts"],"names":[],"mappings":"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","sourcesContent":["import { Arg, Args, Ctx, Directive, FieldResolver, Query, Resolver, Root, UseMiddleware } from 'type-graphql'\nimport { LessThan, MoreThan } from 'typeorm'\n\nimport { User } from '@things-factory/auth-base'\nimport { Domain, getQueryBuilderFromListParams, getRepository, ListParam, Refusal, SpeakRefusals } from '@things-factory/shell'\n\nimport { Attendee } from '../attendee/attendee'\nimport { Calendar } from '../calendar/calendar'\nimport { EventSeries } from '../event-series/event-series'\nimport { Event } from './event'\nimport { EventList } from './event-type'\nimport { myEventsOf } from './event-service'\n\n@UseMiddleware(SpeakRefusals)\n@Resolver(Event)\nexport class EventQuery {\n @Directive('@privilege(category: \"calendar\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => Event, { nullable: true, description: 'To fetch an event' })\n event(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<Event> {\n return getRepository(Event).findOne({ where: { domain: { id: context.state.domain.id }, id } })\n }\n\n @Directive('@privilege(category: \"calendar\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => EventList, { description: 'To fetch a page of events' })\n async events(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<EventList> {\n const queryBuilder = getQueryBuilderFromListParams({\n domain: context.state.domain,\n params,\n repository: getRepository(Event),\n searchables: ['name', 'description', 'location']\n })\n const [items, total] = await queryBuilder.getManyAndCount()\n return { items, total }\n }\n\n @Directive('@privilege(category: \"calendar\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => [Event], { description: 'To fetch the events that overlap a time range, earliest first' })\n eventsBetween(\n @Arg('from') from: Date,\n @Arg('to') to: Date,\n @Arg('calendarId', { nullable: true }) calendarId: string,\n @Ctx() context: ResolverContext\n ): Promise<Event[]> {\n if (!(to > from)) throw new Refusal('event-range-invalid', {}, 'The range ends after it starts')\n return getRepository(Event).find({\n where: { domain: { id: context.state.domain.id }, startAt: LessThan(to), endAt: MoreThan(from), ...(calendarId ? { calendar: { id: calendarId } } : {}) },\n order: { startAt: 'ASC' }\n })\n }\n\n /*\n * A door of this module that asks for no calendar privilege (daysOff is the other): it reads only the caller's own - the events they\n * are named on or responsible for - as a person's work calendar does with their to-dos (worklist activityEvents).\n */\n @Query(returns => [Event], { description: \"The caller's own events that overlap a time range - those they are named on or responsible for, earliest first\" })\n myEvents(@Arg('from') from: Date, @Arg('to') to: Date, @Ctx() context: ResolverContext): Promise<Event[]> {\n return myEventsOf(getRepository(Event).manager, context.state.domain, context.state.user, from, to)\n }\n\n @FieldResolver(type => [Attendee], { description: 'The people named on the event' })\n attendees(@Root() event: Event): Promise<Attendee[]> {\n return getRepository(Attendee).find({ where: { event: { id: event.id } }, order: { createdAt: 'ASC' } })\n }\n\n @FieldResolver(type => EventSeries, { nullable: true })\n series(@Root() event: Event): Promise<EventSeries> {\n return event.seriesId ? getRepository(EventSeries).findOne({ where: { id: event.seriesId }, withDeleted: true }) : null\n }\n\n @FieldResolver(type => Calendar, { nullable: true })\n calendar(@Root() event: Event): Promise<Calendar> {\n return event.calendarId ? getRepository(Calendar).findOneBy({ id: event.calendarId }) : null\n }\n\n @FieldResolver(type => User, { nullable: true })\n organizer(@Root() event: Event): Promise<User> {\n return event.organizerId ? getRepository(User).findOneBy({ id: event.organizerId }) : null\n }\n\n @FieldResolver(type => Domain)\n domain(@Root() event: Event): Promise<Domain> {\n return getRepository(Domain).findOneBy({ id: event.domainId })\n }\n\n @FieldResolver(type => User)\n updater(@Root() event: Event): Promise<User> {\n return event.updaterId ? getRepository(User).findOneBy({ id: event.updaterId }) : null\n }\n\n @FieldResolver(type => User)\n creator(@Root() event: Event): Promise<User> {\n return event.creatorId ? getRepository(User).findOneBy({ id: event.creatorId }) : null\n }\n}\n"]}
@@ -13,6 +13,8 @@ export declare function checkRule(rule: string, timezone: string): void;
13
13
  * would be more than `MAX_OCCURRENCES`.
14
14
  */
15
15
  export declare function occurrenceTimes(rule: string, timezone: string, from: Date, until: Date): Date[];
16
+ /** The instant a wall-clock time of a calendar day is in a timezone - once more for a day whose offset changes. */
17
+ export declare function instantOf(day: string, hour: number, minute: number, second: number, timezone: string): Date;
16
18
  /** The first second of a calendar day (YYYY-MM-DD) in a timezone - where "from that day" begins. */
17
19
  export declare function dayStartIn(day: string, timezone: string): Date;
18
20
  /** The last second of a calendar day (YYYY-MM-DD) in a timezone - a deadline "by that day" as that calendar keeps it. */
@@ -4,6 +4,7 @@ exports.MAX_OCCURRENCES = void 0;
4
4
  exports.validTimezone = validTimezone;
5
5
  exports.checkRule = checkRule;
6
6
  exports.occurrenceTimes = occurrenceTimes;
7
+ exports.instantOf = instantOf;
7
8
  exports.dayStartIn = dayStartIn;
8
9
  exports.dayEndIn = dayEndIn;
9
10
  const cron_parser_1 = require("cron-parser");
@@ -1 +1 @@
1
- {"version":3,"file":"recurrence.js","sourceRoot":"","sources":["../../../server/service/event-series/recurrence.ts"],"names":[],"mappings":"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","sourcesContent":["import { parseExpression } from 'cron-parser'\n\nimport { Refusal } from '@things-factory/shell'\n\n/**\n * The times a series' rule gives — a five-field cron expression read in an IANA timezone, the dialect the\n * framework's schedules already use (`dataset`, `scheduler-client`). Cron cannot say \"every other week\"; a series\n * that needs it waits for an interval option on the series.\n */\n\n/** The most occurrences one call creates or plans. A longer horizon is the caller's to split. */\nexport const MAX_OCCURRENCES = 400\n\nexport function validTimezone(timezone: string): boolean {\n if (!timezone) return false\n try {\n new Intl.DateTimeFormat('en-US', { timeZone: timezone })\n return true\n } catch {\n return false\n }\n}\n\n/** Refuses a rule that is not five cron fields or does not parse, and a timezone that is not IANA. */\nexport function checkRule(rule: string, timezone: string) {\n if (!validTimezone(timezone)) throw new Refusal('event-series-timezone-invalid', { timezone: timezone ?? null }, 'Give an IANA timezone such as Asia/Seoul')\n const fields = rule?.trim().split(/\\s+/) ?? []\n if (fields.length !== 5) throw new Refusal('event-series-rule-invalid', {}, 'Give the rule as five cron fields: minute hour day-of-month month day-of-week')\n try {\n parseExpression(fields.join(' '), { tz: timezone })\n } catch {\n throw new Refusal('event-series-rule-invalid', {}, 'Give the rule as five cron fields: minute hour day-of-month month day-of-week')\n }\n}\n\n/**\n * The rule's times from `from` to `until`, both included, oldest first. Refuses rather than cuts short when there\n * would be more than `MAX_OCCURRENCES`.\n */\nexport function occurrenceTimes(rule: string, timezone: string, from: Date, until: Date): Date[] {\n checkRule(rule, timezone)\n if (!(until >= from)) return []\n\n const times: Date[] = []\n const interval = parseExpression(rule.trim().split(/\\s+/).join(' '), {\n tz: timezone,\n currentDate: new Date(from.getTime() - 1),\n endDate: until,\n iterator: false\n })\n while (interval.hasNext()) {\n const next = interval.next().toDate()\n if (next > until) break\n if (times.length === MAX_OCCURRENCES)\n throw new Refusal('event-series-too-many-occurrences', { limit: MAX_OCCURRENCES }, 'Too many occurrences at once; give a shorter horizon')\n times.push(next)\n }\n return times\n}\n\n/** The wall clock of an instant in a timezone, read as if it were UTC - how far the zone is from UTC then. */\nfunction wallOf(at: number, timezone: string): number {\n const parts = Object.fromEntries(\n new Intl.DateTimeFormat('en-US', { timeZone: timezone, hourCycle: 'h23', year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' })\n .formatToParts(at)\n .map(part => [part.type, part.value])\n )\n return Date.UTC(Number(parts.year), Number(parts.month) - 1, Number(parts.day), Number(parts.hour), Number(parts.minute), Number(parts.second))\n}\n\n/** The instant a wall-clock time of a calendar day is in a timezone - once more for a day whose offset changes. */\nfunction instantOf(day: string, hour: number, minute: number, second: number, timezone: string): Date {\n if (!validTimezone(timezone)) throw new Refusal('event-series-timezone-invalid', { timezone: timezone ?? null }, 'Give an IANA timezone such as Asia/Seoul')\n if (!/^\\d{4}-\\d{2}-\\d{2}$/.test(day ?? '')) throw new Refusal('event-day-invalid', { day: day ?? null }, 'Give the day as YYYY-MM-DD')\n const [year, month, date] = day.split('-').map(Number)\n const wanted = Date.UTC(year, month - 1, date, hour, minute, second)\n let at = wanted - (wallOf(wanted, timezone) - wanted)\n at += wanted - wallOf(at, timezone)\n return new Date(at)\n}\n\n/** The first second of a calendar day (YYYY-MM-DD) in a timezone - where \"from that day\" begins. */\nexport function dayStartIn(day: string, timezone: string): Date {\n return instantOf(day, 0, 0, 0, timezone)\n}\n\n/** The last second of a calendar day (YYYY-MM-DD) in a timezone - a deadline \"by that day\" as that calendar keeps it. */\nexport function dayEndIn(day: string, timezone: string): Date {\n return instantOf(day, 23, 59, 59, timezone)\n}\n"]}
1
+ {"version":3,"file":"recurrence.js","sourceRoot":"","sources":["../../../server/service/event-series/recurrence.ts"],"names":[],"mappings":"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","sourcesContent":["import { parseExpression } from 'cron-parser'\n\nimport { Refusal } from '@things-factory/shell'\n\n/**\n * The times a series' rule gives — a five-field cron expression read in an IANA timezone, the dialect the\n * framework's schedules already use (`dataset`, `scheduler-client`). Cron cannot say \"every other week\"; a series\n * that needs it waits for an interval option on the series.\n */\n\n/** The most occurrences one call creates or plans. A longer horizon is the caller's to split. */\nexport const MAX_OCCURRENCES = 400\n\nexport function validTimezone(timezone: string): boolean {\n if (!timezone) return false\n try {\n new Intl.DateTimeFormat('en-US', { timeZone: timezone })\n return true\n } catch {\n return false\n }\n}\n\n/** Refuses a rule that is not five cron fields or does not parse, and a timezone that is not IANA. */\nexport function checkRule(rule: string, timezone: string) {\n if (!validTimezone(timezone)) throw new Refusal('event-series-timezone-invalid', { timezone: timezone ?? null }, 'Give an IANA timezone such as Asia/Seoul')\n const fields = rule?.trim().split(/\\s+/) ?? []\n if (fields.length !== 5) throw new Refusal('event-series-rule-invalid', {}, 'Give the rule as five cron fields: minute hour day-of-month month day-of-week')\n try {\n parseExpression(fields.join(' '), { tz: timezone })\n } catch {\n throw new Refusal('event-series-rule-invalid', {}, 'Give the rule as five cron fields: minute hour day-of-month month day-of-week')\n }\n}\n\n/**\n * The rule's times from `from` to `until`, both included, oldest first. Refuses rather than cuts short when there\n * would be more than `MAX_OCCURRENCES`.\n */\nexport function occurrenceTimes(rule: string, timezone: string, from: Date, until: Date): Date[] {\n checkRule(rule, timezone)\n if (!(until >= from)) return []\n\n const times: Date[] = []\n const interval = parseExpression(rule.trim().split(/\\s+/).join(' '), {\n tz: timezone,\n currentDate: new Date(from.getTime() - 1),\n endDate: until,\n iterator: false\n })\n while (interval.hasNext()) {\n const next = interval.next().toDate()\n if (next > until) break\n if (times.length === MAX_OCCURRENCES)\n throw new Refusal('event-series-too-many-occurrences', { limit: MAX_OCCURRENCES }, 'Too many occurrences at once; give a shorter horizon')\n times.push(next)\n }\n return times\n}\n\n/** The wall clock of an instant in a timezone, read as if it were UTC - how far the zone is from UTC then. */\nfunction wallOf(at: number, timezone: string): number {\n const parts = Object.fromEntries(\n new Intl.DateTimeFormat('en-US', { timeZone: timezone, hourCycle: 'h23', year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' })\n .formatToParts(at)\n .map(part => [part.type, part.value])\n )\n return Date.UTC(Number(parts.year), Number(parts.month) - 1, Number(parts.day), Number(parts.hour), Number(parts.minute), Number(parts.second))\n}\n\n/** The instant a wall-clock time of a calendar day is in a timezone - once more for a day whose offset changes. */\nexport function instantOf(day: string, hour: number, minute: number, second: number, timezone: string): Date {\n if (!validTimezone(timezone)) throw new Refusal('event-series-timezone-invalid', { timezone: timezone ?? null }, 'Give an IANA timezone such as Asia/Seoul')\n if (!/^\\d{4}-\\d{2}-\\d{2}$/.test(day ?? '')) throw new Refusal('event-day-invalid', { day: day ?? null }, 'Give the day as YYYY-MM-DD')\n const [year, month, date] = day.split('-').map(Number)\n const wanted = Date.UTC(year, month - 1, date, hour, minute, second)\n let at = wanted - (wallOf(wanted, timezone) - wanted)\n at += wanted - wallOf(at, timezone)\n return new Date(at)\n}\n\n/** The first second of a calendar day (YYYY-MM-DD) in a timezone - where \"from that day\" begins. */\nexport function dayStartIn(day: string, timezone: string): Date {\n return instantOf(day, 0, 0, 0, timezone)\n}\n\n/** The last second of a calendar day (YYYY-MM-DD) in a timezone - a deadline \"by that day\" as that calendar keeps it. */\nexport function dayEndIn(day: string, timezone: string): Date {\n return instantOf(day, 23, 59, 59, timezone)\n}\n"]}
@@ -8,12 +8,13 @@ export * from './event/event-type';
8
8
  export * from './event-series/event-series-type';
9
9
  export * from './attendee/attendee-type';
10
10
  export * from './calendar/calendar-service';
11
+ export * from './calendar/working-days';
11
12
  export * from './event/event-service';
12
13
  export * from './event-series/event-series-service';
13
14
  export * from './event-series/recurrence';
14
15
  export { attendeesOf } from './attendee/attendee-service';
15
- export declare const entities: (typeof import("./calendar/calendar").Calendar | typeof import("./event/event").Event | typeof import("./attendee/attendee").Attendee)[];
16
+ export declare const entities: (typeof import("./calendar/calendar").Calendar | typeof import("./event-series/event-series").EventSeries | typeof import("./event/event").Event | typeof import("./attendee/attendee").Attendee)[];
16
17
  export declare const subscribers: any[];
17
18
  export declare const schema: {
18
- resolverClasses: (typeof import("./calendar/calendar-query").CalendarQuery | typeof import("./calendar/calendar-mutation").CalendarMutation | typeof import("./event/event-query").EventQuery | typeof import("./event/event-mutation").EventMutation | typeof import("./event-series/event-series-query").EventSeriesQuery | typeof import("./event-series/event-series-mutation").EventSeriesMutation | typeof import("./attendee/attendee-query").AttendeeQuery)[];
19
+ resolverClasses: (typeof import("./calendar/calendar-query").CalendarQuery | typeof import("./calendar/calendar-mutation").CalendarMutation | typeof import("./calendar/working-days-resolver").WorkingDaysResolver | typeof import("./event/event-query").EventQuery | typeof import("./event/event-mutation").EventMutation | typeof import("./event-series/event-series-query").EventSeriesQuery | typeof import("./event-series/event-series-mutation").EventSeriesMutation | typeof import("./attendee/attendee-query").AttendeeQuery)[];
19
20
  };
@@ -15,6 +15,7 @@ tslib_1.__exportStar(require("./event-series/event-series-type"), exports);
15
15
  tslib_1.__exportStar(require("./attendee/attendee-type"), exports);
16
16
  /* EXPORT SERVICES — an application calls these inside its own transaction, behind its own doors */
17
17
  tslib_1.__exportStar(require("./calendar/calendar-service"), exports);
18
+ tslib_1.__exportStar(require("./calendar/working-days"), exports);
18
19
  tslib_1.__exportStar(require("./event/event-service"), exports);
19
20
  tslib_1.__exportStar(require("./event-series/event-series-service"), exports);
20
21
  tslib_1.__exportStar(require("./event-series/recurrence"), exports);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/service/index.ts"],"names":[],"mappings":";;;;AAAA,yBAAyB;AACzB,8DAAmC;AACnC,wDAA6B;AAC7B,8DAAmC;AACnC,sEAA2C;AAC3C,8DAAmC;AAEnC,mCAAmC;AACnC,mEAAwC;AACxC,6DAAkC;AAClC,2EAAgD;AAChD,mEAAwC;AAExC,mGAAmG;AACnG,sEAA2C;AAC3C,gEAAqC;AACrC,8EAAmD;AACnD,oEAAyC;AACzC,gEAAyD;AAAhD,+GAAA,WAAW,OAAA;AAEpB,mCAAmC;AACnC,yCAA6H;AAC7H,mCAAiH;AACjH,iDAA0I;AAC1I,yCAA6H;AAEhH,QAAA,QAAQ,GAAG;IACtB,cAAc;IACd,GAAG,mBAAgB;IACnB,GAAG,gBAAa;IAChB,GAAG,uBAAmB;IACtB,GAAG,mBAAgB;CACpB,CAAA;AAEY,QAAA,WAAW,GAAG;IACzB,iBAAiB;IACjB,GAAG,sBAAmB;IACtB,GAAG,mBAAgB;IACnB,GAAG,0BAAsB;IACzB,GAAG,sBAAmB;CACvB,CAAA;AAEY,QAAA,MAAM,GAAG;IACpB,eAAe,EAAE;QACf,sBAAsB;QACtB,GAAG,oBAAiB;QACpB,GAAG,iBAAc;QACjB,GAAG,wBAAoB;QACvB,GAAG,oBAAiB;KACrB;CACF,CAAA","sourcesContent":["/* EXPORT ENTITY TYPES */\nexport * from './calendar/calendar'\nexport * from './event/event'\nexport * from './event/event-rules'\nexport * from './event-series/event-series'\nexport * from './attendee/attendee'\n\n/* EXPORT INPUT AND RESULT TYPES */\nexport * from './calendar/calendar-type'\nexport * from './event/event-type'\nexport * from './event-series/event-series-type'\nexport * from './attendee/attendee-type'\n\n/* EXPORT SERVICES — an application calls these inside its own transaction, behind its own doors */\nexport * from './calendar/calendar-service'\nexport * from './event/event-service'\nexport * from './event-series/event-series-service'\nexport * from './event-series/recurrence'\nexport { attendeesOf } from './attendee/attendee-service'\n\n/* IMPORT ENTITIES AND RESOLVERS */\nimport { entities as CalendarEntities, resolvers as CalendarResolvers, subscribers as CalendarSubscribers } from './calendar'\nimport { entities as EventEntities, resolvers as EventResolvers, subscribers as EventSubscribers } from './event'\nimport { entities as EventSeriesEntities, resolvers as EventSeriesResolvers, subscribers as EventSeriesSubscribers } from './event-series'\nimport { entities as AttendeeEntities, resolvers as AttendeeResolvers, subscribers as AttendeeSubscribers } from './attendee'\n\nexport const entities = [\n /* ENTITIES */\n ...CalendarEntities,\n ...EventEntities,\n ...EventSeriesEntities,\n ...AttendeeEntities\n]\n\nexport const subscribers = [\n /* SUBSCRIBERS */\n ...CalendarSubscribers,\n ...EventSubscribers,\n ...EventSeriesSubscribers,\n ...AttendeeSubscribers\n]\n\nexport const schema = {\n resolverClasses: [\n /* RESOLVER CLASSES */\n ...CalendarResolvers,\n ...EventResolvers,\n ...EventSeriesResolvers,\n ...AttendeeResolvers\n ]\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/service/index.ts"],"names":[],"mappings":";;;;AAAA,yBAAyB;AACzB,8DAAmC;AACnC,wDAA6B;AAC7B,8DAAmC;AACnC,sEAA2C;AAC3C,8DAAmC;AAEnC,mCAAmC;AACnC,mEAAwC;AACxC,6DAAkC;AAClC,2EAAgD;AAChD,mEAAwC;AAExC,mGAAmG;AACnG,sEAA2C;AAC3C,kEAAuC;AACvC,gEAAqC;AACrC,8EAAmD;AACnD,oEAAyC;AACzC,gEAAyD;AAAhD,+GAAA,WAAW,OAAA;AAEpB,mCAAmC;AACnC,yCAA6H;AAC7H,mCAAiH;AACjH,iDAA0I;AAC1I,yCAA6H;AAEhH,QAAA,QAAQ,GAAG;IACtB,cAAc;IACd,GAAG,mBAAgB;IACnB,GAAG,gBAAa;IAChB,GAAG,uBAAmB;IACtB,GAAG,mBAAgB;CACpB,CAAA;AAEY,QAAA,WAAW,GAAG;IACzB,iBAAiB;IACjB,GAAG,sBAAmB;IACtB,GAAG,mBAAgB;IACnB,GAAG,0BAAsB;IACzB,GAAG,sBAAmB;CACvB,CAAA;AAEY,QAAA,MAAM,GAAG;IACpB,eAAe,EAAE;QACf,sBAAsB;QACtB,GAAG,oBAAiB;QACpB,GAAG,iBAAc;QACjB,GAAG,wBAAoB;QACvB,GAAG,oBAAiB;KACrB;CACF,CAAA","sourcesContent":["/* EXPORT ENTITY TYPES */\nexport * from './calendar/calendar'\nexport * from './event/event'\nexport * from './event/event-rules'\nexport * from './event-series/event-series'\nexport * from './attendee/attendee'\n\n/* EXPORT INPUT AND RESULT TYPES */\nexport * from './calendar/calendar-type'\nexport * from './event/event-type'\nexport * from './event-series/event-series-type'\nexport * from './attendee/attendee-type'\n\n/* EXPORT SERVICES — an application calls these inside its own transaction, behind its own doors */\nexport * from './calendar/calendar-service'\nexport * from './calendar/working-days'\nexport * from './event/event-service'\nexport * from './event-series/event-series-service'\nexport * from './event-series/recurrence'\nexport { attendeesOf } from './attendee/attendee-service'\n\n/* IMPORT ENTITIES AND RESOLVERS */\nimport { entities as CalendarEntities, resolvers as CalendarResolvers, subscribers as CalendarSubscribers } from './calendar'\nimport { entities as EventEntities, resolvers as EventResolvers, subscribers as EventSubscribers } from './event'\nimport { entities as EventSeriesEntities, resolvers as EventSeriesResolvers, subscribers as EventSeriesSubscribers } from './event-series'\nimport { entities as AttendeeEntities, resolvers as AttendeeResolvers, subscribers as AttendeeSubscribers } from './attendee'\n\nexport const entities = [\n /* ENTITIES */\n ...CalendarEntities,\n ...EventEntities,\n ...EventSeriesEntities,\n ...AttendeeEntities\n]\n\nexport const subscribers = [\n /* SUBSCRIBERS */\n ...CalendarSubscribers,\n ...EventSubscribers,\n ...EventSeriesSubscribers,\n ...AttendeeSubscribers\n]\n\nexport const schema = {\n resolverClasses: [\n /* RESOLVER CLASSES */\n ...CalendarResolvers,\n ...EventResolvers,\n ...EventSeriesResolvers,\n ...AttendeeResolvers\n ]\n}\n"]}