@lupinum/ginko 0.4.0 → 0.6.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 (90) hide show
  1. package/CHANGELOG.md +161 -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 +59 -56
  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 +108 -15
  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.BBkZW2nW.d.ts → ginko.6cI8KXZP.d.mts} +2 -2
  55. package/dist/shared/{ginko.CrNRBkS5.mjs → ginko.B1-DoOea.mjs} +253 -309
  56. package/dist/shared/ginko.BNVC-g9n.mjs +10389 -0
  57. package/dist/shared/{ginko.Bwan3BB-.d.mts → ginko.B_pACpVU.d.ts} +2 -2
  58. package/dist/shared/ginko.BaFweQcB.d.mts +91 -0
  59. package/dist/shared/ginko.BiDXgjLy.d.ts +91 -0
  60. package/dist/shared/ginko.C-oz8wwI.mjs +95 -0
  61. package/dist/shared/{ginko.BDF3bW0C.d.ts → ginko.CLzVaDi4.d.ts} +56 -5
  62. package/dist/shared/ginko.C_nq6vk7.d.mts +1622 -0
  63. package/dist/shared/ginko.CjWB8N7f.d.ts +128 -0
  64. package/dist/shared/ginko.D1PeQ3ds.mjs +36 -0
  65. package/dist/shared/ginko.D2oht0Kx.d.ts +1622 -0
  66. package/dist/shared/ginko.DCHGkQ7P.mjs +116 -0
  67. package/dist/shared/ginko.DIl7l-75.mjs +26 -0
  68. package/dist/shared/{ginko.akHewCGt.d.mts → ginko.DkIc0ZZk.d.mts} +56 -5
  69. package/dist/shared/{ginko.D-fNojn9.d.mts → ginko.DrkUeYFP.d.mts} +824 -10
  70. package/dist/shared/{ginko.D-fNojn9.d.ts → ginko.DrkUeYFP.d.ts} +824 -10
  71. package/dist/shared/ginko.WAfoMhv1.mjs +601 -0
  72. package/dist/shared/ginko.o1yIKlgj.d.mts +128 -0
  73. package/dist/shared/{ginko.Dht0xxE5.mjs → ginko.raCzOPYC.mjs} +1 -2
  74. package/dist/shared/{ginko.C90fPpMP.mjs → ginko.t-gbwzxh.mjs} +128 -101
  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.CQvxhyez.mjs +0 -40
  89. package/dist/shared/ginko.Dc0oNcDv.d.ts +0 -172
  90. package/dist/shared/ginko.Mn30dmlm.mjs +0 -20
@@ -0,0 +1,416 @@
1
+ import { f, O as studioLabel } from './shared/ginko.BNVC-g9n.mjs';
2
+ import { s as single, c as collection } from './shared/ginko.C-oz8wwI.mjs';
3
+ export { h as withoutFiles } from './shared/ginko.C-oz8wwI.mjs';
4
+ import { o as organizationLd, c as compact, a as articleLd, p as postalAddressLd, d as faqLd } from './shared/ginko.DCHGkQ7P.mjs';
5
+ import { s as sourceOf, w as weekFor, a as addDays, h as hoursOn, b as weekdayOf, c as spansOf, d as weekdayKeys, e as both } from './shared/ginko.WAfoMhv1.mjs';
6
+ export { f as austrianHolidays, g as calendarIn, i as calendarOf, j as closedOnClosure, k as eventJsonLd, l as events, m as germanHolidays, n as holidayOn, o as hourPlans, p as hours, q as hoursLine, r as notices, t as noticesOn, u as offerJsonLd, v as planOn, x as sessions, y as sessionsInOrder, z as singaporeHolidays, A as spansText, B as timesOf, C as todayState, D as upcomingClosures, E as upcomingHolidayClosures, F as upcomingPlans, G as weekOf } from './shared/ginko.WAfoMhv1.mjs';
7
+ import { l as limitOf } from './shared/ginko.D1PeQ3ds.mjs';
8
+ export { c as calendarOfCountry } from './shared/ginko.D1PeQ3ds.mjs';
9
+ import 'comark';
10
+ import 'comark/plugins/alert';
11
+ import 'comark/plugins/attributes';
12
+ import 'comark/plugins/components';
13
+ import 'comark/plugins/emoji';
14
+ import 'comark/plugins/footnotes';
15
+ import 'comark/plugins/frontmatter';
16
+ import 'comark/plugins/math';
17
+ import 'comark/plugins/mermaid';
18
+ import 'comark/plugins/task-list';
19
+ import 'comark/plugins/toc';
20
+ import '@lupinum/ginko-i18n/core';
21
+
22
+ function site(o = {}) {
23
+ const plain = { optional: true, translate: false };
24
+ const fields = {
25
+ legalName: f.text(studioLabel("Offizieller Name", "Legal name", "Nombre legal", "Raison sociale"), {
26
+ ...plain,
27
+ hint: studioLabel("Wie im Register eingetragen.", "As registered.", "Tal como figura en el registro.", "Tel qu\u2019enregistr\xE9.")
28
+ }),
29
+ street: f.text(studioLabel("Stra\xDFe und Hausnummer", "Street and number", "Calle y n\xFAmero", "Rue et num\xE9ro"), plain),
30
+ postalCode: f.text(studioLabel("Postleitzahl", "Postcode", "C\xF3digo postal", "Code postal"), { ...plain, max: 10 }),
31
+ city: f.text(studioLabel("Ort", "Town or city", "Localidad", "Ville"), plain),
32
+ email: f.text(studioLabel("E-Mail", "Email", "Correo electr\xF3nico", "E-mail"), { optional: true, format: "email" }),
33
+ phone: f.text(studioLabel("Telefon", "Phone", "Tel\xE9fono", "T\xE9l\xE9phone"), {
34
+ optional: true,
35
+ format: "tel",
36
+ hint: studioLabel("Mit L\xE4ndervorwahl.", "With country code.", "Con prefijo internacional.", "Avec l\u2019indicatif du pays.")
37
+ }),
38
+ registryNumber: f.text(studioLabel("Registernummer", "Registration number", "N\xFAmero de registro", "Num\xE9ro d\u2019immatriculation"), plain),
39
+ registryCourt: f.text(studioLabel("Registergericht", "Registry", "Registro", "Greffe"), { optional: true }),
40
+ vatId: f.text(studioLabel("Umsatzsteuer-ID", "VAT number", "NIF-IVA", "Num\xE9ro de TVA"), plain)
41
+ };
42
+ return single({
43
+ label: o.label ?? studioLabel("Kontakt und Anschrift", "Contact and address", "Contacto y direcci\xF3n", "Contact et adresse"),
44
+ describe: studioLabel(
45
+ "Name, Anschrift und Kontakt, zum Beispiel f\xFCr das Impressum und die Fu\xDFzeile.",
46
+ "Name, address and contact details, for example for the imprint and the footer.",
47
+ "Nombre, direcci\xF3n y datos de contacto, por ejemplo para el aviso legal y el pie de p\xE1gina.",
48
+ "Nom, adresse et coordonn\xE9es, par exemple pour les mentions l\xE9gales et le pied de page."
49
+ ),
50
+ ...sourceOf(o),
51
+ fields,
52
+ tasks: [{
53
+ id: "edit",
54
+ kind: "edit",
55
+ label: studioLabel("Kontaktdaten \xE4ndern", "Change contact details", "Cambiar los datos de contacto", "Modifier les coordonn\xE9es"),
56
+ fields: Object.keys(fields)
57
+ }]
58
+ });
59
+ }
60
+ function siteJsonLd(data, o) {
61
+ return organizationLd({
62
+ ...o,
63
+ legalName: data.legalName,
64
+ email: data.email,
65
+ telephone: data.phone,
66
+ address: { street: data.street, postalCode: data.postalCode, city: data.city, country: o.country },
67
+ vatID: data.vatId
68
+ });
69
+ }
70
+
71
+ const legalTypes = [
72
+ { value: "privacy", label: studioLabel("Datenschutz", "Privacy policy", "Pol\xEDtica de privacidad", "Politique de confidentialit\xE9") },
73
+ { value: "customer-privacy", label: studioLabel("Datenschutz f\xFCr Kunden", "Privacy policy for customers", "Privacidad para clientes", "Confidentialit\xE9 pour les clients") },
74
+ { value: "imprint", label: studioLabel("Impressum", "Imprint", "Aviso legal", "Mentions l\xE9gales") },
75
+ { value: "terms", label: studioLabel("AGB", "Terms and conditions", "T\xE9rminos y condiciones", "Conditions g\xE9n\xE9rales") },
76
+ { value: "cookies", label: studioLabel("Cookies", "Cookie policy", "Pol\xEDtica de cookies", "Politique relative aux cookies") },
77
+ { value: "accessibility", label: studioLabel("Barrierefreiheit", "Accessibility statement", "Declaraci\xF3n de accesibilidad", "D\xE9claration d\u2019accessibilit\xE9") }
78
+ ];
79
+ function legal(o = {}) {
80
+ return collection({
81
+ label: o.label ?? studioLabel("Rechtliches", "Legal", "Textos legales", "Informations l\xE9gales"),
82
+ ...sourceOf(o),
83
+ max: 20,
84
+ order: "manual",
85
+ ...o.route ? { route: o.route } : {},
86
+ tasks: [{
87
+ id: "update",
88
+ kind: "edit",
89
+ pick: true,
90
+ fields: ["body", "updatedAt"],
91
+ prefill: { updatedAt: "@today" },
92
+ label: studioLabel("Rechtstext aktualisieren", "Update a legal text", "Actualizar un texto legal", "Mettre \xE0 jour un texte juridique")
93
+ }],
94
+ fields: {
95
+ title: f.text(studioLabel("Titel", "Title", "T\xEDtulo", "Titre")),
96
+ description: f.longtext(studioLabel("Beschreibung", "Description", "Descripci\xF3n", "Description"), { optional: true }),
97
+ slug: f.slug(studioLabel("Adresse", "Address", "Direcci\xF3n", "Adresse"), { from: "title" }),
98
+ legalType: f.choice(studioLabel("Art", "Kind", "Tipo", "Type"), { options: legalTypes }),
99
+ updatedAt: f.date(studioLabel("Stand", "Last updated", "\xDAltima actualizaci\xF3n", "Derni\xE8re mise \xE0 jour")),
100
+ // Never machine-translated: a legal text in another language is a legal text of its own.
101
+ body: f.markdown(studioLabel("Text", "Text", "Texto", "Texte"), { translate: "manual" })
102
+ }
103
+ });
104
+ }
105
+
106
+ const slugField = () => f.slug(studioLabel("Adresse", "Address", "Direcci\xF3n", "Adresse"), { from: "name" });
107
+ function people(o = {}) {
108
+ return collection({
109
+ label: o.label ?? studioLabel("Personen", "People", "Personas", "Personnes"),
110
+ ...sourceOf(o),
111
+ max: 100,
112
+ order: "manual",
113
+ ...o.route ? { route: o.route } : {},
114
+ tasks: [
115
+ { id: "add", kind: "create", label: studioLabel("Person hinzuf\xFCgen", "Add a person", "A\xF1adir una persona", "Ajouter une personne") },
116
+ { id: "edit", kind: "edit", pick: true, fields: ["name", "role", "bio", "email", "phone", "photo"], label: studioLabel("Person bearbeiten", "Edit a person", "Editar una persona", "Modifier une personne") },
117
+ { id: "remove", kind: "remove", label: studioLabel("Person entfernen", "Remove a person", "Eliminar una persona", "Retirer une personne") }
118
+ ],
119
+ fields: {
120
+ // A name is the same in every language.
121
+ name: f.text(studioLabel("Name", "Name", "Nombre", "Nom"), { translate: false }),
122
+ role: f.text(studioLabel("Rolle", "Role", "Funci\xF3n", "Fonction"), { optional: true }),
123
+ bio: f.longtext(studioLabel("Kurzbiografie", "Short bio", "Biograf\xEDa breve", "Courte biographie"), { optional: true }),
124
+ email: f.text(studioLabel("E-Mail", "Email", "Correo electr\xF3nico", "E-mail"), { optional: true, format: "email", translate: false }),
125
+ phone: f.text(studioLabel("Telefon", "Phone", "Tel\xE9fono", "T\xE9l\xE9phone"), { optional: true, format: "tel", translate: false }),
126
+ photo: f.image(studioLabel("Foto", "Photo", "Foto", "Photo"), { optional: true }),
127
+ // Only with a route: the stored shape of people without pages does not change.
128
+ // `o.route` is the runtime value of `R`, so the spread matches SlugOf<R>; TypeScript cannot narrow a generic by it.
129
+ ...o.route ? { slug: slugField() } : {}
130
+ }
131
+ });
132
+ }
133
+ function personJsonLd(p) {
134
+ return compact({
135
+ "@context": "https://schema.org",
136
+ "@type": "Person",
137
+ name: p.name,
138
+ jobTitle: p.role,
139
+ description: p.bio,
140
+ email: p.email,
141
+ telephone: p.phone,
142
+ image: p.image,
143
+ url: p.url,
144
+ worksFor: p.worksFor ? compact({ "@type": "Organization", name: p.worksFor.name, url: p.worksFor.url }) : void 0
145
+ });
146
+ }
147
+
148
+ function posts(o = {}) {
149
+ const tag = studioLabel("Schlagwort", "Tag", "Etiqueta", "Mot-cl\xE9");
150
+ return collection({
151
+ label: o.label ?? studioLabel("Beitr\xE4ge", "Posts", "Art\xEDculos", "Articles"),
152
+ ...sourceOf(o),
153
+ max: o.max ?? 500,
154
+ order: { field: "date", dir: "desc" },
155
+ ...o.route ? { route: o.route } : {},
156
+ fields: {
157
+ title: f.text(studioLabel("Titel", "Title", "T\xEDtulo", "Titre")),
158
+ description: f.longtext(studioLabel("Beschreibung", "Description", "Descripci\xF3n", "Description"), { optional: true }),
159
+ date: f.date(studioLabel("Datum", "Date", "Fecha", "Date")),
160
+ authors: f.refs(studioLabel("Autorinnen und Autoren", "Authors", "Autores", "Auteurs"), { to: o.authors ?? "authors", optional: true }),
161
+ image: f.image(studioLabel("Bild", "Image", "Imagen", "Image"), { optional: true }),
162
+ tags: f.list(studioLabel("Schlagw\xF6rter", "Tags", "Etiquetas", "Mots-cl\xE9s"), { itemLabel: tag, fields: { tag: f.text(tag, { max: 40 }) }, max: 10, optional: true }),
163
+ slug: f.slug(studioLabel("Adresse", "Address", "Direcci\xF3n", "Adresse"), { from: "title" }),
164
+ body: f.markdown(studioLabel("Text", "Text", "Texto", "Texte"), { profile: "page" })
165
+ }
166
+ });
167
+ }
168
+ function authors(o = {}) {
169
+ return collection({
170
+ label: o.label ?? studioLabel("Autorinnen und Autoren", "Authors", "Autores", "Auteurs"),
171
+ ...sourceOf(o),
172
+ max: 100,
173
+ order: "manual",
174
+ fields: {
175
+ name: f.text(studioLabel("Name", "Name", "Nombre", "Nom")),
176
+ role: f.text(studioLabel("Rolle", "Role", "Funci\xF3n", "Fonction"), { optional: true }),
177
+ avatar: f.image(studioLabel("Bild", "Image", "Imagen", "Image"), { optional: true }),
178
+ /** Shown as a link on the author's name. */
179
+ url: f.text(studioLabel("Website", "Website", "Sitio web", "Site web"), { format: "url", optional: true })
180
+ }
181
+ });
182
+ }
183
+ function changelog(o = {}) {
184
+ return collection({
185
+ label: o.label ?? studioLabel("\xC4nderungen", "Changelog", "Registro de cambios", "Journal des modifications"),
186
+ ...sourceOf(o),
187
+ max: o.max ?? 500,
188
+ order: { field: "date", dir: "desc" },
189
+ // A release written ahead is the normal case: an entry dated after the build day is scheduled, `draft: true` holds it back.
190
+ drafts: true,
191
+ fields: {
192
+ version: f.text(studioLabel("Version", "Version", "Versi\xF3n", "Version"), { max: 40, translate: false }),
193
+ date: f.date(studioLabel("Datum", "Date", "Fecha", "Date")),
194
+ body: f.markdown(studioLabel("Text", "Text", "Texto", "Texte"), { profile: "page" })
195
+ }
196
+ });
197
+ }
198
+ function releaseAnchor(version) {
199
+ return `v${version.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "")}`;
200
+ }
201
+ function postJsonLd(p) {
202
+ return articleLd({
203
+ type: "BlogPosting",
204
+ title: p.title,
205
+ url: p.url,
206
+ description: p.description,
207
+ image: p.image,
208
+ published: p.date,
209
+ modified: p.modified,
210
+ author: p.authors,
211
+ keywords: p.tags,
212
+ lang: p.lang
213
+ });
214
+ }
215
+
216
+ const employmentTypes = [
217
+ { value: "fullTime", label: studioLabel("Vollzeit", "Full time", "Jornada completa", "Temps plein") },
218
+ { value: "partTime", label: studioLabel("Teilzeit", "Part time", "Media jornada", "Temps partiel") },
219
+ { value: "freelance", label: studioLabel("Freie Mitarbeit", "Freelance", "Aut\xF3nomo", "Ind\xE9pendant") },
220
+ { value: "internship", label: studioLabel("Praktikum", "Internship", "Pr\xE1cticas", "Stage") },
221
+ { value: "apprenticeship", label: studioLabel("Lehre", "Apprenticeship", "Formaci\xF3n profesional", "Apprentissage") }
222
+ ];
223
+ const workPlaces = [
224
+ { value: "onsite", label: studioLabel("Vor Ort", "On site", "Presencial", "Sur place") },
225
+ { value: "hybrid", label: studioLabel("Hybrid", "Hybrid", "H\xEDbrido", "Hybride") },
226
+ { value: "remote", label: studioLabel("Remote", "Remote", "Remoto", "\xC0 distance") }
227
+ ];
228
+ function jobs(o = {}) {
229
+ return collection({
230
+ label: o.label ?? studioLabel("Stellen", "Jobs", "Empleos", "Offres d\u2019emploi"),
231
+ ...sourceOf(o),
232
+ max: 30,
233
+ order: { field: "title", dir: "asc" },
234
+ ...o.route ? { route: o.route } : {},
235
+ visible: { mode: "until", field: "validUntil" },
236
+ tasks: [
237
+ { id: "add", kind: "create", label: studioLabel("Stelle ausschreiben", "Post a job", "Publicar una oferta", "Publier une offre") },
238
+ { id: "extend", kind: "edit", pick: true, fields: ["validUntil"], label: studioLabel("Bewerbungsfrist \xE4ndern", "Change the deadline", "Cambiar el plazo", "Modifier la date limite") },
239
+ { id: "remove", kind: "remove", label: studioLabel("Stelle entfernen", "Remove a job", "Eliminar una oferta", "Retirer une offre") }
240
+ ],
241
+ fields: {
242
+ title: f.text(studioLabel("Titel", "Title", "T\xEDtulo", "Titre")),
243
+ description: f.longtext(studioLabel("Kurzbeschreibung", "Short description", "Descripci\xF3n breve", "Description courte")),
244
+ slug: f.slug(studioLabel("Adresse", "Address", "Direcci\xF3n", "Adresse"), { from: "title" }),
245
+ location: f.text(studioLabel("Ort", "Location", "Ubicaci\xF3n", "Lieu")),
246
+ employmentType: f.choice(studioLabel("Anstellung", "Employment", "Tipo de contrato", "Type de contrat"), { options: employmentTypes }),
247
+ remote: f.choice(studioLabel("Arbeitsort", "Work place", "Lugar de trabajo", "Lieu de travail"), { options: workPlaces, default: "onsite" }),
248
+ applyUrl: f.text(studioLabel("Bewerbungs-Link", "Application link", "Enlace de solicitud", "Lien de candidature"), { format: "url", optional: true }),
249
+ applyEmail: f.text(studioLabel("Bewerbungs-E-Mail", "Application email", "Correo de solicitud", "E-mail de candidature"), { format: "email", optional: true }),
250
+ validUntil: f.date(studioLabel("Ausgeschrieben bis", "Open until", "Abierta hasta", "Ouverte jusqu\u2019au"), {
251
+ hint: studioLabel("Am Tag danach verschwindet die Stelle von der Website.", "The job leaves the website the day after.", "La oferta desaparece del sitio web al d\xEDa siguiente.", "L\u2019offre dispara\xEEt du site le lendemain.")
252
+ }),
253
+ body: f.markdown(studioLabel("Beschreibung", "Description", "Descripci\xF3n", "Description"))
254
+ }
255
+ });
256
+ }
257
+ const SCHEMA_EMPLOYMENT = {
258
+ fullTime: "FULL_TIME",
259
+ partTime: "PART_TIME",
260
+ freelance: "CONTRACTOR",
261
+ internship: "INTERN",
262
+ apprenticeship: "OTHER"
263
+ };
264
+ function jobJsonLd(j) {
265
+ const remote = j.remote === "remote";
266
+ return compact({
267
+ "@context": "https://schema.org",
268
+ "@type": "JobPosting",
269
+ title: j.title,
270
+ url: j.url,
271
+ description: j.description,
272
+ datePosted: j.datePosted,
273
+ validThrough: j.validUntil,
274
+ employmentType: j.employmentType && Object.hasOwn(SCHEMA_EMPLOYMENT, j.employmentType) ? SCHEMA_EMPLOYMENT[j.employmentType] : void 0,
275
+ hiringOrganization: compact({ "@type": "Organization", name: j.organization.name, sameAs: j.organization.url, logo: j.organization.logo }),
276
+ jobLocation: j.location ? { "@type": "Place", address: postalAddressLd({ city: j.location, country: j.country }) } : void 0,
277
+ jobLocationType: remote ? "TELECOMMUTE" : void 0,
278
+ applicantLocationRequirements: remote && j.country ? { "@type": "Country", name: j.country } : void 0
279
+ });
280
+ }
281
+
282
+ function faq(o = {}) {
283
+ return collection({
284
+ label: o.label ?? studioLabel("H\xE4ufige Fragen", "FAQ", "Preguntas frecuentes", "Questions fr\xE9quentes"),
285
+ ...sourceOf(o),
286
+ max: 100,
287
+ order: "manual",
288
+ tasks: [
289
+ { id: "add", kind: "create", label: studioLabel("Frage hinzuf\xFCgen", "Add a question", "A\xF1adir una pregunta", "Ajouter une question") },
290
+ { id: "edit", kind: "edit", pick: true, fields: ["question", "answer"], label: studioLabel("Frage bearbeiten", "Edit a question", "Editar una pregunta", "Modifier une question") },
291
+ { id: "remove", kind: "remove", label: studioLabel("Frage entfernen", "Remove a question", "Eliminar una pregunta", "Retirer une question") }
292
+ ],
293
+ fields: {
294
+ question: f.text(studioLabel("Frage", "Question", "Pregunta", "Question")),
295
+ answer: f.longtext(studioLabel("Antwort", "Answer", "Respuesta", "R\xE9ponse"))
296
+ }
297
+ });
298
+ }
299
+ function faqJsonLd(items) {
300
+ return faqLd([...items]);
301
+ }
302
+
303
+ function downloads(o = {}) {
304
+ return collection({
305
+ label: o.label ?? studioLabel("Downloads", "Downloads", "Descargas", "T\xE9l\xE9chargements"),
306
+ ...sourceOf(o),
307
+ max: 100,
308
+ order: { field: "date", dir: "desc" },
309
+ tasks: [
310
+ { id: "add", kind: "create", label: studioLabel("Datei hinzuf\xFCgen", "Add a file", "A\xF1adir un archivo", "Ajouter un fichier") },
311
+ { id: "edit", kind: "edit", pick: true, fields: ["title", "summary", "date", "file", "language"], label: studioLabel("Datei bearbeiten", "Edit a file", "Editar un archivo", "Modifier un fichier") },
312
+ { id: "remove", kind: "remove", label: studioLabel("Datei entfernen", "Remove a file", "Eliminar un archivo", "Retirer un fichier") }
313
+ ],
314
+ fields: {
315
+ title: f.text(studioLabel("Titel", "Title", "T\xEDtulo", "Titre")),
316
+ summary: f.longtext(studioLabel("Kurzbeschreibung", "Summary", "Resumen", "R\xE9sum\xE9"), { optional: true }),
317
+ date: f.date(studioLabel("Datum", "Date", "Fecha", "Date")),
318
+ file: f.file(studioLabel("Datei", "File", "Archivo", "Fichier"), { accept: ["pdf"], maxBytes: limitOf("file") }),
319
+ // The language of the document (a code such as `en`): one file serves every language of the site, so the list
320
+ // can say "in English" on the German page. Empty: the site's main language.
321
+ language: f.text(studioLabel("Sprache des Dokuments", "Language of the document", "Idioma del documento", "Langue du document"), {
322
+ optional: true,
323
+ translate: false,
324
+ max: 35,
325
+ hint: studioLabel("Sprachcode, zum Beispiel de oder en.", "A language code, for example en or de.", "Un c\xF3digo de idioma, por ejemplo es o en.", "Un code de langue, par exemple fr ou en.")
326
+ })
327
+ }
328
+ });
329
+ }
330
+
331
+ const escapeXml = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
332
+ const rfc822 = (day) => (/* @__PURE__ */ new Date(`${day}T00:00:00Z`)).toUTCString();
333
+ const tag = (name, value) => `<${name}>${escapeXml(value)}</${name}>`;
334
+ function rssXml(feed) {
335
+ const items = feed.items.map((item) => [
336
+ " <item>",
337
+ ` ${tag("title", item.title)}`,
338
+ ` ${tag("link", item.link)}`,
339
+ ` <guid isPermaLink="true">${escapeXml(item.link)}</guid>`,
340
+ ` ${tag("pubDate", rfc822(item.date))}`,
341
+ ...item.description ? [` ${tag("description", item.description)}`] : [],
342
+ ...item.authors.map((a) => ` ${tag("dc:creator", a)}`),
343
+ " </item>"
344
+ ].join("\n"));
345
+ const newest = feed.items.map((i) => i.date).sort().at(-1);
346
+ return [
347
+ '<?xml version="1.0" encoding="UTF-8"?>',
348
+ '<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">',
349
+ " <channel>",
350
+ ` ${tag("title", feed.title)}`,
351
+ ` ${tag("link", feed.link)}`,
352
+ ` ${tag("description", feed.description)}`,
353
+ ` ${tag("language", feed.language)}`,
354
+ ...newest ? [` ${tag("lastBuildDate", rfc822(newest))}`] : [],
355
+ ` <atom:link href="${escapeXml(feed.self)}" rel="self" type="application/rss+xml"/>`,
356
+ ...items,
357
+ " </channel>",
358
+ "</rss>",
359
+ ""
360
+ ].join("\n");
361
+ }
362
+
363
+ const dayNames = { mo: "Monday", tu: "Tuesday", we: "Wednesday", th: "Thursday", fr: "Friday", sa: "Saturday", su: "Sunday" };
364
+ const spanKey = (spans) => spans.map((s) => `${s.from}-${s.to}`).join(",");
365
+ function weeklySpecs(week) {
366
+ const groups = /* @__PURE__ */ new Map();
367
+ for (const key of weekdayKeys) {
368
+ const day = week.find((d) => d.day === key);
369
+ const spans = !day || day.closed ? [] : spansOf(day);
370
+ if (!spans.length) continue;
371
+ const k = spanKey(spans);
372
+ const group = groups.get(k) ?? { spans, days: [] };
373
+ group.days.push(key);
374
+ groups.set(k, group);
375
+ }
376
+ return [...groups.values()].flatMap((g) => g.spans.map((s) => ({ "@type": "OpeningHoursSpecification", dayOfWeek: g.days.map((d) => dayNames[d]), opens: s.from, closes: s.to })));
377
+ }
378
+ function specialSpecs(ctx, today, days, week) {
379
+ const runs = [];
380
+ for (let i = 0; i < days; i++) {
381
+ const date = addDays(today, i);
382
+ const rule = hoursOn(ctx, date);
383
+ const base = week.find((d) => d.day === weekdayOf(date));
384
+ const baseKey = !base || base.closed ? "" : spanKey(spansOf(base));
385
+ const key = rule.closed ? "" : spanKey(rule.times);
386
+ if (key === baseKey) continue;
387
+ const last = runs[runs.length - 1];
388
+ if (last && last.key === key && last.to === addDays(date, -1)) last.to = date;
389
+ else runs.push({ key, spans: rule.closed ? [] : rule.times, from: date, to: date });
390
+ }
391
+ return runs.flatMap((r) => (r.spans.length ? r.spans : [{ from: "00:00", to: "00:00" }]).map((s) => ({
392
+ "@type": "OpeningHoursSpecification",
393
+ validFrom: r.from,
394
+ validThrough: r.to,
395
+ opens: s.from,
396
+ closes: s.to
397
+ })));
398
+ }
399
+ function openingHoursJsonLd(ctx, today, days = 60) {
400
+ const { week } = weekFor(ctx, today);
401
+ const special = specialSpecs(ctx, today, days, week);
402
+ return { openingHoursSpecification: weeklySpecs(week), ...special.length ? { specialOpeningHoursSpecification: special } : {} };
403
+ }
404
+
405
+ function gallery(opts = {}) {
406
+ return single({
407
+ label: opts.label ?? both("Galerie", "Gallery"),
408
+ ...sourceOf(opts),
409
+ fields: {
410
+ images: f.list(both("Bilder", "Images"), { itemLabel: both("Bild", "Image"), fields: { image: f.image(both("Bild", "Image")) }, max: 24, default: [] })
411
+ },
412
+ tasks: [{ id: "edit", label: both("Fotos bearbeiten", "Edit photos"), kind: "edit", fields: ["images"] }]
413
+ });
414
+ }
415
+
416
+ export { authors, changelog, downloads, faq, faqJsonLd, gallery, hoursOn, jobJsonLd, jobs, legal, legalTypes, openingHoursJsonLd, people, personJsonLd, postJsonLd, posts, releaseAnchor, rssXml, site, siteJsonLd, spansOf, weekFor, weekdayOf };
@@ -2,12 +2,15 @@
2
2
  import { existsSync, realpathSync, readFileSync } from 'node:fs';
3
3
  import { join, dirname, resolve } from 'node:path';
4
4
  import { pathToFileURL, fileURLToPath } from 'node:url';
5
- import { f as fileLinkIssues } from '../shared/ginko.Dht0xxE5.mjs';
6
- import { n as coreMessage, i as isTranslatable } from '../shared/ginko.C-QjDn3O.mjs';
7
- import { s as serializeSchema, a as checkSite, G as validate, g as SITE_BUDGET, A as platformSchema, j as checkSchema } from '../shared/ginko.CrNRBkS5.mjs';
8
- import { e as defaultsFor, h as fieldsChanged, r as releaseData, m as markdownTrees, g as diffSchema } from '../shared/ginko.C90fPpMP.mjs';
5
+ import { C as CliError, i as isObject, d as defaultsFor, f as fieldsChanged, r as releaseData, S as SITE_BUDGET, e as entryBytes, a as fitsBudget, b as diffSchema, l as largestEntryBytes, c as largestFieldBytes, p as parseExport, g as parseReleaseState, h as pathOf, j as defaultDir } from '../shared/ginko.t-gbwzxh.mjs';
6
+ import { g as siteForContent } from '../shared/ginko.C-oz8wwI.mjs';
7
+ import { f as fileLinkIssues } from '../shared/ginko.raCzOPYC.mjs';
8
+ import { F as coreMessage, i as isTranslatable, g as effectiveMax } from '../shared/ginko.BNVC-g9n.mjs';
9
+ import { s as serializeSchema, c as checkSite, v as validate, p as platformSchema, a as checkSchema } from '../shared/ginko.B1-DoOea.mjs';
10
+ import { c as calendarOfCountry } from '../shared/ginko.D1PeQ3ds.mjs';
9
11
  import { mkdir, writeFile, rm, readdir } from 'node:fs/promises';
10
12
  import { execFile } from 'node:child_process';
13
+ import '../shared/ginko.yKMIjJ68.mjs';
11
14
  import 'comark';
12
15
  import 'comark/plugins/alert';
13
16
  import 'comark/plugins/attributes';
@@ -20,30 +23,6 @@ import 'comark/plugins/mermaid';
20
23
  import 'comark/plugins/task-list';
21
24
  import 'comark/plugins/toc';
22
25
  import '@lupinum/ginko-i18n/core';
23
- import '../shared/ginko.CQvxhyez.mjs';
24
-
25
- class CliError extends Error {
26
- code;
27
- constructor(message, code = "INVALID") {
28
- super(message);
29
- this.code = code;
30
- }
31
- }
32
- const isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
33
- function parseReleaseState(value) {
34
- if (!isObject(value)) throw new CliError("The platform did not return a valid state.", "UNAVAILABLE");
35
- const { site, schema, schemaRev, contentRev, migrations, entries } = value;
36
- const valid = typeof site === "string" && isObject(schema) && isObject(schema.bricks) && Array.isArray(schema.migrations) && isObject(schema.retired) && typeof schemaRev === "number" && typeof contentRev === "number" && Array.isArray(migrations) && Array.isArray(entries) && entries.every((e) => isObject(e) && typeof e.id === "string" && typeof e.brick === "string" && isObject(e.data));
37
- if (!valid) throw new CliError("The platform returned an incomplete state.", "UNAVAILABLE");
38
- return value;
39
- }
40
- function parseExport(value, locator, problem) {
41
- const optionalNumber = (v) => v === void 0 || typeof v === "number";
42
- const optionalString = (v) => v === void 0 || typeof v === "string";
43
- const valid = isObject(value) && value.format === 1 && typeof value.site === "string" && typeof value.name === "string" && typeof value.exportedAt === "string" && isObject(value.schema) && isObject(value.schema.bricks) && typeof value.schemaRev === "number" && typeof value.contentRev === "number" && Array.isArray(value.entries) && value.entries.every((e) => isObject(e) && typeof e.id === "string" && typeof e.brick === "string" && (e.kind === "single" || e.kind === "item") && typeof e.order === "number" && isObject(e.data) && isObject(e.prints) && typeof e.rev === "number" && optionalNumber(e.deletedAt)) && Array.isArray(value.files) && value.files.every((f) => isObject(f) && typeof f.id === "string" && typeof f.name === "string" && typeof f.mime === "string" && typeof f.size === "number" && typeof f[locator] === "string" && optionalNumber(f.width) && optionalNumber(f.height) && optionalString(f.folder) && optionalString(f.sha256) && (f.kept === void 0 || typeof f.kept === "boolean") && (f.focus === void 0 || isObject(f.focus) && typeof f.focus.x === "number" && typeof f.focus.y === "number")) && (value.folders === void 0 || Array.isArray(value.folders) && value.folders.every((f) => isObject(f) && typeof f.id === "string" && typeof f.name === "string" && optionalString(f.parent)));
44
- if (!valid) throw new CliError(problem, "UNAVAILABLE" );
45
- return value;
46
- }
47
26
 
48
27
  async function loadConfig(path, revision = 0) {
49
28
  const url = pathToFileURL(path).href + (revision > 0 ? `?rev=${revision}` : "");
@@ -57,13 +36,18 @@ async function loadConfig(path, revision = 0) {
57
36
  if (!isObject(config) || typeof config.site !== "string" || !isObject(config.bricks)) {
58
37
  throw new CliError(`${path} needs a default export with defineSite(\u2026).`);
59
38
  }
60
- return config;
39
+ try {
40
+ return siteForContent(config, process.env.GINKO_CONTENT);
41
+ } catch (error) {
42
+ throw new CliError(error.message);
43
+ }
61
44
  }
62
45
 
63
46
  function english(m) {
64
47
  return coreMessage("en", m.key, m.params);
65
48
  }
66
49
  function explainRejected(rejected) {
50
+ if (rejected.length === 0) return [];
67
51
  const lines = ["The schema changes in a way that is not allowed:"];
68
52
  for (const r of rejected) {
69
53
  lines.push(` \u2717 ${r.path}: ${english(r)}`);
@@ -109,8 +93,6 @@ async function checkFileLinks(input) {
109
93
  return ok;
110
94
  }
111
95
 
112
- const BUDGET = SITE_BUDGET;
113
- const byteSize = (value) => new TextEncoder().encode(JSON.stringify(value ?? null)).byteLength;
114
96
  async function buildCandidate(state, site, schema) {
115
97
  const problems = checkSite(site, schema);
116
98
  const applied = new Set([...state.schema.migrations, ...state.migrations].map((m) => m.id));
@@ -175,20 +157,27 @@ async function buildCandidate(state, site, schema) {
175
157
  }
176
158
  const changedData = new Map(changed.map((c) => [c.id, c.data]));
177
159
  const bytes = { site: 0, largest: 0 };
178
- const count = (size, label) => {
160
+ let siteBefore = 0;
161
+ const limit = `the limit is ${kib(SITE_BUDGET.entryBytes)}`;
162
+ for (const entry of state.entries) {
163
+ if (entry.deletedAt !== null) continue;
164
+ const before = entryBytes(entry.data);
165
+ const next = changedData.get(entry.id);
166
+ const after = next ? entryBytes(next) : before;
167
+ siteBefore += before;
168
+ bytes.site += after;
169
+ bytes.largest = Math.max(bytes.largest, after);
170
+ if (!fitsBudget(after, before, SITE_BUDGET.entryBytes)) problems.push(`Entry ${entry.id} (${entry.brick}) would be ${kib(after)} after the release; ${limit}.`);
171
+ }
172
+ for (const single of created) {
173
+ const size = entryBytes(single.data);
179
174
  bytes.site += size;
180
175
  bytes.largest = Math.max(bytes.largest, size);
181
- if (size > BUDGET.entryBytes) problems.push(`${label} is too large (at most 256 KiB).`);
182
- };
183
- const sizeOf = async (brick, data) => {
184
- const fields = (schema.bricks[brick] ?? state.schema.bricks[brick])?.fields ?? {};
185
- return byteSize(data) + byteSize(await markdownTrees(fields, data));
186
- };
187
- for (const entry of state.entries) count(await sizeOf(entry.brick, changedData.get(entry.id) ?? entry.data), `Entry ${entry.id} (${entry.brick})`);
188
- for (const single of created) count(await sizeOf(single.brick, single.data), `The new single brick \u201C${single.brick}\u201D`);
189
- if (bytes.site > BUDGET.siteBytes) problems.push("The site has reached its storage budget (4 MiB).");
176
+ if (size > SITE_BUDGET.entryBytes) problems.push(`The new single brick \u201C${single.brick}\u201D would be ${kib(size)}; ${limit}.`);
177
+ }
178
+ if (!fitsBudget(bytes.site, siteBefore, SITE_BUDGET.siteBytes)) problems.push(`The site would use ${kib(bytes.site)} of its ${kib(SITE_BUDGET.siteBytes)} of storage.`);
190
179
  const liveEntries = state.entries.filter((entry) => entry.deletedAt === null).length;
191
- if (liveEntries + created.length > BUDGET.entries) problems.push(`The site has reached its limit of ${BUDGET.entries} entries.`);
180
+ if (liveEntries + created.length > SITE_BUDGET.entries) problems.push(`The site has reached its limit of ${SITE_BUDGET.entries} entries.`);
192
181
  return {
193
182
  schema,
194
183
  writes: [...writes].map(([entry, fields]) => ({ entry, fields })),
@@ -246,6 +235,8 @@ async function check(input) {
246
235
  for (const problem of problems) input.log(` \u2717 ${problem}`);
247
236
  return false;
248
237
  }
238
+ warnEntrySize(input.config, input.log);
239
+ warnNoCalendar(input.config, input.log);
249
240
  const image = checkSeoImage(input.config, input.root, input.log);
250
241
  const links = await checkFileLinks({ config: input.config, root: input.root, log: input.log, fetch: input.fetch }) && image;
251
242
  if (!input.connect) {
@@ -260,6 +251,26 @@ async function check(input) {
260
251
  return false;
261
252
  }
262
253
  }
254
+ function warnEntrySize(config, log) {
255
+ const { bricks, locales } = platformSchema(serializeSchema(config));
256
+ for (const [key, brick] of Object.entries(bricks)) {
257
+ const max = largestEntryBytes(brick.fields, locales);
258
+ if (max <= SITE_BUDGET.entryBytes) continue;
259
+ const fields = Object.entries(brick.fields).map(([name, field]) => ({ name, field, bytes: largestFieldBytes(field, locales) }));
260
+ const largest = fields.filter((f) => f.bytes > 0).sort((a, b) => b.bytes - a.bytes).slice(0, 3).map(({ name, field, bytes }) => {
261
+ if (field.type === "list") return `${name} ${field.max} rows`;
262
+ if (field.type !== "text" && field.type !== "longtext" && field.type !== "markdown") return `${name} ${kib(bytes)}`;
263
+ const languages = isTranslatable(field) && locales.length > 1 ? ` \xD7 ${locales.length} languages` : "";
264
+ return `${name} ${effectiveMax(field).toLocaleString("en-US")}${languages}`;
265
+ });
266
+ const uncounted = fields.filter(({ field }) => field.type === "ref" && field.multiple && field.max === void 0).map((f) => f.name);
267
+ log(`Warning: brick \u201C${key}\u201D can hold about ${kib(max)} per entry (largest: ${largest.join(", ")}); the entry limit is ${kib(SITE_BUDGET.entryBytes)}. Lower a max so a full entry fits.${uncounted.length ? ` Not counted: ${uncounted.join(", ")} (entries without a max).` : ""}`);
268
+ }
269
+ }
270
+ function warnNoCalendar(config, log) {
271
+ if (config.country === void 0 || config.calendar !== void 0 || calendarOfCountry(config.country)) return;
272
+ log(`Warning: Ginko has no public-holiday calendar for country \u201C${config.country}\u201D, so opening hours count no public holidays (stored as holidays: 'none'). Set \`calendar\` in ginko.config.ts to your own function (year) => holidays, or to 'none' to keep it so.`);
273
+ }
263
274
  function checkSeoImage(config, root, log) {
264
275
  const image = config.seo?.image;
265
276
  if (!image || !image.startsWith("/") || image.startsWith("//")) return true;
@@ -286,7 +297,7 @@ async function checkRelease(input) {
286
297
  const candidate = result.candidate;
287
298
  const migrations = result.schema.migrations.filter((m) => !state.migrations.some((s) => s.id === m.id) && !state.schema.migrations.some((s) => s.id === m.id));
288
299
  if (migrations.length > 0) input.log(`New migrations: ${migrations.map((m) => m.id).join(", ")}. Writes: ${candidate.writes.length} entries.`);
289
- input.log(`Size: site ${kib(candidate.bytes.site)} of 4 MiB, largest entry ${kib(candidate.bytes.largest)} (limit 256 KiB).`);
300
+ input.log(`Size: site ${kib(candidate.bytes.site)} of ${kib(SITE_BUDGET.siteBytes)}, largest entry ${kib(candidate.bytes.largest)} (limit ${kib(SITE_BUDGET.entryBytes)}).`);
290
301
  if (candidate.problems.length > 0) {
291
302
  for (const line of explainProblems(candidate.problems)) input.log(line);
292
303
  return false;
@@ -391,24 +402,11 @@ function createPlatform(options) {
391
402
  };
392
403
  }
393
404
 
394
- const EXTENSIONS = {
395
- "image/jpeg": ".jpg",
396
- "image/png": ".png",
397
- "image/webp": ".webp",
398
- "image/avif": ".avif",
399
- "application/pdf": ".pdf"
400
- };
401
405
  function siteOfKey(key) {
402
406
  const match = PUSH_KEY_FORMAT.exec(key.trim());
403
407
  if (!match) throw new CliError("GINKO_KEY is not a valid key.", "USAGE");
404
408
  return match[1];
405
409
  }
406
- function defaultDir(site, now) {
407
- return `ginko-export-${site}-${now.toISOString().slice(0, 10)}`;
408
- }
409
- function pathOf(file) {
410
- return `files/${file.id}${EXTENSIONS[file.mime] ?? ""}`;
411
- }
412
410
  async function requireEmpty(dir) {
413
411
  let names;
414
412
  try {
@@ -543,6 +541,10 @@ function parseArgs(argv) {
543
541
  const value = arg === "--config" ? argv[++i] : arg.slice("--config=".length);
544
542
  if (!value) throw new CliError("--config needs a path.", "USAGE");
545
543
  args.config = value;
544
+ } else if (arg === "--site" || arg.startsWith("--site=")) {
545
+ const value = arg === "--site" ? argv[++i] : arg.slice("--site=".length);
546
+ if (!value) throw new CliError("--site needs a site slug.", "USAGE");
547
+ args.site = value;
546
548
  } else if (arg.startsWith("-")) args.flags.add(arg);
547
549
  else if (args.command === void 0) args.command = arg;
548
550
  else if (args.folder === void 0) args.folder = arg;
@@ -568,6 +570,7 @@ async function runCli(argv, deps, cli) {
568
570
  const name = `${cli.prefix}${args.command}`;
569
571
  const flag = [...args.flags].find((f) => f !== "--json" && !command.flags?.includes(f));
570
572
  if (flag) throw new CliError(`Unknown option \u201C${flag}\u201D for ${name}.`, "USAGE");
573
+ if (args.site !== void 0 && !command.site) throw new CliError(`Unknown option \u201C--site\u201D for ${name}.`, "USAGE");
571
574
  if (args.folder !== void 0 && !command.folder) throw new CliError(`Unexpected argument \u201C${args.folder}\u201D for ${name}.`, "USAGE");
572
575
  if (args.folder === void 0 && command.folder === "required") throw new CliError(`${name} needs a folder: ${name} <folder>.`, "USAGE");
573
576
  return command.run(args);
package/dist/index.d.mts CHANGED
@@ -1,6 +1,7 @@
1
- export { L as LinkConfig, M as Migration, b as MigrationContext, R as Redirect, c as RouteTemplate, S as SiteInput, e as collection, f as defineSite, s as single } from './shared/ginko.akHewCGt.mjs';
2
- export { B as Brick, d as BrickSource, k as ChoiceOption, e as CollectionBrick, m as ComponentDef, a as ComponentRegistry, C as Currency, q as DataOf, t as Field, F as Fields, v as FileValue, y as ImageValue, z as ItemOf, J as Json, c as Label, L as Locale, Q as MarkdownProfile, U as MarkdownToc, M as MarkdownTree, X as MarkdownValue, Z as Money, f as SingleBrick, T as Task, a8 as TocLink, ab as UnknownChoice, V as Visibility, W as WeekInfo, aj as defineComponents, ao as f, au as plainText, ax as startOfWeek } from './shared/ginko.D-fNojn9.mjs';
3
- export { C as Condition, N as NavNode, Q as Query, d as SeoImage, j as formatMoney, s as safeHref } from './shared/ginko.BTUfZCfa.mjs';
4
- import './shared/ginko.Bwan3BB-.mjs';
1
+ export { L as LinkConfig, M as Migration, b as MigrationContext, c as Redirect, R as RouteTemplate, S as SiteInput, e as collection, f as defineSite, s as single } from './shared/ginko.DkIc0ZZk.mjs';
2
+ export { B as Brick, d as BrickSource, Q as BrickStatus, Y as ChoiceOption, f as CollectionBrick, e as CollectionStatus, _ as ComponentDef, b as ComponentRegistry, C as Currency, a2 as DataOf, p as Field, F as Fields, a4 as FileValue, a7 as ImageValue, a8 as ItemOf, J as Json, c as Label, L as Locale, ab as MarkdownProfile, ac as MarkdownToc, M as MarkdownTree, ad as MarkdownValue, af as Money, h as SingleBrick, g as SingleStatus, T as Task, al as TocLink, ao as UnknownChoice, V as Visibility, W as WeekInfo, aw as defineComponents, aB as f, aJ as plainText, aM as startOfWeek, aQ as wordsOf } from './shared/ginko.DrkUeYFP.mjs';
3
+ export { N as NavNode, b as SeoImage, e as breadcrumbJsonLd } from './shared/ginko.o1yIKlgj.mjs';
4
+ export { C as Condition, F as FormatMoneyOptions, Q as Query, f as formatMoney, s as safeHref } from './shared/ginko.BaFweQcB.mjs';
5
+ import './shared/ginko.6cI8KXZP.mjs';
5
6
  import '@lupinum/ginko-i18n/core';
6
7
  import 'comark';