@lupinum/ginko 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/CHANGELOG.md +150 -0
  2. package/README.md +2 -2
  3. package/dist/basics.d.mts +492 -0
  4. package/dist/basics.d.ts +492 -0
  5. package/dist/basics.mjs +416 -0
  6. package/dist/cli/index.mjs +54 -55
  7. package/dist/index.d.mts +5 -4
  8. package/dist/index.d.ts +5 -4
  9. package/dist/index.mjs +3 -3
  10. package/dist/internal/search.mjs +6 -3
  11. package/dist/internal.d.mts +387 -41
  12. package/dist/internal.d.ts +387 -41
  13. package/dist/internal.mjs +267 -101
  14. package/dist/nuxt/module.d.mts +2 -2
  15. package/dist/nuxt/module.d.ts +2 -2
  16. package/dist/nuxt/module.mjs +105 -13
  17. package/dist/nuxt/runtime/GinkoImage.d.mts +1 -1
  18. package/dist/nuxt/runtime/GinkoMarkdown.mjs +2 -2
  19. package/dist/nuxt/runtime/composables.d.mts +4 -3
  20. package/dist/nuxt/runtime/composables.mjs +19 -15
  21. package/dist/nuxt/runtime/fetch.d.mts +3 -1
  22. package/dist/nuxt/runtime/fetch.mjs +12 -6
  23. package/dist/nuxt/runtime/markdown.d.mts +3 -0
  24. package/dist/nuxt/runtime/markdown.mjs +15 -3
  25. package/dist/nuxt/runtime/plugin.mjs +6 -35
  26. package/dist/nuxt/runtime/preview-listener.d.mts +13 -0
  27. package/dist/nuxt/runtime/preview-listener.mjs +37 -0
  28. package/dist/nuxt/runtime/preview.vue +3 -1
  29. package/dist/nuxt/runtime/schema.d.ts +6 -0
  30. package/dist/nuxt/runtime/server/bundles.d.ts +4 -0
  31. package/dist/nuxt/runtime/server/files-md.d.ts +2 -0
  32. package/dist/nuxt/runtime/server/files.d.ts +2 -0
  33. package/dist/nuxt/runtime/server/headers.d.ts +2 -0
  34. package/dist/nuxt/runtime/server/items.d.mts +12 -0
  35. package/dist/nuxt/runtime/server/items.d.ts +12 -0
  36. package/dist/nuxt/runtime/server/items.mjs +17 -0
  37. package/dist/nuxt/runtime/server/llms-full.d.ts +2 -0
  38. package/dist/nuxt/runtime/server/llms.d.ts +2 -0
  39. package/dist/nuxt/runtime/server/markdown-trees.d.ts +2 -0
  40. package/dist/nuxt/runtime/server/markdown.d.ts +2 -0
  41. package/dist/nuxt/runtime/server/markdown.mjs +1 -1
  42. package/dist/nuxt/runtime/server/platform-md.d.mts +3 -0
  43. package/dist/nuxt/runtime/server/platform-md.d.ts +3 -0
  44. package/dist/nuxt/runtime/server/platform-md.mjs +71 -0
  45. package/dist/nuxt/runtime/server/redirects.d.ts +2 -0
  46. package/dist/nuxt/runtime/server/robots.d.ts +2 -0
  47. package/dist/nuxt/runtime/server/search.d.ts +2 -0
  48. package/dist/nuxt/runtime/server/site-config.d.ts +14 -0
  49. package/dist/nuxt/runtime/server/sitemap-source.d.ts +2 -0
  50. package/dist/nuxt/runtime/server/sitemap-xml.d.ts +24 -0
  51. package/dist/nuxt/runtime/server/sitemap.d.ts +2 -0
  52. package/dist/nuxt/runtime/shared.d.mts +10 -3
  53. package/dist/nuxt/runtime/shared.mjs +2 -2
  54. package/dist/shared/ginko.1wwZv4B_.d.ts +1622 -0
  55. package/dist/shared/{ginko.BBkZW2nW.d.ts → ginko.6cI8KXZP.d.mts} +2 -2
  56. package/dist/shared/{ginko.CQvxhyez.mjs → ginko.AprJymER.mjs} +30 -1
  57. package/dist/shared/ginko.BNVC-g9n.mjs +10389 -0
  58. package/dist/shared/{ginko.Bwan3BB-.d.mts → ginko.B_pACpVU.d.ts} +2 -2
  59. package/dist/shared/{ginko.CrNRBkS5.mjs → ginko.C4gcOovG.mjs} +253 -309
  60. package/dist/shared/{ginko.C90fPpMP.mjs → ginko.CXA2R3aS.mjs} +128 -101
  61. package/dist/shared/ginko.CjWB8N7f.d.ts +128 -0
  62. package/dist/shared/ginko.D1PeQ3ds.mjs +36 -0
  63. package/dist/shared/ginko.DIl7l-75.mjs +26 -0
  64. package/dist/shared/ginko.DY7RbCEw.mjs +601 -0
  65. package/dist/shared/ginko.Dmgixfpf.mjs +130 -0
  66. package/dist/shared/{ginko.D-fNojn9.d.mts → ginko.DrkUeYFP.d.mts} +824 -10
  67. package/dist/shared/{ginko.D-fNojn9.d.ts → ginko.DrkUeYFP.d.ts} +824 -10
  68. package/dist/shared/{ginko.akHewCGt.d.mts → ginko.DvPtlHIz.d.mts} +50 -5
  69. package/dist/shared/{ginko.BDF3bW0C.d.ts → ginko.EelR5dt9.d.ts} +50 -5
  70. package/dist/shared/ginko.ULKQnAWd.d.ts +91 -0
  71. package/dist/shared/ginko.loGIxGZg.d.mts +91 -0
  72. package/dist/shared/ginko.o1yIKlgj.d.mts +128 -0
  73. package/dist/shared/ginko.plW4dLmm.d.mts +1622 -0
  74. package/dist/shared/{ginko.Dht0xxE5.mjs → ginko.raCzOPYC.mjs} +1 -2
  75. package/dist/shared/ginko.yKMIjJ68.mjs +84 -0
  76. package/dist/vue.d.mts +5 -4
  77. package/dist/vue.d.ts +5 -4
  78. package/dist/vue.mjs +1 -1
  79. package/i18n/ar.json +212 -0
  80. package/i18n/de.json +9 -1
  81. package/i18n/en.json +9 -1
  82. package/i18n/es.json +12 -4
  83. package/i18n/fr.json +212 -0
  84. package/i18n/it.json +212 -0
  85. package/package.json +7 -3
  86. package/dist/shared/ginko.BTUfZCfa.d.mts +0 -172
  87. package/dist/shared/ginko.C-QjDn3O.mjs +0 -4944
  88. package/dist/shared/ginko.Dc0oNcDv.d.ts +0 -172
  89. package/dist/shared/ginko.Mn30dmlm.mjs +0 -20
@@ -0,0 +1,601 @@
1
+ import { c as calendarOfCountry } from './ginko.D1PeQ3ds.mjs';
2
+ import { t as todayIn, z as zonedDateTime } from './ginko.AprJymER.mjs';
3
+ import { f, p as plainText } from './ginko.BNVC-g9n.mjs';
4
+ import { m as minorDigits, c as collection, s as single } from './ginko.Dmgixfpf.mjs';
5
+
6
+ const sourceOf = (o) => o.source ? { source: o.source } : {};
7
+
8
+ function postalAddress(a, siteCountry) {
9
+ const country = a.country ?? siteCountry;
10
+ return { "@type": "PostalAddress", streetAddress: a.street, ...a.postalCode ? { postalCode: a.postalCode } : {}, addressLocality: a.city, ...country ? { addressCountry: country } : {} };
11
+ }
12
+ function offerPrice(m, siteCurrency) {
13
+ const currency = m.currency ?? siteCurrency;
14
+ if (!currency) throw new Error("JSON-LD: pass `currency` (the site\u2019s currency) for prices that carry none.");
15
+ const digits = minorDigits(currency);
16
+ return { price: (m.amount / 10 ** digits).toFixed(digits), priceCurrency: currency };
17
+ }
18
+ function offerJsonLd(m, siteCurrency, o = {}) {
19
+ const price = offerPrice(m, siteCurrency);
20
+ const vat = o.valueAddedTaxIncluded === void 0 ? {} : { valueAddedTaxIncluded: o.valueAddedTaxIncluded };
21
+ const specification = o.unitCode ? { "@type": "UnitPriceSpecification", ...price, ...vat, referenceQuantity: { "@type": "QuantitativeValue", value: 1, unitCode: o.unitCode } } : o.valueAddedTaxIncluded === void 0 ? void 0 : { "@type": "PriceSpecification", ...price, ...vat };
22
+ return { "@type": "Offer", ...price, ...specification ? { priceSpecification: specification } : {} };
23
+ }
24
+
25
+ const both = (de, en) => ({ de, en });
26
+ const weekdays = [
27
+ { value: "mo", label: both("Montag", "Monday") },
28
+ { value: "tu", label: both("Dienstag", "Tuesday") },
29
+ { value: "we", label: both("Mittwoch", "Wednesday") },
30
+ { value: "th", label: both("Donnerstag", "Thursday") },
31
+ { value: "fr", label: both("Freitag", "Friday") },
32
+ { value: "sa", label: both("Samstag", "Saturday") },
33
+ { value: "su", label: both("Sonntag", "Sunday") }
34
+ ];
35
+ const officeWeek = weekdays.map((d) => {
36
+ const closed = d.value === "sa" || d.value === "su";
37
+ return { _id: `wk${d.value}000000`, day: d.value, closed, ...closed ? {} : { from1: "09:00", to1: "17:00" } };
38
+ });
39
+ const neutral = {
40
+ hours: both("\xD6ffnungszeiten", "Opening hours"),
41
+ week: both("Wochenplan", "Weekly hours"),
42
+ day: both("Tag", "Day"),
43
+ closed: both("Ganzt\xE4gig geschlossen", "Closed all day"),
44
+ opens: both("\xD6ffnet um", "Opens at"),
45
+ closes: both("Schlie\xDFt um", "Closes at"),
46
+ opensAgain: both("\xD6ffnet wieder um", "Opens again at"),
47
+ opensAgainHint: both("Nur wenn du untertags eine Pause machst.", "Only if you close for a break during the day."),
48
+ closesAgain: both("Schlie\xDFt wieder um", "Closes again at"),
49
+ exceptions: both("Schlie\xDFtage und Ausnahmen", "Closures and exceptions"),
50
+ exception: both("Ausnahme", "Exception"),
51
+ dates: both("Zeitraum", "Dates"),
52
+ reason: both("Grund", "Reason"),
53
+ reasonHint: both("Kurz, z. B. \u201EUrlaub\u201C oder \u201EInventur\u201C. Steht so auf der Website.", "Keep it short, e.g. \u201CHoliday\u201D or \u201CStocktaking\u201D. Shown like this on the website."),
54
+ exceptionClosed: both("Geschlossen", "Closed"),
55
+ closedOn: both("Geschlossen am", "Closed on"),
56
+ closedOnHint: both("Ein einzelner Tag. F\xFCr mehrere Tage nimm \u201ESchlie\xDFtage und Ausnahmen\u201C.", "A single day. For several days, use \u201CClosures and exceptions\u201D."),
57
+ closedNote: both("Grund f\xFCr Besucher", "Reason for visitors"),
58
+ closedNoteHint: both("Steht auf der Website, z. B. \u201EWegen eines Wasserrohrbruchs\u201C.", "Shown on the website, e.g. \u201CBecause of a burst water pipe\u201D."),
59
+ noticeDays: both("Schlie\xDFungen ank\xFCndigen (Tage vorher)", "Announce closures (days ahead)"),
60
+ noticeDaysHint: both("So viele Tage vorher k\xFCndigt die Website eine Schlie\xDFung oder einen geschlossenen Feiertag an.", "How many days ahead the website announces a closure or a public holiday when you are closed."),
61
+ holidays: both("An Feiertagen", "On public holidays"),
62
+ holidaySunday: both("wie am Sonntag", "same as Sunday"),
63
+ holidayClosed: both("geschlossen", "closed"),
64
+ holidayNormal: both("normale Zeiten des Wochentags", "the weekday\u2019s usual hours"),
65
+ holidaysHint: both(
66
+ "Gilt f\xFCr alle gesetzlichen Feiertage. \u201EWie am Sonntag\u201C nimmt die Sonntagszeiten, die an dem Tag gelten. F\xFCr andere Zeiten an einem einzelnen Feiertag trag unter \u201ESchlie\xDFtage und Ausnahmen\u201C eine Ausnahme ein.",
67
+ "Applies to every public holiday. \u201CSame as Sunday\u201D uses the Sunday hours that apply on that day. For other hours on a single holiday, add an exception under \u201CClosures and exceptions\u201D."
68
+ ),
69
+ closedToday: both("Heute geschlossen", "Closed today"),
70
+ holidaySetting: both("\xD6ffnungszeiten an Feiertagen", "Opening hours on public holidays"),
71
+ plans: both("\xD6ffnungszeiten ab Datum", "Opening hours from a date"),
72
+ planName: both("Name", "Name"),
73
+ planFrom: both("Gilt ab", "Applies from"),
74
+ planTo: both("Gilt bis", "Applies until"),
75
+ yearly: both("Jedes Jahr wiederholen", "Repeat every year"),
76
+ planAdd: both("Neue \xD6ffnungszeiten ab \u2026", "New opening hours from \u2026"),
77
+ planEdit: both("\xD6ffnungszeiten ab Datum bearbeiten", "Edit hours from a date"),
78
+ planRemove: both("\xD6ffnungszeiten ab Datum entfernen", "Remove hours from a date")
79
+ };
80
+ const open = { field: "closed", not: true };
81
+ function timeFields(t) {
82
+ return {
83
+ from1: f.time(t.opens, { optional: true, showWhen: open }),
84
+ to1: f.time(t.closes, { optional: true, showWhen: open }),
85
+ from2: f.time(t.opensAgain, { optional: true, showWhen: open, hint: t.opensAgainHint }),
86
+ to2: f.time(t.closesAgain, { optional: true, showWhen: open })
87
+ };
88
+ }
89
+ function weekField(t, week) {
90
+ return f.list(t.week, {
91
+ itemLabel: t.day,
92
+ fields: {
93
+ day: f.choice(t.day, { options: weekdays }),
94
+ closed: f.boolean(t.closed),
95
+ ...timeFields(t)
96
+ },
97
+ min: 7,
98
+ max: 7,
99
+ unique: "day",
100
+ default: week
101
+ });
102
+ }
103
+ function hoursBrick(t, week, opts) {
104
+ return single({
105
+ label: opts.label ?? t.hours,
106
+ ...sourceOf(opts),
107
+ // Today's hours on the Studio's Today page (closed, open from … to …, or due when nothing is entered for today),
108
+ // with the hour plans when the site names them.
109
+ status: { hours: opts.plans ? { plans: opts.plans } : true },
110
+ fields: {
111
+ week: weekField(t, week),
112
+ exceptions: f.list(t.exceptions, {
113
+ itemLabel: t.exception,
114
+ fields: {
115
+ when: f.daterange(t.dates),
116
+ reason: f.text(t.reason, { max: 140, hint: t.reasonHint }),
117
+ // A new exception is a closure unless the owner says otherwise.
118
+ closed: f.boolean(t.exceptionClosed, { default: true }),
119
+ // Special hours: "closed" off and times set, e.g. 24 December from 11:00 to 14:00.
120
+ ...timeFields(t)
121
+ },
122
+ max: 20,
123
+ default: []
124
+ }),
125
+ // Optional: "Clear" in the Studio clears it, and saving it empty lifts the closure.
126
+ closedOn: f.date(t.closedOn, { optional: true, hint: t.closedOnHint }),
127
+ closedNote: f.text(t.closedNote, { max: 140, optional: true, hint: t.closedNoteHint }),
128
+ holidayNoticeDays: f.number(t.noticeDays, { min: 0, max: 60, step: 1, default: 14, hint: t.noticeDaysHint }),
129
+ // Which holidays count is the site's `country` or `calendar` (code), so the hint says what the choice covers and
130
+ // where a single holiday gets other hours.
131
+ holidays: f.choice(t.holidays, {
132
+ options: [
133
+ { value: "sunday", label: t.holidaySunday },
134
+ { value: "closed", label: t.holidayClosed },
135
+ { value: "normal", label: t.holidayNormal }
136
+ ],
137
+ optional: true,
138
+ default: "sunday",
139
+ hint: t.holidaysHint
140
+ })
141
+ },
142
+ tasks: [
143
+ { id: "closedToday", label: t.closedToday, kind: "edit", fields: ["closedOn", "closedNote"], prefill: { closedOn: "@today" } },
144
+ // The lead time sits under the list, so the owner sees when visitors first read about a closure.
145
+ { id: "holiday", label: t.exceptions, kind: "edit", fields: ["exceptions", "holidayNoticeDays"] },
146
+ { id: "holidaySetting", label: t.holidaySetting, kind: "edit", fields: ["holidays"] }
147
+ ]
148
+ });
149
+ }
150
+ function hourPlansBrick(t, week, opts) {
151
+ return collection({
152
+ label: opts.label ?? t.plans,
153
+ ...sourceOf(opts),
154
+ fields: {
155
+ label: f.text(t.planName, { max: 80 }),
156
+ from: f.date(t.planFrom),
157
+ to: f.date(t.planTo, { optional: true }),
158
+ yearly: f.boolean(t.yearly, { default: false }),
159
+ week: weekField(t, week)
160
+ },
161
+ max: 12,
162
+ order: { field: "from", dir: "asc" },
163
+ tasks: [
164
+ { id: "add", label: t.planAdd, kind: "create" },
165
+ { id: "edit", label: t.planEdit, kind: "edit", pick: true, fields: ["label", "from", "to", "yearly", "week"] },
166
+ { id: "remove", label: t.planRemove, kind: "remove" }
167
+ ]
168
+ });
169
+ }
170
+ function hours(opts = {}) {
171
+ return hoursBrick(neutral, officeWeek, opts);
172
+ }
173
+ function hourPlans(opts = {}) {
174
+ return hourPlansBrick(neutral, officeWeek, opts);
175
+ }
176
+
177
+ const weekdayKeys = ["mo", "tu", "we", "th", "fr", "sa", "su"];
178
+ function weekdayOf(date) {
179
+ const day = (/* @__PURE__ */ new Date(`${date}T12:00:00Z`)).getUTCDay();
180
+ return weekdayKeys[(day + 6) % 7];
181
+ }
182
+ function addDays(date, n) {
183
+ const d = /* @__PURE__ */ new Date(`${date}T12:00:00Z`);
184
+ d.setUTCDate(d.getUTCDate() + n);
185
+ return d.toISOString().slice(0, 10);
186
+ }
187
+
188
+ function easter(year) {
189
+ const a = year % 19;
190
+ const b = Math.floor(year / 100);
191
+ const c = year % 100;
192
+ const d = Math.floor(b / 4);
193
+ const e = b % 4;
194
+ const g = Math.floor((8 * b + 13) / 25);
195
+ const h = (19 * a + b - d - g + 15) % 30;
196
+ const i = Math.floor(c / 4);
197
+ const k = c % 4;
198
+ const l = (32 + 2 * e + 2 * i - h - k) % 7;
199
+ const m = Math.floor((a + 11 * h + 19 * l) / 433);
200
+ const month = Math.floor((h + l - 7 * m + 90) / 25);
201
+ const day = (h + l - 7 * m + 114) % 31 + 1;
202
+ return `${year}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
203
+ }
204
+ const byDate = (list) => list.sort((p, q) => p.date.localeCompare(q.date));
205
+ function austrianHolidays(year) {
206
+ const y = String(year);
207
+ const easterSunday = easter(year);
208
+ return byDate([
209
+ { date: `${y}-01-01`, de: "Neujahr", en: "New Year\u2019s Day" },
210
+ { date: `${y}-01-06`, de: "Heilige Drei K\xF6nige", en: "Epiphany" },
211
+ { date: addDays(easterSunday, 1), de: "Ostermontag", en: "Easter Monday" },
212
+ { date: `${y}-05-01`, de: "Staatsfeiertag", en: "Labour Day" },
213
+ { date: addDays(easterSunday, 39), de: "Christi Himmelfahrt", en: "Ascension Day" },
214
+ { date: addDays(easterSunday, 50), de: "Pfingstmontag", en: "Whit Monday" },
215
+ { date: addDays(easterSunday, 60), de: "Fronleichnam", en: "Corpus Christi" },
216
+ { date: `${y}-08-15`, de: "Mari\xE4 Himmelfahrt", en: "Assumption Day" },
217
+ { date: `${y}-10-26`, de: "Nationalfeiertag", en: "Austrian National Day" },
218
+ { date: `${y}-11-01`, de: "Allerheiligen", en: "All Saints\u2019 Day" },
219
+ { date: `${y}-12-08`, de: "Mari\xE4 Empf\xE4ngnis", en: "Immaculate Conception" },
220
+ { date: `${y}-12-25`, de: "Christtag", en: "Christmas Day" },
221
+ { date: `${y}-12-26`, de: "Stefanitag", en: "St. Stephen\u2019s Day" }
222
+ ]);
223
+ }
224
+ function germanHolidays(year) {
225
+ const y = String(year);
226
+ const easterSunday = easter(year);
227
+ return byDate([
228
+ { date: `${y}-01-01`, de: "Neujahr", en: "New Year\u2019s Day" },
229
+ { date: addDays(easterSunday, -2), de: "Karfreitag", en: "Good Friday" },
230
+ { date: addDays(easterSunday, 1), de: "Ostermontag", en: "Easter Monday" },
231
+ { date: `${y}-05-01`, de: "Tag der Arbeit", en: "Labour Day" },
232
+ { date: addDays(easterSunday, 39), de: "Christi Himmelfahrt", en: "Ascension Day" },
233
+ { date: addDays(easterSunday, 50), de: "Pfingstmontag", en: "Whit Monday" },
234
+ { date: `${y}-10-03`, de: "Tag der Deutschen Einheit", en: "German Unity Day" },
235
+ { date: `${y}-12-25`, de: "1. Weihnachtstag", en: "Christmas Day" },
236
+ { date: `${y}-12-26`, de: "2. Weihnachtstag", en: "Boxing Day" }
237
+ ]);
238
+ }
239
+ const singaporeTable = {
240
+ 2026: [
241
+ { date: "2026-02-17", de: "Chinesisches Neujahr", en: "Chinese New Year" },
242
+ { date: "2026-02-18", de: "Chinesisches Neujahr", en: "Chinese New Year" },
243
+ { date: "2026-03-21", de: "Hari Raya Puasa", en: "Hari Raya Puasa" },
244
+ { date: "2026-04-03", de: "Karfreitag", en: "Good Friday" },
245
+ { date: "2026-05-27", de: "Hari Raya Haji", en: "Hari Raya Haji" },
246
+ { date: "2026-05-31", de: "Vesak", en: "Vesak Day" },
247
+ { date: "2026-11-08", de: "Deepavali", en: "Deepavali" }
248
+ ],
249
+ 2027: [
250
+ { date: "2027-02-06", de: "Chinesisches Neujahr", en: "Chinese New Year" },
251
+ { date: "2027-02-07", de: "Chinesisches Neujahr", en: "Chinese New Year" },
252
+ { date: "2027-03-10", de: "Hari Raya Puasa", en: "Hari Raya Puasa" },
253
+ { date: "2027-03-26", de: "Karfreitag", en: "Good Friday" },
254
+ { date: "2027-05-17", de: "Hari Raya Haji", en: "Hari Raya Haji" },
255
+ { date: "2027-05-20", de: "Vesak", en: "Vesak Day" },
256
+ { date: "2027-10-28", de: "Deepavali", en: "Deepavali" }
257
+ ]
258
+ };
259
+ function singaporeHolidays(year) {
260
+ const y = String(year);
261
+ const base = [
262
+ { date: `${y}-01-01`, de: "Neujahr", en: "New Year\u2019s Day" },
263
+ { date: `${y}-05-01`, de: "Tag der Arbeit", en: "Labour Day" },
264
+ { date: `${y}-08-09`, de: "Nationalfeiertag", en: "National Day" },
265
+ { date: `${y}-12-25`, de: "Weihnachten", en: "Christmas Day" },
266
+ ...singaporeTable[year] ?? []
267
+ ];
268
+ const out = [...base];
269
+ for (const h of base) {
270
+ if (weekdayOf(h.date) === "su") out.push({ date: addDays(h.date, 1), de: `${h.de} (Ersatztag)`, en: `${h.en} (observed)` });
271
+ }
272
+ return byDate(out);
273
+ }
274
+ const calendars = { AT: austrianHolidays, DE: germanHolidays, SG: singaporeHolidays, none: () => [] };
275
+ function calendarOf(option) {
276
+ return typeof option === "function" ? option : calendars[option];
277
+ }
278
+
279
+ function spansOf(t) {
280
+ return [[t.from1, t.to1], [t.from2, t.to2]].filter((p) => !!p[0] && !!p[1]).map(([from, to]) => ({ from, to }));
281
+ }
282
+ function spansText(spans, and) {
283
+ return spans.map((s) => `${s.from}\u2013${s.to}`).join(` ${and} `);
284
+ }
285
+ function timesOf(day, and) {
286
+ if (!day || day.closed) return "";
287
+ return spansText(spansOf(day), and);
288
+ }
289
+ function holidayOn(date, calendar) {
290
+ return calendarOf(calendar)(Number(date.slice(0, 4))).find((h) => h.date === date);
291
+ }
292
+ function calendarIn(ctx) {
293
+ return ctx.calendar ?? (ctx.country ? calendarOfCountry(ctx.country) : void 0) ?? "none";
294
+ }
295
+ function planCovers(plan, date) {
296
+ if (date < plan.from) return false;
297
+ if (!plan.yearly) return !plan.to || date <= plan.to;
298
+ const md = date.slice(5);
299
+ const from = plan.from.slice(5);
300
+ if (!plan.to) return md >= from;
301
+ const to = plan.to.slice(5);
302
+ return from <= to ? md >= from && md <= to : md >= from || md <= to;
303
+ }
304
+ function planOn(plans, date) {
305
+ return (plans ?? []).filter((p) => planCovers(p, date)).sort((a, b) => b.from.localeCompare(a.from))[0];
306
+ }
307
+ function weekFor(ctx, date) {
308
+ const plan = planOn(ctx.plans, date);
309
+ return plan ? { week: plan.week, plan } : { week: ctx.hours.week };
310
+ }
311
+ function ruleOfDay(day, source) {
312
+ if (!day || day.closed) return { closed: true, times: [], source };
313
+ return { closed: false, times: spansOf(day), source };
314
+ }
315
+ function hoursOn(ctx, date) {
316
+ const { hours } = ctx;
317
+ if (hours.closedOn === date) return { closed: true, times: [], reason: hours.closedNote || void 0, source: "today", field: "closedNote" };
318
+ const covering = (hours.exceptions ?? []).filter((e) => e.when.from <= date && date <= e.when.to && (e.closed || spansOf(e).length > 0));
319
+ const exception = covering.find((e) => e.closed) ?? covering[0];
320
+ if (exception) {
321
+ const field = `exceptions.${exception._id}.reason`;
322
+ return exception.closed ? { closed: true, times: [], reason: exception.reason, source: "exception", field } : { closed: false, times: spansOf(exception), reason: exception.reason, source: "exception", field };
323
+ }
324
+ const { week, plan } = weekFor(ctx, date);
325
+ const holiday = holidayOn(date, calendarIn(ctx));
326
+ const setting = hours.holidays ?? "sunday";
327
+ if (holiday && setting !== "normal") {
328
+ const base = setting === "closed" ? void 0 : week.find((d) => d.day === "su");
329
+ return { ...ruleOfDay(base, "holiday"), holiday: { de: holiday.de, en: holiday.en } };
330
+ }
331
+ return ruleOfDay(week.find((d) => d.day === weekdayOf(date)), plan ? "plan" : "week");
332
+ }
333
+ function weekOf(ctx, monday) {
334
+ return Array.from({ length: 7 }, (_, i) => {
335
+ const date = addDays(monday, i);
336
+ return { date, weekday: weekdayOf(date), ...hoursOn(ctx, date) };
337
+ });
338
+ }
339
+ function todayState(ctx, today) {
340
+ const rule = hoursOn(ctx, today);
341
+ if (!rule.closed) return { kind: "open", times: rule.times, ...rule.source === "exception" ? { reason: rule.reason, field: rule.field } : {} };
342
+ if (rule.source === "today" || rule.source === "exception") return { kind: "closed", reason: rule.reason, field: rule.field ?? "" };
343
+ if (rule.holiday) return { kind: "holiday", holiday: rule.holiday };
344
+ return { kind: "restDay" };
345
+ }
346
+ function noticesOn(notices, ctx, today) {
347
+ if (todayState(ctx, today).kind !== "closed") return [...notices];
348
+ return notices.filter((n) => n.data.until !== today);
349
+ }
350
+ function upcomingHolidayClosures(ctx, today, days) {
351
+ const out = [];
352
+ for (let i = 1; i <= days; i++) {
353
+ const date = addDays(today, i);
354
+ const rule = hoursOn(ctx, date);
355
+ if (rule.source === "holiday" && rule.closed && rule.holiday && !ruleOfDay(weekFor(ctx, date).week.find((d) => d.day === weekdayOf(date)), "week").closed) {
356
+ out.push({ _id: `holiday-${date}`, when: { from: date, to: date }, closed: true, holiday: rule.holiday });
357
+ }
358
+ }
359
+ return out;
360
+ }
361
+ function closedOnClosure(hours, today) {
362
+ const day = hours.closedOn;
363
+ return day && day > today ? { _id: "closedOn", when: { from: day, to: day }, reason: hours.closedNote ?? "", closed: true, field: "closedNote" } : void 0;
364
+ }
365
+ function upcomingClosures(ctx, today, days) {
366
+ const until = addDays(today, days);
367
+ const { hours } = ctx;
368
+ const closures = (hours.exceptions ?? []).filter((e) => e.closed && e.when.from > today && e.when.from <= until).map((e) => ({ ...e, field: `exceptions.${e._id}.reason` }));
369
+ const closedOn = closedOnClosure(hours, today);
370
+ if (closedOn && closedOn.when.from <= until) closures.push(closedOn);
371
+ closures.push(...upcomingHolidayClosures(ctx, today, days));
372
+ return closures.sort((a, b) => a.when.from.localeCompare(b.when.from));
373
+ }
374
+ function endOf(plan, starts) {
375
+ if (!plan.to || !plan.yearly) return plan.to;
376
+ const year = Number(starts.slice(0, 4));
377
+ const md = plan.to.slice(5);
378
+ return `${md < plan.from.slice(5) ? year + 1 : year}-${md}`;
379
+ }
380
+ function upcomingPlans(plans, today, days) {
381
+ const until = addDays(today, days);
382
+ const out = [];
383
+ for (const plan of plans) {
384
+ let starts = plan.from;
385
+ if (plan.yearly) {
386
+ const year = Number(today.slice(0, 4));
387
+ const md = plan.from.slice(5);
388
+ starts = `${year}-${md}` > today ? `${year}-${md}` : `${year + 1}-${md}`;
389
+ if (plan.from > starts) starts = plan.from;
390
+ }
391
+ let ends = endOf(plan, starts);
392
+ if (ends) {
393
+ for (let day = ends, i = 0; day >= starts && i < 366; day = addDays(day, -1), i++) {
394
+ if (planOn(plans, day) === plan) {
395
+ ends = day;
396
+ break;
397
+ }
398
+ }
399
+ }
400
+ if (starts > today && starts <= until) out.push({ plan, starts, ...ends ? { ends } : {} });
401
+ }
402
+ return out.sort((a, b) => a.starts.localeCompare(b.starts));
403
+ }
404
+ const TIME = /^([01]\d|2[0-3]):[0-5]\d$/;
405
+ const isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
406
+ const isText = (v) => typeof v === "string" ? v !== "" : isRecord(v) && Object.values(v).every((x) => typeof x === "string");
407
+ function asHours(data) {
408
+ if (!isRecord(data) || !Array.isArray(data.week) || !data.week.every((d) => isRecord(d) && typeof d.day === "string")) return void 0;
409
+ if (data.exceptions !== void 0 && data.exceptions !== null && !(Array.isArray(data.exceptions) && data.exceptions.every((e) => isRecord(e) && isRecord(e.when)))) return void 0;
410
+ return { ...data, exceptions: data.exceptions ?? [] };
411
+ }
412
+ function hoursLine(data, ctx) {
413
+ const hours = asHours(data);
414
+ if (!hours) return { state: "unknown" };
415
+ const today = todayIn(ctx.timeZone, ctx.now);
416
+ if (ctx.holidays === void 0) {
417
+ const [none, at] = [lineOn(hours, today, ctx.plans, "none"), lineOn(hours, today, ctx.plans, "AT")];
418
+ if (JSON.stringify(none) === JSON.stringify(at)) return none;
419
+ return none.state === "unknown" ? none : at.state === "unknown" ? at : { state: "unknown", calendar: true };
420
+ }
421
+ const rule = hoursOn({ hours, plans: ctx.plans, calendar: ctx.holidays === "custom" ? "none" : ctx.holidays }, today);
422
+ const line = lineOf(rule, hours, today, ctx.plans);
423
+ if (ctx.holidays === "custom" && line.state !== "unknown" && rule.source !== "today" && rule.source !== "exception") return { state: "unknown", calendar: true };
424
+ return line;
425
+ }
426
+ function lineOn(hours, today, plans, calendar) {
427
+ return lineOf(hoursOn({ hours, plans, calendar }, today), hours, today, plans);
428
+ }
429
+ function lineOf(rule, hours, today, plans) {
430
+ if (rule.source === "week" || rule.source === "plan") {
431
+ if (!weekFor({ hours, plans }, today).week.some((d) => d.day === weekdayOf(today))) return { state: "unknown" };
432
+ }
433
+ if (rule.closed) {
434
+ if (rule.holiday) return { state: "closed", holiday: { de: rule.holiday.de, en: rule.holiday.en } };
435
+ return isText(rule.reason) ? { state: "closed", reason: rule.reason } : { state: "closed" };
436
+ }
437
+ const ranges = rule.times.filter((s) => TIME.test(s.from) && TIME.test(s.to)).map((s) => [s.from, s.to]);
438
+ return ranges.length ? { state: "open", ranges } : { state: "unknown" };
439
+ }
440
+
441
+ const noticeFields = () => ({
442
+ text: f.text(both("Text", "Text"), { max: 140 }),
443
+ until: f.date(both("Gilt bis", "Shown until"), {
444
+ optional: true,
445
+ hint: both("Leer lassen, wenn der Hinweis bleiben soll, bis du ihn entfernst.", "Leave empty to keep the notice until you remove it.")
446
+ })
447
+ });
448
+ function noticesBrick(opts, first) {
449
+ return collection({
450
+ label: opts.label ?? both("Hinweise", "Notices"),
451
+ ...sourceOf(opts),
452
+ fields: noticeFields(),
453
+ max: 5,
454
+ order: "manual",
455
+ visible: { mode: "until", field: "until", deleteExpired: true },
456
+ tasks: [
457
+ ...first,
458
+ { id: "add", label: both("Hinweis hinzuf\xFCgen", "Add a notice"), kind: "create" },
459
+ { id: "remove", label: both("Hinweis entfernen", "Remove a notice"), kind: "remove" }
460
+ ]
461
+ });
462
+ }
463
+ function notices(opts = {}) {
464
+ return noticesBrick(opts, []);
465
+ }
466
+
467
+ function eventsBrick(opts, booking, extra, afterBooking) {
468
+ const fields = {
469
+ title: f.text(both("Titel", "Title"), { max: 120 }),
470
+ slug: f.slug(both("Adresse", "Web address"), { from: "title" }),
471
+ when: f.daterange(both("Wann", "When")),
472
+ // The clock time of the first day. `when` stays the order and visibility field.
473
+ start: f.time(both("Beginn", "Starts at"), { optional: true }),
474
+ ...extra,
475
+ description: f.markdown(both("Beschreibung", "Description"), { profile: "basic", max: 5e3 }),
476
+ price: f.money(both("Preis", "Price"), { optional: true }),
477
+ booking: f.text(booking, { format: "url", optional: true }),
478
+ ...afterBooking ?? {},
479
+ image: f.image(both("Bild", "Image"), { optional: true }),
480
+ // The site shows "fully booked" instead of the booking link. Optional: a missing value means bookable.
481
+ soldOut: f.boolean(both("Ausgebucht", "Fully booked"), { optional: true })
482
+ };
483
+ const tasks = [
484
+ { id: "add", label: both("Veranstaltung hinzuf\xFCgen", "Add an event"), kind: "create" },
485
+ { id: "soldOut", label: both("Veranstaltung ausgebucht", "Event fully booked"), kind: "edit", pick: true, fields: ["soldOut"] },
486
+ { id: "remove", label: both("Veranstaltung entfernen", "Remove an event"), kind: "remove" }
487
+ ];
488
+ return collection({
489
+ label: opts.label ?? both("Veranstaltungen", "Events"),
490
+ ...sourceOf(opts),
491
+ fields,
492
+ max: 30,
493
+ order: { field: "when", dir: "asc" },
494
+ // `extra` keeps the fields generic, so the type cannot see that `when` is still the daterange.
495
+ visible: { mode: "until", field: "when" },
496
+ ...opts.route ? { route: opts.route } : {},
497
+ tasks
498
+ });
499
+ }
500
+ function events(opts) {
501
+ return eventsBrick(opts, both("Buchungs-Link", "Booking link"), {
502
+ // The clock time of the last day.
503
+ end: f.time(both("Ende", "Ends at"), { optional: true }),
504
+ location: f.text(both("Ort", "Location"), { max: 200, optional: true }),
505
+ online: f.text(both("Online-Link", "Online link"), { format: "url", optional: true })
506
+ }, {
507
+ // Ticket sales or bookings that close before the event: the site hides the booking link after this day.
508
+ bookingUntil: f.date(both("Buchbar bis", "Bookable until"), {
509
+ optional: true,
510
+ hint: both("Letzter Tag, an dem der Buchungs-Link erscheint. Leer: bis zur Veranstaltung.", "Last day the booking link shows. Empty: until the event.")
511
+ })
512
+ });
513
+ }
514
+ function sessionsBrick(opts, speakers) {
515
+ return collection({
516
+ label: opts.label ?? both("Programmpunkte", "Sessions"),
517
+ ...sourceOf(opts),
518
+ fields: {
519
+ event: f.ref(both("Veranstaltung", "Event"), { to: opts.events ?? "events" }),
520
+ date: f.date(both("Datum", "Date")),
521
+ from: f.time(both("Beginn", "Starts at")),
522
+ to: f.time(both("Ende", "Ends at"), { after: "from" }),
523
+ title: f.text(both("Titel", "Title"), { max: 120 }),
524
+ // One or two sentences: what the session is about and who it is for.
525
+ summary: f.text(both("Kurzbeschreibung", "Summary"), { max: 200, optional: true }),
526
+ room: f.text(both("Raum", "Room"), { max: 80, optional: true }),
527
+ speakers,
528
+ status: f.choice(both("Status", "Status"), {
529
+ options: [
530
+ { value: "open", label: both("Findet statt", "Open") },
531
+ { value: "soldOut", label: both("Ausgebucht", "Sold out") },
532
+ { value: "cancelled", label: both("Abgesagt", "Cancelled") }
533
+ ],
534
+ default: "open"
535
+ })
536
+ },
537
+ max: 300,
538
+ // Core orders by one field: sessions of one day share a date and come in no fixed order here (also in the Studio
539
+ // list). Pages sort with `sessionsInOrder` (date, then start time).
540
+ order: { field: "date", dir: "asc" },
541
+ visible: { mode: "until", field: "date" },
542
+ tasks: [
543
+ // The job during the event: a session moves, changes its room, fills up or is cancelled.
544
+ { id: "change", label: both("Programmpunkt \xE4ndern", "Change a session"), kind: "edit", pick: true, fields: ["from", "to", "room", "status"] },
545
+ { id: "soldOut", label: both("Ausgebucht", "Sold out"), kind: "edit", pick: true, fields: ["status"], prefill: { status: "soldOut" } }
546
+ ]
547
+ });
548
+ }
549
+ function sessions(opts = {}) {
550
+ return opts.people ? sessionsBrick(opts, speakerRefs(opts.people)) : sessionsBrick(opts, speakerText());
551
+ }
552
+ const speakerRefs = (people) => f.refs(both("Vortragende", "Speakers"), { to: people, max: 10, optional: true });
553
+ const speakerText = () => f.text(both("Vortragende", "Speakers"), { max: 200, optional: true });
554
+ function sessionsInOrder(items) {
555
+ return [...items].sort((a, b) => a.data.date.localeCompare(b.data.date) || a.data.from.localeCompare(b.data.from) || String(a.data.title ?? "").localeCompare(String(b.data.title ?? "")));
556
+ }
557
+ const SCHEDULED = "https://schema.org/EventScheduled";
558
+ const SOLD_OUT = "https://schema.org/SoldOut";
559
+ function attendanceOf(input) {
560
+ const mode = input.online ? input.location ? "Mixed" : "Online" : "Offline";
561
+ return `https://schema.org/${mode}EventAttendanceMode`;
562
+ }
563
+ function locationOf(input) {
564
+ const place = input.location ? { "@type": "Place", name: input.location.name, ...input.location.address ? { address: postalAddress(input.location.address, input.country) } : {} } : void 0;
565
+ const virtual = input.online ? { "@type": "VirtualLocation", url: input.online } : void 0;
566
+ return place && virtual ? [place, virtual] : place ?? virtual;
567
+ }
568
+ function subEvent(s, input) {
569
+ return {
570
+ "@type": "Event",
571
+ name: s.title,
572
+ startDate: zonedDateTime(s.date, s.from, input.timeZone),
573
+ endDate: zonedDateTime(s.date, s.to, input.timeZone),
574
+ eventStatus: s.status === "cancelled" ? "https://schema.org/EventCancelled" : SCHEDULED,
575
+ ...s.room ? { location: { "@type": "Place", name: s.room } } : {},
576
+ ...s.speakers?.length ? { performer: s.speakers.map((name) => ({ "@type": "Person", name })) } : {},
577
+ ...s.status === "soldOut" ? { offers: { "@type": "Offer", availability: SOLD_OUT } } : {}
578
+ };
579
+ }
580
+ function eventJsonLd(input) {
581
+ const description = input.description ? plainText(input.description) : "";
582
+ const offers = input.soldOut ? { "@type": "Offer", ...input.price ? offerPrice(input.price, input.currency) : {}, availability: SOLD_OUT } : input.price ? { "@type": "Offer", ...offerPrice(input.price, input.currency), ...input.booking ? { url: input.booking } : {} } : void 0;
583
+ const location = locationOf(input);
584
+ return {
585
+ "@context": "https://schema.org",
586
+ "@type": "Event",
587
+ name: input.title,
588
+ startDate: input.start ? zonedDateTime(input.when.from, input.start, input.timeZone) : input.when.from,
589
+ endDate: input.end ? zonedDateTime(input.when.to, input.end, input.timeZone) : input.when.to,
590
+ eventStatus: SCHEDULED,
591
+ eventAttendanceMode: attendanceOf(input),
592
+ ...location ? { location } : {},
593
+ url: input.url,
594
+ ...input.image ? { image: input.image } : {},
595
+ ...description ? { description } : {},
596
+ ...offers ? { offers } : {},
597
+ ...input.sessions?.length ? { subEvent: input.sessions.map((s) => subEvent(s, input)) } : {}
598
+ };
599
+ }
600
+
601
+ export { spansText as A, timesOf as B, todayState as C, upcomingClosures as D, upcomingHolidayClosures as E, upcomingPlans as F, weekOf as G, eventsBrick as H, hourPlansBrick as I, hoursBrick as J, noticesBrick as K, offerPrice as L, postalAddress as M, weekdays as N, addDays as a, weekdayOf as b, spansOf as c, weekdayKeys as d, both as e, austrianHolidays as f, calendarIn as g, hoursOn as h, calendarOf as i, closedOnClosure as j, eventJsonLd as k, events as l, germanHolidays as m, holidayOn as n, hourPlans as o, hours as p, hoursLine as q, notices as r, sourceOf as s, noticesOn as t, offerJsonLd as u, planOn as v, weekFor as w, sessions as x, sessionsInOrder as y, singaporeHolidays as z };