@royalschedule/maps 4.0.19 → 4.0.23

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.
@@ -7,7 +7,7 @@ import { PlannedScheduledDuration } from "../../core/interfaces/vertices/propert
7
7
  import { MinBreakLength } from "../../core/interfaces/vertices/properties/min-break-length.js";
8
8
  import { Types } from "./types/index.js";
9
9
  import "../../index.js";
10
- import * as moment197 from "moment";
10
+ import * as moment199 from "moment";
11
11
 
12
12
  //#region src/Excel/v1/index.d.ts
13
13
  declare class Map {
@@ -200,8 +200,8 @@ declare class Map {
200
200
  lastModifiedBy?: string | undefined;
201
201
  course?: string | undefined;
202
202
  period?: string | undefined;
203
- start?: (moment197.Moment | string) | undefined;
204
- end?: (moment197.Moment | string) | undefined;
203
+ start?: (moment199.Moment | string) | undefined;
204
+ end?: (moment199.Moment | string) | undefined;
205
205
  belongsTo?: string | undefined;
206
206
  potentialCenter?: string | undefined;
207
207
  type?: string | undefined;
@@ -7,7 +7,7 @@ import { PlannedScheduledDuration } from "../../core/interfaces/vertices/propert
7
7
  import { MinBreakLength } from "../../core/interfaces/vertices/properties/min-break-length.js";
8
8
  import { Types } from "./types/index.js";
9
9
  import "../../index.js";
10
- import * as moment199 from "moment";
10
+ import * as moment197 from "moment";
11
11
 
12
12
  //#region src/Excel/v2/index.d.ts
13
13
  declare class Map {
@@ -198,8 +198,8 @@ declare class Map {
198
198
  createdAt?: string | undefined;
199
199
  updatedAt?: string | undefined;
200
200
  lastModifiedBy?: string | undefined;
201
- start?: (moment199.Moment | string) | undefined;
202
- end?: (moment199.Moment | string) | undefined;
201
+ start?: (moment197.Moment | string) | undefined;
202
+ end?: (moment197.Moment | string) | undefined;
203
203
  potentialCenter?: string | undefined;
204
204
  type?: string | undefined;
205
205
  tags?: Tag[] | undefined;
@@ -25,7 +25,7 @@ function parseEvents(events, settings, options, periodsMap) {
25
25
  const duration = event.preferredDuration;
26
26
  if (!duration) throw new Error(`(RS::To::Events) Event "${id}" has no duration`);
27
27
  const forcedOverlapId = event.overlapSpecies?.species?.find(({ to }) => to == event)?.id;
28
- const maxLengthVariance = forcedOverlapId != null ? event.durationVariance ?? 0 : 0;
28
+ const maxLengthVariance = forcedOverlapId == null ? event.durationVariance : 0;
29
29
  const doc = {
30
30
  id: idOf.event(event, options),
31
31
  length: duration,
@@ -1 +1 @@
1
- {"version":3,"file":"events.js","names":["doc: Types.event","id"],"sources":["../../../../src/RS/to/input/events.ts"],"sourcesContent":["import moment from 'moment';\nimport { omitBy } from 'lodash-es';\nimport { parseMinimumBreakLength } from './util/parse-minimum-break-length';\nimport { attachLockedTimes } from './util/attach-locked-times';\nimport type { Types } from '../../types';\nimport { parseGroupReferences } from './util/parse-group-references';\nimport { parseLocationReferences, parseSelectedLocations } from './util/parse-location-references';\nimport type { ConnectedTypes } from '../../make-connected';\nimport { getDayIndex, getVertexId } from '../../../core/util';\nimport { getPeriodIndex, idOf } from './util/util';\nimport { parseDays } from './util/parse-days';\nimport type { DateType } from '../../../common/types';\nimport { parseIntervals } from './util/parse-intervals';\nimport { getDefaultInterval } from './intervals';\n\nexport function toDayAndStart (start: DateType) {\n return {\n start: parseFloat(moment.utc(start).format('HH.mm')),\n day: getDayIndex(start)\n };\n}\n\nexport function parseEvents (\n events: ConnectedTypes.event[] | undefined,\n settings: ConnectedTypes.divisionSettings,\n options: Types.parsedToOptions,\n periodsMap: Map<string | undefined, number>\n): Types.event[] {\n const defaultInterval = getDefaultInterval(settings);\n\n return (events ?? [])\n .map((event): Types.event | undefined => {\n const id = getVertexId(event, options);\n const intervals = event.intervals ?? event.course?.intervals ?? defaultInterval;\n\n const duration = event.preferredDuration;\n if (!duration) throw new Error(`(RS::To::Events) Event \"${id}\" has no duration`);\n\n const forcedOverlapId = event.overlapSpecies?.species?.find(({ to }) => to == event)?.id;\n\n // if the event is linked, force duration variance to 0\n const maxLengthVariance = forcedOverlapId != null\n ? event.durationVariance ?? 0\n : 0;\n\n const doc: Types.event = {\n id: idOf.event(event, options),\n length: duration,\n maxLengthVariance: maxLengthVariance,\n weight: event.weight,\n density: event.density,\n potentialCenter: event.centerOfAttraction ? parseFloat(event.centerOfAttraction.replace(':', '.')) : undefined,\n forcedOverlapId: forcedOverlapId,\n period: getPeriodIndex (event.period, periodsMap, options),\n _period: event.period ? getVertexId(event.period, options) : undefined,\n days: parseDays (event.days, settings),\n dependencies: parseLocationReferences (event.locations, options),\n groups: parseGroupReferences ({ type: 'event', item: event }, options),\n intervals: options.oldFormat\n ? parseIntervals(intervals, undefined, settings)\n : idOf.intervalPairReference(intervals, undefined, options),\n lockedTimes: attachLockedTimes (event.lockedTimes, options),\n minBreakLength: parseMinimumBreakLength (event.minBreakLength),\n };\n\n // if the event has a fixed day and start\n if (event.fixedStart && event.start) {\n Object.assign(doc, toDayAndStart(event.start));\n }\n\n if (options.includeEntityMeta) {\n doc.meta = omitBy({\n name: event.displayName,\n ids: event.ids,\n parked: event.parked,\n visible: event.visible,\n start: event.start ? moment.utc(event.start) : undefined,\n end: event.end ? moment.utc(event.start) : undefined,\n course: event.course ? getVertexId(event.course, options) : undefined,\n inLocations: event.inLocations ? parseSelectedLocations(event, options) : undefined\n }, x => x == null);\n }\n\n ////\n //// filter events based on partialScheduleOptions\n ////\n if (options.partialScheduleOptions) {\n const { includedEvents, omittedEventsHandling } = options.partialScheduleOptions;\n if (includedEvents && !includedEvents.has(doc.id)) { // collection.id => take into account dynamic locked times too!\n\n if (omittedEventsHandling == 'ignore') return;\n\n if (omittedEventsHandling == 'freeze') {\n // must not be parked and have a start and duration to be frozen, otherwise it's ignored\n if (event.parked || !event.start || !event.duration) return;\n\n // fix day, start and end\n Object.assign(doc, toDayAndStart(event.start));\n doc.length = event.duration;\n doc.maxLengthVariance = 0;\n\n // override intervals and days to not cause conflicts\n const numDays = settings.numDays ?? 5;\n doc.days = Array.from({ length: numDays }, (_, i) => i);\n doc.intervals = Array.from({ length: numDays }, () => [{ beg: 0, end: 23.55 }]);\n\n // fix locations\n doc.dependencies = (event.inLocations ?? [])\n .filter((x): x is NonNullable<typeof x> => !!x)\n .map(x => {\n const id = getVertexId(x, options);\n\n // filter location references based on partial schedule options\n const includedLocations = options.partialScheduleOptions?.includedLocations;\n if (includedLocations && !includedLocations.has(id)) return;\n\n return [{ dependency: id }] as Types.availableDependency[];\n })\n .filter(x => x != null);\n }\n\n }\n }\n\n return omitBy(doc, x => x == null) as Types.event;\n })\n .filter((x): x is NonNullable<typeof x> => !!x);\n}"],"mappings":"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"}
1
+ {"version":3,"file":"events.js","names":["doc: Types.event","id"],"sources":["../../../../src/RS/to/input/events.ts"],"sourcesContent":["import moment from 'moment';\nimport { omitBy } from 'lodash-es';\nimport { parseMinimumBreakLength } from './util/parse-minimum-break-length';\nimport { attachLockedTimes } from './util/attach-locked-times';\nimport type { Types } from '../../types';\nimport { parseGroupReferences } from './util/parse-group-references';\nimport { parseLocationReferences, parseSelectedLocations } from './util/parse-location-references';\nimport type { ConnectedTypes } from '../../make-connected';\nimport { getDayIndex, getVertexId } from '../../../core/util';\nimport { getPeriodIndex, idOf } from './util/util';\nimport { parseDays } from './util/parse-days';\nimport type { DateType } from '../../../common/types';\nimport { parseIntervals } from './util/parse-intervals';\nimport { getDefaultInterval } from './intervals';\n\nexport function toDayAndStart (start: DateType) {\n return {\n start: parseFloat(moment.utc(start).format('HH.mm')),\n day: getDayIndex(start)\n };\n}\n\nexport function parseEvents (\n events: ConnectedTypes.event[] | undefined,\n settings: ConnectedTypes.divisionSettings,\n options: Types.parsedToOptions,\n periodsMap: Map<string | undefined, number>\n): Types.event[] {\n const defaultInterval = getDefaultInterval(settings);\n\n return (events ?? [])\n .map((event): Types.event | undefined => {\n const id = getVertexId(event, options);\n const intervals = event.intervals ?? event.course?.intervals ?? defaultInterval;\n\n const duration = event.preferredDuration;\n if (!duration) throw new Error(`(RS::To::Events) Event \"${id}\" has no duration`);\n\n const forcedOverlapId = event.overlapSpecies?.species?.find(({ to }) => to == event)?.id;\n\n // allow duration variance only if the event isn't linked\n const maxLengthVariance = forcedOverlapId == null\n ? event.durationVariance\n : 0;\n\n const doc: Types.event = {\n id: idOf.event(event, options),\n length: duration,\n maxLengthVariance: maxLengthVariance,\n weight: event.weight,\n density: event.density,\n potentialCenter: event.centerOfAttraction ? parseFloat(event.centerOfAttraction.replace(':', '.')) : undefined,\n forcedOverlapId: forcedOverlapId,\n period: getPeriodIndex (event.period, periodsMap, options),\n _period: event.period ? getVertexId(event.period, options) : undefined,\n days: parseDays (event.days, settings),\n dependencies: parseLocationReferences (event.locations, options),\n groups: parseGroupReferences ({ type: 'event', item: event }, options),\n intervals: options.oldFormat\n ? parseIntervals(intervals, undefined, settings)\n : idOf.intervalPairReference(intervals, undefined, options),\n lockedTimes: attachLockedTimes (event.lockedTimes, options),\n minBreakLength: parseMinimumBreakLength (event.minBreakLength),\n };\n\n // if the event has a fixed day and start\n if (event.fixedStart && event.start) {\n Object.assign(doc, toDayAndStart(event.start));\n }\n\n if (options.includeEntityMeta) {\n doc.meta = omitBy({\n name: event.displayName,\n ids: event.ids,\n parked: event.parked,\n visible: event.visible,\n start: event.start ? moment.utc(event.start) : undefined,\n end: event.end ? moment.utc(event.start) : undefined,\n course: event.course ? getVertexId(event.course, options) : undefined,\n inLocations: event.inLocations ? parseSelectedLocations(event, options) : undefined\n }, x => x == null);\n }\n\n ////\n //// filter events based on partialScheduleOptions\n ////\n if (options.partialScheduleOptions) {\n const { includedEvents, omittedEventsHandling } = options.partialScheduleOptions;\n if (includedEvents && !includedEvents.has(doc.id)) { // collection.id => take into account dynamic locked times too!\n\n if (omittedEventsHandling == 'ignore') return;\n\n if (omittedEventsHandling == 'freeze') {\n // must not be parked and have a start and duration to be frozen, otherwise it's ignored\n if (event.parked || !event.start || !event.duration) return;\n\n // fix day, start and end\n Object.assign(doc, toDayAndStart(event.start));\n doc.length = event.duration;\n doc.maxLengthVariance = 0;\n\n // override intervals and days to not cause conflicts\n const numDays = settings.numDays ?? 5;\n doc.days = Array.from({ length: numDays }, (_, i) => i);\n doc.intervals = Array.from({ length: numDays }, () => [{ beg: 0, end: 23.55 }]);\n\n // fix locations\n doc.dependencies = (event.inLocations ?? [])\n .filter((x): x is NonNullable<typeof x> => !!x)\n .map(x => {\n const id = getVertexId(x, options);\n\n // filter location references based on partial schedule options\n const includedLocations = options.partialScheduleOptions?.includedLocations;\n if (includedLocations && !includedLocations.has(id)) return;\n\n return [{ dependency: id }] as Types.availableDependency[];\n })\n .filter(x => x != null);\n }\n\n }\n }\n\n return omitBy(doc, x => x == null) as Types.event;\n })\n .filter((x): x is NonNullable<typeof x> => !!x);\n}"],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { CoreMap } from "../../core/index.js";
2
+ import { COLLECTION_ID } from "./input/util/util.js";
2
3
  import { parseInput } from "./input/input.js";
3
4
  import { makeConnected } from "../make-connected.js";
4
5
  import { initialConfiguration } from "./initial-configuration.js";
@@ -6,6 +7,15 @@ import { structure } from "../types/to.js";
6
7
  import { pick } from "lodash-es";
7
8
 
8
9
  //#region src/RS/to/schedules.ts
10
+ function transformId(id) {
11
+ const indexOfDot = id.indexOf(".");
12
+ if (indexOfDot == -1) return COLLECTION_ID.events + "." + id;
13
+ const prefix = id.substring(0, indexOfDot).toLowerCase();
14
+ const suffix = id.substring(indexOfDot + 1);
15
+ if (prefix == COLLECTION_ID.lockedTimes.toLowerCase()) return COLLECTION_ID.lockedTimes + "." + suffix;
16
+ if (prefix == COLLECTION_ID.events.toLowerCase()) return COLLECTION_ID.events + "." + suffix;
17
+ throw new Error(`(RS::To::transformId) Unknown id prefix "${prefix}" in id "${id}"`);
18
+ }
9
19
  function schedules(data, _options = {}) {
10
20
  const options = (() => {
11
21
  const x = _options.partialScheduleOptions;
@@ -19,10 +29,7 @@ function schedules(data, _options = {}) {
19
29
  partialScheduleOptions
20
30
  };
21
31
  })();
22
- if (options.partialScheduleOptions?.includedEvents?.size) options.partialScheduleOptions.includedEvents = new Set([...options.partialScheduleOptions.includedEvents].map((x) => {
23
- if (x.startsWith("events.") || x.startsWith("lockedTimes.")) return x;
24
- return `events.${x}`;
25
- }));
32
+ if (options.partialScheduleOptions?.includedEvents?.size) options.partialScheduleOptions.includedEvents = new Set([...options.partialScheduleOptions.includedEvents].map((x) => transformId(x)));
26
33
  const connectedData = makeConnected(data);
27
34
  return {
28
35
  meta: {
@@ -1 +1 @@
1
- {"version":3,"file":"schedules.js","names":["partialScheduleOptions: Types.parsedToOptions['partialScheduleOptions']"],"sources":["../../../src/RS/to/schedules.ts"],"sourcesContent":["import { pick } from 'lodash-es';\nimport { parseInput } from './input/input';\nimport { makeConnected } from '../make-connected';\nimport { initialConfiguration } from './initial-configuration';\nimport type { Types } from '../types';\nimport { CoreMap } from '../../core';\nimport { structure } from '../types/to';\n\nexport function schedules (\n data: Types.toInput,\n _options: Types.toOptions = {}\n): Types.toOutput {\n ////\n //// parse the options\n ////\n const options = ((): Types.parsedToOptions => {\n const x = _options.partialScheduleOptions;\n const partialScheduleOptions: Types.parsedToOptions['partialScheduleOptions'] = x\n ? {\n includedEvents: x.includedEvents ? new Set(x.includedEvents) : undefined,\n includedLocations: x.includedLocations ? new Set(x.includedLocations) : undefined,\n omittedEventsHandling: x.omittedEventsHandling\n }\n : undefined;\n\n return { ..._options, partialScheduleOptions };\n })();\n\n ////\n //// for backward compatibility: add default collection to included events set\n ////\n if (options.partialScheduleOptions?.includedEvents?.size) {\n options.partialScheduleOptions.includedEvents = new Set(\n [...options.partialScheduleOptions.includedEvents]\n .map(x => {\n // should start with 'events.' or 'lockedTimes.' prefix\n if (x.startsWith('events.') || x.startsWith('lockedTimes.')) return x;\n return `events.${x}`;\n })\n );\n }\n\n\n // we will work only with connected schedule data\n const connectedData = makeConnected(data);\n\n return {\n meta: {\n structure: structure,\n division: pick(connectedData.division, 'displayName', 'start', 'end'),\n },\n algorithmParameters: {\n weights: options.algorithmWeightParameters,\n computeTimeMultiplier: options.computeTimeMultiplier\n },\n input: parseInput(connectedData, options),\n ...options.appendCoreData && { coreData: CoreMap.to.schedules(data) },\n ...options.appendOutput && { output: initialConfiguration(connectedData.events, connectedData.lockedTimes, options) },\n };\n};"],"mappings":";;;;;;;;AAQA,SAAgB,UACd,MACA,WAA4B,EAAE,EACd;CAIhB,MAAM,iBAAwC;EAC5C,MAAM,IAAI,SAAS;EACnB,MAAMA,yBAA0E,IAC5E;GACA,gBAAuB,EAAE,iBAAoB,IAAI,IAAI,EAAE,eAAe,GAAM;GAC5E,mBAAuB,EAAE,oBAAoB,IAAI,IAAI,EAAE,kBAAkB,GAAG;GAC5E,uBAAuB,EAAE;GAC1B,GACC;AAEJ,SAAO;GAAE,GAAG;GAAU;GAAwB;KAC5C;AAKJ,KAAI,QAAQ,wBAAwB,gBAAgB,KAClD,SAAQ,uBAAuB,iBAAiB,IAAI,IAClD,CAAC,GAAG,QAAQ,uBAAuB,eAAe,CAC/C,KAAI,MAAK;AAER,MAAI,EAAE,WAAW,UAAU,IAAI,EAAE,WAAW,eAAe,CAAE,QAAO;AACpE,SAAO,UAAU;GACjB,CACL;CAKH,MAAM,gBAAgB,cAAc,KAAK;AAEzC,QAAO;EACL,MAAM;GACO;GACX,UAAW,KAAK,cAAc,UAAU,eAAe,SAAS,MAAM;GACvE;EACD,qBAAqB;GACnB,SAAuB,QAAQ;GAC/B,uBAAuB,QAAQ;GAChC;EACD,OAAO,WAAW,eAAe,QAAQ;EACzC,GAAG,QAAQ,kBAAkB,EAAE,UAAU,QAAQ,GAAG,UAAU,KAAK,EAAE;EACrE,GAAG,QAAQ,gBAAkB,EAAE,QAAQ,qBAAqB,cAAc,QAAQ,cAAc,aAAa,QAAQ,EAAE;EACxH"}
1
+ {"version":3,"file":"schedules.js","names":["partialScheduleOptions: Types.parsedToOptions['partialScheduleOptions']"],"sources":["../../../src/RS/to/schedules.ts"],"sourcesContent":["import { pick } from 'lodash-es';\nimport { parseInput } from './input/input';\nimport { makeConnected } from '../make-connected';\nimport { initialConfiguration } from './initial-configuration';\nimport type { Types } from '../types';\nimport { CoreMap } from '../../core';\nimport { structure } from '../types/to';\nimport { COLLECTION_ID } from './input/util/util';\n\nfunction transformId (id: string): string {\n // should be interpreted as event id\n const indexOfDot = id.indexOf('.');\n if (indexOfDot == -1) return COLLECTION_ID.events + '.' + id;\n\n // split on first dot only\n const prefix = id.substring(0, indexOfDot).toLowerCase();\n const suffix = id.substring(indexOfDot + 1);\n\n if (prefix == COLLECTION_ID.lockedTimes.toLowerCase()) return COLLECTION_ID.lockedTimes + '.' + suffix;\n if (prefix == COLLECTION_ID.events .toLowerCase()) return COLLECTION_ID.events + '.' + suffix;\n\n throw new Error(`(RS::To::transformId) Unknown id prefix \"${prefix}\" in id \"${id}\"`);\n}\n\nexport function schedules (\n data: Types.toInput,\n _options: Types.toOptions = {}\n): Types.toOutput {\n ////\n //// parse the options\n ////\n const options = ((): Types.parsedToOptions => {\n const x = _options.partialScheduleOptions;\n const partialScheduleOptions: Types.parsedToOptions['partialScheduleOptions'] = x\n ? {\n includedEvents: x.includedEvents ? new Set(x.includedEvents) : undefined,\n includedLocations: x.includedLocations ? new Set(x.includedLocations) : undefined,\n omittedEventsHandling: x.omittedEventsHandling\n }\n : undefined;\n\n return { ..._options, partialScheduleOptions };\n })();\n\n ////\n //// for backward compatibility: add default collection to included events set\n ////\n if (options.partialScheduleOptions?.includedEvents?.size) {\n options.partialScheduleOptions.includedEvents = new Set(\n [...options.partialScheduleOptions.includedEvents].map(x => transformId(x))\n );\n }\n\n\n // we will work only with connected schedule data\n const connectedData = makeConnected(data);\n\n return {\n meta: {\n structure: structure,\n division: pick(connectedData.division, 'displayName', 'start', 'end'),\n },\n algorithmParameters: {\n weights: options.algorithmWeightParameters,\n computeTimeMultiplier: options.computeTimeMultiplier\n },\n input: parseInput(connectedData, options),\n ...options.appendCoreData && { coreData: CoreMap.to.schedules(data) },\n ...options.appendOutput && { output: initialConfiguration(connectedData.events, connectedData.lockedTimes, options) },\n };\n};"],"mappings":";;;;;;;;;AASA,SAAS,YAAa,IAAoB;CAExC,MAAM,aAAa,GAAG,QAAQ,IAAI;AAClC,KAAI,cAAc,GAAI,QAAO,cAAc,SAAS,MAAM;CAG1D,MAAM,SAAS,GAAG,UAAU,GAAG,WAAW,CAAC,aAAa;CACxD,MAAM,SAAS,GAAG,UAAU,aAAa,EAAE;AAE3C,KAAI,UAAU,cAAc,YAAY,aAAa,CAAE,QAAO,cAAc,cAAc,MAAM;AAChG,KAAI,UAAU,cAAc,OAAY,aAAa,CAAE,QAAO,cAAc,SAAc,MAAM;AAEhG,OAAM,IAAI,MAAM,4CAA4C,OAAO,WAAW,GAAG,GAAG;;AAGtF,SAAgB,UACd,MACA,WAA4B,EAAE,EACd;CAIhB,MAAM,iBAAwC;EAC5C,MAAM,IAAI,SAAS;EACnB,MAAMA,yBAA0E,IAC5E;GACA,gBAAuB,EAAE,iBAAoB,IAAI,IAAI,EAAE,eAAe,GAAM;GAC5E,mBAAuB,EAAE,oBAAoB,IAAI,IAAI,EAAE,kBAAkB,GAAG;GAC5E,uBAAuB,EAAE;GAC1B,GACC;AAEJ,SAAO;GAAE,GAAG;GAAU;GAAwB;KAC5C;AAKJ,KAAI,QAAQ,wBAAwB,gBAAgB,KAClD,SAAQ,uBAAuB,iBAAiB,IAAI,IAClD,CAAC,GAAG,QAAQ,uBAAuB,eAAe,CAAC,KAAI,MAAK,YAAY,EAAE,CAAC,CAC5E;CAKH,MAAM,gBAAgB,cAAc,KAAK;AAEzC,QAAO;EACL,MAAM;GACO;GACX,UAAW,KAAK,cAAc,UAAU,eAAe,SAAS,MAAM;GACvE;EACD,qBAAqB;GACnB,SAAuB,QAAQ;GAC/B,uBAAuB,QAAQ;GAChC;EACD,OAAO,WAAW,eAAe,QAAQ;EACzC,GAAG,QAAQ,kBAAkB,EAAE,UAAU,QAAQ,GAAG,UAAU,KAAK,EAAE;EACrE,GAAG,QAAQ,gBAAkB,EAAE,QAAQ,qBAAqB,cAAc,QAAQ,cAAc,aAAa,QAAQ,EAAE;EACxH"}
@@ -84,7 +84,7 @@ const schedules = (input, options = {}) => {
84
84
  }] },
85
85
  person: `teacher.${extractId(x.GUID)}`
86
86
  }));
87
- const teacherPersons = tables.Teacher?.map((x) => ({
87
+ const teacherPersons = tables.Teacher?.filter((x) => x.FirstName && x.LastName)?.map((x) => ({
88
88
  ids: `teacher.${extractId(x.GUID)}`,
89
89
  firstName: x.FirstName,
90
90
  lastName: x.LastName,
@@ -175,7 +175,7 @@ const schedules = (input, options = {}) => {
175
175
  plannedDuration: x.TotalHours ? `${x.TotalHours} hrs` : x.Weektime ? `${x.Weektime} min/week` : void 0,
176
176
  ...x.Period && { period: periodMap.get(x.Period) }
177
177
  };
178
- });
178
+ }) ?? [];
179
179
  tables.TA?.forEach((x) => {
180
180
  courseMap.set(`${x.Subject}.${x.Teacher}.${x.Group}`, x.GUID ? extractId(x.GUID) : void 0);
181
181
  });
@@ -188,7 +188,8 @@ const schedules = (input, options = {}) => {
188
188
  value: x.Category
189
189
  }] }
190
190
  }));
191
- let events = tables.Lesson?.filter((x) => !isNaN(parseInt(x.Length))).map((x) => {
191
+ let lessonMissingCourseWarningsCount = 0;
192
+ const events = tables.Lesson?.filter((x) => !isNaN(parseInt(x.Length))).map((x) => {
192
193
  const position = (() => {
193
194
  const dayIndex = daysToIndex[x.DayOfWeek];
194
195
  if (dayIndex == null) {
@@ -206,12 +207,29 @@ const schedules = (input, options = {}) => {
206
207
  const periodName = options.createPeriodsFrom ? x[options.createPeriodsFrom] : x.Period;
207
208
  const periodId = periodMap.get(periodName);
208
209
  const courseKey = `${x.Subject}.${x.Teacher}.${x.Group}`;
209
- const courseId = courseMap.get(courseKey);
210
+ let courseId = courseMap.get(courseKey);
210
211
  const inLocations = (x.Room ?? []).split(",").map((x$1) => locationMap.get(x$1.trim())).filter((x$1) => !!x$1);
211
- const displayName = courseId ? void 0 : parentCourseMap.get(x.Course)?.FullText || subjectMap.get(x.Subject)?.FullText || x.Course || x.Subject;
212
+ let displayName = courseId ? void 0 : parentCourseMap.get(x.Course)?.FullText || subjectMap.get(x.Subject)?.FullText || x.Course || x.Subject;
212
213
  const teachers$1 = courseId ? void 0 : x.Teacher?.split(",").map((x$1) => teacherMap.get(x$1.trim())).filter((x$1) => !!x$1).map((to) => ({ to }));
213
214
  const groups$1 = courseId ? void 0 : x.Group?.split(",").map((x$1) => groupMap.get(x$1.trim())).filter((x$1) => !!x$1).map((to) => ({ to }));
214
215
  const duration = x.Length ? parseInt(x.Length) : void 0;
216
+ if (!courseId && options.includeEventsMissingCourse) {
217
+ courseId = v4();
218
+ const subject = subjectMap.get(x.Subject);
219
+ const subjectName = subject?.FullText || x.Subject;
220
+ displayName = displayName || subject?.FullText || x.Subject;
221
+ const newCourse = {
222
+ ids: courseId,
223
+ displayName,
224
+ subject: subjectName,
225
+ teachers: teachers$1,
226
+ groups: groups$1
227
+ };
228
+ courses?.push(newCourse);
229
+ } else if (!courseId) {
230
+ lessonMissingCourseWarningsCount += 1;
231
+ return null;
232
+ }
215
233
  return {
216
234
  ids: extractId(x.GUID),
217
235
  displayName,
@@ -220,35 +238,23 @@ const schedules = (input, options = {}) => {
220
238
  preferredDuration: duration ?? 0,
221
239
  course: courseId,
222
240
  period: periodId,
223
- teachers: teachers$1,
224
- groups: groups$1,
225
- inLocations
241
+ inLocations,
242
+ ...!options.includeEventsMissingCourse ? {
243
+ teachers: teachers$1,
244
+ groups: groups$1
245
+ } : void 0
226
246
  };
227
- });
247
+ }).filter((x) => x != null);
228
248
  const periods = tables.Period?.map((x) => parsePeriod(x, scheduleStart, warnings)).filter((x) => x.ranges.length > 0);
229
249
  periodsFromWeekRanges.forEach((x) => {
230
250
  const ids = periodMap.get(x);
231
251
  if (!ids) throw new Error(`Unable to find period for ${x}`);
232
252
  periods?.push(fromWeekNumberRange(ids, x, scheduleStart));
233
253
  });
234
- if (options.includeEventsMissingCourse) events?.filter((x) => !x.course).forEach((event) => {
235
- const courseId = v4();
236
- courses?.push({
237
- ids: courseId,
238
- displayName: event.displayName
239
- });
240
- event.course = courseId;
241
- delete event.displayName;
254
+ if (lessonMissingCourseWarningsCount > 0 && !options.includeEventsMissingCourse) warnings.unshift({
255
+ code: "ignoring_lessons_missing_course",
256
+ context: { count: lessonMissingCourseWarningsCount }
242
257
  });
243
- else {
244
- const numEventsPrior = events?.length ?? 0;
245
- events = events?.filter((x) => x.course);
246
- const diff = numEventsPrior - (events?.length ?? 0);
247
- if (diff) warnings.unshift({
248
- code: "ignoring_lessons_missing_course",
249
- context: { count: diff }
250
- });
251
- }
252
258
  return {
253
259
  settings,
254
260
  periods: periods ?? [],
@@ -1 +1 @@
1
- {"version":3,"file":"schedules.js","names":["warnings: Warning[]","settings: OutTypes.settings","x","uuidv4","teachers","groups","getDate"],"sources":["../../../../src/Skola24/txt/from/schedules.ts"],"sourcesContent":["import Papa from 'papaparse';\nimport { v4 as uuidv4 } from 'uuid'; // as we cannot use crypto's randomUUID in browser\nimport moment from 'moment';\nimport getDate from '../../../common/get-date';\nimport type { CoreTypes } from '../../../core';\nimport type { Data, Options, Types as Skola24Types, Warning } from '../types';\nimport { extractId } from './util';\nimport { fromWeekNumberRange, parsePeriod } from './period';\nimport { parseCalendarExceptions } from './calendar-exceptions';\nimport { extractStartAndEnd } from './division';\n\n\nconst daysToIndex = {\n 'Måndag': 0,\n 'Tisdag': 1,\n 'Onsdag': 2,\n 'Torsdag': 3,\n 'Fredag': 4,\n 'Lördag': 5,\n 'Söndag': 6,\n\n // and if ÅÄÖ is loaded correctly\n 'M�ndag': 0,\n 'L�rdag': 5,\n 'S�ndag': 6,\n} as Record<string, number | undefined>;\n\n\nfunction transformHeader (header: string) {\n return header.split(/[^a-zA-Z]+/g)[0];\n}\nconst parseConfig = {\n quotes: false, //or array of booleans\n quoteChar: '\"',\n escapeChar: '\"',\n delimiter: '\\t',\n header: true,\n // newline: \"\\r\\n\",\n skipEmptyLines: false, //other option is 'greedy', meaning skip delimiters, quotes, and whitespace.\n} as Papa.ParseConfig;\n\n\nfunction extractTables (input: string, warnings: Warning[]) {\n // split at [Tables] to get meta section and table section\n const split = input.split('[Tables]');\n const tables = split.at(1);\n if (!tables) throw new Error('Unable to extract tables from input');\n\n ////\n //// Capture the TABLE NAME and the TABLE CONTENT using regex\n ////\n //// TABLE NAME\n //// [Rows]\n //// TABLE CONTENT\n ////\n const regex = /([^\\r\\n]+)(?:\\r\\n|\\r|\\n)\\[Rows\\](?:\\r\\n|\\r|\\n)((?:[^\\r\\n].+(?:(?:\\r\\n|\\r|\\n)|$))*)/gm;\n const matches = [...tables.trim().matchAll(regex)];\n\n return matches\n .map(x => {\n // extract only the true table name \"<table name> (<number>)\", i.e., ignore the brackets and the preceding space\n const tableName = x.at(1)\n ?.trim()\n .match(/(.*?)(?:\\s*\\(\\d+\\))$/m)\n ?.at(1);\n if (!tableName) return;\n\n const tableContent = x.at(2)?.trim();\n if (!tableContent) return;\n const parsed = Papa.parse(tableContent, { ...parseConfig, transformHeader });\n return {\n tableName: tableName as keyof Data,\n parsed: parsed.data\n };\n })\n .filter((x): x is NonNullable<typeof x> => x != null)\n .reduce<Data>((acc, { tableName, parsed }) => {\n acc[tableName] = parsed;\n return acc;\n }, {} as Data);\n}\n\n\nfunction parseColor (colorValue: string | undefined) {\n if (colorValue == null) return;\n\n const integerValue = parseInt(colorValue);\n if (isNaN(integerValue)) return;\n\n // convert to decimal string\n const hex = integerValue.toString(16);\n\n // ensure its a valid color\n if (!/^(?:[0-9a-fA-F]{3}){1,2}$/.test(hex)) return;\n return `#${ hex }`;\n}\n\n\nexport namespace OutTypes {\n export type settings = CoreTypes.SerializedWithOptionalId.Settings;\n export type teacher = CoreTypes.SerializedWithOptionalId.Teacher;\n export type person = CoreTypes.SerializedWithOptionalId.Person;\n export type group = CoreTypes.SerializedWithOptionalId.Group;\n export type course = CoreTypes.SerializedWithOptionalId.Course;\n export type location = CoreTypes.SerializedWithOptionalId.Location;\n export type event = CoreTypes.SerializedWithOptionalId.Event;\n export type period = CoreTypes.SerializedWithOptionalId.Period;\n}\n\ntype Out = {\n settings: OutTypes.settings;\n periods: OutTypes.period [];\n teachers: OutTypes.teacher [];\n persons: OutTypes.person [];\n groups: OutTypes.group [];\n locations: OutTypes.location[];\n courses: OutTypes.course [];\n events: OutTypes.event [];\n meta?: {\n warnings: Warning[]\n };\n};\n\n\nexport const schedules = (\n input: string,\n options: Options = {}\n): Out => {\n const warnings: Warning[] = [];\n\n const tables = extractTables(input, warnings);\n\n const _scheduleRange = extractStartAndEnd(input); // need whole input file\n if (!_scheduleRange) throw new Error('Unable to extract schedule range');\n\n const scheduleStart = moment.utc(_scheduleRange.start);\n const startYear = scheduleStart.year();\n\n const settings: OutTypes.settings = {\n discretization: 5,\n numDays: 5,\n dayStart: '8:00',\n potentialCenter: '12:00',\n dayEnd: '17:00',\n calendarExceptions: parseCalendarExceptions(tables.PeriodGeneral?.at(0), startYear, warnings)\n };\n\n\n // Map to core structure\n const teachers = tables.Teacher\n ?.map((x): OutTypes.teacher => ({\n ids: extractId(x.GUID),\n signature: x.Teacher,\n displayName: `${x.FirstName} ${x.LastName}`,\n ...x.Category && {\n tags: [\n {\n type: 'category',\n value: x.Category\n }\n ]\n },\n person: `teacher.${ extractId(x.GUID) }`\n }));\n const teacherPersons = tables.Teacher\n ?.map((x): OutTypes.person => ({\n ids: `teacher.${ extractId(x.GUID) }`,\n firstName: x.FirstName,\n lastName: x.LastName,\n ...x.PersonalCode && {\n SSN: {\n value: x.PersonalCode,\n }\n },\n ...x.EMail && {\n emails: [{\n value: x.EMail,\n type: 'organization'\n }]\n },\n ...x.Phone && {\n phoneNumbers: [\n {\n value: x.Phone,\n type: 'organization'\n }\n ]\n }\n }));\n\n const studentMap = new Map<string, string>();\n // create map of student SSN to student GUID\n tables.Student?.forEach(x => {\n const id = extractId(x.GUID);\n if (id) studentMap.set(x.Student, id);\n });\n\n const students = tables.Student?.map((x): OutTypes.person => ({\n ids: extractId(x.GUID),\n firstName: x.FirstName,\n lastName: x.LastName,\n type: 'Student',\n ...x.Student && {\n SSN: {\n value: x.Student,\n }\n },\n ...x.EMail && {\n emails: [{\n value: x.EMail,\n type: 'organization'\n }]\n }\n }));\n\n const groups = tables.Group?.map((x): OutTypes.group => ({\n ids: extractId(x.GUID),\n displayName: x.Group,\n ...(x.Class == '1') && { species: 'class' },\n members: x.Student?.split(',').map(x => studentMap.get(x)).filter(x => x != null) ?? []\n }));\n\n const periodMap = new Map<string, string>();\n const periodsFromWeekRanges = new Set<string>();\n if (tables.Period) {\n tables.Period.forEach(period => {\n const id = extractId(period.GUID);\n if (id) periodMap.set(period.Period, id);\n });\n\n // create additional periods corresponding to the lessons' week/actual weeks\n const _createPeriodsFrom = options.createPeriodsFrom;\n if (_createPeriodsFrom) {\n tables.Lesson?.forEach(x => {\n const key = x[_createPeriodsFrom];\n if (key) periodsFromWeekRanges.add(key);\n });\n periodsFromWeekRanges.forEach(x => periodMap.set(x, uuidv4()));\n }\n }\n\n const teacherMap = new Map<string, string>();\n if (tables.Teacher) {\n tables.Teacher.forEach(teacher => {\n const id = extractId(teacher.GUID);\n if (id) teacherMap.set(teacher.Teacher, id);\n });\n }\n\n const groupMap = new Map<string, string>();\n if (tables.Group) {\n tables.Group.forEach(group => {\n const id = extractId(group.GUID);\n if (id) groupMap.set(group.Group, id);\n });\n }\n\n const locationMap = new Map<string, string>();\n if (tables.Room) {\n tables.Room.forEach(room => {\n const id = extractId(room.GUID);\n if (id) locationMap.set(room.Room, id);\n });\n }\n\n // subject contains BackgroundColor(decimal) and FullText\n const subjectMap = new Map<string, Skola24Types.subject>();\n if (tables.Subject) {\n tables.Subject.forEach(subject => {\n subjectMap.set(subject.Subject, subject);\n });\n }\n\n // Course can contain FullText and Subject\n const parentCourseMap = new Map<string, Skola24Types.course>();\n if (tables.Course) {\n tables.Course.forEach(course => {\n parentCourseMap.set(course.Course, course);\n });\n }\n\n const courseMap = new Map<string, string | undefined>();\n const courses = tables.TA?.map((x): OutTypes.course => {\n const color = parseColor(subjectMap.get(x.Subject)?.BackgroundColor);\n\n // Course name is most specific (eg Administration 1), then subject name (Administration), then course code (eg ADMADM01), then subject code (eg ADM)\n const displayName = parentCourseMap.get(x.Course)?.FullText || subjectMap.get(x.Subject)?.FullText || x.Course || x.Subject;\n\n const teachers = (x.Teacher ?? [])\n .split(',')\n .map(x => teacherMap.get(x.trim()))\n .filter((x): x is NonNullable<typeof x> => !!x)\n .map(to => ({ to }));\n const groups = (x.Group ?? [])\n .split(',')\n .map(x => groupMap.get(x.trim()))\n .filter((x): x is NonNullable<typeof x> => !!x)\n .map(to => ({ to }));\n\n return {\n ids: x.GUID ? extractId(x.GUID) : uuidv4(),\n displayName: displayName,\n color: color,\n subject: x.Subject || parentCourseMap.get(x.Course)?.Subject,\n teachers: teachers,\n groups: groups,\n plannedDuration: x.TotalHours ? `${x.TotalHours} hrs` : (x.Weektime ? `${x.Weektime} min/week` : undefined),\n ...x.Period && { period: periodMap.get(x.Period) },\n } satisfies OutTypes.course;\n });\n\n tables.TA?.forEach(x => {\n courseMap.set(`${x.Subject}.${x.Teacher}.${x.Group}`, x.GUID ? extractId(x.GUID) : undefined);\n });\n\n const locations = tables.Room?.map((x): OutTypes.location => ({\n ids: extractId(x.GUID),\n displayName: x.Room,\n maximumCapacity: x.SeatingCapacity ? parseInt(x.SeatingCapacity) : undefined,\n ...x.Category?.length && {\n tags: [\n {\n type: 'category',\n value: x.Category\n }\n ]\n }\n }));\n\n let events = tables.Lesson\n ?.filter(x => !isNaN(parseInt(x.Length)))\n .map(x => {\n\n const position = (() => {\n const dayIndex = daysToIndex[x.DayOfWeek];\n\n if (dayIndex == null) {\n warnings.push({ code: 'lesson.invalid_day', context: x } satisfies Warning);\n return;\n }\n\n return {\n start: getDate(dayIndex, x.Starttime),\n end: getDate(dayIndex, x.Starttime).add(x.Length, 'minutes'),\n };\n })();\n\n const periodName = options.createPeriodsFrom ? x[options.createPeriodsFrom] : x.Period;\n const periodId = periodMap.get(periodName);\n\n const courseKey = `${x.Subject}.${x.Teacher}.${x.Group}`;\n const courseId = courseMap.get(courseKey);\n\n const inLocations = (x.Room ?? [])\n .split(',')\n .map(x => locationMap.get(x.trim()))\n .filter((x): x is NonNullable<typeof x> => !!x);\n\n // if there is a connected course use its display name\n const displayName = courseId\n ? undefined\n : parentCourseMap.get(x.Course)?.FullText || subjectMap.get(x.Subject)?.FullText || x.Course || x.Subject;\n\n // if there is a connected course use its teachers\n const teachers = courseId\n ? undefined\n : x.Teacher\n ?.split(',')\n .map(x => teacherMap.get(x.trim()))\n .filter((x): x is NonNullable<typeof x> => !!x)\n .map(to => ({ to }));\n\n // if there is a connected course use its groups\n const groups = courseId\n ? undefined\n : x.Group\n ?.split(',')\n .map(x => groupMap.get(x.trim()))\n .filter((x): x is NonNullable<typeof x> => !!x)\n .map(to => ({ to }));\n\n const duration = x.Length ? parseInt(x.Length) : undefined;\n\n return {\n ids: extractId(x.GUID),\n displayName: displayName,\n ...position,\n duration: duration,\n preferredDuration: duration ?? 0,\n course: courseId,\n period: periodId,\n teachers, groups, inLocations\n } satisfies OutTypes.event;\n });\n\n const periods = tables.Period\n ?.map(x => parsePeriod(x, scheduleStart, warnings))\n .filter(x => x.ranges.length > 0);\n\n periodsFromWeekRanges.forEach(x => {\n const ids = periodMap.get(x);\n if (!ids) {\n throw new Error(`Unable to find period for ${x}`);\n return;\n }\n periods?.push(fromWeekNumberRange(ids, x, scheduleStart));\n });\n\n // handle events without a course\n if (options.includeEventsMissingCourse) {\n // create a dummy course for each event without a course\n events?.filter(x => !x.course)\n .forEach(event => {\n // create a dummy course\n const courseId = uuidv4();\n courses?.push({\n ids: courseId,\n displayName: event.displayName\n } satisfies OutTypes.course);\n\n // assign the course to the event and remove its display name\n event.course = courseId;\n delete event.displayName;\n });\n\n } else {\n // remove all events without a course\n const numEventsPrior = events?.length ?? 0;\n events = events?.filter(x => x.course);\n const diff = numEventsPrior - (events?.length ?? 0);\n if (diff) {\n warnings.unshift({\n code: 'ignoring_lessons_missing_course',\n context: { count: diff }\n } satisfies Warning);\n }\n }\n\n\n return {\n settings,\n periods: periods ?? [],\n teachers: teachers ?? [],\n persons: (students ?? []).concat(teacherPersons ?? []),\n groups: groups ?? [],\n locations: locations ?? [],\n courses: courses ?? [],\n events: events ?? [],\n\n meta: { warnings }\n } satisfies Out;\n};"],"mappings":"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"}
1
+ {"version":3,"file":"schedules.js","names":["warnings: Warning[]","settings: OutTypes.settings","x","uuidv4","teachers","groups","getDate","newCourse: OutTypes.course"],"sources":["../../../../src/Skola24/txt/from/schedules.ts"],"sourcesContent":["import Papa from 'papaparse';\nimport { v4 as uuidv4 } from 'uuid'; // as we cannot use crypto's randomUUID in browser\nimport moment from 'moment';\nimport getDate from '../../../common/get-date';\nimport type { CoreTypes } from '../../../core';\nimport type { Data, Options, Types as Skola24Types, Warning } from '../types';\nimport { extractId } from './util';\nimport { fromWeekNumberRange, parsePeriod } from './period';\nimport { parseCalendarExceptions } from './calendar-exceptions';\nimport { extractStartAndEnd } from './division';\n\n\nconst daysToIndex = {\n 'Måndag': 0,\n 'Tisdag': 1,\n 'Onsdag': 2,\n 'Torsdag': 3,\n 'Fredag': 4,\n 'Lördag': 5,\n 'Söndag': 6,\n\n // and if ÅÄÖ is loaded correctly\n 'M�ndag': 0,\n 'L�rdag': 5,\n 'S�ndag': 6,\n} as Record<string, number | undefined>;\n\n\nfunction transformHeader (header: string) {\n return header.split(/[^a-zA-Z]+/g)[0];\n}\nconst parseConfig = {\n quotes: false, //or array of booleans\n quoteChar: '\"',\n escapeChar: '\"',\n delimiter: '\\t',\n header: true,\n // newline: \"\\r\\n\",\n skipEmptyLines: false, //other option is 'greedy', meaning skip delimiters, quotes, and whitespace.\n} as Papa.ParseConfig;\n\n\nfunction extractTables (input: string, warnings: Warning[]) {\n // split at [Tables] to get meta section and table section\n const split = input.split('[Tables]');\n const tables = split.at(1);\n if (!tables) throw new Error('Unable to extract tables from input');\n\n ////\n //// Capture the TABLE NAME and the TABLE CONTENT using regex\n ////\n //// TABLE NAME\n //// [Rows]\n //// TABLE CONTENT\n ////\n const regex = /([^\\r\\n]+)(?:\\r\\n|\\r|\\n)\\[Rows\\](?:\\r\\n|\\r|\\n)((?:[^\\r\\n].+(?:(?:\\r\\n|\\r|\\n)|$))*)/gm;\n const matches = [...tables.trim().matchAll(regex)];\n\n return matches\n .map(x => {\n // extract only the true table name \"<table name> (<number>)\", i.e., ignore the brackets and the preceding space\n const tableName = x.at(1)\n ?.trim()\n .match(/(.*?)(?:\\s*\\(\\d+\\))$/m)\n ?.at(1);\n if (!tableName) return;\n\n const tableContent = x.at(2)?.trim();\n if (!tableContent) return;\n const parsed = Papa.parse(tableContent, { ...parseConfig, transformHeader });\n return {\n tableName: tableName as keyof Data,\n parsed: parsed.data\n };\n })\n .filter((x): x is NonNullable<typeof x> => x != null)\n .reduce<Data>((acc, { tableName, parsed }) => {\n acc[tableName] = parsed;\n return acc;\n }, {} as Data);\n}\n\n\nfunction parseColor (colorValue: string | undefined) {\n if (colorValue == null) return;\n\n const integerValue = parseInt(colorValue);\n if (isNaN(integerValue)) return;\n\n // convert to decimal string\n const hex = integerValue.toString(16);\n\n // ensure its a valid color\n if (!/^(?:[0-9a-fA-F]{3}){1,2}$/.test(hex)) return;\n return `#${ hex }`;\n}\n\n\nexport namespace OutTypes {\n export type settings = CoreTypes.SerializedWithOptionalId.Settings;\n export type teacher = CoreTypes.SerializedWithOptionalId.Teacher;\n export type person = CoreTypes.SerializedWithOptionalId.Person;\n export type group = CoreTypes.SerializedWithOptionalId.Group;\n export type course = CoreTypes.SerializedWithOptionalId.Course;\n export type location = CoreTypes.SerializedWithOptionalId.Location;\n export type event = CoreTypes.SerializedWithOptionalId.Event;\n export type period = CoreTypes.SerializedWithOptionalId.Period;\n}\n\ntype Out = {\n settings: OutTypes.settings;\n periods: OutTypes.period [];\n teachers: OutTypes.teacher [];\n persons: OutTypes.person [];\n groups: OutTypes.group [];\n locations: OutTypes.location[];\n courses: OutTypes.course [];\n events: OutTypes.event [];\n meta?: {\n warnings: Warning[]\n };\n};\n\n\nexport const schedules = (\n input: string,\n options: Options = {}\n): Out => {\n const warnings: Warning[] = [];\n\n const tables = extractTables(input, warnings);\n\n const _scheduleRange = extractStartAndEnd(input); // need whole input file\n if (!_scheduleRange) throw new Error('Unable to extract schedule range');\n\n const scheduleStart = moment.utc(_scheduleRange.start);\n const startYear = scheduleStart.year();\n\n const settings: OutTypes.settings = {\n discretization: 5,\n numDays: 5,\n dayStart: '8:00',\n potentialCenter: '12:00',\n dayEnd: '17:00',\n calendarExceptions: parseCalendarExceptions(tables.PeriodGeneral?.at(0), startYear, warnings)\n };\n\n\n // Map to core structure\n const teachers = tables.Teacher\n ?.map((x): OutTypes.teacher => ({\n ids: extractId(x.GUID),\n signature: x.Teacher,\n displayName: `${x.FirstName} ${x.LastName}`,\n ...x.Category && {\n tags: [\n {\n type: 'category',\n value: x.Category\n }\n ]\n },\n person: `teacher.${ extractId(x.GUID) }`\n }));\n const teacherPersons = tables.Teacher\n // Filter out where either of firstname, lastname i missing, else it gives invalid persons\n ?.filter(x => x.FirstName && x.LastName)\n ?.map((x): OutTypes.person => ({\n ids: `teacher.${ extractId(x.GUID) }`,\n firstName: x.FirstName,\n lastName: x.LastName,\n ...x.PersonalCode && {\n SSN: {\n value: x.PersonalCode,\n }\n },\n ...x.EMail && {\n emails: [{\n value: x.EMail,\n type: 'organization'\n }]\n },\n ...x.Phone && {\n phoneNumbers: [\n {\n value: x.Phone,\n type: 'organization'\n }\n ]\n }\n }));\n\n const studentMap = new Map<string, string>();\n // create map of student SSN to student GUID\n tables.Student?.forEach(x => {\n const id = extractId(x.GUID);\n if (id) studentMap.set(x.Student, id);\n });\n\n const students = tables.Student?.map((x): OutTypes.person => ({\n ids: extractId(x.GUID),\n firstName: x.FirstName,\n lastName: x.LastName,\n type: 'Student',\n ...x.Student && {\n SSN: {\n value: x.Student,\n }\n },\n ...x.EMail && {\n emails: [{\n value: x.EMail,\n type: 'organization'\n }]\n }\n }));\n\n const groups = tables.Group?.map((x): OutTypes.group => ({\n ids: extractId(x.GUID),\n displayName: x.Group,\n ...(x.Class == '1') && { species: 'class' },\n members: x.Student?.split(',').map(x => studentMap.get(x)).filter(x => x != null) ?? []\n }));\n\n const periodMap = new Map<string, string>();\n const periodsFromWeekRanges = new Set<string>();\n if (tables.Period) {\n tables.Period.forEach(period => {\n const id = extractId(period.GUID);\n if (id) periodMap.set(period.Period, id);\n });\n\n // create additional periods corresponding to the lessons' week/actual weeks\n const _createPeriodsFrom = options.createPeriodsFrom;\n if (_createPeriodsFrom) {\n tables.Lesson?.forEach(x => {\n const key = x[_createPeriodsFrom];\n if (key) periodsFromWeekRanges.add(key);\n });\n periodsFromWeekRanges.forEach(x => periodMap.set(x, uuidv4()));\n }\n }\n\n const teacherMap = new Map<string, string>();\n if (tables.Teacher) {\n tables.Teacher.forEach(teacher => {\n const id = extractId(teacher.GUID);\n if (id) teacherMap.set(teacher.Teacher, id);\n });\n }\n\n const groupMap = new Map<string, string>();\n if (tables.Group) {\n tables.Group.forEach(group => {\n const id = extractId(group.GUID);\n if (id) groupMap.set(group.Group, id);\n });\n }\n\n const locationMap = new Map<string, string>();\n if (tables.Room) {\n tables.Room.forEach(room => {\n const id = extractId(room.GUID);\n if (id) locationMap.set(room.Room, id);\n });\n }\n\n // subject contains BackgroundColor(decimal) and FullText\n const subjectMap = new Map<string, Skola24Types.subject>();\n if (tables.Subject) {\n tables.Subject.forEach(subject => {\n subjectMap.set(subject.Subject, subject);\n });\n }\n\n // Course can contain FullText and Subject\n const parentCourseMap = new Map<string, Skola24Types.course>();\n if (tables.Course) {\n tables.Course.forEach(course => {\n parentCourseMap.set(course.Course, course);\n });\n }\n\n const courseMap = new Map<string, string | undefined>();\n const courses = tables.TA?.map((x): OutTypes.course => {\n const color = parseColor(subjectMap.get(x.Subject)?.BackgroundColor);\n\n // Course name is most specific (eg Administration 1), then subject name (Administration), then course code (eg ADMADM01), then subject code (eg ADM)\n const displayName = parentCourseMap.get(x.Course)?.FullText || subjectMap.get(x.Subject)?.FullText || x.Course || x.Subject;\n\n const teachers = (x.Teacher ?? [])\n .split(',')\n .map(x => teacherMap.get(x.trim()))\n .filter((x): x is NonNullable<typeof x> => !!x)\n .map(to => ({ to }));\n const groups = (x.Group ?? [])\n .split(',')\n .map(x => groupMap.get(x.trim()))\n .filter((x): x is NonNullable<typeof x> => !!x)\n .map(to => ({ to }));\n\n return {\n ids: x.GUID ? extractId(x.GUID) : uuidv4(),\n displayName: displayName,\n color: color,\n subject: x.Subject || parentCourseMap.get(x.Course)?.Subject,\n teachers: teachers,\n groups: groups,\n plannedDuration: x.TotalHours ? `${x.TotalHours} hrs` : (x.Weektime ? `${x.Weektime} min/week` : undefined),\n ...x.Period && { period: periodMap.get(x.Period) },\n } satisfies OutTypes.course;\n }) ?? [];\n\n tables.TA?.forEach(x => {\n courseMap.set(`${x.Subject}.${x.Teacher}.${x.Group}`, x.GUID ? extractId(x.GUID) : undefined);\n });\n\n const locations = tables.Room?.map((x): OutTypes.location => ({\n ids: extractId(x.GUID),\n displayName: x.Room,\n maximumCapacity: x.SeatingCapacity ? parseInt(x.SeatingCapacity) : undefined,\n ...x.Category?.length && {\n tags: [\n {\n type: 'category',\n value: x.Category\n }\n ]\n }\n }));\n\n let lessonMissingCourseWarningsCount = 0;\n\n const events = tables.Lesson\n ?.filter(x => !isNaN(parseInt(x.Length)))\n .map(x => {\n\n const position = (() => {\n const dayIndex = daysToIndex[x.DayOfWeek];\n\n if (dayIndex == null) {\n warnings.push({ code: 'lesson.invalid_day', context: x } satisfies Warning);\n return;\n }\n\n return {\n start: getDate(dayIndex, x.Starttime),\n end: getDate(dayIndex, x.Starttime).add(x.Length, 'minutes'),\n };\n })();\n\n const periodName = options.createPeriodsFrom ? x[options.createPeriodsFrom] : x.Period;\n const periodId = periodMap.get(periodName);\n\n const courseKey = `${x.Subject}.${x.Teacher}.${x.Group}`;\n let courseId = courseMap.get(courseKey);\n\n const inLocations = (x.Room ?? [])\n .split(',')\n .map(x => locationMap.get(x.trim()))\n .filter((x): x is NonNullable<typeof x> => !!x);\n\n // if there is a connected course use its display name\n let displayName = courseId\n ? undefined\n : parentCourseMap.get(x.Course)?.FullText || subjectMap.get(x.Subject)?.FullText || x.Course || x.Subject;\n\n // if there is a connected course use its teachers\n const teachers = courseId\n ? undefined\n : x.Teacher\n ?.split(',')\n .map(x => teacherMap.get(x.trim()))\n .filter((x): x is NonNullable<typeof x> => !!x)\n .map(to => ({ to }));\n\n // if there is a connected course use its groups\n const groups = courseId\n ? undefined\n : x.Group\n ?.split(',')\n .map(x => groupMap.get(x.trim()))\n .filter((x): x is NonNullable<typeof x> => !!x)\n .map(to => ({ to }));\n\n const duration = x.Length ? parseInt(x.Length) : undefined;\n\n // If no courseId found, we handle it according to options.includeEventsMissingCourse\n if (!courseId && options.includeEventsMissingCourse) {\n // Create a new course for this event\n courseId = uuidv4();\n const subject = subjectMap.get(x.Subject);\n const subjectName = subject?.FullText || x.Subject;\n displayName = displayName || subject?.FullText || x.Subject;\n const newCourse: OutTypes.course = {\n ids: courseId,\n displayName: displayName,\n subject: subjectName,\n teachers: teachers,\n groups: groups,\n };\n courses?.push(newCourse);\n } else if (!courseId) {\n lessonMissingCourseWarningsCount += 1;\n return null; // will be filtered out later\n }\n\n return {\n ids: extractId(x.GUID),\n displayName: displayName,\n ...position,\n duration: duration,\n preferredDuration: duration ?? 0,\n course: courseId,\n period: periodId,\n inLocations,\n ...(!options.includeEventsMissingCourse ? { teachers, groups } : undefined),\n } satisfies OutTypes.event;\n }).filter((x): x is NonNullable<typeof x> => x != null);\n\n const periods = tables.Period\n ?.map(x => parsePeriod(x, scheduleStart, warnings))\n .filter(x => x.ranges.length > 0);\n\n periodsFromWeekRanges.forEach(x => {\n const ids = periodMap.get(x);\n if (!ids) {\n throw new Error(`Unable to find period for ${x}`);\n return;\n }\n periods?.push(fromWeekNumberRange(ids, x, scheduleStart));\n });\n\n if (lessonMissingCourseWarningsCount > 0 && !options.includeEventsMissingCourse) {\n warnings.unshift({\n code: 'ignoring_lessons_missing_course',\n context: { count: lessonMissingCourseWarningsCount }\n } satisfies Warning);\n }\n\n return {\n settings,\n periods: periods ?? [],\n teachers: teachers ?? [],\n persons: (students ?? []).concat(teacherPersons ?? []),\n groups: groups ?? [],\n locations: locations ?? [],\n courses: courses ?? [],\n events: events ?? [],\n\n meta: { warnings }\n } satisfies Out;\n};"],"mappings":"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"}
@@ -6,7 +6,7 @@ import { Deep } from "../vertices/index.js";
6
6
 
7
7
  //#region src/core/interfaces/other/policies.d.ts
8
8
  interface Action {
9
- targetResourceModel: string;
9
+ targetResourceModel?: string;
10
10
  operation: (Access['CREATE'] | Access['READ'] | Access['UPDATE'] | Access['DELETE'])[];
11
11
  }
12
12
  interface Statement {
@@ -0,0 +1,16 @@
1
+ import { Collection, Vertex } from "../vertex.js";
2
+ import { DeepNonNullable } from "../util.js";
3
+ import { ExpandKey } from "../common.js";
4
+ import { VertexQuery } from "../vertex-query/index.js";
5
+ import { Lookup } from "./util.js";
6
+
7
+ //#region src/core/interfaces/vertices/util/custom-vertex-export/assert-no-invalid-expand-keys.d.ts
8
+ type _AssertNoInvalidExpandKeys<Selected extends object, Available extends object> = { [K in keyof Selected]: K extends keyof Available ? Lookup<Selected[K], ExpandKey, false> extends infer a extends object ? Lookup<Available[K], ExpandKey, false> extends infer b extends object ? _AssertNoInvalidExpandKeys<a, b> : 'wtf!?' : never : `invalid expand key: ${K & string}` }[keyof Selected];
9
+ /**
10
+ * Recursively asserts that all keys in the 'expand' section of Q are valid.
11
+ * If any invalid keys are found, a descriptive string literal type is returned, otherwise `unknown` is returned.
12
+ */
13
+ type AssertNoInvalidExpandKeys<V extends Vertex<Collection>, Q extends VertexQuery<V>> = ExpandKey & keyof Q extends never ? unknown : [_AssertNoInvalidExpandKeys<DeepNonNullable<Q>[ExpandKey], DeepNonNullable<VertexQuery<V>>[ExpandKey]>] extends [infer T] ? [T] extends [never] ? unknown : T : never;
14
+ //#endregion
15
+ export { AssertNoInvalidExpandKeys };
16
+ //# sourceMappingURL=assert-no-invalid-expand-keys.d.ts.map
@@ -5,17 +5,19 @@ import { ExpandKey, OmitEdges, PickEdges } from "../common.js";
5
5
  import { KeysOf } from "../keys-of.js";
6
6
  import { VertexQuery } from "../vertex-query/index.js";
7
7
  import { ExtractChildKeys, ExtractChildObject, ExtractParentKeys, IntersectExpandSelectPath, Lookup, RelaxedPick } from "./util.js";
8
+ import { AssertNoInvalidExpandKeys } from "./assert-no-invalid-expand-keys.js";
8
9
 
9
10
  //#region src/core/interfaces/vertices/util/custom-vertex-export/index.d.ts
10
11
  declare namespace HandleEdge {
11
- type Recursion<V extends Vertex<Collection>, Query> = [Query] extends [never] ? string : Query extends VertexQuery<V> ? CustomVertexExport<V, Query> : never;
12
+ type Recursion<V extends Vertex<Collection>, Query> = [Query] extends [never] ? string : Query extends VertexQuery<V> ? _CustomVertexExport<V, Query> : never;
12
13
  export type Plain<E$1 extends EdgeBase<any, any>, Query> = E$1 extends Plain.Edge<infer V> ? Recursion<V, Query> : never;
13
14
  export type Wrapped<E$1 extends EdgeBase<any, any>, Query> = E$1 extends Wrapped.Edge<infer K, infer V extends Vertex<Collection>, infer Extra extends object> ? Wrapped.Util.Value<K, Recursion<V, IntersectExpandSelectPath<Lookup<Query, K>, VertexQuery<V>>>, Extra> : never;
14
- export type Coalesced<E$1 extends EdgeBase<any, any>, Query> = E$1 extends Coalesced.Edge<infer V, infer O extends object, infer Extra extends object> ? V extends Vertex<Collection> ? Coalesced.Util.Value<Recursion<V, IntersectExpandSelectPath<Lookup<Query, Coalesced.Util.Keys.To>, VertexQuery<V>>>, Coalesced.Util.ToModel<V>, O, Extra> : never : never;
15
+ export type Coalesced<E$1 extends EdgeBase<any, any>, Query> = E$1 extends Coalesced.Edge<infer V, infer O, infer Extra extends object> ? V extends Vertex<Collection> ? Coalesced.Util.Value<Recursion<V, IntersectExpandSelectPath<Lookup<Query, Coalesced.Util.Keys.To>, VertexQuery<V>>>, Coalesced.Util.ToModel<V>, O, Extra> : never : never;
15
16
  export type GroupWithExclude<E$1 extends EdgeBase<any, any>, Query> = E$1 extends GroupWithExclude.Edge<infer G extends Vertex<any>, infer P extends Vertex<any>, infer Extra extends object> ? GroupWithExclude.Util.Value<Recursion<G, IntersectExpandSelectPath<Lookup<Query, GroupWithExclude.Util.Keys.To>, VertexQuery<G>>>, Recursion<P, IntersectExpandSelectPath<Lookup<Query, GroupWithExclude.Util.Keys.Exclude>, VertexQuery<P>>>, Extra> : never;
16
17
  export type AvailableLocation<E$1 extends EdgeBase<any, any>, Query> = E$1 extends AvailableLocation.Edge<infer L extends Vertex<any>, infer Extra extends object> ? AvailableLocation.Util.Value<Recursion<L, IntersectExpandSelectPath<Lookup<Query, AvailableLocation.Util.Keys.Locations>, VertexQuery<L>>>, Extra> : never;
17
18
  export {};
18
19
  }
20
+ type _CustomVertexExport<V extends Vertex<Collection>, Q extends VertexQuery<V>> = Prettify<{ [K in keyof OmitEdges<V> as K extends KeysOf<V, Q> ? K : never]: V[K] } & { [K in keyof PickEdges<V> as K extends ExtractParentKeys<KeysOf<V, Q>> ? K : never]: [V[K]] extends [never] ? never : NonNullable<V[K]> extends EdgeWrapper<infer M, infer E extends EdgeBase<any, any> | null | undefined> ? (SetMultiplicity<Prettify<E extends EdgeBase<any, any> ? (HandleEdge.Plain<E, Lookup<Q[ExpandKey], K>> | RelaxedPick<HandleEdge.Wrapped<E, ExtractChildObject<Q[ExpandKey], K>> | HandleEdge.Coalesced<E, ExtractChildObject<Q[ExpandKey], K>> | HandleEdge.GroupWithExclude<E, ExtractChildObject<Q[ExpandKey], K>> | HandleEdge.AvailableLocation<E, ExtractChildObject<Q[ExpandKey], K>>, ExtractChildKeys<KeysOf<V, Q>, K>>) : E>, M> | ExtractNullish<V[K]>) : never }>;
19
21
  /**
20
22
  * Recursively exports a Vertex type to a plain object structure,
21
23
  * selecting and expanding properties and edges according to the provided expand/select path.
@@ -25,7 +27,7 @@ declare namespace HandleEdge {
25
27
  * - Invalid or unsupported edge unions are filtered out as `never`.
26
28
  * - Supports dot-notation for nested property selection and expansion.
27
29
  */
28
- type CustomVertexExport<V extends Vertex<Collection>, Q extends VertexQuery<V> | never> = Prettify<{ [K in keyof OmitEdges<V> as K extends KeysOf<V, Q> ? K : never]: V[K] } & { [K in keyof PickEdges<V> as K extends ExtractParentKeys<KeysOf<V, Q>> ? K : never]: [V[K]] extends [never] ? never : NonNullable<V[K]> extends EdgeWrapper<infer M, infer E extends EdgeBase<any, any> | null | undefined> ? (SetMultiplicity<Prettify<E extends EdgeBase<any, any> ? (HandleEdge.Plain<E, Lookup<Q[ExpandKey], K>> | RelaxedPick<HandleEdge.Wrapped<E, ExtractChildObject<Q[ExpandKey], K>> | HandleEdge.Coalesced<E, ExtractChildObject<Q[ExpandKey], K>> | HandleEdge.GroupWithExclude<E, ExtractChildObject<Q[ExpandKey], K>> | HandleEdge.AvailableLocation<E, ExtractChildObject<Q[ExpandKey], K>>, ExtractChildKeys<KeysOf<V, Q>, K>>) : E>, M> | ExtractNullish<V[K]>) : never }>;
30
+ type CustomVertexExport<V extends Vertex<Collection>, Q extends VertexQuery<V> & AssertNoInvalidExpandKeys<V, Q>> = _CustomVertexExport<V, Q>;
29
31
  //#endregion
30
32
  export { CustomVertexExport };
31
33
  //# sourceMappingURL=index.d.ts.map
@@ -1,3 +1,4 @@
1
+ import { DeepNonNullable } from "../util.js";
1
2
  import { ExpandKey, SelectKey } from "../common.js";
2
3
 
3
4
  //#region src/core/interfaces/vertices/util/custom-vertex-export/util.d.ts
@@ -5,7 +6,7 @@ import { ExpandKey, SelectKey } from "../common.js";
5
6
  /**
6
7
  * Lookup type that returns the value type for key K in object Obj,
7
8
  */
8
- type Lookup<Obj, K$1> = K$1 extends keyof Obj ? Obj[K$1] : never;
9
+ type Lookup<Obj, K$1, ELSE = never> = K$1 extends keyof Obj ? Obj[K$1] : ELSE;
9
10
  /**
10
11
  * Intersect two expand/select specifications.
11
12
  *
@@ -18,7 +19,8 @@ type Lookup<Obj, K$1> = K$1 extends keyof Obj ? Obj[K$1] : never;
18
19
  * Useful to constrain a requested expand/select spec (A) by an allowed/spec (B),
19
20
  * yielding only the common subset.
20
21
  */
21
- type IntersectExpandSelectPath<A, B$1> = [A] extends [never] ? never : { [K in keyof A & keyof B$1]: K extends SelectKey ? NonNullable<A[K]> extends (infer V1)[] ? NonNullable<B$1[K]> extends (infer V2)[] ? (V1 & V2)[] | Extract<A[K], null | undefined> : never : never : K extends ExpandKey ? { [S in keyof A[K] & keyof B$1[K]]: A[K][S] extends object ? B$1[K][S] extends object ? IntersectExpandSelectPath<A[K][S], B$1[K][S]> : never : never } : never };
22
+ type IntersectExpandSelectPath<A, B$1> = _IntersectExpandSelectPath<DeepNonNullable<A>, DeepNonNullable<B$1>>;
23
+ type _IntersectExpandSelectPath<A, B$1> = [A] extends [never] ? never : { [K in keyof A & keyof B$1]: K extends SelectKey ? A[K] extends (infer V1)[] ? B$1[K] extends (infer V2)[] ? (V1 & V2)[] | Extract<A[K], null | undefined> : never : never : K extends ExpandKey ? { [S in keyof A[K] & keyof B$1[K]]: A[K][S] extends object ? B$1[K][S] extends object ? IntersectExpandSelectPath<A[K][S], B$1[K][S]> : never : never } : never };
22
24
  /**
23
25
  * Extracts the parent keys from a set of dot-notation keys.
24
26
  *
@@ -1,5 +1,6 @@
1
1
  import { Collection, PseudoCollection, RealCollection, Vertex, VertexFactory } from "./vertex.js";
2
2
  import { AvailableLocation, Coalesced, EdgeWrapper, GroupWithExclude, Plain, Wrapped } from "./edges.js";
3
+ import { AssertNoInvalidExpandKeys } from "./custom-vertex-export/assert-no-invalid-expand-keys.js";
3
4
  import { CustomVertexExport } from "./custom-vertex-export/index.js";
4
5
  import { DeepVertexExport, MixedVertexExport, MixedWithOptionalIdVertexExport } from "./deep-vertex-export/index.js";
5
6
  import { SerializedVertexExport } from "./serialized-vertex-export.js";
@@ -26,6 +26,10 @@ type MakeMixed<T$1> = MakeMixedNs.MakeMixed<T$1>;
26
26
  * Deep partial type that allows for partial properties at any level of nesting.
27
27
  */
28
28
  type DeepPartial<T$1> = T$1 extends Array<infer U> ? Array<DeepPartial<U>> : T$1 extends object ? { [K in keyof T$1]?: DeepPartial<T$1[K]> } : T$1;
29
+ /**
30
+ * A deep version of NonNullable<T> that recursively removes null and undefined from all properties and nested properties.
31
+ */
32
+ type DeepNonNullable<T$1> = T$1 extends any ? T$1 extends ((...args: any[]) => any) ? T$1 : T$1 extends Array<infer U> ? Array<DeepNonNullable<NonNullable<U>>> : T$1 extends object ? { [K in keyof T$1]-?: DeepNonNullable<NonNullable<T$1[K]>> } : NonNullable<T$1> : never;
29
33
  type _IsUnion<T$1, U$1 = T$1> = T$1 extends any ? [U$1] extends [T$1] ? false : true : never;
30
34
  /**
31
35
  * Lets a type pass if it is a union type, otherwise returns `never`.
@@ -72,5 +76,5 @@ type SetMultiplicity<T$1, M = EdgeMultiplicity> = M extends 'array' ? T$1[] : T$
72
76
  */
73
77
  type ExtractNullish<T$1> = Extract<T$1, null | undefined>;
74
78
  //#endregion
75
- export { AllKeys, AllTrue, DeepPartial, ExtractNullish, IsUnion, MakeMixed, NoneIsUnion, Prettify, SetMultiplicity, Tail };
79
+ export { AllKeys, AllTrue, DeepNonNullable, DeepPartial, ExtractNullish, IsUnion, MakeMixed, NoneIsUnion, Prettify, SetMultiplicity, Tail };
76
80
  //# sourceMappingURL=util.d.ts.map
@@ -11,7 +11,7 @@ declare namespace HandleEdge {
11
11
  type Wrapped<E$1 extends EdgeBase<any, any>, D extends SupportedDepths> = E$1 extends Wrapped.Edge<infer K extends string, infer V extends Vertex<Collection>, object> ? ({
12
12
  [FLATTEN_KEY]: true;
13
13
  } & { [k in K]: V extends any ? _VertexQuery<V, D> : never }) : never;
14
- type Coalesced<E$1 extends EdgeBase<any, any>, D extends SupportedDepths> = (E$1 extends Coalesced.Edge<infer V, object, object> ? V : null) extends (infer V extends Vertex<Collection>) ? {
14
+ type Coalesced<E$1 extends EdgeBase<any, any>, D extends SupportedDepths> = (E$1 extends Coalesced.Edge<infer V, any, object> ? V : null) extends (infer V extends Vertex<Collection>) ? {
15
15
  [FLATTEN_KEY]: true;
16
16
  [Coalesced.Util.Keys.TO]: MakeMixed<
17
17
  // must mix here to support all different vertex types
@@ -30,7 +30,9 @@ declare namespace HandleEdge {
30
30
  /**
31
31
  *
32
32
  */
33
- type _VertexQuery<V$1 extends Vertex<Collection>, D extends SupportedDepths> = D extends MinDepth ? any : {
33
+ type _VertexQuery<V$1 extends Vertex<Collection>, D extends SupportedDepths> = D extends MinDepth ? {
34
+ [SELECT_KEY]: Array<AllKeysOf<V$1>>;
35
+ } : {
34
36
  [EXPAND_KEY]: { [K in keyof PickEdges<V$1> as V$1[K] extends never ? K : never]: InvalidEdgeMessage } & _Flatten<RemoveNeverValues<{ [K in keyof PickEdges<V$1> as V$1[K] extends never ? never : K]-?: NonNullable<V$1[K]> extends EdgeWrapper<EdgeMultiplicity, infer E extends EdgeBase<any, any> | null | undefined> ? (HandleEdge.Plain<NonNullable<E>, Decrement<D>> | HandleEdge.Wrapped<NonNullable<E>, Decrement<D>> | HandleEdge.Coalesced<NonNullable<E>, Decrement<D>> | HandleEdge.GroupWithExclude<NonNullable<E>, Decrement<D>> | HandleEdge.AvailableLocation<NonNullable<E>, Decrement<D>>) : never }>>;
35
37
  } & {
36
38
  [SELECT_KEY]: Array<AllKeysOf<V$1>>;
@@ -1,4 +1,5 @@
1
1
  import { VertexQuery } from "./util/vertex-query/index.js";
2
+ import { AssertNoInvalidExpandKeys } from "./util/custom-vertex-export/assert-no-invalid-expand-keys.js";
2
3
  import { CustomVertexExport } from "./util/custom-vertex-export/index.js";
3
4
  import "./util/index.js";
4
5
  import { PeriodVertex } from "./periods.js";
@@ -49,7 +50,7 @@ type BaseQueryRecord<C$1 extends keyof VertexRecord> = VertexQuery<VertexRecord[
49
50
  /**
50
51
  * See {@link createVertexQuery}
51
52
  */
52
- declare class VertexQuery$1<C$1 extends keyof VertexRecord, Q$1 extends BaseQueryRecord<C$1>> {
53
+ declare class VertexQuery$1<C$1 extends keyof VertexRecord, Q$1 extends BaseQueryRecord<C$1> & AssertNoInvalidExpandKeys<VertexRecord[C$1], Q$1>> {
53
54
  private readonly _collection;
54
55
  private readonly _query;
55
56
  constructor(_collection: C$1, _query: Q$1);
@@ -79,25 +80,25 @@ type Infer<T extends VertexQuery$1<any, any>> = T extends VertexQuery$1<infer C,
79
80
  * type GroupType = Infer<typeof groupQuery>;
80
81
  */
81
82
  declare namespace createVertexQuery {
82
- function division<Q$1 extends VertexQuery<DivisionVertex>>(query: Q$1): VertexQuery$1<"divisions", Q$1>;
83
- function settings<Q$1 extends VertexQuery<DivisionSettingsVertex>>(query: Q$1): VertexQuery$1<"settings", Q$1>;
84
- function exception<Q$1 extends VertexQuery<ExceptionVertex>>(query: Q$1): VertexQuery$1<"exceptions", Q$1>;
85
- function event<Q$1 extends VertexQuery<EventVertex>>(query: Q$1): VertexQuery$1<"events", Q$1>;
86
- function scheduleEvent<Q$1 extends VertexQuery<ScheduleEventVertex>>(query: Q$1): VertexQuery$1<"scheduleEvents", Q$1>;
87
- function calendarEvent<Q$1 extends VertexQuery<CalendarEventVertex>>(query: Q$1): VertexQuery$1<"calendarEvents", Q$1>;
88
- function course<Q$1 extends VertexQuery<CourseVertex>>(query: Q$1): VertexQuery$1<"courses", Q$1>;
89
- function generation<Q$1 extends VertexQuery<GenerationVertex>>(query: Q$1): VertexQuery$1<"generations", Q$1>;
90
- function location<Q$1 extends VertexQuery<LocationVertex>>(query: Q$1): VertexQuery$1<"locations", Q$1>;
91
- function lockedTime<Q$1 extends VertexQuery<LockedTimeVertex>>(query: Q$1): VertexQuery$1<"lockedTimes", Q$1>;
92
- function overlapGroup<Q$1 extends VertexQuery<OverlapGroupVertex>>(query: Q$1): VertexQuery$1<"overlapGroups", Q$1>;
93
- function period<Q$1 extends VertexQuery<PeriodVertex>>(query: Q$1): VertexQuery$1<"periods", Q$1>;
94
- function person<Q$1 extends VertexQuery<PersonVertex>>(query: Q$1): VertexQuery$1<"persons", Q$1>;
95
- function student<Q$1 extends VertexQuery<PersonVertex>>(query: Q$1): VertexQuery$1<"students", Q$1>;
96
- function rootInterval<Q$1 extends VertexQuery<RootIntervalVertex>>(query: Q$1): VertexQuery$1<"rootIntervals", Q$1>;
97
- function syllabus<Q$1 extends VertexQuery<SyllabusVertex>>(query: Q$1): VertexQuery$1<"syllabuses", Q$1>;
98
- function group<Q$1 extends VertexQuery<GroupVertex>>(query: Q$1): VertexQuery$1<"groups", Q$1>;
99
- function teacher<Q$1 extends VertexQuery<TeacherVertex>>(query: Q$1): VertexQuery$1<"teachers", Q$1>;
100
- function configuration<Q$1 extends VertexQuery<ConfigurationVertex>>(query: Q$1): VertexQuery$1<"configurations", Q$1>;
83
+ function division<Q$1 extends VertexQuery<DivisionVertex> & AssertNoInvalidExpandKeys<DivisionVertex, Q$1>>(query: Q$1): VertexQuery$1<"divisions", Q$1>;
84
+ function settings<Q$1 extends VertexQuery<DivisionSettingsVertex> & AssertNoInvalidExpandKeys<DivisionSettingsVertex, Q$1>>(query: Q$1): VertexQuery$1<"settings", Q$1>;
85
+ function exception<Q$1 extends VertexQuery<ExceptionVertex> & AssertNoInvalidExpandKeys<ExceptionVertex, Q$1>>(query: Q$1): VertexQuery$1<"exceptions", Q$1>;
86
+ function event<Q$1 extends VertexQuery<EventVertex> & AssertNoInvalidExpandKeys<EventVertex, Q$1>>(query: Q$1): VertexQuery$1<"events", Q$1>;
87
+ function scheduleEvent<Q$1 extends VertexQuery<ScheduleEventVertex> & AssertNoInvalidExpandKeys<ScheduleEventVertex, Q$1>>(query: Q$1): VertexQuery$1<"scheduleEvents", Q$1>;
88
+ function calendarEvent<Q$1 extends VertexQuery<CalendarEventVertex> & AssertNoInvalidExpandKeys<CalendarEventVertex, Q$1>>(query: Q$1): VertexQuery$1<"calendarEvents", Q$1>;
89
+ function course<Q$1 extends VertexQuery<CourseVertex> & AssertNoInvalidExpandKeys<CourseVertex, Q$1>>(query: Q$1): VertexQuery$1<"courses", Q$1>;
90
+ function generation<Q$1 extends VertexQuery<GenerationVertex> & AssertNoInvalidExpandKeys<GenerationVertex, Q$1>>(query: Q$1): VertexQuery$1<"generations", Q$1>;
91
+ function location<Q$1 extends VertexQuery<LocationVertex> & AssertNoInvalidExpandKeys<LocationVertex, Q$1>>(query: Q$1): VertexQuery$1<"locations", Q$1>;
92
+ function lockedTime<Q$1 extends VertexQuery<LockedTimeVertex> & AssertNoInvalidExpandKeys<LockedTimeVertex, Q$1>>(query: Q$1): VertexQuery$1<"lockedTimes", Q$1>;
93
+ function overlapGroup<Q$1 extends VertexQuery<OverlapGroupVertex> & AssertNoInvalidExpandKeys<OverlapGroupVertex, Q$1>>(query: Q$1): VertexQuery$1<"overlapGroups", Q$1>;
94
+ function period<Q$1 extends VertexQuery<PeriodVertex> & AssertNoInvalidExpandKeys<PeriodVertex, Q$1>>(query: Q$1): VertexQuery$1<"periods", Q$1>;
95
+ function person<Q$1 extends VertexQuery<PersonVertex> & AssertNoInvalidExpandKeys<PersonVertex, Q$1>>(query: Q$1): VertexQuery$1<"persons", Q$1>;
96
+ function student<Q$1 extends VertexQuery<PersonVertex> & AssertNoInvalidExpandKeys<PersonVertex, Q$1>>(query: Q$1): VertexQuery$1<"students", Q$1>;
97
+ function rootInterval<Q$1 extends VertexQuery<RootIntervalVertex> & AssertNoInvalidExpandKeys<RootIntervalVertex, Q$1>>(query: Q$1): VertexQuery$1<"rootIntervals", Q$1>;
98
+ function syllabus<Q$1 extends VertexQuery<SyllabusVertex> & AssertNoInvalidExpandKeys<SyllabusVertex, Q$1>>(query: Q$1): VertexQuery$1<"syllabuses", Q$1>;
99
+ function group<Q$1 extends VertexQuery<GroupVertex> & AssertNoInvalidExpandKeys<GroupVertex, Q$1>>(query: Q$1): VertexQuery$1<"groups", Q$1>;
100
+ function teacher<Q$1 extends VertexQuery<TeacherVertex> & AssertNoInvalidExpandKeys<TeacherVertex, Q$1>>(query: Q$1): VertexQuery$1<"teachers", Q$1>;
101
+ function configuration<Q$1 extends VertexQuery<ConfigurationVertex> & AssertNoInvalidExpandKeys<ConfigurationVertex, Q$1>>(query: Q$1): VertexQuery$1<"configurations", Q$1>;
101
102
  }
102
103
  //#endregion
103
104
  export { Infer, VertexQuery$1 as VertexQuery, createVertexQuery };
@@ -1 +1 @@
1
- {"version":3,"file":"vertex-query.js","names":["_collection: C","_query: Q"],"sources":["../../../../src/core/interfaces/vertices/vertex-query.ts"],"sourcesContent":["import type { Collection, CustomVertexExport } from './util';\nimport type { TeacherVertex } from './teachers';\nimport type { ExceptionVertex } from './exceptions';\nimport type { EventVertex, ScheduleEventVertex, CalendarEventVertex } from './events';\nimport type { CourseVertex } from './courses';\nimport type { DivisionSettingsVertex } from './division-settings';\nimport type { DivisionVertex } from './divisions';\nimport type { GenerationVertex } from './generations';\nimport type { GroupVertex } from './groups';\nimport type { LocationVertex } from './locations';\nimport type { LockedTimeVertex } from './locked-times';\nimport type { OverlapGroupVertex } from './overlap-groups';\nimport type { PeriodVertex } from './periods';\nimport type { PersonVertex } from './persons';\nimport type { RootIntervalVertex } from './root-intervals';\nimport type { SyllabusVertex } from './syllabuses';\nimport type { ConfigurationVertex } from './configurations';\nimport type { CompanyVertex } from './companies';\nimport type { UserVertex } from './users';\nimport type { VertexQuery as Query } from './util/vertex-query';\nimport type { TypeEqual } from 'ts-expect';\nimport { expectType } from 'ts-expect';\n\n\ntype VertexRecord = {\n divisions: DivisionVertex;\n divisionSettings: DivisionSettingsVertex;\n settings: DivisionSettingsVertex;\n teachers: TeacherVertex;\n exceptions: ExceptionVertex;\n events: EventVertex;\n scheduleEvents: ScheduleEventVertex;\n calendarEvents: CalendarEventVertex;\n courses: CourseVertex;\n generations: GenerationVertex;\n groups: GroupVertex;\n locations: LocationVertex;\n lockedTimes: LockedTimeVertex;\n overlapGroups: OverlapGroupVertex;\n periods: PeriodVertex;\n persons: PersonVertex;\n students: PersonVertex;\n rootIntervals: RootIntervalVertex;\n syllabuses: SyllabusVertex;\n configurations: ConfigurationVertex;\n companies: CompanyVertex;\n users: UserVertex;\n};\nexport type BaseQueryRecord<C extends keyof VertexRecord> = Query<VertexRecord[C]>;\n{\n // the keys must all be a collection\n type Mismatches = keyof {\n [K in keyof VertexRecord as K extends Collection ? never : K]: true\n };\n expectType<TypeEqual<Mismatches, never>>(true);\n}\n\n/**\n * See {@link createVertexQuery}\n */\nexport class VertexQuery <\n C extends keyof VertexRecord,\n Q extends BaseQueryRecord<C>\n> {\n constructor (\n private readonly _collection: C,\n private readonly _query: Q\n ) {}\n\n get collection () { return this._collection }\n get query () { return this._query }\n}\n\n\n/**\n * Infers the result type for a query created via createVertexQuery.\n *\n * @example\n * const personQuery = createVertexQuery('persons', { select: ['id', 'firstName'] });\n * type PersonType = Infer<typeof personQuery>;\n */\nexport type Infer<T extends VertexQuery<any, any>> = // eslint-disable-line @typescript-eslint/no-explicit-any\n T extends VertexQuery<infer C, infer Q>\n ? CustomVertexExport<VertexRecord[C], Q>\n : never;\n\n\n/**\n * Builds a typed vertex query for a given vertex type using a select/expand shape.\n *\n * Use together with Infer<T> to obtain the serialized result type.\n *\n * @typeParam Q - The select/expand query shape for that vertex.\n *\n * @example\n * const groupQuery = createVertexQuery.group({\n * select: ['id', 'displayName'],\n * expand: { members: { select: ['id'] } }\n * });\n * type GroupType = Infer<typeof groupQuery>;\n */\nexport namespace createVertexQuery {\n //\n // The following did not work: it seems we need to define Q as an completely independent type parameter (?)\n //\n // export function createVertexQuery <\n // C extends keyof VertexRecord,\n // const Q extends Query<VertexRecord[C]>\n // > (\n // collection: C,\n // query: Q\n // ) {\n // return new VertexQuery(collection, query);\n // }\n\n export function division <Q extends Query<DivisionVertex >> (query: Q) { return new VertexQuery('divisions', query) }\n export function settings <Q extends Query<DivisionSettingsVertex>> (query: Q) { return new VertexQuery('settings', query) }\n export function exception <Q extends Query<ExceptionVertex >> (query: Q) { return new VertexQuery('exceptions', query) }\n export function event <Q extends Query<EventVertex >> (query: Q) { return new VertexQuery('events', query) }\n export function scheduleEvent<Q extends Query<ScheduleEventVertex >> (query: Q) { return new VertexQuery('scheduleEvents', query) }\n export function calendarEvent<Q extends Query<CalendarEventVertex >> (query: Q) { return new VertexQuery('calendarEvents', query) }\n export function course <Q extends Query<CourseVertex >> (query: Q) { return new VertexQuery('courses', query) }\n export function generation <Q extends Query<GenerationVertex >> (query: Q) { return new VertexQuery('generations', query) }\n export function location <Q extends Query<LocationVertex >> (query: Q) { return new VertexQuery('locations', query) }\n export function lockedTime <Q extends Query<LockedTimeVertex >> (query: Q) { return new VertexQuery('lockedTimes', query) }\n export function overlapGroup <Q extends Query<OverlapGroupVertex >> (query: Q) { return new VertexQuery('overlapGroups', query) }\n export function period <Q extends Query<PeriodVertex >> (query: Q) { return new VertexQuery('periods', query) }\n export function person <Q extends Query<PersonVertex >> (query: Q) { return new VertexQuery('persons', query) }\n export function student <Q extends Query<PersonVertex >> (query: Q) { return new VertexQuery('students', query) }\n export function rootInterval <Q extends Query<RootIntervalVertex >> (query: Q) { return new VertexQuery('rootIntervals', query) }\n export function syllabus <Q extends Query<SyllabusVertex >> (query: Q) { return new VertexQuery('syllabuses', query) }\n export function group <Q extends Query<GroupVertex >> (query: Q) { return new VertexQuery('groups', query) }\n export function teacher <Q extends Query<TeacherVertex >> (query: Q) { return new VertexQuery('teachers', query) }\n export function configuration<Q extends Query<ConfigurationVertex >> (query: Q) { return new VertexQuery('configurations', query) }\n}"],"mappings":";;;AAsDE,WAAyC,KAAK;;;;AAMhD,IAAa,cAAb,MAGE;CACA,YACE,AAAiBA,aACjB,AAAiBC,QACjB;EAFiB;EACA;;CAGnB,IAAI,aAAc;AAAE,SAAO,KAAK;;CAChC,IAAI,QAAc;AAAE,SAAO,KAAK;;;;;CA6CzB,SAAS,SAAwD,OAAU;AAAE,SAAO,IAAI,YAAY,aAAoB,MAAM;;;CAC9H,SAAS,SAAwD,OAAU;AAAE,SAAO,IAAI,YAAY,YAAoB,MAAM;;;CAC9H,SAAS,UAAwD,OAAU;AAAE,SAAO,IAAI,YAAY,cAAoB,MAAM;;;CAC9H,SAAS,MAAwD,OAAU;AAAE,SAAO,IAAI,YAAY,UAAoB,MAAM;;;CAC9H,SAAS,cAAwD,OAAU;AAAE,SAAO,IAAI,YAAY,kBAAoB,MAAM;;;CAC9H,SAAS,cAAwD,OAAU;AAAE,SAAO,IAAI,YAAY,kBAAoB,MAAM;;;CAC9H,SAAS,OAAwD,OAAU;AAAE,SAAO,IAAI,YAAY,WAAoB,MAAM;;;CAC9H,SAAS,WAAwD,OAAU;AAAE,SAAO,IAAI,YAAY,eAAoB,MAAM;;;CAC9H,SAAS,SAAwD,OAAU;AAAE,SAAO,IAAI,YAAY,aAAoB,MAAM;;;CAC9H,SAAS,WAAwD,OAAU;AAAE,SAAO,IAAI,YAAY,eAAoB,MAAM;;;CAC9H,SAAS,aAAwD,OAAU;AAAE,SAAO,IAAI,YAAY,iBAAoB,MAAM;;;CAC9H,SAAS,OAAwD,OAAU;AAAE,SAAO,IAAI,YAAY,WAAoB,MAAM;;;CAC9H,SAAS,OAAwD,OAAU;AAAE,SAAO,IAAI,YAAY,WAAoB,MAAM;;;CAC9H,SAAS,QAAwD,OAAU;AAAE,SAAO,IAAI,YAAY,YAAoB,MAAM;;;CAC9H,SAAS,aAAwD,OAAU;AAAE,SAAO,IAAI,YAAY,iBAAoB,MAAM;;;CAC9H,SAAS,SAAwD,OAAU;AAAE,SAAO,IAAI,YAAY,cAAoB,MAAM;;;CAC9H,SAAS,MAAwD,OAAU;AAAE,SAAO,IAAI,YAAY,UAAoB,MAAM;;;CAC9H,SAAS,QAAwD,OAAU;AAAE,SAAO,IAAI,YAAY,YAAoB,MAAM;;;CAC9H,SAAS,cAAwD,OAAU;AAAE,SAAO,IAAI,YAAY,kBAAoB,MAAM"}
1
+ {"version":3,"file":"vertex-query.js","names":["_collection: C","_query: Q"],"sources":["../../../../src/core/interfaces/vertices/vertex-query.ts"],"sourcesContent":["import type { AssertNoInvalidExpandKeys, Collection, CustomVertexExport } from './util';\nimport type { TeacherVertex } from './teachers';\nimport type { ExceptionVertex } from './exceptions';\nimport type { EventVertex, ScheduleEventVertex, CalendarEventVertex } from './events';\nimport type { CourseVertex } from './courses';\nimport type { DivisionSettingsVertex } from './division-settings';\nimport type { DivisionVertex } from './divisions';\nimport type { GenerationVertex } from './generations';\nimport type { GroupVertex } from './groups';\nimport type { LocationVertex } from './locations';\nimport type { LockedTimeVertex } from './locked-times';\nimport type { OverlapGroupVertex } from './overlap-groups';\nimport type { PeriodVertex } from './periods';\nimport type { PersonVertex } from './persons';\nimport type { RootIntervalVertex } from './root-intervals';\nimport type { SyllabusVertex } from './syllabuses';\nimport type { ConfigurationVertex } from './configurations';\nimport type { CompanyVertex } from './companies';\nimport type { UserVertex } from './users';\nimport type { VertexQuery as Query } from './util/vertex-query';\nimport type { TypeEqual } from 'ts-expect';\nimport { expectType } from 'ts-expect';\n\n\nexport type VertexRecord = {\n divisions: DivisionVertex;\n divisionSettings: DivisionSettingsVertex;\n settings: DivisionSettingsVertex;\n teachers: TeacherVertex;\n exceptions: ExceptionVertex;\n events: EventVertex;\n scheduleEvents: ScheduleEventVertex;\n calendarEvents: CalendarEventVertex;\n courses: CourseVertex;\n generations: GenerationVertex;\n groups: GroupVertex;\n locations: LocationVertex;\n lockedTimes: LockedTimeVertex;\n overlapGroups: OverlapGroupVertex;\n periods: PeriodVertex;\n persons: PersonVertex;\n students: PersonVertex;\n rootIntervals: RootIntervalVertex;\n syllabuses: SyllabusVertex;\n configurations: ConfigurationVertex;\n companies: CompanyVertex;\n users: UserVertex;\n};\nexport type BaseQueryRecord<C extends keyof VertexRecord> = Query<VertexRecord[C]>;\n{\n // the keys must all be a collection\n type Mismatches = keyof {\n [K in keyof VertexRecord as K extends Collection ? never : K]: true\n };\n expectType<TypeEqual<Mismatches, never>>(true);\n}\n\n/**\n * See {@link createVertexQuery}\n */\nexport class VertexQuery <\n C extends keyof VertexRecord,\n Q extends BaseQueryRecord<C> & AssertNoInvalidExpandKeys<VertexRecord[C], Q>\n> {\n constructor (\n private readonly _collection: C,\n private readonly _query: Q\n ) {}\n\n get collection () { return this._collection }\n get query () { return this._query }\n}\n\n\n/**\n * Infers the result type for a query created via createVertexQuery.\n *\n * @example\n * const personQuery = createVertexQuery('persons', { select: ['id', 'firstName'] });\n * type PersonType = Infer<typeof personQuery>;\n */\nexport type Infer<T extends VertexQuery<any, any>> = // eslint-disable-line @typescript-eslint/no-explicit-any\n T extends VertexQuery<infer C, infer Q>\n ? CustomVertexExport<VertexRecord[C], Q>\n : never;\n\n\n/**\n * Builds a typed vertex query for a given vertex type using a select/expand shape.\n *\n * Use together with Infer<T> to obtain the serialized result type.\n *\n * @typeParam Q - The select/expand query shape for that vertex.\n *\n * @example\n * const groupQuery = createVertexQuery.group({\n * select: ['id', 'displayName'],\n * expand: { members: { select: ['id'] } }\n * });\n * type GroupType = Infer<typeof groupQuery>;\n */\nexport namespace createVertexQuery {\n //\n // The following did not work: it seems we need to define Q as an completely independent type parameter (?)\n //\n // export function createVertexQuery <\n // C extends keyof VertexRecord,\n // const Q extends Query<VertexRecord[C]>\n // > (\n // collection: C,\n // query: Q\n // ) {\n // return new VertexQuery(collection, query);\n // }\n\n export function division <Q extends Query<DivisionVertex > & AssertNoInvalidExpandKeys<DivisionVertex, Q>> (query: Q) { return new VertexQuery('divisions', query) }\n export function settings <Q extends Query<DivisionSettingsVertex> & AssertNoInvalidExpandKeys<DivisionSettingsVertex, Q>> (query: Q) { return new VertexQuery('settings', query) }\n export function exception <Q extends Query<ExceptionVertex > & AssertNoInvalidExpandKeys<ExceptionVertex, Q>> (query: Q) { return new VertexQuery('exceptions', query) }\n export function event <Q extends Query<EventVertex > & AssertNoInvalidExpandKeys<EventVertex, Q>> (query: Q) { return new VertexQuery('events', query) }\n export function scheduleEvent<Q extends Query<ScheduleEventVertex > & AssertNoInvalidExpandKeys<ScheduleEventVertex, Q>> (query: Q) { return new VertexQuery('scheduleEvents', query) }\n export function calendarEvent<Q extends Query<CalendarEventVertex > & AssertNoInvalidExpandKeys<CalendarEventVertex, Q>> (query: Q) { return new VertexQuery('calendarEvents', query) }\n export function course <Q extends Query<CourseVertex > & AssertNoInvalidExpandKeys<CourseVertex, Q>> (query: Q) { return new VertexQuery('courses', query) }\n export function generation <Q extends Query<GenerationVertex > & AssertNoInvalidExpandKeys<GenerationVertex, Q>> (query: Q) { return new VertexQuery('generations', query) }\n export function location <Q extends Query<LocationVertex > & AssertNoInvalidExpandKeys<LocationVertex, Q>> (query: Q) { return new VertexQuery('locations', query) }\n export function lockedTime <Q extends Query<LockedTimeVertex > & AssertNoInvalidExpandKeys<LockedTimeVertex, Q>> (query: Q) { return new VertexQuery('lockedTimes', query) }\n export function overlapGroup <Q extends Query<OverlapGroupVertex > & AssertNoInvalidExpandKeys<OverlapGroupVertex, Q>> (query: Q) { return new VertexQuery('overlapGroups', query) }\n export function period <Q extends Query<PeriodVertex > & AssertNoInvalidExpandKeys<PeriodVertex, Q>> (query: Q) { return new VertexQuery('periods', query) }\n export function person <Q extends Query<PersonVertex > & AssertNoInvalidExpandKeys<PersonVertex, Q>> (query: Q) { return new VertexQuery('persons', query) }\n export function student <Q extends Query<PersonVertex > & AssertNoInvalidExpandKeys<PersonVertex, Q>> (query: Q) { return new VertexQuery('students', query) }\n export function rootInterval <Q extends Query<RootIntervalVertex > & AssertNoInvalidExpandKeys<RootIntervalVertex, Q>> (query: Q) { return new VertexQuery('rootIntervals', query) }\n export function syllabus <Q extends Query<SyllabusVertex > & AssertNoInvalidExpandKeys<SyllabusVertex, Q>> (query: Q) { return new VertexQuery('syllabuses', query) }\n export function group <Q extends Query<GroupVertex > & AssertNoInvalidExpandKeys<GroupVertex, Q>> (query: Q) { return new VertexQuery('groups', query) }\n export function teacher <Q extends Query<TeacherVertex > & AssertNoInvalidExpandKeys<TeacherVertex, Q>> (query: Q) { return new VertexQuery('teachers', query) }\n export function configuration<Q extends Query<ConfigurationVertex > & AssertNoInvalidExpandKeys<ConfigurationVertex, Q>> (query: Q) { return new VertexQuery('configurations', query) }\n}"],"mappings":";;;AAsDE,WAAyC,KAAK;;;;AAMhD,IAAa,cAAb,MAGE;CACA,YACE,AAAiBA,aACjB,AAAiBC,QACjB;EAFiB;EACA;;CAGnB,IAAI,aAAc;AAAE,SAAO,KAAK;;CAChC,IAAI,QAAc;AAAE,SAAO,KAAK;;;;;CA6CzB,SAAS,SAA+G,OAAU;AAAE,SAAO,IAAI,YAAY,aAAoB,MAAM;;;CACrL,SAAS,SAA+G,OAAU;AAAE,SAAO,IAAI,YAAY,YAAoB,MAAM;;;CACrL,SAAS,UAA+G,OAAU;AAAE,SAAO,IAAI,YAAY,cAAoB,MAAM;;;CACrL,SAAS,MAA+G,OAAU;AAAE,SAAO,IAAI,YAAY,UAAoB,MAAM;;;CACrL,SAAS,cAA+G,OAAU;AAAE,SAAO,IAAI,YAAY,kBAAoB,MAAM;;;CACrL,SAAS,cAA+G,OAAU;AAAE,SAAO,IAAI,YAAY,kBAAoB,MAAM;;;CACrL,SAAS,OAA+G,OAAU;AAAE,SAAO,IAAI,YAAY,WAAoB,MAAM;;;CACrL,SAAS,WAA+G,OAAU;AAAE,SAAO,IAAI,YAAY,eAAoB,MAAM;;;CACrL,SAAS,SAA+G,OAAU;AAAE,SAAO,IAAI,YAAY,aAAoB,MAAM;;;CACrL,SAAS,WAA+G,OAAU;AAAE,SAAO,IAAI,YAAY,eAAoB,MAAM;;;CACrL,SAAS,aAA+G,OAAU;AAAE,SAAO,IAAI,YAAY,iBAAoB,MAAM;;;CACrL,SAAS,OAA+G,OAAU;AAAE,SAAO,IAAI,YAAY,WAAoB,MAAM;;;CACrL,SAAS,OAA+G,OAAU;AAAE,SAAO,IAAI,YAAY,WAAoB,MAAM;;;CACrL,SAAS,QAA+G,OAAU;AAAE,SAAO,IAAI,YAAY,YAAoB,MAAM;;;CACrL,SAAS,aAA+G,OAAU;AAAE,SAAO,IAAI,YAAY,iBAAoB,MAAM;;;CACrL,SAAS,SAA+G,OAAU;AAAE,SAAO,IAAI,YAAY,cAAoB,MAAM;;;CACrL,SAAS,MAA+G,OAAU;AAAE,SAAO,IAAI,YAAY,UAAoB,MAAM;;;CACrL,SAAS,QAA+G,OAAU;AAAE,SAAO,IAAI,YAAY,YAAoB,MAAM;;;CACrL,SAAS,cAA+G,OAAU;AAAE,SAAO,IAAI,YAAY,kBAAoB,MAAM"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@royalschedule/maps",
3
3
  "description": "",
4
- "version": "4.0.19",
4
+ "version": "4.0.23",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -70,7 +70,7 @@
70
70
  "tsx": "^4.20.6",
71
71
  "typescript": "^5.9.3",
72
72
  "typescript-eslint": "^8.46.2",
73
- "vitest": "^4.0.6"
73
+ "vitest": "^4.0.14"
74
74
  },
75
75
  "dependencies": {
76
76
  "@json2csv/plainjs": "^7.0.6",