polite-crawler 0.1.2 → 0.1.4
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.
- package/README.md +555 -8
- package/build/cli-daemon-ZYRdy6EW.mjs +38 -0
- package/build/cli-daemon-ZYRdy6EW.mjs.map +1 -0
- package/build/cli-recipes-Bci6HREA.mjs +59 -0
- package/build/cli-recipes-Bci6HREA.mjs.map +1 -0
- package/build/cli.mjs +673 -26
- package/build/cli.mjs.map +1 -1
- package/build/daemon-DK6U51_v.mjs +515 -0
- package/build/daemon-DK6U51_v.mjs.map +1 -0
- package/build/{open-Ca1lcWR3.d.mts → daemon-wwIWsUmU.d.mts} +919 -17
- package/build/{diff-Dk2eSvi-.mjs → diff-BUM91Rev.mjs} +2 -2
- package/build/{diff-Dk2eSvi-.mjs.map → diff-BUM91Rev.mjs.map} +1 -1
- package/build/{extract-DcwNrTK7.mjs → extract-CefB5Vfn.mjs} +2 -2
- package/build/{extract-DcwNrTK7.mjs.map → extract-CefB5Vfn.mjs.map} +1 -1
- package/build/hours.d.mts +26 -0
- package/build/hours.mjs +48 -1
- package/build/hours.mjs.map +1 -1
- package/build/index.d.mts +491 -11
- package/build/index.mjs +14 -9
- package/build/loopback-C_QYowSf.mjs +22 -0
- package/build/loopback-C_QYowSf.mjs.map +1 -0
- package/build/mcp.d.mts +158 -2
- package/build/mcp.mjs +567 -18
- package/build/mcp.mjs.map +1 -1
- package/build/ollama-BJxkjny4.mjs +160 -0
- package/build/ollama-BJxkjny4.mjs.map +1 -0
- package/build/{open-DnrWgkC0.mjs → open-CbpSCEZc.mjs} +1127 -16
- package/build/open-CbpSCEZc.mjs.map +1 -0
- package/build/recipe-GiFQojmo.mjs +279 -0
- package/build/recipe-GiFQojmo.mjs.map +1 -0
- package/build/recipe-product-Dellqudu.mjs +892 -0
- package/build/recipe-product-Dellqudu.mjs.map +1 -0
- package/build/{runtime-k8mo1_7U.mjs → runtime-D_r2SYge.mjs} +3 -3
- package/build/{runtime-k8mo1_7U.mjs.map → runtime-D_r2SYge.mjs.map} +1 -1
- package/build/transformers-BQMlaGj4.mjs +133 -0
- package/build/transformers-BQMlaGj4.mjs.map +1 -0
- package/build/{duration-iIuMMofi.mjs → version-BPem-XZd.mjs} +10 -10
- package/build/version-BPem-XZd.mjs.map +1 -0
- package/build/{wacz-sfOLT8e9.mjs → wacz-QyzLp4mC.mjs} +3 -3
- package/build/{wacz-sfOLT8e9.mjs.map → wacz-QyzLp4mC.mjs.map} +1 -1
- package/build/{warc-VIPscWxY.mjs → warc-BHcaRKHw.mjs} +2 -2
- package/build/{warc-VIPscWxY.mjs.map → warc-BHcaRKHw.mjs.map} +1 -1
- package/build/webbotauth-CjJdTAfE.mjs.map +1 -1
- package/package.json +10 -2
- package/build/duration-iIuMMofi.mjs.map +0 -1
- package/build/open-DnrWgkC0.mjs.map +0 -1
package/build/hours.d.mts
CHANGED
|
@@ -109,5 +109,31 @@ export declare const toHoursWeek: (week: Week) => {
|
|
|
109
109
|
};
|
|
110
110
|
/** This function gives the days where two readings disagree, from the days that the two readings state. */
|
|
111
111
|
export declare const disagreements: (a: Week, b: Week) => Day[];
|
|
112
|
+
/** The result of `compareHours`. */
|
|
113
|
+
export interface HoursComparison {
|
|
114
|
+
/**
|
|
115
|
+
* The value `new` tells that the stored week states no day. The value `changed` tells that a day
|
|
116
|
+
* of the two weeks is different. Else the value is `same`.
|
|
117
|
+
*/
|
|
118
|
+
readonly status: "same" | "changed" | "new";
|
|
119
|
+
/** These days changed. For `new`, these are the days that the reading states. */
|
|
120
|
+
readonly days: readonly Day[];
|
|
121
|
+
/** The reading states these days, and the stored week does not. They do not make a change. */
|
|
122
|
+
readonly added: readonly Day[];
|
|
123
|
+
/** The comparison left out these days, because a side has two or more intervals (`skipSplit`). */
|
|
124
|
+
readonly skipped: readonly Day[];
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* This function compares a reading with a stored week, day by day. It compares only the days that
|
|
128
|
+
* the two weeks state. A day that only the reading states is in `added`, and it is not a change.
|
|
129
|
+
* The times `24:00` and `00:00` are the same. With `skipSplit`, a day with two or more intervals on
|
|
130
|
+
* a side is not compared, for a store that keeps one interval for each day.
|
|
131
|
+
*
|
|
132
|
+
* The function is pure. It does not decide if a change is true: an application confirms a change
|
|
133
|
+
* with a second reading, as a watch does.
|
|
134
|
+
*/
|
|
135
|
+
export declare const compareHours: (stored: Week | undefined, reading: Week, options?: {
|
|
136
|
+
readonly skipSplit?: boolean | undefined;
|
|
137
|
+
}) => HoursComparison;
|
|
112
138
|
//#endregion
|
|
113
139
|
//# sourceMappingURL=hours.d.mts.map
|
package/build/hours.mjs
CHANGED
|
@@ -899,7 +899,54 @@ const toHoursWeek = (week) => {
|
|
|
899
899
|
};
|
|
900
900
|
/** This function gives the days where two readings disagree, from the days that the two readings state. */
|
|
901
901
|
const disagreements = (a, b) => DAYS.filter((day) => a[day] !== void 0 && b[day] !== void 0 && !sameDay(a[day], b[day]));
|
|
902
|
+
/** This function writes a time in one form: `9:00` is `09:00`, and `24:00` is midnight, `00:00`. */
|
|
903
|
+
const clockForm = (time) => {
|
|
904
|
+
const text = time.trim();
|
|
905
|
+
if (text === "24:00") return "00:00";
|
|
906
|
+
return /^\d:\d{2}$/.test(text) ? `0${text}` : text;
|
|
907
|
+
};
|
|
908
|
+
const dayForm = (intervals) => intervals.map(([open, close]) => `${clockForm(open)}-${clockForm(close)}`).sort((a, b) => a < b ? -1 : a > b ? 1 : 0).join(",");
|
|
909
|
+
/**
|
|
910
|
+
* This function compares a reading with a stored week, day by day. It compares only the days that
|
|
911
|
+
* the two weeks state. A day that only the reading states is in `added`, and it is not a change.
|
|
912
|
+
* The times `24:00` and `00:00` are the same. With `skipSplit`, a day with two or more intervals on
|
|
913
|
+
* a side is not compared, for a store that keeps one interval for each day.
|
|
914
|
+
*
|
|
915
|
+
* The function is pure. It does not decide if a change is true: an application confirms a change
|
|
916
|
+
* with a second reading, as a watch does.
|
|
917
|
+
*/
|
|
918
|
+
const compareHours = (stored, reading, options = {}) => {
|
|
919
|
+
const stated = DAYS.filter((day) => reading[day] !== void 0);
|
|
920
|
+
if (stored === void 0 || DAYS.every((day) => stored[day] === void 0)) return {
|
|
921
|
+
status: "new",
|
|
922
|
+
days: stated,
|
|
923
|
+
added: [],
|
|
924
|
+
skipped: []
|
|
925
|
+
};
|
|
926
|
+
const days = [];
|
|
927
|
+
const added = [];
|
|
928
|
+
const skipped = [];
|
|
929
|
+
for (const day of stated) {
|
|
930
|
+
const kept = stored[day];
|
|
931
|
+
const said = reading[day];
|
|
932
|
+
if (kept === void 0) {
|
|
933
|
+
added.push(day);
|
|
934
|
+
continue;
|
|
935
|
+
}
|
|
936
|
+
if (options.skipSplit === true && (kept.length > 1 || said.length > 1)) {
|
|
937
|
+
skipped.push(day);
|
|
938
|
+
continue;
|
|
939
|
+
}
|
|
940
|
+
if (dayForm(kept) !== dayForm(said)) days.push(day);
|
|
941
|
+
}
|
|
942
|
+
return {
|
|
943
|
+
status: days.length > 0 ? "changed" : "same",
|
|
944
|
+
days,
|
|
945
|
+
added,
|
|
946
|
+
skipped
|
|
947
|
+
};
|
|
948
|
+
};
|
|
902
949
|
//#endregion
|
|
903
|
-
export { DAYS, dayRange, describeWeek, disagreements, fromCompact, fromSchemaOrg, fromText, readRange, toHoursWeek };
|
|
950
|
+
export { DAYS, compareHours, dayRange, describeWeek, disagreements, fromCompact, fromSchemaOrg, fromText, readRange, toHoursWeek };
|
|
904
951
|
|
|
905
952
|
//# sourceMappingURL=hours.mjs.map
|
package/build/hours.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hours.mjs","names":[],"sources":["../src/hours.ts"],"sourcesContent":["/**\n * The hours reader reads opening hours from the three places where they occur:\n *\n * - schema.org JSON-LD: the `openingHours` strings or `openingHoursSpecification`.\n * - The `opening_hours` tag of OpenStreetMap, in its common subset. It does not guess a rule\n * that it cannot fully read (holidays, months, sunrise). It leaves that rule out and says so.\n * - The text of the page: \"Mon–Thu 11am–10pm, Fri–Sat 11am–1am\", \"Lunch Mon-Fri 11:30-2:30 /\n * Dinner 5-10\", \"Sunday: Closed\". The text reader reads English.\n *\n * A day can have more than one interval (lunch and dinner). A day that is not in a week is\n * unknown, not closed. When a time has no am or pm, the parser guesses it and the result says so\n * (`inferred`). Thus a guess does not look like a fact. A guess is not longer than 16 hours.\n *\n * The time of each reader grows in proportion to the length of its input.\n */\n\nexport type Day = \"mon\" | \"tue\" | \"wed\" | \"thu\" | \"fri\" | \"sat\" | \"sun\";\nexport const DAYS: readonly Day[] = [\"mon\", \"tue\", \"wed\", \"thu\", \"fri\", \"sat\", \"sun\"];\n\n/**\n * An interval is [open, close] as HH:MM. A close at or before the open is after midnight, and\n * [\"00:00\", \"00:00\"] is open 24 hours.\n */\nexport type Interval = readonly [string, string];\n/** A week gives the intervals of each day, and [] for a closed day. A day that is not there is unknown. */\nexport type Week = Partial<Record<Day, readonly Interval[]>>;\n\nexport interface Hours {\n readonly week: Week;\n readonly from: \"jsonld\" | \"osm\" | \"text\";\n /** This text is the words that the hours came from. */\n readonly raw: string;\n /** This value is true when the parser guessed an am or a pm (\"11-9\"). */\n readonly inferred: boolean;\n /**\n * This value is true when the reader left out a rule or a line. The rule is not about the week (a\n * holiday, a date) or the reader cannot read it (\"sunrise\").\n */\n readonly partial?: boolean;\n /** This text names the season or the dates of the hours, for example \"Summer\" or \"2026-06-01/2026-08-31\". */\n readonly season?: string;\n}\n\nconst DAY_WORDS: Readonly<Record<string, Day>> = {\n m: \"mon\",\n mo: \"mon\",\n mon: \"mon\",\n monday: \"mon\",\n mondays: \"mon\",\n tu: \"tue\",\n tue: \"tue\",\n tues: \"tue\",\n tuesday: \"tue\",\n tuesdays: \"tue\",\n w: \"wed\",\n we: \"wed\",\n wed: \"wed\",\n weds: \"wed\",\n wednesday: \"wed\",\n wednesdays: \"wed\",\n th: \"thu\",\n thu: \"thu\",\n thur: \"thu\",\n thurs: \"thu\",\n thursday: \"thu\",\n thursdays: \"thu\",\n f: \"fri\",\n fr: \"fri\",\n fri: \"fri\",\n friday: \"fri\",\n fridays: \"fri\",\n sa: \"sat\",\n sat: \"sat\",\n saturday: \"sat\",\n saturdays: \"sat\",\n su: \"sun\",\n sun: \"sun\",\n sunday: \"sun\",\n sundays: \"sun\",\n};\n\nconst dayOf = (word: string): Day | undefined => {\n const clean = word.toLowerCase().replace(/\\.$/, \"\");\n return DAY_WORDS[clean] ?? DAY_WORDS[clean.replace(/s$/, \"\")] ?? DAY_WORDS[clean.slice(0, 3)];\n};\n\n/** This function gives the days from one day to another, with the two days. It continues past Sunday (Fri-Mon). */\nexport const dayRange = (from: Day, to: Day): Day[] => {\n const out: Day[] = [];\n for (let index = DAYS.indexOf(from); ; index = (index + 1) % 7) {\n out.push(DAYS[index]!);\n if (DAYS[index] === to || out.length === 7) return out;\n }\n};\n\nconst pad = (value: number): string => String(value).padStart(2, \"0\");\nconst hhmm = (hour: number, minute: number): string => `${pad(hour % 24)}:${pad(minute)}`;\n\nconst minutesOf = (clock: string): number =>\n Number(clock.slice(0, 2)) * 60 + Number(clock.slice(3, 5));\n/** This function gives the minutes from the start of the day. A close at or before the open is on the next day (\"17:00-02:00\" ends at 26:00). */\nconst spanOf = ([open, close]: Interval): readonly [number, number] => {\n const start = minutesOf(open);\n const end = minutesOf(close);\n return [start, end <= start ? end + 1440 : end];\n};\nconst fromMinutes = (minutes: number): string => hhmm(Math.floor(minutes / 60), minutes % 60);\n/** This function gives the length of an interval in minutes: 1440 for open 24 hours. */\nconst lengthOf = (open: number, close: number): number => {\n const length = (close - open + 1440) % 1440;\n return length === 0 ? 1440 : length;\n};\n\nconst ALL_DAY: Interval = [\"00:00\", \"00:00\"];\n/** No real day has more intervals than this. More is a list of something else, and the reader stops. */\nconst MAX_INTERVALS = 8;\n\nconst addInterval = (\n week: Record<string, Interval[]>,\n days: readonly Day[],\n interval: Interval,\n): void => {\n for (const day of days) {\n let list = week[day] ?? [];\n if (list.length >= MAX_INTERVALS) continue;\n if (!list.some(([open, close]) => open === interval[0] && close === interval[1])) {\n // intervals that overlap are one opening (\"open 6:30-3, delivery 8-2\"\n // is open 6:30-3; \"11-10:15pm, 11-1am\" is open 11-1am); lunch and\n // dinner with a gap stay two\n const [start, end] = spanOf(interval);\n const overlaps = (other: Interval): boolean => {\n const [otherStart, otherEnd] = spanOf(other);\n return otherStart <= end && start <= otherEnd;\n };\n const [first, last] = list.filter(overlaps).reduce<readonly [number, number]>(\n ([low, high], other) => {\n const [otherStart, otherEnd] = spanOf(other);\n return [Math.min(low, otherStart), Math.max(high, otherEnd)];\n },\n [start, end],\n );\n // a union of a whole day or more is open around the clock\n const merged: Interval = [\n fromMinutes(first),\n fromMinutes(last - first >= 1440 ? first : last),\n ];\n list = [...list.filter((other) => !overlaps(other)), merged];\n }\n list.sort((a, b) => a[0].localeCompare(b[0]));\n week[day] = list;\n }\n};\n\n/** This function closes the days that nothing has stated. */\nconst closeDays = (week: Record<string, Interval[]>, days: readonly Day[]): void => {\n for (const day of days) if (week[day] === undefined) week[day] = [];\n};\n\n/** These words name a month. */\nconst MONTH =\n \"(?:jan(?:uary)?|feb(?:ruary)?|mar(?:ch)?|apr(?:il)?|may|june?|july?|aug(?:ust)?|sep(?:t(?:ember)?)?|oct(?:ober)?|nov(?:ember)?|dec(?:ember)?)\\\\.?\";\n\n// ── the OSM / schema.org compact syntax ─────────────────────────────────────\n\nconst OSM_DAYS = /^(Mo|Tu|We|Th|Fr|Sa|Su)(?:-(Mo|Tu|We|Th|Fr|Sa|Su))?$/i;\nconst NAMED_DAYS = /^([a-z]{2,9})(?:-([a-z]{2,9}))?$/i;\nconst OSM_TIME = /^(\\d{1,2}):(\\d{2})-(\\d{1,2}):(\\d{2})\\+?$/;\n/** A rule for dates, not for a week: months, weeks, a year, an nth weekday, Easter. */\nconst DATED_RULE = new RegExp(`\\\\b${MONTH}(?![a-z])|\\\\bweek\\\\b|\\\\[|\\\\beaster\\\\b|\\\\b\\\\d{4}\\\\b`, \"i\");\nconst MAX_COMPACT_CHARS = 2000;\n\n/** This function reads one OSM interval, \"09:00-17:00\", or gives undefined. A close past 24:00 is on the next day. */\nconst osmInterval = (span: string): Interval | undefined => {\n const time = OSM_TIME.exec(span);\n if (time === null) return undefined;\n const [openHour, openMinute, closeHour, closeMinute] = [time[1], time[2], time[3], time[4]].map(\n Number,\n );\n if (openMinute! > 59 || closeMinute! > 59 || openHour! > 24 || closeHour! > 48) return undefined;\n return [hhmm(openHour!, openMinute!), hhmm(closeHour!, closeMinute!)];\n};\n\n/**\n * This function reads the `opening_hours` notation of OpenStreetMap, for example\n * \"Mo-Th 11:00-23:00; Fr,Sa 11:00-01:00; Su off\". The `openingHours` of schema.org uses the same\n * notation. A later rule replaces an earlier rule for the same days, as OpenStreetMap specifies.\n * The reader leaves out a rule for dates or holidays, and the days of a rule that it cannot read\n * stay unknown. In both cases the result is `partial`.\n */\nexport const fromCompact = (value: string, from: \"osm\" | \"jsonld\" = \"osm\"): Hours | undefined => {\n if (value.length > MAX_COMPACT_CHARS) return undefined;\n const text = value.replace(/\\s+/g, \" \").trim();\n if (text.length === 0) return undefined;\n const week: Record<string, Interval[]> = {};\n if (text === \"24/7\") {\n addInterval(week, DAYS, ALL_DAY);\n return { week, from, raw: value, inferred: false };\n }\n let partial = false;\n let inferred = false;\n // a quoted comment (\"breakfast\", \"kitchen closes at 10\") says nothing a\n // parser can use. \";\" and \"||\" start a rule that replaces what earlier\n // rules said about its days; \",\" after a time starts an additional rule,\n // which adds to them (\"Mo-Fr 11:00-21:00, Sa-Su 12:00-20:00\", a common\n // shape in OSM); a day after a time starts a rule too (\"Mo-Fr 09:00-17:00 Sa 10:00-14:00\")\n const rules = text\n .replace(/\"[^\"]*\"/g, \" \")\n .replace(/(\\d) (?=(?:Mo|Tu|We|Th|Fr|Sa|Su|PH|SH)\\b)/gi, \"$1; \")\n .split(/ ?(?:;|\\|\\|) ?/)\n .flatMap((rule) =>\n rule\n .split(/(?<=\\d:\\d{2}\\+?) ?, ?(?=(?:Mo|Tu|We|Th|Fr|Sa|Su|PH|SH)\\b)/i)\n .map((part, index) => ({ part: part.trim(), additional: index > 0 })),\n )\n .filter(({ part }) => part.length > 0);\n let read = false;\n for (const { part: rule, additional } of rules) {\n if (DATED_RULE.test(rule)) {\n // an exception for dates (\"Dec 25 off\") or a season is not the week\n partial = true;\n continue;\n }\n // the days (\"Mo-Fr\", \"Tu,Th\", \"Monday-Friday\", \", Tu\"), then the times\n const parts =\n /^[\\s,]*([A-Za-z]{1,9}(?:-[A-Za-z]{1,9})?(?: ?, ?[A-Za-z]{1,9}(?:-[A-Za-z]{1,9})?)*)? ?:? ?(.*)$/s.exec(\n rule,\n );\n let dayPart = (parts?.[1] ?? \"\").replace(/\\s/g, \"\");\n let timePart = (parts?.[2] ?? \"\").trim();\n if (timePart.length === 0 && /^(off|closed|open)$/i.test(dayPart)) {\n timePart = dayPart;\n dayPart = \"\";\n }\n let days: Day[] = [...DAYS];\n if (dayPart.length > 0) {\n days = [];\n let holidays = false;\n let unreadable = false;\n // empty pieces too: Squarespace writes \", Tu 16:00-21:00, ... Sa 16:00-21:00, \"\n for (const piece of dayPart.split(\",\").filter((part) => part.length > 0)) {\n // public and school holidays are not part of a week: \"PH off\" says\n // nothing about a Tuesday, and \"Su,PH 11:00-18:00\" still says Sunday\n if (/^(PH|SH)$/i.test(piece)) {\n holidays = true;\n continue;\n }\n const range = OSM_DAYS.exec(piece) ?? NAMED_DAYS.exec(piece);\n const first = range === null ? undefined : dayOf(range[1]!);\n const last = range === null ? undefined : dayOf(range[2] ?? range[1]!);\n if (first === undefined || last === undefined) {\n unreadable = true;\n break;\n }\n days.push(...dayRange(first, last));\n }\n if (unreadable) {\n partial = true;\n continue;\n }\n if (days.length === 0) {\n if (holidays) partial = true;\n continue;\n }\n }\n if (timePart.length === 0 || /^open$/i.test(timePart)) {\n partial = true;\n continue;\n }\n // a later rule replaces what earlier ones said about these days\n if (!additional) for (const day of days) delete week[day];\n if (/^(off|closed)$/i.test(timePart)) {\n // an additional \"off\" closes its days too (\"Mo-Fr 09:00-17:00, We off\")\n for (const day of days) delete week[day];\n closeDays(week, days);\n read = true;\n continue;\n }\n const intervals: Interval[] = [];\n for (const span of timePart.split(/ ?, ?/).filter((part) => part.length > 0)) {\n const interval = osmInterval(span);\n if (interval !== undefined) {\n intervals.push(interval);\n continue;\n }\n // \"9am-5pm\" in a field that wants \"09:00-17:00\"\n const parts = splitRange(span);\n const range = parts === undefined ? undefined : readRange(parts[0], parts[1]);\n if (range === undefined) {\n intervals.length = 0;\n break;\n }\n inferred ||= range.inferred;\n intervals.push(range.interval);\n }\n if (intervals.length === 0) {\n // \"sunrise-sunset\", \"25:99\": these days stay unknown, never guessed\n for (const day of days) delete week[day];\n partial = true;\n continue;\n }\n for (const interval of intervals) addInterval(week, days, interval);\n read = true;\n }\n if (!read || Object.keys(week).length === 0) return undefined;\n return { week, from, raw: value, inferred, ...(partial ? { partial } : {}) };\n};\n\n// ── schema.org JSON-LD ──────────────────────────────────────────────────────\n\nconst SCHEMA_DAY = /(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)$/i;\n\n/** This function reads a schema.org time: \"17:00\", \"17:00:00\", \"17:00:00-05:00\" or \"5:00 PM\". */\nconst clockOf = (\n value: unknown,\n): { readonly minutes: number; readonly raw: string } | undefined => {\n if (typeof value !== \"string\") return undefined;\n const raw = value.trim().slice(0, 40);\n const match =\n /^(\\d{1,2})(?::(\\d{2}))?(?::\\d{2}(?:\\.\\d+)?)?\\s*(a\\.?m\\.?|p\\.?m\\.?)?\\s*(?:z|[+-]\\d{2}:?\\d{2})?$/i.exec(\n raw,\n );\n if (match === null) return undefined;\n let hour = Number(match[1]);\n const minute = Number(match[2] ?? 0);\n if (minute > 59 || hour > 24 || (hour === 24 && minute > 0)) return undefined;\n const meridiem =\n match[3]?.toLowerCase().startsWith(\"p\") === true\n ? \"pm\"\n : match[3] === undefined\n ? undefined\n : \"am\";\n if (meridiem !== undefined) {\n if (hour === 0 || hour > 12) return undefined;\n hour = meridiem === \"am\" ? hour % 12 : (hour % 12) + 12;\n }\n return { minutes: hour * 60 + minute, raw };\n};\n\n/** This function gives the days of a `dayOfWeek`: a name, a URL, an `@id` or a short name. */\nconst daysOfSpec = (raw: unknown): { readonly days: Day[]; readonly holidays: boolean } => {\n const values = Array.isArray(raw) ? raw : [raw];\n const days: Day[] = [];\n let holidays = false;\n for (const value of values) {\n const name =\n typeof value === \"string\"\n ? value\n : value !== null && typeof value === \"object\"\n ? (value as Record<string, unknown>)[\"@id\"]\n : undefined;\n if (typeof name !== \"string\") continue;\n if (/PublicHolidays$/i.test(name.trim())) {\n holidays = true;\n continue;\n }\n const full = SCHEMA_DAY.exec(name.trim());\n const day = full === null ? dayOf(name.trim().replace(/^.*[/#:]/, \"\")) : dayOf(full[1]!);\n if (day !== undefined) days.push(day);\n }\n return { days, holidays };\n};\n\n/** The text of a date of schema.org, or an empty string for a value that is not text. */\nconst dateText = (value: unknown): string => (typeof value === \"string\" ? value : \"\");\n\nconst dateOf = (value: unknown): number | undefined => {\n if (typeof value !== \"string\") return undefined;\n const time = Date.parse(value.trim().slice(0, 40));\n return Number.isNaN(time) ? undefined : time;\n};\n\n/**\n * This function reads the hours of one JSON-LD object, for example a `Restaurant`. It reads the\n * `openingHoursSpecification` first, and the `openingHours` when there is no specification. In\n * schema.org, a closed day has the same opening and closing time, 00:00, and 00:00 to 24:00 is\n * open 24 hours.\n *\n * A specification for less than a week (`validFrom` to `validThrough`) is a holiday: the reader\n * leaves it out. When specifications hold for different seasons, the option `at` picks the\n * season of that date. With no date, the reader keeps the specifications with no season, or else\n * the first season, and it names the season.\n */\nexport const fromSchemaOrg = (\n record: Readonly<Record<string, unknown>>,\n options: { readonly at?: string | Date | undefined } = {},\n): Hours | undefined => {\n const specs = record[\"openingHoursSpecification\"];\n const list = (Array.isArray(specs) ? specs : specs !== undefined && specs !== null ? [specs] : [])\n .slice(0, 200)\n .filter((spec): spec is Record<string, unknown> => spec !== null && typeof spec === \"object\");\n if (list.length > 0) {\n let partial = false;\n const at = options.at === undefined ? undefined : new Date(options.at).getTime();\n const windowed = list.map((entry) => {\n const fromDate = dateOf(entry[\"validFrom\"]);\n const throughDate = dateOf(entry[\"validThrough\"]);\n const key =\n fromDate === undefined && throughDate === undefined\n ? undefined\n : `${dateText(entry[\"validFrom\"])}/${dateText(entry[\"validThrough\"])}`;\n const short =\n fromDate !== undefined &&\n throughDate !== undefined &&\n throughDate - fromDate < 7 * 86_400_000;\n return { entry, key, fromDate, throughDate, short };\n });\n if (windowed.some(({ short }) => short)) partial = true;\n const seasonal = windowed.filter(({ short }) => !short);\n let season: string | undefined;\n let chosen = seasonal.filter(({ key }) => key === undefined);\n if (at !== undefined && !Number.isNaN(at)) {\n const now = seasonal.filter(\n ({ key, fromDate, throughDate }) =>\n key !== undefined &&\n (fromDate ?? -Infinity) <= at &&\n at <= (throughDate ?? Infinity) + 86_399_999,\n );\n if (now.length > 0) {\n season = now[0]!.key;\n chosen = [...chosen, ...now.filter(({ key }) => key === season)];\n }\n } else if (chosen.length === 0) {\n season = seasonal.find(({ key }) => key !== undefined)?.key;\n chosen = seasonal.filter(({ key }) => key === season);\n }\n if (seasonal.some(({ key }) => key !== undefined && key !== season)) partial = true;\n const week: Record<string, Interval[]> = {};\n for (const { entry } of chosen) {\n const { days, holidays } = daysOfSpec(entry[\"dayOfWeek\"]);\n if (holidays) partial = true;\n const opens = clockOf(entry[\"opens\"]);\n const closes = clockOf(entry[\"closes\"]);\n if (days.length === 0 || opens === undefined || closes === undefined) {\n if (days.length > 0) partial = true;\n continue;\n }\n const allDay =\n opens.minutes === 0 && (closes.raw.startsWith(\"24:00\") || closes.minutes === 23 * 60 + 59);\n if (allDay) {\n addInterval(week, days, ALL_DAY);\n } else if (opens.minutes === closes.minutes) {\n // 00:00 to 00:00 is closed; 09:00 to 09:00 says nothing certain\n if (opens.minutes === 0) {\n for (const day of days) delete week[day];\n closeDays(week, days);\n } else {\n partial = true;\n }\n } else {\n addInterval(week, days, [fromMinutes(opens.minutes), fromMinutes(closes.minutes % 1440)]);\n }\n }\n if (Object.keys(week).length > 0) {\n return {\n week,\n from: \"jsonld\",\n raw: JSON.stringify(list).slice(0, 500),\n inferred: false,\n ...(partial ? { partial } : {}),\n ...(season !== undefined ? { season } : {}),\n };\n }\n }\n const compact = record[\"openingHours\"];\n const strings = (Array.isArray(compact) ? compact : [compact])\n .filter((value): value is string => typeof value === \"string\")\n .slice(0, 50);\n if (strings.length === 0) return undefined;\n // schema.org writes one rule per string; a comma inside one is a day list\n // (\"Tu,Th 16:00-20:00\") or, after a time, another rule, as in OSM\n const joined = strings.join(\"; \");\n const parsed = fromCompact(joined, \"jsonld\") ?? textFallback(joined);\n return parsed === undefined ? undefined : { ...parsed, raw: strings.join(\", \") };\n};\n\n/** This function reads an `openingHours` value that is not in the compact notation (\"Monday-Friday 9am-5pm\"). */\nconst textFallback = (value: string): Hours | undefined => {\n const [found] = fromText(value.replace(/;/g, \"\\n\"));\n if (found === undefined) return undefined;\n const { label: _label, ...hours } = found;\n return { ...hours, from: \"jsonld\" };\n};\n\n// ── free text ───────────────────────────────────────────────────────────────\n\nconst DAY_WORD =\n \"(?:mon(?:day)?s?|tue(?:s(?:day)?)?s?|wed(?:s|nesday)?s?|thu(?:r(?:s(?:day)?)?)?s?|fri(?:day)?s?|sat(?:urday)?s?|sun(?:day)?s?)\\\\.?\";\n/** These short day names count only in a span with a dash (\"M-F\", \"Tu-Th\", \"Mo-Fr\"). */\nconst SHORT_DAY = \"(?:mo|tu|we|th|fr|sa|su|m|w|f)\";\n/** A dash of any kind joins a range. U+FFFD is a dash that a wrong decoder lost. */\nconst DASH = \"[-\\\\u2010-\\\\u2015\\\\u2212~\\\\u301c\\\\ufffd]\";\nconst RANGE_SEP = `\\\\s?(?:${DASH}|to|thru|through|until|till|['’]til|til)\\\\s?`;\nconst TIME =\n \"(?:12\\\\s?(?:midnight|noon|mn|nn)\\\\b|\\\\d{1,2}(?:[:.]\\\\d{2}|h(?:\\\\d{2})?(?![a-z]))?(?:\\\\s?(?:a\\\\.?m\\\\.?|p\\\\.?m\\\\.?))?|noon|midnight)\";\n/** This pattern is one time with am or pm: \"7:00 am\". */\nconst CLOCK =\n \"(?:12\\\\s?(?:midnight|noon)\\\\b|\\\\d{1,2}(?:[:.]\\\\d{2})?\\\\s?(?:a\\\\.?m\\\\.?|p\\\\.?m\\\\.?)|noon|midnight)\";\n/** \"Open 24 hours\", \"24/7\": open all day. \"Within 24 hours\" and \"24 hours notice\" are not. */\nconst ALL_DAY_WORDS =\n \"(?:open\\\\s)?(?<!(?:within|in|for|last|past|next|every|after|than)\\\\s)(?:24\\\\s?(?:hours|hrs?|h)\\\\b(?!\\\\s?(?:notice|in advance|advance|prior|before|ahead|later|turnaround|delivery|of|to|from))|24\\\\s?\\\\/\\\\s?7\\\\b|around the clock)\";\n\nconst TOKEN = new RegExp(\n [\n `(?<allday>\\\\b${ALL_DAY_WORDS})`,\n `(?<every>\\\\b(?:daily|every\\\\s?day|everyday|7\\\\s?days(?:\\\\s?a\\\\s?week)?|seven\\\\s?days(?:\\\\s?a\\\\s?week)?)\\\\b)`,\n `(?<weekdays>\\\\bweekdays?\\\\b)`,\n `(?<weekends>\\\\bweekends?\\\\b)`,\n // \"except Monday\", \"but Sunday\": the days after it are closed\n `(?<except>\\\\b(?:except(?:\\\\s(?:on|for))?|but(?:\\\\snot)?(?:\\\\son)?|excluding|other\\\\sthan|apart\\\\sfrom)(?=\\\\s(?:${DAY_WORD}|weekends?|weekdays?)(?![a-z])))`,\n `(?<span>\\\\b(?:${DAY_WORD}${RANGE_SEP}${DAY_WORD}|(?:${DAY_WORD}|${SHORT_DAY})\\\\s?${DASH}\\\\s?(?:${DAY_WORD}|${SHORT_DAY}))(?![a-z]))`,\n // \"Sun-dried tomato\" and \"Sunday's game\" name no day\n `(?<day>\\\\b${DAY_WORD}(?![a-z])(?!-(?!closed)[a-z])(?!['’]s\\\\b))`,\n `(?<times>(?<![\\\\d:.$€£¥])${TIME}${RANGE_SEP}${TIME}(?!\\\\d))`,\n // a meridiem ends its word: \"73 Ames St\" is no clock\n `(?<clock>(?<![\\\\d:.$€£¥])${CLOCK}(?![a-z\\\\d]))`,\n `(?<closed>\\\\bclosed\\\\b)`,\n ].join(\"|\"),\n \"gi\",\n);\n\ninterface Clock {\n readonly hour: number;\n readonly minute: number;\n readonly meridiem: \"am\" | \"pm\" | undefined;\n /** This value is true when the time uses the 24-hour clock (\"17:00\", \"09:30\", \"9h\"). */\n readonly h24: boolean;\n}\n\nconst readClock = (value: string): Clock | undefined => {\n const text = value.trim().toLowerCase();\n if (/^(?:12\\s?)?(?:noon|nn)$/.test(text))\n return { hour: 12, minute: 0, meridiem: \"pm\", h24: false };\n if (/^(?:12\\s?)?(?:midnight|mn)$/.test(text)) {\n return { hour: 12, minute: 0, meridiem: \"am\", h24: false };\n }\n const match = /^(\\d{1,2})(?:[:.](\\d{2})|(h)(\\d{2})?)?\\s?(a\\.?m\\.?|p\\.?m\\.?)?$/.exec(text);\n if (match === null) return undefined;\n const hour = Number(match[1]);\n const minute = Number(match[2] ?? match[4] ?? 0);\n if (hour > 24 || minute > 59 || (hour === 24 && minute > 0)) return undefined;\n let meridiem: \"am\" | \"pm\" | undefined =\n match[5] === undefined ? undefined : match[5].startsWith(\"a\") ? \"am\" : \"pm\";\n // \"17:00 pm\" is written in haste: the clock says it all\n if (meridiem !== undefined && (hour === 0 || hour > 12)) meridiem = undefined;\n return {\n hour,\n minute,\n meridiem,\n h24:\n meridiem === undefined &&\n (hour > 12 || hour === 0 || match[1]!.startsWith(\"0\") || match[3] !== undefined),\n };\n};\n\nconst to24 = (clock: Clock, meridiem: \"am\" | \"pm\"): number =>\n meridiem === \"am\"\n ? clock.hour === 12\n ? 0\n : clock.hour\n : clock.hour === 12\n ? 12\n : clock.hour + 12;\n\nconst flip = (meridiem: \"am\" | \"pm\"): \"am\" | \"pm\" => (meridiem === \"am\" ? \"pm\" : \"am\");\n\n/** The longest opening that a guess can give, in minutes. */\nconst MAX_GUESS_MINUTES = 16 * 60;\n\n/** This function reads a range with a 24-hour time. A bare number on the other side is the reading that makes sense. */\nconst read24 = (\n a: Clock,\n b: Clock,\n): { readonly interval: Interval; readonly inferred: boolean } | undefined => {\n const options = (clock: Clock): number[] =>\n clock.h24 || clock.meridiem !== undefined\n ? [clock.meridiem === undefined ? clock.hour % 24 : to24(clock, clock.meridiem)]\n : clock.hour === 12\n ? [12, 0]\n : [clock.hour, clock.hour + 12];\n const candidates: Array<{ open: number; close: number; length: number; written: boolean }> = [];\n for (const [aIndex, openHour] of options(a).entries()) {\n for (const [bIndex, closeHour] of options(b).entries()) {\n const open = openHour * 60 + a.minute;\n const close = closeHour * 60 + b.minute;\n candidates.push({\n open,\n close,\n length: lengthOf(open, close),\n written: aIndex === 0 && bIndex === 0,\n });\n }\n }\n const stated = a.h24 && b.h24;\n if (stated) {\n const [only] = candidates;\n // \"21-14\" is a score, not a night: an opening past midnight ends in the morning\n if (only === undefined || (only.close <= only.open && only.close > 12 * 60)) return undefined;\n return { interval: [fromMinutes(only.open), fromMinutes(only.close)], inferred: false };\n }\n // the reading as written first, then: within the day and three hours or\n // more, three hours or more, within the day, anything sensible\n const sensible = candidates\n .filter(({ length }) => length >= 60 && length <= MAX_GUESS_MINUTES)\n .sort((x, y) => Number(y.written) - Number(x.written));\n const best =\n sensible.find(({ close, open, length }) => close > open && length >= 180) ??\n sensible.find(({ length }) => length >= 180) ??\n sensible.find(({ close, open }) => close > open) ??\n sensible[0];\n if (best === undefined) return undefined;\n return { interval: [fromMinutes(best.open), fromMinutes(best.close)], inferred: !best.written };\n};\n\n/**\n * This function reads a range of two times, as a restaurant writes it:\n *\n * - \"5-10pm\" is 17:00-22:00, and \"11-3pm\" is 11:00-15:00. The opening gets the am or pm of the\n * close, but not when that puts the opening after the close.\n * - \"11-1am\" is 11:00-01:00, because 23:00-01:00 is too short to be the meaning. The result is\n * then `inferred`.\n * - \"11am-10\" closes at 22:00.\n * - With no am, pm or 24-hour time (\"11-9\"), an opening before 7 is pm, but \"6-2\" is 06:00-14:00.\n * A close at or before the opening is pm, and \"7-12\" closes at noon. The result is then\n * `inferred`, and it is not longer than 16 hours.\n * - \"21-14\" is no range: a 24-hour opening past midnight does not end after noon.\n */\nexport const readRange = (\n start: string,\n end: string,\n): { readonly interval: Interval; readonly inferred: boolean } | undefined => {\n const a = readClock(start);\n const b = readClock(end);\n if (a === undefined || b === undefined) return undefined;\n if (a.h24 || b.h24) return read24(a, b);\n let open: number;\n let close: number;\n let inferred = false;\n if (a.meridiem !== undefined && b.meridiem !== undefined) {\n open = to24(a, a.meridiem);\n close = to24(b, b.meridiem);\n } else if (b.meridiem !== undefined) {\n // the opening shares the close's meridiem unless that puts it after the\n // close: \"5-10pm\" opens at 5pm, \"11-3pm\" at 11am, \"10-2am\" at 10pm\n close = to24(b, b.meridiem);\n const same = to24(a, b.meridiem);\n const other = to24(a, flip(b.meridiem));\n const closeAt = close * 60 + b.minute;\n if (same * 60 + a.minute < closeAt) {\n open = same;\n // \"12-1am\" opens at noon: an hour from midnight is no opening\n if (a.hour === 12 && b.meridiem === \"am\" && lengthOf(same * 60 + a.minute, closeAt) <= 120) {\n open = 12;\n inferred = true;\n }\n } else if (\n lengthOf(other * 60 + a.minute, closeAt) <= 180 &&\n lengthOf(same * 60 + a.minute, closeAt) <= 18 * 60\n ) {\n // \"11-1am\": a bar open from 11am, not for two hours from 11pm\n open = same;\n inferred = true;\n } else {\n open = other;\n }\n } else if (a.meridiem !== undefined) {\n open = to24(a, a.meridiem);\n const same = to24(b, a.meridiem);\n close = same > open ? same : to24(b, flip(a.meridiem));\n } else {\n // no meridiem anywhere: read as a restaurant would mean it, and say so.\n // An opening before 7 is evening (\"5-10\"), but a diner's \"6-2\" is\n // daytime. A close at or before the opening, or within three hours of a\n // morning one (\"10-11\"), is pm; a close of 12 after a morning opening is noon\n inferred = true;\n open =\n a.hour === 12\n ? 12\n : a.hour >= 7 || (a.hour >= 5 && b.hour >= 1 && b.hour <= 3)\n ? a.hour\n : a.hour + 12;\n if (b.hour === 12) close = open < 12 ? 12 : 0;\n else if (open < 12) close = b.hour <= a.hour || b.hour - open < 3 ? b.hour + 12 : b.hour;\n else close = b.hour + 12 > open ? b.hour + 12 : b.hour;\n }\n const openAt = open * 60 + a.minute;\n const closeAt = close * 60 + b.minute;\n if (inferred && lengthOf(openAt, closeAt) > MAX_GUESS_MINUTES) return undefined;\n return { interval: [fromMinutes(openAt % 1440), fromMinutes(closeAt % 1440)], inferred };\n};\n\nconst splitRange = (value: string): [string, string] | undefined => {\n const match = new RegExp(`^(${TIME})${RANGE_SEP}(${TIME})$`, \"i\").exec(\n value.replace(/\\s+/g, \" \").trim().slice(0, 80),\n );\n return match === null ? undefined : [match[1]!, match[2]!];\n};\n\n/** This function gives the days of a token and how specific it is: 0 for \"daily\", 1 for a span, 2 for one day. */\nconst daysOfToken = (\n groups: Record<string, string | undefined>,\n): { readonly days: Day[]; readonly spec: number } | undefined => {\n if (groups[\"every\"] !== undefined) return { days: [...DAYS], spec: 0 };\n if (groups[\"weekdays\"] !== undefined)\n return { days: [\"mon\", \"tue\", \"wed\", \"thu\", \"fri\"], spec: 0 };\n if (groups[\"weekends\"] !== undefined) return { days: [\"sat\", \"sun\"], spec: 0 };\n if (groups[\"span\"] !== undefined) {\n const [from, to] = groups[\"span\"].split(new RegExp(RANGE_SEP, \"i\"));\n const first = from === undefined ? undefined : dayOf(from.trim());\n const last = to === undefined ? undefined : dayOf(to.trim());\n return first === undefined || last === undefined\n ? undefined\n : { days: dayRange(first, last), spec: 1 };\n }\n if (groups[\"day\"] !== undefined) {\n const day = dayOf(groups[\"day\"]);\n return day === undefined ? undefined : { days: [day], spec: 2 };\n }\n return undefined;\n};\n\n/** A date names one day of the year, not a day of the week: \"Dec 24\", \"24 December\", \"12/24\", \"2026-12-24\". */\nconst DATE = new RegExp(\n `\\\\b${MONTH}\\\\s?\\\\d{1,2}(?:st|nd|rd|th)?\\\\b(?!:\\\\d|\\\\s?[ap]\\\\.?m\\\\b)|\\\\b\\\\d{1,2}(?:st|nd|rd|th)?\\\\s(?:of\\\\s)?${MONTH}(?![a-z])|(?<![\\\\d/])\\\\d{1,2}\\\\/\\\\d{1,2}(?:\\\\/\\\\d{2,4})?(?![\\\\d/])|\\\\b\\\\d{4}-\\\\d{2}-\\\\d{2}\\\\b`,\n \"i\",\n);\nconst DATE_RANGE = new RegExp(\n `\\\\b${MONTH}(?:\\\\s?\\\\d{1,2}(?:st|nd|rd|th)?)?${RANGE_SEP}${MONTH}(?:\\\\s?\\\\d{1,2}(?:st|nd|rd|th)?)?(?![a-z])`,\n \"i\",\n);\n/** These words tell that a line is for one occasion, not for the week. */\nconst OCCASION =\n /\\b(?:holidays?|christmas|xmas|thanksgiving|easter|hanukkah|new year['’]?s?|nye|memorial day|labou?r day|independence day|valentine['’]?s|mother['’]?s day|father['’]?s day|private (?:events?|parties|party|hire|dining)|special events?|events? only|today|tonight)\\b/i;\n/**\n * These words name a service or an event beside the opening hours (\"Happy hour 6-8pm\", \"Delivery\n * 6pm-9pm\", \"Live music 9pm-12am\"). Some lines have no day and name a service before their hours.\n * Under a line with days and hours, such a line is not the week. A meal (\"Lunch\", \"Dinner\") or\n * \"Kitchen\" stays a part of the opening hours.\n */\nconst SERVICE =\n /\\b(?:happy hours?|delivery|deliveries|take[- ]?out|take[- ]?away|pick[- ]?up|curbside|drive[- ]?(?:thru|through)|live (?:music|jazz|band)|music|dj|karaoke|trivia|bingo|open mic|brunch|bottomless)\\b/i;\nconst SEASON =\n /^(?:summer|winter|spring|autumn|fall|off[- ]?season|high season|low season|peak season)\\b|\\b(?:summer|winter|spring|autumn|fall|off[- ]?season)(?=\\s?(?:hours|schedule|season|:|\\())/i;\n\n/** This function names the season of a line (\"Summer\", \"June-August\"), or gives undefined. */\nconst seasonOf = (line: string): string | undefined =>\n (SEASON.exec(line) ?? DATE_RANGE.exec(line))?.[0].trim();\n\n/** The longest line that can hold hours. A longer line is prose, and the reader does not read it. */\nconst MAX_LINE = 1000;\n/** A block of hours longer than this is a list of something else. */\nconst MAX_BLOCK_LINES = 100;\n\nconst normalize = (line: string): string =>\n line\n .normalize(\"NFKC\")\n .replace(/[\\u200b-\\u200d\\u2060\\ufeff\\u00ad]/g, \"\")\n .replace(/\\s+/g, \" \")\n .trim();\n\ninterface Token {\n readonly groups: Record<string, string | undefined>;\n /** This is the line of the token in its block. */\n readonly line: number;\n readonly start: number;\n readonly end: number;\n readonly days: Day[] | undefined;\n readonly spec: number;\n}\n\nconst tokensOf = (line: string, at: number): Token[] =>\n line.length > MAX_LINE\n ? []\n : [...line.matchAll(TOKEN)].map((match) => {\n const groups = match.groups ?? {};\n const days = daysOfToken(groups);\n return {\n groups,\n line: at,\n start: match.index ?? 0,\n end: (match.index ?? 0) + match[0].length,\n days: days?.days,\n spec: days?.spec ?? 2,\n };\n });\n\n/** A range with no am, pm, minutes or \"h\" is a bare range (\"5-10\"): it can be a price, an age or a score. */\nconst isStrong = (times: string): boolean => /[ap]\\.?m|[:.]\\d{2}|noon|midnight|\\dh/i.test(times);\n\n/** The words that join days or a day and its hours, and say nothing more. */\nconst JOINERS = /^(?:[\\s,&/:;.()|–—-]|\\band\\b|\\bor\\b|\\bfrom\\b|\\bopen\\b|\\bhours\\b)*$/i;\n\n/**\n * This function reads the hours in the text of a page. It reads the lines with days and times as\n * one stream. Thus \"Monday – Thursday\" on one line and \"11:30am – 9pm\" on the next line go\n * together. A list of days (\"Mon, Wed & Fri\") waits for a time or for \"closed\".\n *\n * Each group of these lines is a candidate. Its label is the short line above it (\"Hours\",\n * \"Dinner\", \"Happy hour\"). A page often shows more than one schedule, and the choice between them\n * is a judgment, not a parse. A season (\"Summer:\", \"June-August\") starts a new candidate.\n *\n * A line about a date or an occasion (\"Dec 24: 9am-2pm\", \"New Year's Eve 8pm-1am\", \"Private events\n * 11am-3pm\") is not the week. A line with no day that names a service before its hours (\"Happy hour\n * 6-8pm\") under a line with days and hours is not the week either. The reader leaves it out, and\n * the candidate is `partial`.\n */\nexport const fromText = (text: string): Array<Hours & { readonly label: string | null }> => {\n const original = text.split(/\\n+/).map((line) => line.trim());\n const lines = original.map((line) => (line.length > MAX_LINE ? line : normalize(line)));\n const out: Array<Hours & { readonly label: string | null }> = [];\n /** A season or the size can cut a block from the block before it. Then the line above the block is not its label. */\n let block:\n { start: number; lines: number[]; season: string | undefined; cut: boolean } | undefined;\n const flush = (): void => {\n if (block === undefined) return;\n const current = block;\n block = undefined;\n const above = current.cut ? \"\" : (lines[current.start - 1] ?? \"\");\n const label = current.cut ? undefined : original[current.start - 1];\n const week: Record<string, Interval[]> = {};\n let inferred = false;\n let partial = false;\n // the most specific selector that opened or closed each day: a closed\n // Monday stays closed under \"daily\", but \"Monday 9-12\" reopens \"Mon-Fri closed\"\n const closedBy = new Map<Day, number>();\n const openedBy = new Map<Day, number>();\n const heading = /hour|open/i.test(above);\n\n const perLine = current.lines.map((index, at) => tokensOf(lines[index]!, at));\n const oneOff = current.lines.map((index, at) => {\n const line = lines[index]!.replace(/\\b24 ?\\/ ?7\\b/g, \" \");\n const onLine = perLine[at]!;\n const weekly = onLine.some((token) => token.days !== undefined);\n // a date before the hours names the day they are for (\"Dec 24: 9am-2pm\");\n // one after them is a note (\"Mon-Fri 9-5 (closed 12/25)\")\n const date = DATE_RANGE.test(line) ? null : DATE.exec(line);\n const value = onLine.find(\n (token) => token.groups[\"times\"] !== undefined || token.groups[\"clock\"] !== undefined,\n );\n // \"Happy hour 6-8pm\" under \"Mon-Fri 9am-5pm\" is not more opening hours of those days; under\n // \"Sat-Sun\" alone (\"Brunch 10am-2pm\") it is the only time those days have\n const above = at > 0 ? perLine[at - 1]! : [];\n const service =\n !weekly &&\n value !== undefined &&\n SERVICE.test(line.slice(0, value.start)) &&\n above.some((token) => token.days !== undefined) &&\n above.some(\n (token) => token.groups[\"times\"] !== undefined || token.groups[\"closed\"] !== undefined,\n );\n return (\n (date !== null && (value === undefined || date.index < value.start)) ||\n (!weekly && OCCASION.test(line)) ||\n service\n );\n });\n const tokens: Token[] = [];\n const breaks = new Set<number>();\n perLine.forEach((onLine, at) => {\n if (oneOff[at]) {\n // a date or an occasion: not the week, and the days above it do not reach past it\n partial = true;\n breaks.add(at);\n return;\n }\n const line = lines[current.lines[at]!]!;\n const dayTokens = onLine.filter((token) => token.days !== undefined);\n const broad =\n dayTokens.length >= 2 ||\n dayTokens.some((token) => token.spec < 2) ||\n heading ||\n /^(?:hours|open)\\b/i.test(line);\n const daysOnlyAbove =\n at > 0 &&\n !breaks.has(at - 1) &&\n perLine[at - 1]!.length > 0 &&\n perLine[at - 1]!.every((token) => token.days !== undefined);\n onLine.forEach((token, index) => {\n const times = token.groups[\"times\"];\n if (times !== undefined && !isStrong(times) && !broad) {\n // a bare range counts only beside its day (\"Sunday 12-9\"), not\n // after other words (\"Kids Club Saturdays, ages 5-10\")\n const before = onLine[index - 1];\n const after = onLine[index + 1];\n const beside =\n (before?.days !== undefined && JOINERS.test(line.slice(before.end, token.start))) ||\n (after?.days !== undefined && JOINERS.test(line.slice(token.end, after.start))) ||\n (index === 0 && daysOnlyAbove && JOINERS.test(line.slice(0, token.start)));\n if (!beside) return;\n }\n tokens.push(token);\n });\n });\n const isDays = (token: Token | undefined): boolean => token?.days !== undefined;\n const isValue = (token: Token): boolean =>\n token.groups[\"times\"] !== undefined ||\n token.groups[\"closed\"] !== undefined ||\n token.groups[\"allday\"] !== undefined;\n // a line whose days are followed by other words (\"Monday: By appointment\")\n // ends its list of days there: the next line's days do not join it\n const dangling = new Set<number>();\n perLine.forEach((onLine, at) => {\n const last = onLine.at(-1);\n if (last?.days === undefined) return;\n const line = lines[current.lines[at]!]!;\n if (!JOINERS.test(line.slice(last.end))) dangling.add(at);\n });\n // a line that starts with a value and names its days after it (\"11am-9pm\n // Mon-Thu, 11am-10pm Fri-Sat\", \"10 AM - 8 PM daily\", \"Closed Mondays\")\n // gives each value to the days that follow it, not the days before\n const valueFirst = new Set<number>();\n const firstOnLine = new Map<number, Token>();\n const lineHasDays = new Set<number>();\n for (const token of tokens) {\n if (isDays(token)) lineHasDays.add(token.line);\n if (!firstOnLine.has(token.line) && (isDays(token) || isValue(token)))\n firstOnLine.set(token.line, token);\n }\n for (const [line, first] of firstOnLine)\n if (isValue(first) && lineHasDays.has(line)) valueFirst.add(line);\n\n const apply = (\n value: Interval | \"closed\",\n days: readonly Day[],\n spec: number,\n explicit: boolean,\n ): void => {\n for (const day of days) {\n if (value === \"closed\") {\n if (!explicit) {\n // \"closed\" with no days of its own: only the days nothing has stated\n if (week[day] === undefined) {\n week[day] = [];\n closedBy.set(day, -1);\n }\n continue;\n }\n if ((openedBy.get(day) ?? -1) > spec) continue;\n week[day] = [];\n openedBy.delete(day);\n closedBy.set(day, Math.max(closedBy.get(day) ?? -1, spec));\n continue;\n }\n const closed = closedBy.get(day);\n // a closed day stays closed, unless the opening names it more closely\n if (closed !== undefined && closed >= spec) continue;\n if (closed !== undefined) {\n week[day] = [];\n closedBy.delete(day);\n }\n addInterval(week, [day], value);\n openedBy.set(day, Math.max(openedBy.get(day) ?? -1, spec));\n }\n };\n const valueOf = (\n token: Token,\n ): { value: Interval | \"closed\"; inferred: boolean } | undefined => {\n if (token.groups[\"closed\"] !== undefined) return { value: \"closed\", inferred: false };\n if (token.groups[\"allday\"] !== undefined) return { value: ALL_DAY, inferred: false };\n const parts =\n token.groups[\"times\"] === undefined ? undefined : splitRange(token.groups[\"times\"]);\n const range = parts === undefined ? undefined : readRange(parts[0], parts[1]);\n return range === undefined ? undefined : { value: range.interval, inferred: range.inferred };\n };\n /** This function gives the tokens of one kind in sequence from `start`, each on a separate line. */\n const runOf = (start: number, kind: (token: Token) => boolean): number => {\n let end = start;\n while (\n tokens[end + 1] !== undefined &&\n kind(tokens[end + 1]!) &&\n tokens[end + 1]!.line !== tokens[end]!.line &&\n !breaks.has(tokens[end + 1]!.line - 1)\n ) {\n end += 1;\n }\n return end;\n };\n\n let currentDays: Day[] = [];\n let currentSpec = 2;\n let lastWasDay = false;\n let pending: Array<{ value: Interval | \"closed\"; inferred: boolean }> = [];\n let excepting = false;\n let previous: Token | undefined;\n for (let at = 0; at < tokens.length; at += 1) {\n const token = tokens[at]!;\n if (previous !== undefined && token.line !== previous.line) {\n excepting = false;\n let broken = dangling.has(previous.line) && lastWasDay;\n for (let line = previous.line; line < token.line && !broken; line += 1)\n broken = breaks.has(line);\n if (broken) {\n currentDays = [];\n lastWasDay = false;\n pending = [];\n }\n }\n previous = token;\n if (token.groups[\"except\"] !== undefined) {\n excepting = true;\n continue;\n }\n const days = token.days;\n if (days !== undefined) {\n if (excepting) {\n // \"daily except Monday\": Monday is closed, and the days to come leave it out\n apply(\"closed\", days, 2, true);\n currentDays = currentDays.filter((day) => !days.includes(day));\n continue;\n }\n if (!lastWasDay && pending.length === 0) {\n // a column of days beside a column of hours (Sunday to\n // Saturday, then seven ranges): the n-th range is the n-th day's.\n // Columns of different lengths say nothing certain, so nothing is read\n const lastDay = runOf(at, isDays);\n const firstValue = tokens[lastDay + 1];\n if (\n lastDay > at &&\n firstValue !== undefined &&\n isValue(firstValue) &&\n firstValue.line !== tokens[lastDay]!.line\n ) {\n const lastValue = runOf(lastDay + 1, isValue);\n const dayRun = tokens.slice(at, lastDay + 1);\n const valueRun = tokens.slice(lastDay + 1, lastValue + 1);\n if (valueRun.length >= 2) {\n if (valueRun.length === dayRun.length) {\n dayRun.forEach((dayToken, index) => {\n const read = valueOf(valueRun[index]!);\n if (read === undefined) return;\n inferred ||= read.inferred;\n apply(read.value, dayToken.days!, dayToken.spec, true);\n });\n }\n at = lastValue;\n previous = tokens[at];\n continue;\n }\n }\n }\n currentDays = lastWasDay ? [...new Set([...currentDays, ...days])] : [...days];\n currentSpec = lastWasDay ? Math.min(currentSpec, token.spec) : token.spec;\n lastWasDay = true;\n const next = tokens[at + 1];\n // the list of days ends where the next token is not a day on this line\n if (pending.length > 0 && !(isDays(next) && next!.line === token.line)) {\n for (const read of pending) {\n inferred ||= read.inferred;\n apply(read.value, currentDays, currentSpec, true);\n }\n pending = [];\n // the list is used: the next line's days start a new one\n lastWasDay = false;\n }\n continue;\n }\n excepting = false;\n const wasDay = lastWasDay;\n lastWasDay = false;\n let read = isValue(token) ? valueOf(token) : undefined;\n if (read === undefined && token.groups[\"clock\"] !== undefined && wasDay) {\n // a widget that writes a day, its opening and its closing on three\n // lines; a lone time anywhere else (a booking slot) is no schedule\n const close = tokens[at + 1]?.groups[\"clock\"];\n const range = close === undefined ? undefined : readRange(token.groups[\"clock\"], close);\n if (range !== undefined) {\n read = { value: range.interval, inferred: range.inferred };\n at += 1;\n previous = tokens[at];\n }\n }\n if (read === undefined) continue;\n const next = tokens[at + 1];\n const daysFollow = isDays(next) && next!.line === tokens[at]!.line;\n // a value before its days, or \"..., closed Fri\" after a schedule\n if (daysFollow && (valueFirst.has(token.line) || (read.value === \"closed\" && !wasDay))) {\n pending.push(read);\n continue;\n }\n if (currentDays.length > 0) {\n inferred ||= read.inferred;\n apply(read.value, currentDays, currentSpec, wasDay);\n }\n }\n // one range under an hours heading, or after \"Open\", with no day named\n // (\"HOURS OF OPERATION: 7:00am - 10:30pm\", \"Open 7am-10:30pm\") is every\n // day, and says it was a guess; two ranges with no days (mini golf 7-10:30,\n // batting cages 9-10) say nothing certain\n if (Object.keys(week).length === 0 && !tokens.some(isDays)) {\n const ranges = tokens.filter(\n (token) => token.groups[\"times\"] !== undefined || token.groups[\"allday\"] !== undefined,\n );\n const body = current.lines.map((index) => lines[index]!);\n // \"Today's Hours\" are one day's (\"Now open\" is an exhibition's)\n const oneDay = /\\b(today|tonight|now)\\b/i.test(`${above} ${body.join(\" \")}`);\n const opens = !oneDay && (heading || /^open\\b/i.test(body[0] ?? \"\"));\n const said = new Set(\n ranges.map((token) => (token.groups[\"times\"] ?? \"24h\").replace(/\\s+/g, \"\")),\n );\n const first = ranges[0] === undefined ? undefined : valueOf(ranges[0]);\n if (opens && said.size === 1 && first !== undefined && first.value !== \"closed\") {\n addInterval(week, DAYS, first.value);\n // \"Open 24/7\" says the days itself\n inferred = !/24 ?\\/ ?7/.test(ranges[0]!.groups[\"allday\"] ?? \"\");\n }\n }\n if (Object.keys(week).length === 0) return;\n const season = current.season ?? (label !== undefined ? seasonOf(above) : undefined);\n out.push({\n week,\n from: \"text\",\n raw: current.lines.map((index) => original[index]!).join(\"\\n\"),\n inferred,\n ...(partial ? { partial } : {}),\n ...(season !== undefined ? { season } : {}),\n label: label !== undefined && label.length > 0 && label.length <= 40 ? label : null,\n });\n };\n lines.forEach((line, index) => {\n const tokens = tokensOf(line, 0);\n const names = tokens.some(\n (token) =>\n token.days !== undefined ||\n token.groups[\"closed\"] !== undefined ||\n token.groups[\"allday\"] !== undefined,\n );\n // a clock with am/pm or minutes reads as hours on its own line; a bare\n // range (\"12-14 Main St\") only beside a day\n const clock = tokens.some(\n (token) =>\n token.groups[\"clock\"] !== undefined ||\n (token.groups[\"times\"] !== undefined && isStrong(token.groups[\"times\"])),\n );\n // a long sentence that happens to mention a day is prose, not a schedule;\n // a long line with several time ranges is a schedule written as one\n // paragraph (\"Dinner: Tuesday, Wednesday and Thursday 5pm to 9pm / ...\")\n const ranges = tokens.filter((token) => token.groups[\"times\"] !== undefined).length;\n if ((names || clock) && (line.length <= 160 || ranges >= 2)) {\n // a season starts a schedule of its own (\"Summer: Mon-Sun 10am-8pm\")\n const season = seasonOf(line);\n const cut =\n block !== undefined && (season !== undefined || block.lines.length >= MAX_BLOCK_LINES);\n if (cut) flush();\n block ??= { start: index, lines: [], season, cut };\n block.lines.push(index);\n } else {\n flush();\n }\n });\n flush();\n return out;\n};\n\n// ── what the place keeps ────────────────────────────────────────────────────\n\nconst LABEL: Readonly<Record<Day, string>> = {\n mon: \"Mon\",\n tue: \"Tue\",\n wed: \"Wed\",\n thu: \"Thu\",\n fri: \"Fri\",\n sat: \"Sat\",\n sun: \"Sun\",\n};\n\nconst sameDay = (a: readonly Interval[] | undefined, b: readonly Interval[] | undefined): boolean =>\n JSON.stringify(a) === JSON.stringify(b);\n\n/** This function writes a week as text, for example `Mon–Thu 11:00–22:00, Fri–Sat 11:00–23:00`. */\nexport const describeWeek = (week: Week): string => {\n const parts: string[] = [];\n let index = 0;\n while (index < 7) {\n const day = DAYS[index]!;\n const value = week[day];\n let end = index;\n while (end + 1 < 7 && sameDay(week[DAYS[end + 1]!], value)) end += 1;\n const span = end === index ? LABEL[day] : `${LABEL[day]}–${LABEL[DAYS[end]!]}`;\n if (value !== undefined) {\n parts.push(\n value.length === 0\n ? `${span} closed`\n : `${span} ${value\n .map(([open, close]) =>\n open === \"00:00\" && close === \"00:00\" ? \"open 24 hours\" : `${open}–${close}`,\n )\n .join(\", \")}`,\n );\n }\n index = end + 1;\n }\n return parts.join(\"; \");\n};\n\n/**\n * This function gives one interval for each day (`hours_week`), and null for a closed day. One\n * interval cannot show a day with two intervals and a gap between them. Thus such a day is not in\n * `hours_week`, and `split` names it.\n */\nexport const toHoursWeek = (\n week: Week,\n): {\n readonly hours_week: Partial<Record<Day, readonly [string, string] | null>>;\n readonly split: readonly Day[];\n} => {\n const hoursWeek: Partial<Record<Day, readonly [string, string] | null>> = {};\n const split: Day[] = [];\n for (const day of DAYS) {\n const value = week[day];\n if (value === undefined) continue;\n if (value.length === 0) hoursWeek[day] = null;\n else if (value.length === 1) hoursWeek[day] = value[0]!;\n else split.push(day);\n }\n return { hours_week: hoursWeek, split };\n};\n\n/** This function gives the days where two readings disagree, from the days that the two readings state. */\nexport const disagreements = (a: Week, b: Week): Day[] =>\n DAYS.filter((day) => a[day] !== undefined && b[day] !== undefined && !sameDay(a[day], b[day]));\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"hours.mjs","names":[],"sources":["../src/hours.ts"],"sourcesContent":["/**\n * The hours reader reads opening hours from the three places where they occur:\n *\n * - schema.org JSON-LD: the `openingHours` strings or `openingHoursSpecification`.\n * - The `opening_hours` tag of OpenStreetMap, in its common subset. It does not guess a rule\n * that it cannot fully read (holidays, months, sunrise). It leaves that rule out and says so.\n * - The text of the page: \"Mon–Thu 11am–10pm, Fri–Sat 11am–1am\", \"Lunch Mon-Fri 11:30-2:30 /\n * Dinner 5-10\", \"Sunday: Closed\". The text reader reads English.\n *\n * A day can have more than one interval (lunch and dinner). A day that is not in a week is\n * unknown, not closed. When a time has no am or pm, the parser guesses it and the result says so\n * (`inferred`). Thus a guess does not look like a fact. A guess is not longer than 16 hours.\n *\n * The time of each reader grows in proportion to the length of its input.\n */\n\nexport type Day = \"mon\" | \"tue\" | \"wed\" | \"thu\" | \"fri\" | \"sat\" | \"sun\";\nexport const DAYS: readonly Day[] = [\"mon\", \"tue\", \"wed\", \"thu\", \"fri\", \"sat\", \"sun\"];\n\n/**\n * An interval is [open, close] as HH:MM. A close at or before the open is after midnight, and\n * [\"00:00\", \"00:00\"] is open 24 hours.\n */\nexport type Interval = readonly [string, string];\n/** A week gives the intervals of each day, and [] for a closed day. A day that is not there is unknown. */\nexport type Week = Partial<Record<Day, readonly Interval[]>>;\n\nexport interface Hours {\n readonly week: Week;\n readonly from: \"jsonld\" | \"osm\" | \"text\";\n /** This text is the words that the hours came from. */\n readonly raw: string;\n /** This value is true when the parser guessed an am or a pm (\"11-9\"). */\n readonly inferred: boolean;\n /**\n * This value is true when the reader left out a rule or a line. The rule is not about the week (a\n * holiday, a date) or the reader cannot read it (\"sunrise\").\n */\n readonly partial?: boolean;\n /** This text names the season or the dates of the hours, for example \"Summer\" or \"2026-06-01/2026-08-31\". */\n readonly season?: string;\n}\n\nconst DAY_WORDS: Readonly<Record<string, Day>> = {\n m: \"mon\",\n mo: \"mon\",\n mon: \"mon\",\n monday: \"mon\",\n mondays: \"mon\",\n tu: \"tue\",\n tue: \"tue\",\n tues: \"tue\",\n tuesday: \"tue\",\n tuesdays: \"tue\",\n w: \"wed\",\n we: \"wed\",\n wed: \"wed\",\n weds: \"wed\",\n wednesday: \"wed\",\n wednesdays: \"wed\",\n th: \"thu\",\n thu: \"thu\",\n thur: \"thu\",\n thurs: \"thu\",\n thursday: \"thu\",\n thursdays: \"thu\",\n f: \"fri\",\n fr: \"fri\",\n fri: \"fri\",\n friday: \"fri\",\n fridays: \"fri\",\n sa: \"sat\",\n sat: \"sat\",\n saturday: \"sat\",\n saturdays: \"sat\",\n su: \"sun\",\n sun: \"sun\",\n sunday: \"sun\",\n sundays: \"sun\",\n};\n\nconst dayOf = (word: string): Day | undefined => {\n const clean = word.toLowerCase().replace(/\\.$/, \"\");\n return DAY_WORDS[clean] ?? DAY_WORDS[clean.replace(/s$/, \"\")] ?? DAY_WORDS[clean.slice(0, 3)];\n};\n\n/** This function gives the days from one day to another, with the two days. It continues past Sunday (Fri-Mon). */\nexport const dayRange = (from: Day, to: Day): Day[] => {\n const out: Day[] = [];\n for (let index = DAYS.indexOf(from); ; index = (index + 1) % 7) {\n out.push(DAYS[index]!);\n if (DAYS[index] === to || out.length === 7) return out;\n }\n};\n\nconst pad = (value: number): string => String(value).padStart(2, \"0\");\nconst hhmm = (hour: number, minute: number): string => `${pad(hour % 24)}:${pad(minute)}`;\n\nconst minutesOf = (clock: string): number =>\n Number(clock.slice(0, 2)) * 60 + Number(clock.slice(3, 5));\n/** This function gives the minutes from the start of the day. A close at or before the open is on the next day (\"17:00-02:00\" ends at 26:00). */\nconst spanOf = ([open, close]: Interval): readonly [number, number] => {\n const start = minutesOf(open);\n const end = minutesOf(close);\n return [start, end <= start ? end + 1440 : end];\n};\nconst fromMinutes = (minutes: number): string => hhmm(Math.floor(minutes / 60), minutes % 60);\n/** This function gives the length of an interval in minutes: 1440 for open 24 hours. */\nconst lengthOf = (open: number, close: number): number => {\n const length = (close - open + 1440) % 1440;\n return length === 0 ? 1440 : length;\n};\n\nconst ALL_DAY: Interval = [\"00:00\", \"00:00\"];\n/** No real day has more intervals than this. More is a list of something else, and the reader stops. */\nconst MAX_INTERVALS = 8;\n\nconst addInterval = (\n week: Record<string, Interval[]>,\n days: readonly Day[],\n interval: Interval,\n): void => {\n for (const day of days) {\n let list = week[day] ?? [];\n if (list.length >= MAX_INTERVALS) continue;\n if (!list.some(([open, close]) => open === interval[0] && close === interval[1])) {\n // intervals that overlap are one opening (\"open 6:30-3, delivery 8-2\"\n // is open 6:30-3; \"11-10:15pm, 11-1am\" is open 11-1am); lunch and\n // dinner with a gap stay two\n const [start, end] = spanOf(interval);\n const overlaps = (other: Interval): boolean => {\n const [otherStart, otherEnd] = spanOf(other);\n return otherStart <= end && start <= otherEnd;\n };\n const [first, last] = list.filter(overlaps).reduce<readonly [number, number]>(\n ([low, high], other) => {\n const [otherStart, otherEnd] = spanOf(other);\n return [Math.min(low, otherStart), Math.max(high, otherEnd)];\n },\n [start, end],\n );\n // a union of a whole day or more is open around the clock\n const merged: Interval = [\n fromMinutes(first),\n fromMinutes(last - first >= 1440 ? first : last),\n ];\n list = [...list.filter((other) => !overlaps(other)), merged];\n }\n list.sort((a, b) => a[0].localeCompare(b[0]));\n week[day] = list;\n }\n};\n\n/** This function closes the days that nothing has stated. */\nconst closeDays = (week: Record<string, Interval[]>, days: readonly Day[]): void => {\n for (const day of days) if (week[day] === undefined) week[day] = [];\n};\n\n/** These words name a month. */\nconst MONTH =\n \"(?:jan(?:uary)?|feb(?:ruary)?|mar(?:ch)?|apr(?:il)?|may|june?|july?|aug(?:ust)?|sep(?:t(?:ember)?)?|oct(?:ober)?|nov(?:ember)?|dec(?:ember)?)\\\\.?\";\n\n// ── the OSM / schema.org compact syntax ─────────────────────────────────────\n\nconst OSM_DAYS = /^(Mo|Tu|We|Th|Fr|Sa|Su)(?:-(Mo|Tu|We|Th|Fr|Sa|Su))?$/i;\nconst NAMED_DAYS = /^([a-z]{2,9})(?:-([a-z]{2,9}))?$/i;\nconst OSM_TIME = /^(\\d{1,2}):(\\d{2})-(\\d{1,2}):(\\d{2})\\+?$/;\n/** A rule for dates, not for a week: months, weeks, a year, an nth weekday, Easter. */\nconst DATED_RULE = new RegExp(`\\\\b${MONTH}(?![a-z])|\\\\bweek\\\\b|\\\\[|\\\\beaster\\\\b|\\\\b\\\\d{4}\\\\b`, \"i\");\nconst MAX_COMPACT_CHARS = 2000;\n\n/** This function reads one OSM interval, \"09:00-17:00\", or gives undefined. A close past 24:00 is on the next day. */\nconst osmInterval = (span: string): Interval | undefined => {\n const time = OSM_TIME.exec(span);\n if (time === null) return undefined;\n const [openHour, openMinute, closeHour, closeMinute] = [time[1], time[2], time[3], time[4]].map(\n Number,\n );\n if (openMinute! > 59 || closeMinute! > 59 || openHour! > 24 || closeHour! > 48) return undefined;\n return [hhmm(openHour!, openMinute!), hhmm(closeHour!, closeMinute!)];\n};\n\n/**\n * This function reads the `opening_hours` notation of OpenStreetMap, for example\n * \"Mo-Th 11:00-23:00; Fr,Sa 11:00-01:00; Su off\". The `openingHours` of schema.org uses the same\n * notation. A later rule replaces an earlier rule for the same days, as OpenStreetMap specifies.\n * The reader leaves out a rule for dates or holidays, and the days of a rule that it cannot read\n * stay unknown. In both cases the result is `partial`.\n */\nexport const fromCompact = (value: string, from: \"osm\" | \"jsonld\" = \"osm\"): Hours | undefined => {\n if (value.length > MAX_COMPACT_CHARS) return undefined;\n const text = value.replace(/\\s+/g, \" \").trim();\n if (text.length === 0) return undefined;\n const week: Record<string, Interval[]> = {};\n if (text === \"24/7\") {\n addInterval(week, DAYS, ALL_DAY);\n return { week, from, raw: value, inferred: false };\n }\n let partial = false;\n let inferred = false;\n // a quoted comment (\"breakfast\", \"kitchen closes at 10\") says nothing a\n // parser can use. \";\" and \"||\" start a rule that replaces what earlier\n // rules said about its days; \",\" after a time starts an additional rule,\n // which adds to them (\"Mo-Fr 11:00-21:00, Sa-Su 12:00-20:00\", a common\n // shape in OSM); a day after a time starts a rule too (\"Mo-Fr 09:00-17:00 Sa 10:00-14:00\")\n const rules = text\n .replace(/\"[^\"]*\"/g, \" \")\n .replace(/(\\d) (?=(?:Mo|Tu|We|Th|Fr|Sa|Su|PH|SH)\\b)/gi, \"$1; \")\n .split(/ ?(?:;|\\|\\|) ?/)\n .flatMap((rule) =>\n rule\n .split(/(?<=\\d:\\d{2}\\+?) ?, ?(?=(?:Mo|Tu|We|Th|Fr|Sa|Su|PH|SH)\\b)/i)\n .map((part, index) => ({ part: part.trim(), additional: index > 0 })),\n )\n .filter(({ part }) => part.length > 0);\n let read = false;\n for (const { part: rule, additional } of rules) {\n if (DATED_RULE.test(rule)) {\n // an exception for dates (\"Dec 25 off\") or a season is not the week\n partial = true;\n continue;\n }\n // the days (\"Mo-Fr\", \"Tu,Th\", \"Monday-Friday\", \", Tu\"), then the times\n const parts =\n /^[\\s,]*([A-Za-z]{1,9}(?:-[A-Za-z]{1,9})?(?: ?, ?[A-Za-z]{1,9}(?:-[A-Za-z]{1,9})?)*)? ?:? ?(.*)$/s.exec(\n rule,\n );\n let dayPart = (parts?.[1] ?? \"\").replace(/\\s/g, \"\");\n let timePart = (parts?.[2] ?? \"\").trim();\n if (timePart.length === 0 && /^(off|closed|open)$/i.test(dayPart)) {\n timePart = dayPart;\n dayPart = \"\";\n }\n let days: Day[] = [...DAYS];\n if (dayPart.length > 0) {\n days = [];\n let holidays = false;\n let unreadable = false;\n // empty pieces too: Squarespace writes \", Tu 16:00-21:00, ... Sa 16:00-21:00, \"\n for (const piece of dayPart.split(\",\").filter((part) => part.length > 0)) {\n // public and school holidays are not part of a week: \"PH off\" says\n // nothing about a Tuesday, and \"Su,PH 11:00-18:00\" still says Sunday\n if (/^(PH|SH)$/i.test(piece)) {\n holidays = true;\n continue;\n }\n const range = OSM_DAYS.exec(piece) ?? NAMED_DAYS.exec(piece);\n const first = range === null ? undefined : dayOf(range[1]!);\n const last = range === null ? undefined : dayOf(range[2] ?? range[1]!);\n if (first === undefined || last === undefined) {\n unreadable = true;\n break;\n }\n days.push(...dayRange(first, last));\n }\n if (unreadable) {\n partial = true;\n continue;\n }\n if (days.length === 0) {\n if (holidays) partial = true;\n continue;\n }\n }\n if (timePart.length === 0 || /^open$/i.test(timePart)) {\n partial = true;\n continue;\n }\n // a later rule replaces what earlier ones said about these days\n if (!additional) for (const day of days) delete week[day];\n if (/^(off|closed)$/i.test(timePart)) {\n // an additional \"off\" closes its days too (\"Mo-Fr 09:00-17:00, We off\")\n for (const day of days) delete week[day];\n closeDays(week, days);\n read = true;\n continue;\n }\n const intervals: Interval[] = [];\n for (const span of timePart.split(/ ?, ?/).filter((part) => part.length > 0)) {\n const interval = osmInterval(span);\n if (interval !== undefined) {\n intervals.push(interval);\n continue;\n }\n // \"9am-5pm\" in a field that wants \"09:00-17:00\"\n const parts = splitRange(span);\n const range = parts === undefined ? undefined : readRange(parts[0], parts[1]);\n if (range === undefined) {\n intervals.length = 0;\n break;\n }\n inferred ||= range.inferred;\n intervals.push(range.interval);\n }\n if (intervals.length === 0) {\n // \"sunrise-sunset\", \"25:99\": these days stay unknown, never guessed\n for (const day of days) delete week[day];\n partial = true;\n continue;\n }\n for (const interval of intervals) addInterval(week, days, interval);\n read = true;\n }\n if (!read || Object.keys(week).length === 0) return undefined;\n return { week, from, raw: value, inferred, ...(partial ? { partial } : {}) };\n};\n\n// ── schema.org JSON-LD ──────────────────────────────────────────────────────\n\nconst SCHEMA_DAY = /(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)$/i;\n\n/** This function reads a schema.org time: \"17:00\", \"17:00:00\", \"17:00:00-05:00\" or \"5:00 PM\". */\nconst clockOf = (\n value: unknown,\n): { readonly minutes: number; readonly raw: string } | undefined => {\n if (typeof value !== \"string\") return undefined;\n const raw = value.trim().slice(0, 40);\n const match =\n /^(\\d{1,2})(?::(\\d{2}))?(?::\\d{2}(?:\\.\\d+)?)?\\s*(a\\.?m\\.?|p\\.?m\\.?)?\\s*(?:z|[+-]\\d{2}:?\\d{2})?$/i.exec(\n raw,\n );\n if (match === null) return undefined;\n let hour = Number(match[1]);\n const minute = Number(match[2] ?? 0);\n if (minute > 59 || hour > 24 || (hour === 24 && minute > 0)) return undefined;\n const meridiem =\n match[3]?.toLowerCase().startsWith(\"p\") === true\n ? \"pm\"\n : match[3] === undefined\n ? undefined\n : \"am\";\n if (meridiem !== undefined) {\n if (hour === 0 || hour > 12) return undefined;\n hour = meridiem === \"am\" ? hour % 12 : (hour % 12) + 12;\n }\n return { minutes: hour * 60 + minute, raw };\n};\n\n/** This function gives the days of a `dayOfWeek`: a name, a URL, an `@id` or a short name. */\nconst daysOfSpec = (raw: unknown): { readonly days: Day[]; readonly holidays: boolean } => {\n const values = Array.isArray(raw) ? raw : [raw];\n const days: Day[] = [];\n let holidays = false;\n for (const value of values) {\n const name =\n typeof value === \"string\"\n ? value\n : value !== null && typeof value === \"object\"\n ? (value as Record<string, unknown>)[\"@id\"]\n : undefined;\n if (typeof name !== \"string\") continue;\n if (/PublicHolidays$/i.test(name.trim())) {\n holidays = true;\n continue;\n }\n const full = SCHEMA_DAY.exec(name.trim());\n const day = full === null ? dayOf(name.trim().replace(/^.*[/#:]/, \"\")) : dayOf(full[1]!);\n if (day !== undefined) days.push(day);\n }\n return { days, holidays };\n};\n\n/** The text of a date of schema.org, or an empty string for a value that is not text. */\nconst dateText = (value: unknown): string => (typeof value === \"string\" ? value : \"\");\n\nconst dateOf = (value: unknown): number | undefined => {\n if (typeof value !== \"string\") return undefined;\n const time = Date.parse(value.trim().slice(0, 40));\n return Number.isNaN(time) ? undefined : time;\n};\n\n/**\n * This function reads the hours of one JSON-LD object, for example a `Restaurant`. It reads the\n * `openingHoursSpecification` first, and the `openingHours` when there is no specification. In\n * schema.org, a closed day has the same opening and closing time, 00:00, and 00:00 to 24:00 is\n * open 24 hours.\n *\n * A specification for less than a week (`validFrom` to `validThrough`) is a holiday: the reader\n * leaves it out. When specifications hold for different seasons, the option `at` picks the\n * season of that date. With no date, the reader keeps the specifications with no season, or else\n * the first season, and it names the season.\n */\nexport const fromSchemaOrg = (\n record: Readonly<Record<string, unknown>>,\n options: { readonly at?: string | Date | undefined } = {},\n): Hours | undefined => {\n const specs = record[\"openingHoursSpecification\"];\n const list = (Array.isArray(specs) ? specs : specs !== undefined && specs !== null ? [specs] : [])\n .slice(0, 200)\n .filter((spec): spec is Record<string, unknown> => spec !== null && typeof spec === \"object\");\n if (list.length > 0) {\n let partial = false;\n const at = options.at === undefined ? undefined : new Date(options.at).getTime();\n const windowed = list.map((entry) => {\n const fromDate = dateOf(entry[\"validFrom\"]);\n const throughDate = dateOf(entry[\"validThrough\"]);\n const key =\n fromDate === undefined && throughDate === undefined\n ? undefined\n : `${dateText(entry[\"validFrom\"])}/${dateText(entry[\"validThrough\"])}`;\n const short =\n fromDate !== undefined &&\n throughDate !== undefined &&\n throughDate - fromDate < 7 * 86_400_000;\n return { entry, key, fromDate, throughDate, short };\n });\n if (windowed.some(({ short }) => short)) partial = true;\n const seasonal = windowed.filter(({ short }) => !short);\n let season: string | undefined;\n let chosen = seasonal.filter(({ key }) => key === undefined);\n if (at !== undefined && !Number.isNaN(at)) {\n const now = seasonal.filter(\n ({ key, fromDate, throughDate }) =>\n key !== undefined &&\n (fromDate ?? -Infinity) <= at &&\n at <= (throughDate ?? Infinity) + 86_399_999,\n );\n if (now.length > 0) {\n season = now[0]!.key;\n chosen = [...chosen, ...now.filter(({ key }) => key === season)];\n }\n } else if (chosen.length === 0) {\n season = seasonal.find(({ key }) => key !== undefined)?.key;\n chosen = seasonal.filter(({ key }) => key === season);\n }\n if (seasonal.some(({ key }) => key !== undefined && key !== season)) partial = true;\n const week: Record<string, Interval[]> = {};\n for (const { entry } of chosen) {\n const { days, holidays } = daysOfSpec(entry[\"dayOfWeek\"]);\n if (holidays) partial = true;\n const opens = clockOf(entry[\"opens\"]);\n const closes = clockOf(entry[\"closes\"]);\n if (days.length === 0 || opens === undefined || closes === undefined) {\n if (days.length > 0) partial = true;\n continue;\n }\n const allDay =\n opens.minutes === 0 && (closes.raw.startsWith(\"24:00\") || closes.minutes === 23 * 60 + 59);\n if (allDay) {\n addInterval(week, days, ALL_DAY);\n } else if (opens.minutes === closes.minutes) {\n // 00:00 to 00:00 is closed; 09:00 to 09:00 says nothing certain\n if (opens.minutes === 0) {\n for (const day of days) delete week[day];\n closeDays(week, days);\n } else {\n partial = true;\n }\n } else {\n addInterval(week, days, [fromMinutes(opens.minutes), fromMinutes(closes.minutes % 1440)]);\n }\n }\n if (Object.keys(week).length > 0) {\n return {\n week,\n from: \"jsonld\",\n raw: JSON.stringify(list).slice(0, 500),\n inferred: false,\n ...(partial ? { partial } : {}),\n ...(season !== undefined ? { season } : {}),\n };\n }\n }\n const compact = record[\"openingHours\"];\n const strings = (Array.isArray(compact) ? compact : [compact])\n .filter((value): value is string => typeof value === \"string\")\n .slice(0, 50);\n if (strings.length === 0) return undefined;\n // schema.org writes one rule per string; a comma inside one is a day list\n // (\"Tu,Th 16:00-20:00\") or, after a time, another rule, as in OSM\n const joined = strings.join(\"; \");\n const parsed = fromCompact(joined, \"jsonld\") ?? textFallback(joined);\n return parsed === undefined ? undefined : { ...parsed, raw: strings.join(\", \") };\n};\n\n/** This function reads an `openingHours` value that is not in the compact notation (\"Monday-Friday 9am-5pm\"). */\nconst textFallback = (value: string): Hours | undefined => {\n const [found] = fromText(value.replace(/;/g, \"\\n\"));\n if (found === undefined) return undefined;\n const { label: _label, ...hours } = found;\n return { ...hours, from: \"jsonld\" };\n};\n\n// ── free text ───────────────────────────────────────────────────────────────\n\nconst DAY_WORD =\n \"(?:mon(?:day)?s?|tue(?:s(?:day)?)?s?|wed(?:s|nesday)?s?|thu(?:r(?:s(?:day)?)?)?s?|fri(?:day)?s?|sat(?:urday)?s?|sun(?:day)?s?)\\\\.?\";\n/** These short day names count only in a span with a dash (\"M-F\", \"Tu-Th\", \"Mo-Fr\"). */\nconst SHORT_DAY = \"(?:mo|tu|we|th|fr|sa|su|m|w|f)\";\n/** A dash of any kind joins a range. U+FFFD is a dash that a wrong decoder lost. */\nconst DASH = \"[-\\\\u2010-\\\\u2015\\\\u2212~\\\\u301c\\\\ufffd]\";\nconst RANGE_SEP = `\\\\s?(?:${DASH}|to|thru|through|until|till|['’]til|til)\\\\s?`;\nconst TIME =\n \"(?:12\\\\s?(?:midnight|noon|mn|nn)\\\\b|\\\\d{1,2}(?:[:.]\\\\d{2}|h(?:\\\\d{2})?(?![a-z]))?(?:\\\\s?(?:a\\\\.?m\\\\.?|p\\\\.?m\\\\.?))?|noon|midnight)\";\n/** This pattern is one time with am or pm: \"7:00 am\". */\nconst CLOCK =\n \"(?:12\\\\s?(?:midnight|noon)\\\\b|\\\\d{1,2}(?:[:.]\\\\d{2})?\\\\s?(?:a\\\\.?m\\\\.?|p\\\\.?m\\\\.?)|noon|midnight)\";\n/** \"Open 24 hours\", \"24/7\": open all day. \"Within 24 hours\" and \"24 hours notice\" are not. */\nconst ALL_DAY_WORDS =\n \"(?:open\\\\s)?(?<!(?:within|in|for|last|past|next|every|after|than)\\\\s)(?:24\\\\s?(?:hours|hrs?|h)\\\\b(?!\\\\s?(?:notice|in advance|advance|prior|before|ahead|later|turnaround|delivery|of|to|from))|24\\\\s?\\\\/\\\\s?7\\\\b|around the clock)\";\n\nconst TOKEN = new RegExp(\n [\n `(?<allday>\\\\b${ALL_DAY_WORDS})`,\n `(?<every>\\\\b(?:daily|every\\\\s?day|everyday|7\\\\s?days(?:\\\\s?a\\\\s?week)?|seven\\\\s?days(?:\\\\s?a\\\\s?week)?)\\\\b)`,\n `(?<weekdays>\\\\bweekdays?\\\\b)`,\n `(?<weekends>\\\\bweekends?\\\\b)`,\n // \"except Monday\", \"but Sunday\": the days after it are closed\n `(?<except>\\\\b(?:except(?:\\\\s(?:on|for))?|but(?:\\\\snot)?(?:\\\\son)?|excluding|other\\\\sthan|apart\\\\sfrom)(?=\\\\s(?:${DAY_WORD}|weekends?|weekdays?)(?![a-z])))`,\n `(?<span>\\\\b(?:${DAY_WORD}${RANGE_SEP}${DAY_WORD}|(?:${DAY_WORD}|${SHORT_DAY})\\\\s?${DASH}\\\\s?(?:${DAY_WORD}|${SHORT_DAY}))(?![a-z]))`,\n // \"Sun-dried tomato\" and \"Sunday's game\" name no day\n `(?<day>\\\\b${DAY_WORD}(?![a-z])(?!-(?!closed)[a-z])(?!['’]s\\\\b))`,\n `(?<times>(?<![\\\\d:.$€£¥])${TIME}${RANGE_SEP}${TIME}(?!\\\\d))`,\n // a meridiem ends its word: \"73 Ames St\" is no clock\n `(?<clock>(?<![\\\\d:.$€£¥])${CLOCK}(?![a-z\\\\d]))`,\n `(?<closed>\\\\bclosed\\\\b)`,\n ].join(\"|\"),\n \"gi\",\n);\n\ninterface Clock {\n readonly hour: number;\n readonly minute: number;\n readonly meridiem: \"am\" | \"pm\" | undefined;\n /** This value is true when the time uses the 24-hour clock (\"17:00\", \"09:30\", \"9h\"). */\n readonly h24: boolean;\n}\n\nconst readClock = (value: string): Clock | undefined => {\n const text = value.trim().toLowerCase();\n if (/^(?:12\\s?)?(?:noon|nn)$/.test(text))\n return { hour: 12, minute: 0, meridiem: \"pm\", h24: false };\n if (/^(?:12\\s?)?(?:midnight|mn)$/.test(text)) {\n return { hour: 12, minute: 0, meridiem: \"am\", h24: false };\n }\n const match = /^(\\d{1,2})(?:[:.](\\d{2})|(h)(\\d{2})?)?\\s?(a\\.?m\\.?|p\\.?m\\.?)?$/.exec(text);\n if (match === null) return undefined;\n const hour = Number(match[1]);\n const minute = Number(match[2] ?? match[4] ?? 0);\n if (hour > 24 || minute > 59 || (hour === 24 && minute > 0)) return undefined;\n let meridiem: \"am\" | \"pm\" | undefined =\n match[5] === undefined ? undefined : match[5].startsWith(\"a\") ? \"am\" : \"pm\";\n // \"17:00 pm\" is written in haste: the clock says it all\n if (meridiem !== undefined && (hour === 0 || hour > 12)) meridiem = undefined;\n return {\n hour,\n minute,\n meridiem,\n h24:\n meridiem === undefined &&\n (hour > 12 || hour === 0 || match[1]!.startsWith(\"0\") || match[3] !== undefined),\n };\n};\n\nconst to24 = (clock: Clock, meridiem: \"am\" | \"pm\"): number =>\n meridiem === \"am\"\n ? clock.hour === 12\n ? 0\n : clock.hour\n : clock.hour === 12\n ? 12\n : clock.hour + 12;\n\nconst flip = (meridiem: \"am\" | \"pm\"): \"am\" | \"pm\" => (meridiem === \"am\" ? \"pm\" : \"am\");\n\n/** The longest opening that a guess can give, in minutes. */\nconst MAX_GUESS_MINUTES = 16 * 60;\n\n/** This function reads a range with a 24-hour time. A bare number on the other side is the reading that makes sense. */\nconst read24 = (\n a: Clock,\n b: Clock,\n): { readonly interval: Interval; readonly inferred: boolean } | undefined => {\n const options = (clock: Clock): number[] =>\n clock.h24 || clock.meridiem !== undefined\n ? [clock.meridiem === undefined ? clock.hour % 24 : to24(clock, clock.meridiem)]\n : clock.hour === 12\n ? [12, 0]\n : [clock.hour, clock.hour + 12];\n const candidates: Array<{ open: number; close: number; length: number; written: boolean }> = [];\n for (const [aIndex, openHour] of options(a).entries()) {\n for (const [bIndex, closeHour] of options(b).entries()) {\n const open = openHour * 60 + a.minute;\n const close = closeHour * 60 + b.minute;\n candidates.push({\n open,\n close,\n length: lengthOf(open, close),\n written: aIndex === 0 && bIndex === 0,\n });\n }\n }\n const stated = a.h24 && b.h24;\n if (stated) {\n const [only] = candidates;\n // \"21-14\" is a score, not a night: an opening past midnight ends in the morning\n if (only === undefined || (only.close <= only.open && only.close > 12 * 60)) return undefined;\n return { interval: [fromMinutes(only.open), fromMinutes(only.close)], inferred: false };\n }\n // the reading as written first, then: within the day and three hours or\n // more, three hours or more, within the day, anything sensible\n const sensible = candidates\n .filter(({ length }) => length >= 60 && length <= MAX_GUESS_MINUTES)\n .sort((x, y) => Number(y.written) - Number(x.written));\n const best =\n sensible.find(({ close, open, length }) => close > open && length >= 180) ??\n sensible.find(({ length }) => length >= 180) ??\n sensible.find(({ close, open }) => close > open) ??\n sensible[0];\n if (best === undefined) return undefined;\n return { interval: [fromMinutes(best.open), fromMinutes(best.close)], inferred: !best.written };\n};\n\n/**\n * This function reads a range of two times, as a restaurant writes it:\n *\n * - \"5-10pm\" is 17:00-22:00, and \"11-3pm\" is 11:00-15:00. The opening gets the am or pm of the\n * close, but not when that puts the opening after the close.\n * - \"11-1am\" is 11:00-01:00, because 23:00-01:00 is too short to be the meaning. The result is\n * then `inferred`.\n * - \"11am-10\" closes at 22:00.\n * - With no am, pm or 24-hour time (\"11-9\"), an opening before 7 is pm, but \"6-2\" is 06:00-14:00.\n * A close at or before the opening is pm, and \"7-12\" closes at noon. The result is then\n * `inferred`, and it is not longer than 16 hours.\n * - \"21-14\" is no range: a 24-hour opening past midnight does not end after noon.\n */\nexport const readRange = (\n start: string,\n end: string,\n): { readonly interval: Interval; readonly inferred: boolean } | undefined => {\n const a = readClock(start);\n const b = readClock(end);\n if (a === undefined || b === undefined) return undefined;\n if (a.h24 || b.h24) return read24(a, b);\n let open: number;\n let close: number;\n let inferred = false;\n if (a.meridiem !== undefined && b.meridiem !== undefined) {\n open = to24(a, a.meridiem);\n close = to24(b, b.meridiem);\n } else if (b.meridiem !== undefined) {\n // the opening shares the close's meridiem unless that puts it after the\n // close: \"5-10pm\" opens at 5pm, \"11-3pm\" at 11am, \"10-2am\" at 10pm\n close = to24(b, b.meridiem);\n const same = to24(a, b.meridiem);\n const other = to24(a, flip(b.meridiem));\n const closeAt = close * 60 + b.minute;\n if (same * 60 + a.minute < closeAt) {\n open = same;\n // \"12-1am\" opens at noon: an hour from midnight is no opening\n if (a.hour === 12 && b.meridiem === \"am\" && lengthOf(same * 60 + a.minute, closeAt) <= 120) {\n open = 12;\n inferred = true;\n }\n } else if (\n lengthOf(other * 60 + a.minute, closeAt) <= 180 &&\n lengthOf(same * 60 + a.minute, closeAt) <= 18 * 60\n ) {\n // \"11-1am\": a bar open from 11am, not for two hours from 11pm\n open = same;\n inferred = true;\n } else {\n open = other;\n }\n } else if (a.meridiem !== undefined) {\n open = to24(a, a.meridiem);\n const same = to24(b, a.meridiem);\n close = same > open ? same : to24(b, flip(a.meridiem));\n } else {\n // no meridiem anywhere: read as a restaurant would mean it, and say so.\n // An opening before 7 is evening (\"5-10\"), but a diner's \"6-2\" is\n // daytime. A close at or before the opening, or within three hours of a\n // morning one (\"10-11\"), is pm; a close of 12 after a morning opening is noon\n inferred = true;\n open =\n a.hour === 12\n ? 12\n : a.hour >= 7 || (a.hour >= 5 && b.hour >= 1 && b.hour <= 3)\n ? a.hour\n : a.hour + 12;\n if (b.hour === 12) close = open < 12 ? 12 : 0;\n else if (open < 12) close = b.hour <= a.hour || b.hour - open < 3 ? b.hour + 12 : b.hour;\n else close = b.hour + 12 > open ? b.hour + 12 : b.hour;\n }\n const openAt = open * 60 + a.minute;\n const closeAt = close * 60 + b.minute;\n if (inferred && lengthOf(openAt, closeAt) > MAX_GUESS_MINUTES) return undefined;\n return { interval: [fromMinutes(openAt % 1440), fromMinutes(closeAt % 1440)], inferred };\n};\n\nconst splitRange = (value: string): [string, string] | undefined => {\n const match = new RegExp(`^(${TIME})${RANGE_SEP}(${TIME})$`, \"i\").exec(\n value.replace(/\\s+/g, \" \").trim().slice(0, 80),\n );\n return match === null ? undefined : [match[1]!, match[2]!];\n};\n\n/** This function gives the days of a token and how specific it is: 0 for \"daily\", 1 for a span, 2 for one day. */\nconst daysOfToken = (\n groups: Record<string, string | undefined>,\n): { readonly days: Day[]; readonly spec: number } | undefined => {\n if (groups[\"every\"] !== undefined) return { days: [...DAYS], spec: 0 };\n if (groups[\"weekdays\"] !== undefined)\n return { days: [\"mon\", \"tue\", \"wed\", \"thu\", \"fri\"], spec: 0 };\n if (groups[\"weekends\"] !== undefined) return { days: [\"sat\", \"sun\"], spec: 0 };\n if (groups[\"span\"] !== undefined) {\n const [from, to] = groups[\"span\"].split(new RegExp(RANGE_SEP, \"i\"));\n const first = from === undefined ? undefined : dayOf(from.trim());\n const last = to === undefined ? undefined : dayOf(to.trim());\n return first === undefined || last === undefined\n ? undefined\n : { days: dayRange(first, last), spec: 1 };\n }\n if (groups[\"day\"] !== undefined) {\n const day = dayOf(groups[\"day\"]);\n return day === undefined ? undefined : { days: [day], spec: 2 };\n }\n return undefined;\n};\n\n/** A date names one day of the year, not a day of the week: \"Dec 24\", \"24 December\", \"12/24\", \"2026-12-24\". */\nconst DATE = new RegExp(\n `\\\\b${MONTH}\\\\s?\\\\d{1,2}(?:st|nd|rd|th)?\\\\b(?!:\\\\d|\\\\s?[ap]\\\\.?m\\\\b)|\\\\b\\\\d{1,2}(?:st|nd|rd|th)?\\\\s(?:of\\\\s)?${MONTH}(?![a-z])|(?<![\\\\d/])\\\\d{1,2}\\\\/\\\\d{1,2}(?:\\\\/\\\\d{2,4})?(?![\\\\d/])|\\\\b\\\\d{4}-\\\\d{2}-\\\\d{2}\\\\b`,\n \"i\",\n);\nconst DATE_RANGE = new RegExp(\n `\\\\b${MONTH}(?:\\\\s?\\\\d{1,2}(?:st|nd|rd|th)?)?${RANGE_SEP}${MONTH}(?:\\\\s?\\\\d{1,2}(?:st|nd|rd|th)?)?(?![a-z])`,\n \"i\",\n);\n/** These words tell that a line is for one occasion, not for the week. */\nconst OCCASION =\n /\\b(?:holidays?|christmas|xmas|thanksgiving|easter|hanukkah|new year['’]?s?|nye|memorial day|labou?r day|independence day|valentine['’]?s|mother['’]?s day|father['’]?s day|private (?:events?|parties|party|hire|dining)|special events?|events? only|today|tonight)\\b/i;\n/**\n * These words name a service or an event beside the opening hours (\"Happy hour 6-8pm\", \"Delivery\n * 6pm-9pm\", \"Live music 9pm-12am\"). Some lines have no day and name a service before their hours.\n * Under a line with days and hours, such a line is not the week. A meal (\"Lunch\", \"Dinner\") or\n * \"Kitchen\" stays a part of the opening hours.\n */\nconst SERVICE =\n /\\b(?:happy hours?|delivery|deliveries|take[- ]?out|take[- ]?away|pick[- ]?up|curbside|drive[- ]?(?:thru|through)|live (?:music|jazz|band)|music|dj|karaoke|trivia|bingo|open mic|brunch|bottomless)\\b/i;\nconst SEASON =\n /^(?:summer|winter|spring|autumn|fall|off[- ]?season|high season|low season|peak season)\\b|\\b(?:summer|winter|spring|autumn|fall|off[- ]?season)(?=\\s?(?:hours|schedule|season|:|\\())/i;\n\n/** This function names the season of a line (\"Summer\", \"June-August\"), or gives undefined. */\nconst seasonOf = (line: string): string | undefined =>\n (SEASON.exec(line) ?? DATE_RANGE.exec(line))?.[0].trim();\n\n/** The longest line that can hold hours. A longer line is prose, and the reader does not read it. */\nconst MAX_LINE = 1000;\n/** A block of hours longer than this is a list of something else. */\nconst MAX_BLOCK_LINES = 100;\n\nconst normalize = (line: string): string =>\n line\n .normalize(\"NFKC\")\n .replace(/[\\u200b-\\u200d\\u2060\\ufeff\\u00ad]/g, \"\")\n .replace(/\\s+/g, \" \")\n .trim();\n\ninterface Token {\n readonly groups: Record<string, string | undefined>;\n /** This is the line of the token in its block. */\n readonly line: number;\n readonly start: number;\n readonly end: number;\n readonly days: Day[] | undefined;\n readonly spec: number;\n}\n\nconst tokensOf = (line: string, at: number): Token[] =>\n line.length > MAX_LINE\n ? []\n : [...line.matchAll(TOKEN)].map((match) => {\n const groups = match.groups ?? {};\n const days = daysOfToken(groups);\n return {\n groups,\n line: at,\n start: match.index ?? 0,\n end: (match.index ?? 0) + match[0].length,\n days: days?.days,\n spec: days?.spec ?? 2,\n };\n });\n\n/** A range with no am, pm, minutes or \"h\" is a bare range (\"5-10\"): it can be a price, an age or a score. */\nconst isStrong = (times: string): boolean => /[ap]\\.?m|[:.]\\d{2}|noon|midnight|\\dh/i.test(times);\n\n/** The words that join days or a day and its hours, and say nothing more. */\nconst JOINERS = /^(?:[\\s,&/:;.()|–—-]|\\band\\b|\\bor\\b|\\bfrom\\b|\\bopen\\b|\\bhours\\b)*$/i;\n\n/**\n * This function reads the hours in the text of a page. It reads the lines with days and times as\n * one stream. Thus \"Monday – Thursday\" on one line and \"11:30am – 9pm\" on the next line go\n * together. A list of days (\"Mon, Wed & Fri\") waits for a time or for \"closed\".\n *\n * Each group of these lines is a candidate. Its label is the short line above it (\"Hours\",\n * \"Dinner\", \"Happy hour\"). A page often shows more than one schedule, and the choice between them\n * is a judgment, not a parse. A season (\"Summer:\", \"June-August\") starts a new candidate.\n *\n * A line about a date or an occasion (\"Dec 24: 9am-2pm\", \"New Year's Eve 8pm-1am\", \"Private events\n * 11am-3pm\") is not the week. A line with no day that names a service before its hours (\"Happy hour\n * 6-8pm\") under a line with days and hours is not the week either. The reader leaves it out, and\n * the candidate is `partial`.\n */\nexport const fromText = (text: string): Array<Hours & { readonly label: string | null }> => {\n const original = text.split(/\\n+/).map((line) => line.trim());\n const lines = original.map((line) => (line.length > MAX_LINE ? line : normalize(line)));\n const out: Array<Hours & { readonly label: string | null }> = [];\n /** A season or the size can cut a block from the block before it. Then the line above the block is not its label. */\n let block:\n { start: number; lines: number[]; season: string | undefined; cut: boolean } | undefined;\n const flush = (): void => {\n if (block === undefined) return;\n const current = block;\n block = undefined;\n const above = current.cut ? \"\" : (lines[current.start - 1] ?? \"\");\n const label = current.cut ? undefined : original[current.start - 1];\n const week: Record<string, Interval[]> = {};\n let inferred = false;\n let partial = false;\n // the most specific selector that opened or closed each day: a closed\n // Monday stays closed under \"daily\", but \"Monday 9-12\" reopens \"Mon-Fri closed\"\n const closedBy = new Map<Day, number>();\n const openedBy = new Map<Day, number>();\n const heading = /hour|open/i.test(above);\n\n const perLine = current.lines.map((index, at) => tokensOf(lines[index]!, at));\n const oneOff = current.lines.map((index, at) => {\n const line = lines[index]!.replace(/\\b24 ?\\/ ?7\\b/g, \" \");\n const onLine = perLine[at]!;\n const weekly = onLine.some((token) => token.days !== undefined);\n // a date before the hours names the day they are for (\"Dec 24: 9am-2pm\");\n // one after them is a note (\"Mon-Fri 9-5 (closed 12/25)\")\n const date = DATE_RANGE.test(line) ? null : DATE.exec(line);\n const value = onLine.find(\n (token) => token.groups[\"times\"] !== undefined || token.groups[\"clock\"] !== undefined,\n );\n // \"Happy hour 6-8pm\" under \"Mon-Fri 9am-5pm\" is not more opening hours of those days; under\n // \"Sat-Sun\" alone (\"Brunch 10am-2pm\") it is the only time those days have\n const above = at > 0 ? perLine[at - 1]! : [];\n const service =\n !weekly &&\n value !== undefined &&\n SERVICE.test(line.slice(0, value.start)) &&\n above.some((token) => token.days !== undefined) &&\n above.some(\n (token) => token.groups[\"times\"] !== undefined || token.groups[\"closed\"] !== undefined,\n );\n return (\n (date !== null && (value === undefined || date.index < value.start)) ||\n (!weekly && OCCASION.test(line)) ||\n service\n );\n });\n const tokens: Token[] = [];\n const breaks = new Set<number>();\n perLine.forEach((onLine, at) => {\n if (oneOff[at]) {\n // a date or an occasion: not the week, and the days above it do not reach past it\n partial = true;\n breaks.add(at);\n return;\n }\n const line = lines[current.lines[at]!]!;\n const dayTokens = onLine.filter((token) => token.days !== undefined);\n const broad =\n dayTokens.length >= 2 ||\n dayTokens.some((token) => token.spec < 2) ||\n heading ||\n /^(?:hours|open)\\b/i.test(line);\n const daysOnlyAbove =\n at > 0 &&\n !breaks.has(at - 1) &&\n perLine[at - 1]!.length > 0 &&\n perLine[at - 1]!.every((token) => token.days !== undefined);\n onLine.forEach((token, index) => {\n const times = token.groups[\"times\"];\n if (times !== undefined && !isStrong(times) && !broad) {\n // a bare range counts only beside its day (\"Sunday 12-9\"), not\n // after other words (\"Kids Club Saturdays, ages 5-10\")\n const before = onLine[index - 1];\n const after = onLine[index + 1];\n const beside =\n (before?.days !== undefined && JOINERS.test(line.slice(before.end, token.start))) ||\n (after?.days !== undefined && JOINERS.test(line.slice(token.end, after.start))) ||\n (index === 0 && daysOnlyAbove && JOINERS.test(line.slice(0, token.start)));\n if (!beside) return;\n }\n tokens.push(token);\n });\n });\n const isDays = (token: Token | undefined): boolean => token?.days !== undefined;\n const isValue = (token: Token): boolean =>\n token.groups[\"times\"] !== undefined ||\n token.groups[\"closed\"] !== undefined ||\n token.groups[\"allday\"] !== undefined;\n // a line whose days are followed by other words (\"Monday: By appointment\")\n // ends its list of days there: the next line's days do not join it\n const dangling = new Set<number>();\n perLine.forEach((onLine, at) => {\n const last = onLine.at(-1);\n if (last?.days === undefined) return;\n const line = lines[current.lines[at]!]!;\n if (!JOINERS.test(line.slice(last.end))) dangling.add(at);\n });\n // a line that starts with a value and names its days after it (\"11am-9pm\n // Mon-Thu, 11am-10pm Fri-Sat\", \"10 AM - 8 PM daily\", \"Closed Mondays\")\n // gives each value to the days that follow it, not the days before\n const valueFirst = new Set<number>();\n const firstOnLine = new Map<number, Token>();\n const lineHasDays = new Set<number>();\n for (const token of tokens) {\n if (isDays(token)) lineHasDays.add(token.line);\n if (!firstOnLine.has(token.line) && (isDays(token) || isValue(token)))\n firstOnLine.set(token.line, token);\n }\n for (const [line, first] of firstOnLine)\n if (isValue(first) && lineHasDays.has(line)) valueFirst.add(line);\n\n const apply = (\n value: Interval | \"closed\",\n days: readonly Day[],\n spec: number,\n explicit: boolean,\n ): void => {\n for (const day of days) {\n if (value === \"closed\") {\n if (!explicit) {\n // \"closed\" with no days of its own: only the days nothing has stated\n if (week[day] === undefined) {\n week[day] = [];\n closedBy.set(day, -1);\n }\n continue;\n }\n if ((openedBy.get(day) ?? -1) > spec) continue;\n week[day] = [];\n openedBy.delete(day);\n closedBy.set(day, Math.max(closedBy.get(day) ?? -1, spec));\n continue;\n }\n const closed = closedBy.get(day);\n // a closed day stays closed, unless the opening names it more closely\n if (closed !== undefined && closed >= spec) continue;\n if (closed !== undefined) {\n week[day] = [];\n closedBy.delete(day);\n }\n addInterval(week, [day], value);\n openedBy.set(day, Math.max(openedBy.get(day) ?? -1, spec));\n }\n };\n const valueOf = (\n token: Token,\n ): { value: Interval | \"closed\"; inferred: boolean } | undefined => {\n if (token.groups[\"closed\"] !== undefined) return { value: \"closed\", inferred: false };\n if (token.groups[\"allday\"] !== undefined) return { value: ALL_DAY, inferred: false };\n const parts =\n token.groups[\"times\"] === undefined ? undefined : splitRange(token.groups[\"times\"]);\n const range = parts === undefined ? undefined : readRange(parts[0], parts[1]);\n return range === undefined ? undefined : { value: range.interval, inferred: range.inferred };\n };\n /** This function gives the tokens of one kind in sequence from `start`, each on a separate line. */\n const runOf = (start: number, kind: (token: Token) => boolean): number => {\n let end = start;\n while (\n tokens[end + 1] !== undefined &&\n kind(tokens[end + 1]!) &&\n tokens[end + 1]!.line !== tokens[end]!.line &&\n !breaks.has(tokens[end + 1]!.line - 1)\n ) {\n end += 1;\n }\n return end;\n };\n\n let currentDays: Day[] = [];\n let currentSpec = 2;\n let lastWasDay = false;\n let pending: Array<{ value: Interval | \"closed\"; inferred: boolean }> = [];\n let excepting = false;\n let previous: Token | undefined;\n for (let at = 0; at < tokens.length; at += 1) {\n const token = tokens[at]!;\n if (previous !== undefined && token.line !== previous.line) {\n excepting = false;\n let broken = dangling.has(previous.line) && lastWasDay;\n for (let line = previous.line; line < token.line && !broken; line += 1)\n broken = breaks.has(line);\n if (broken) {\n currentDays = [];\n lastWasDay = false;\n pending = [];\n }\n }\n previous = token;\n if (token.groups[\"except\"] !== undefined) {\n excepting = true;\n continue;\n }\n const days = token.days;\n if (days !== undefined) {\n if (excepting) {\n // \"daily except Monday\": Monday is closed, and the days to come leave it out\n apply(\"closed\", days, 2, true);\n currentDays = currentDays.filter((day) => !days.includes(day));\n continue;\n }\n if (!lastWasDay && pending.length === 0) {\n // a column of days beside a column of hours (Sunday to\n // Saturday, then seven ranges): the n-th range is the n-th day's.\n // Columns of different lengths say nothing certain, so nothing is read\n const lastDay = runOf(at, isDays);\n const firstValue = tokens[lastDay + 1];\n if (\n lastDay > at &&\n firstValue !== undefined &&\n isValue(firstValue) &&\n firstValue.line !== tokens[lastDay]!.line\n ) {\n const lastValue = runOf(lastDay + 1, isValue);\n const dayRun = tokens.slice(at, lastDay + 1);\n const valueRun = tokens.slice(lastDay + 1, lastValue + 1);\n if (valueRun.length >= 2) {\n if (valueRun.length === dayRun.length) {\n dayRun.forEach((dayToken, index) => {\n const read = valueOf(valueRun[index]!);\n if (read === undefined) return;\n inferred ||= read.inferred;\n apply(read.value, dayToken.days!, dayToken.spec, true);\n });\n }\n at = lastValue;\n previous = tokens[at];\n continue;\n }\n }\n }\n currentDays = lastWasDay ? [...new Set([...currentDays, ...days])] : [...days];\n currentSpec = lastWasDay ? Math.min(currentSpec, token.spec) : token.spec;\n lastWasDay = true;\n const next = tokens[at + 1];\n // the list of days ends where the next token is not a day on this line\n if (pending.length > 0 && !(isDays(next) && next!.line === token.line)) {\n for (const read of pending) {\n inferred ||= read.inferred;\n apply(read.value, currentDays, currentSpec, true);\n }\n pending = [];\n // the list is used: the next line's days start a new one\n lastWasDay = false;\n }\n continue;\n }\n excepting = false;\n const wasDay = lastWasDay;\n lastWasDay = false;\n let read = isValue(token) ? valueOf(token) : undefined;\n if (read === undefined && token.groups[\"clock\"] !== undefined && wasDay) {\n // a widget that writes a day, its opening and its closing on three\n // lines; a lone time anywhere else (a booking slot) is no schedule\n const close = tokens[at + 1]?.groups[\"clock\"];\n const range = close === undefined ? undefined : readRange(token.groups[\"clock\"], close);\n if (range !== undefined) {\n read = { value: range.interval, inferred: range.inferred };\n at += 1;\n previous = tokens[at];\n }\n }\n if (read === undefined) continue;\n const next = tokens[at + 1];\n const daysFollow = isDays(next) && next!.line === tokens[at]!.line;\n // a value before its days, or \"..., closed Fri\" after a schedule\n if (daysFollow && (valueFirst.has(token.line) || (read.value === \"closed\" && !wasDay))) {\n pending.push(read);\n continue;\n }\n if (currentDays.length > 0) {\n inferred ||= read.inferred;\n apply(read.value, currentDays, currentSpec, wasDay);\n }\n }\n // one range under an hours heading, or after \"Open\", with no day named\n // (\"HOURS OF OPERATION: 7:00am - 10:30pm\", \"Open 7am-10:30pm\") is every\n // day, and says it was a guess; two ranges with no days (mini golf 7-10:30,\n // batting cages 9-10) say nothing certain\n if (Object.keys(week).length === 0 && !tokens.some(isDays)) {\n const ranges = tokens.filter(\n (token) => token.groups[\"times\"] !== undefined || token.groups[\"allday\"] !== undefined,\n );\n const body = current.lines.map((index) => lines[index]!);\n // \"Today's Hours\" are one day's (\"Now open\" is an exhibition's)\n const oneDay = /\\b(today|tonight|now)\\b/i.test(`${above} ${body.join(\" \")}`);\n const opens = !oneDay && (heading || /^open\\b/i.test(body[0] ?? \"\"));\n const said = new Set(\n ranges.map((token) => (token.groups[\"times\"] ?? \"24h\").replace(/\\s+/g, \"\")),\n );\n const first = ranges[0] === undefined ? undefined : valueOf(ranges[0]);\n if (opens && said.size === 1 && first !== undefined && first.value !== \"closed\") {\n addInterval(week, DAYS, first.value);\n // \"Open 24/7\" says the days itself\n inferred = !/24 ?\\/ ?7/.test(ranges[0]!.groups[\"allday\"] ?? \"\");\n }\n }\n if (Object.keys(week).length === 0) return;\n const season = current.season ?? (label !== undefined ? seasonOf(above) : undefined);\n out.push({\n week,\n from: \"text\",\n raw: current.lines.map((index) => original[index]!).join(\"\\n\"),\n inferred,\n ...(partial ? { partial } : {}),\n ...(season !== undefined ? { season } : {}),\n label: label !== undefined && label.length > 0 && label.length <= 40 ? label : null,\n });\n };\n lines.forEach((line, index) => {\n const tokens = tokensOf(line, 0);\n const names = tokens.some(\n (token) =>\n token.days !== undefined ||\n token.groups[\"closed\"] !== undefined ||\n token.groups[\"allday\"] !== undefined,\n );\n // a clock with am/pm or minutes reads as hours on its own line; a bare\n // range (\"12-14 Main St\") only beside a day\n const clock = tokens.some(\n (token) =>\n token.groups[\"clock\"] !== undefined ||\n (token.groups[\"times\"] !== undefined && isStrong(token.groups[\"times\"])),\n );\n // a long sentence that happens to mention a day is prose, not a schedule;\n // a long line with several time ranges is a schedule written as one\n // paragraph (\"Dinner: Tuesday, Wednesday and Thursday 5pm to 9pm / ...\")\n const ranges = tokens.filter((token) => token.groups[\"times\"] !== undefined).length;\n if ((names || clock) && (line.length <= 160 || ranges >= 2)) {\n // a season starts a schedule of its own (\"Summer: Mon-Sun 10am-8pm\")\n const season = seasonOf(line);\n const cut =\n block !== undefined && (season !== undefined || block.lines.length >= MAX_BLOCK_LINES);\n if (cut) flush();\n block ??= { start: index, lines: [], season, cut };\n block.lines.push(index);\n } else {\n flush();\n }\n });\n flush();\n return out;\n};\n\n// ── what the place keeps ────────────────────────────────────────────────────\n\nconst LABEL: Readonly<Record<Day, string>> = {\n mon: \"Mon\",\n tue: \"Tue\",\n wed: \"Wed\",\n thu: \"Thu\",\n fri: \"Fri\",\n sat: \"Sat\",\n sun: \"Sun\",\n};\n\nconst sameDay = (a: readonly Interval[] | undefined, b: readonly Interval[] | undefined): boolean =>\n JSON.stringify(a) === JSON.stringify(b);\n\n/** This function writes a week as text, for example `Mon–Thu 11:00–22:00, Fri–Sat 11:00–23:00`. */\nexport const describeWeek = (week: Week): string => {\n const parts: string[] = [];\n let index = 0;\n while (index < 7) {\n const day = DAYS[index]!;\n const value = week[day];\n let end = index;\n while (end + 1 < 7 && sameDay(week[DAYS[end + 1]!], value)) end += 1;\n const span = end === index ? LABEL[day] : `${LABEL[day]}–${LABEL[DAYS[end]!]}`;\n if (value !== undefined) {\n parts.push(\n value.length === 0\n ? `${span} closed`\n : `${span} ${value\n .map(([open, close]) =>\n open === \"00:00\" && close === \"00:00\" ? \"open 24 hours\" : `${open}–${close}`,\n )\n .join(\", \")}`,\n );\n }\n index = end + 1;\n }\n return parts.join(\"; \");\n};\n\n/**\n * This function gives one interval for each day (`hours_week`), and null for a closed day. One\n * interval cannot show a day with two intervals and a gap between them. Thus such a day is not in\n * `hours_week`, and `split` names it.\n */\nexport const toHoursWeek = (\n week: Week,\n): {\n readonly hours_week: Partial<Record<Day, readonly [string, string] | null>>;\n readonly split: readonly Day[];\n} => {\n const hoursWeek: Partial<Record<Day, readonly [string, string] | null>> = {};\n const split: Day[] = [];\n for (const day of DAYS) {\n const value = week[day];\n if (value === undefined) continue;\n if (value.length === 0) hoursWeek[day] = null;\n else if (value.length === 1) hoursWeek[day] = value[0]!;\n else split.push(day);\n }\n return { hours_week: hoursWeek, split };\n};\n\n/** This function gives the days where two readings disagree, from the days that the two readings state. */\nexport const disagreements = (a: Week, b: Week): Day[] =>\n DAYS.filter((day) => a[day] !== undefined && b[day] !== undefined && !sameDay(a[day], b[day]));\n\n/** The result of `compareHours`. */\nexport interface HoursComparison {\n /**\n * The value `new` tells that the stored week states no day. The value `changed` tells that a day\n * of the two weeks is different. Else the value is `same`.\n */\n readonly status: \"same\" | \"changed\" | \"new\";\n /** These days changed. For `new`, these are the days that the reading states. */\n readonly days: readonly Day[];\n /** The reading states these days, and the stored week does not. They do not make a change. */\n readonly added: readonly Day[];\n /** The comparison left out these days, because a side has two or more intervals (`skipSplit`). */\n readonly skipped: readonly Day[];\n}\n\n/** This function writes a time in one form: `9:00` is `09:00`, and `24:00` is midnight, `00:00`. */\nconst clockForm = (time: string): string => {\n const text = time.trim();\n if (text === \"24:00\") return \"00:00\";\n return /^\\d:\\d{2}$/.test(text) ? `0${text}` : text;\n};\n\nconst dayForm = (intervals: readonly Interval[]): string =>\n intervals\n .map(([open, close]) => `${clockForm(open)}-${clockForm(close)}`)\n .sort((a, b) => (a < b ? -1 : a > b ? 1 : 0))\n .join(\",\");\n\n/**\n * This function compares a reading with a stored week, day by day. It compares only the days that\n * the two weeks state. A day that only the reading states is in `added`, and it is not a change.\n * The times `24:00` and `00:00` are the same. With `skipSplit`, a day with two or more intervals on\n * a side is not compared, for a store that keeps one interval for each day.\n *\n * The function is pure. It does not decide if a change is true: an application confirms a change\n * with a second reading, as a watch does.\n */\nexport const compareHours = (\n stored: Week | undefined,\n reading: Week,\n options: { readonly skipSplit?: boolean | undefined } = {},\n): HoursComparison => {\n const stated = DAYS.filter((day) => reading[day] !== undefined);\n if (stored === undefined || DAYS.every((day) => stored[day] === undefined)) {\n return { status: \"new\", days: stated, added: [], skipped: [] };\n }\n const days: Day[] = [];\n const added: Day[] = [];\n const skipped: Day[] = [];\n for (const day of stated) {\n const kept = stored[day];\n const said = reading[day]!;\n if (kept === undefined) {\n added.push(day);\n continue;\n }\n if (options.skipSplit === true && (kept.length > 1 || said.length > 1)) {\n skipped.push(day);\n continue;\n }\n if (dayForm(kept) !== dayForm(said)) days.push(day);\n }\n return { status: days.length > 0 ? \"changed\" : \"same\", days, added, skipped };\n};\n"],"mappings":"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"}
|