@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,116 @@
1
+ function seoHead(input) {
2
+ const { siteName, description, image, noindex } = input;
3
+ const title = input.title || void 0;
4
+ const template = input.titleTemplate ?? `%s \xB7 ${siteName}`;
5
+ const fullTitle = title ? template.replace("%s", () => title) : siteName;
6
+ const meta = [
7
+ { name: "description", content: description ?? "" },
8
+ { name: "robots", content: noindex ? "noindex, follow" : "" },
9
+ { property: "og:title", content: title ?? siteName },
10
+ { property: "og:description", content: description ?? "" },
11
+ { property: "og:type", content: input.type ?? "website" },
12
+ { property: "og:site_name", content: siteName },
13
+ { property: "og:image", content: image?.url ?? "" },
14
+ { property: "og:image:width", content: image?.width === void 0 ? "" : String(image.width) },
15
+ { property: "og:image:height", content: image?.height === void 0 ? "" : String(image.height) },
16
+ { property: "og:image:alt", content: image?.alt ?? "" },
17
+ { name: "twitter:card", content: image ? "summary_large_image" : "summary" }
18
+ ];
19
+ return { title: fullTitle, meta: meta.filter((m) => m.content !== "") };
20
+ }
21
+ function isPreviewHost(host, siteUrl) {
22
+ const bare = (h) => h.toLowerCase().replace(/^www\./, "");
23
+ return bare(host ?? "") !== bare(new URL(siteUrl).host);
24
+ }
25
+ function robotsTxt(options) {
26
+ if (options.noindex) return "User-agent: *\nDisallow: /\n";
27
+ const rules = options.disallow?.length ? options.disallow.map((path) => `Disallow: ${path}`) : ["Allow: /"];
28
+ const lines = ["User-agent: *", ...rules];
29
+ if (options.sitemap) lines.push("", `Sitemap: ${options.sitemap}`);
30
+ return `${lines.join("\n")}
31
+ `;
32
+ }
33
+ const compact = (o) => Object.fromEntries(Object.entries(o).filter(([, v]) => v !== void 0 && v !== "" && !(Array.isArray(v) && v.length === 0)));
34
+ function postalAddressLd(a) {
35
+ if (!a.street && !a.postalCode && !a.city) return void 0;
36
+ return compact({ "@type": "PostalAddress", streetAddress: a.street, postalCode: a.postalCode, addressLocality: a.city, addressCountry: a.country });
37
+ }
38
+ function websiteLd(o) {
39
+ return { "@context": "https://schema.org", "@type": "WebSite", name: o.name, url: o.url, inLanguage: o.lang };
40
+ }
41
+ function withCountry(node, country) {
42
+ if (!country || node === null || typeof node !== "object") return node;
43
+ if (Array.isArray(node)) return node.map((item) => withCountry(item, country));
44
+ const out = Object.fromEntries(Object.entries(node).map(([key, value]) => [key, withCountry(value, country)]));
45
+ if (out["@type"] === "PostalAddress" && out.addressCountry === void 0) out.addressCountry = country;
46
+ if (out["@type"] === "JobPosting" && out.jobLocationType === "TELECOMMUTE" && out.applicantLocationRequirements === void 0) {
47
+ out.applicantLocationRequirements = { "@type": "Country", name: country };
48
+ }
49
+ return out;
50
+ }
51
+ const ORGANIZATION_TYPES = ["Organization", "LocalBusiness", "NGO"];
52
+ function organizationLd(o) {
53
+ return compact({
54
+ "@context": "https://schema.org",
55
+ "@type": o.type ?? "Organization",
56
+ name: o.name,
57
+ legalName: o.legalName,
58
+ url: o.url,
59
+ logo: o.logo,
60
+ email: o.email,
61
+ telephone: o.telephone,
62
+ address: o.address && postalAddressLd(o.address),
63
+ vatID: o.vatID,
64
+ sameAs: o.sameAs
65
+ });
66
+ }
67
+ function breadcrumbJsonLd(steps, siteUrl) {
68
+ return {
69
+ "@context": "https://schema.org",
70
+ "@type": "BreadcrumbList",
71
+ itemListElement: steps.map((step, i) => ({ "@type": "ListItem", position: i + 1, name: step.title, item: new URL(step.path, siteUrl).href }))
72
+ };
73
+ }
74
+ function articleLd(o) {
75
+ const person = (a) => compact({ "@type": "Person", name: a.name, url: a.url });
76
+ return compact({
77
+ "@context": "https://schema.org",
78
+ "@type": o.type ?? "Article",
79
+ headline: o.title,
80
+ url: o.url,
81
+ description: o.description,
82
+ image: o.image,
83
+ datePublished: o.published,
84
+ dateModified: o.modified,
85
+ author: typeof o.author === "string" ? person({ name: o.author }) : o.author?.map(person),
86
+ keywords: o.keywords?.join(", "),
87
+ inLanguage: o.lang
88
+ });
89
+ }
90
+ function faqLd(pairs) {
91
+ return {
92
+ "@context": "https://schema.org",
93
+ "@type": "FAQPage",
94
+ mainEntity: pairs.map((p) => ({ "@type": "Question", name: p.question, acceptedAnswer: { "@type": "Answer", text: p.answer } }))
95
+ };
96
+ }
97
+ function siteHeaders(input) {
98
+ return {
99
+ "content-security-policy": input.preview && input.platformOrigin ? `frame-ancestors ${input.platformOrigin}` : `frame-ancestors 'self'`,
100
+ "x-content-type-options": "nosniff",
101
+ "referrer-policy": "strict-origin-when-cross-origin",
102
+ ...input.noindex ? { "x-robots-tag": "noindex" } : {}
103
+ };
104
+ }
105
+
106
+ function minorDigits(currency) {
107
+ return new Intl.NumberFormat("en", { style: "currency", currency }).resolvedOptions().maximumFractionDigits ?? 2;
108
+ }
109
+ function formatMoney(money, language, options = {}) {
110
+ const digits = minorDigits(money.currency);
111
+ const whole = options.wholeWithoutMinor === true && money.amount % 10 ** digits === 0;
112
+ const text = new Intl.NumberFormat(language, { style: "currency", currency: money.currency, ...whole ? { minimumFractionDigits: 0, maximumFractionDigits: 0 } : {} }).format(money.amount / 10 ** digits);
113
+ return text.replace(/\s/g, "\xA0");
114
+ }
115
+
116
+ export { ORGANIZATION_TYPES as O, articleLd as a, breadcrumbJsonLd as b, compact as c, faqLd as d, siteHeaders as e, formatMoney as f, withCountry as g, isPreviewHost as i, minorDigits as m, organizationLd as o, postalAddressLd as p, robotsTxt as r, seoHead as s, websiteLd as w };
@@ -0,0 +1,26 @@
1
+ import { w as wordsOf } from './ginko.BNVC-g9n.mjs';
2
+
3
+ const tail = (brick, id, field) => `${encodeURIComponent(brick)}/${id.split("/").map(encodeURIComponent).join("/")}/${encodeURIComponent(field)}.json`;
4
+ function fileMarkdownRef(locale, brick, id, field) {
5
+ return `/__ginko/files/${locale}/md/${tail(brick, id, field)}`;
6
+ }
7
+ function platformMarkdownRef(locale, rev, brick, id, field) {
8
+ return `/__ginko/platform/${locale}/${rev}/md/${tail(brick, id, field)}`;
9
+ }
10
+ function withMarkdownRefs(bundle, schema, trees, refOf) {
11
+ const swap = (brick, id, data) => {
12
+ const out = { ...data };
13
+ for (const [name, field] of Object.entries(schema.bricks[brick]?.fields ?? {})) {
14
+ const value = out[name];
15
+ if (field.type !== "markdown" || !value || !("nodes" in value)) continue;
16
+ const ref = refOf(brick, id, name);
17
+ trees.set(ref, value);
18
+ out[name] = { ref, words: wordsOf(value) };
19
+ }
20
+ return out;
21
+ };
22
+ const bricks = Object.fromEntries(Object.entries(bundle.bricks).map(([key, b]) => [key, b.kind === "single" ? { ...b, data: swap(key, key, b.data) } : { ...b, items: b.items.map((item) => ({ ...item, data: swap(key, item.id, item.data) })) }]));
23
+ return { ...bundle, bricks };
24
+ }
25
+
26
+ export { fileMarkdownRef as f, platformMarkdownRef as p, withMarkdownRefs as w };
@@ -1,5 +1,5 @@
1
- import { b as Msg, S as Schema, F as Fields, J as Json, L as Locale, C as Currency, B as Brick, a as ComponentRegistry, R as RetiredRule, c as Label, d as BrickSource, T as Task, V as Visibility, e as CollectionBrick, f as SingleBrick } from './ginko.D-fNojn9.mjs';
2
- import { B as Bundle } from './ginko.Bwan3BB-.mjs';
1
+ import { a as Msg, S as Schema, F as Fields, J as Json, L as Locale, C as Currency, H as HolidaysOption, B as Brick, b as ComponentRegistry, R as RetiredRule, c as Label, d as BrickSource, T as Task, V as Visibility, e as CollectionStatus, f as CollectionBrick, g as SingleStatus, h as SingleBrick } from './ginko.DrkUeYFP.mjs';
2
+ import { B as Bundle } from './ginko.6cI8KXZP.mjs';
3
3
 
4
4
  interface Redirect {
5
5
  from: string;
@@ -59,10 +59,12 @@ type Pages = Pick<Schema, 'pages' | 'bricks'>;
59
59
  /**
60
60
  * The bundle with `$…` links turned into paths and `::snippet` components replaced by their text.
61
61
  * `$page.<key>` is a page of `schema.pages`; `$<brick>.<slug-or-id>` is an entry's URL; a `#anchor` is kept.
62
+ * `main` is the main-language bundle (from `buildBundle`, links unresolved): an entry left out of this locale because
63
+ * it is not translated (`untranslated: 'hide'`) is found there, and the link goes to its main-language page.
62
64
  * What cannot be resolved stays as it is and gets `data-broken: true`. A snippet is the `body` of the entry
63
65
  * `snippets/<name>` of the collection `snippets`; a snippet inside a snippet stays as it is. No mutation.
64
66
  */
65
- declare function resolveRouteLinks(bundle: Bundle, schema: Pages): Bundle;
67
+ declare function resolveRouteLinks(bundle: Bundle, schema: Pages, main?: Bundle): Bundle;
66
68
  /**
67
69
  * Link check over bundles (one per locale) before any HTML exists. Errors: route links and snippets that
68
70
  * `resolveRouteLinks` could not resolve, and `/…` paths that match no entry URL, no page and no public file.
@@ -117,6 +119,16 @@ interface SiteInput {
117
119
  */
118
120
  firstDayOfWeek?: 1 | 2 | 3 | 4 | 5 | 6 | 7;
119
121
  currency: Currency;
122
+ /**
123
+ * The country of the site (ISO 3166-1 alpha-2, e.g. `AT`). It picks the public-holiday calendar (`calendar`) and is
124
+ * the default `addressCountry` of JSON-LD. A country Ginko has no calendar for keeps no public holidays (`ginko check` warns).
125
+ */
126
+ country?: string;
127
+ /**
128
+ * Which public holidays count for opening hours: `AT`, `DE` (nationwide only), `SG`, `none`, or your own function
129
+ * `(year) => Holiday[]`. Default: the calendar of `country`. Stored in the schema as `holidays` (a function as `custom`).
130
+ */
131
+ calendar?: HolidaysOption;
120
132
  /**
121
133
  * Static pages by key: `{ menu: { de: '/speisekarte', en: '/en/menu' } }`. A plain path is the page in
122
134
  * the main locale only. The home page is the page whose main-locale path is `/`. The sitemap pairs a page's locales by key.
@@ -136,6 +148,7 @@ interface SiteInput {
136
148
  image?: string;
137
149
  /** The brand colour, `#rrggbb` or `#rgb`: the background of generated share cards (nuxt-og-image). */
138
150
  color?: string;
151
+ /** The home page's Organization JSON-LD. A page with its own (`siteJsonLd` of `@lupinum/ginko/basics`) gets none from here. */
139
152
  organization?: {
140
153
  logo?: string;
141
154
  sameAs?: string[];
@@ -170,12 +183,40 @@ type RetiredInput<S extends SiteInput> = {
170
183
  declare function defineSite<const S extends SiteInput>(site: S & {
171
184
  retired?: RetiredInput<S>;
172
185
  }): S;
186
+ /**
187
+ * `site` as it runs on the platform: every brick without `source`, so the owner edits it in the Studio. A template
188
+ * that reads its content from files and this twin must both pass `checkSite`; tools and tests use it to prove that a
189
+ * files site can move to the platform without changing its bricks.
190
+ */
191
+ declare function withoutFiles<const S extends SiteInput>(site: S): WithoutFiles<S>;
192
+ /**
193
+ * The site for the content source `GINKO_CONTENT` names: unset or empty keeps the config as it is, `platform` is
194
+ * `withoutFiles(site)`. A files site deployed with `GINKO_CONTENT=platform` reads the Studio without a code change
195
+ * (the hosted template demos); the Nuxt module and the CLI both read it, so the build and `ginko release` agree.
196
+ */
197
+ declare function siteForContent(site: SiteInput, content: string | undefined): SiteInput;
198
+ /** `S` with no `source` on any brick. */
199
+ type WithoutFiles<S extends SiteInput> = Omit<S, 'bricks'> & {
200
+ bricks: {
201
+ [K in keyof S['bricks']]: Omit<S['bricks'][K], 'source'>;
202
+ };
203
+ };
173
204
  /** The names of the fields in `F` whose type is one of `T`. */
174
205
  type FieldsOfType<F, T extends string> = {
175
206
  [K in keyof F]: F[K] extends {
176
207
  type: T;
177
208
  } ? K : never;
178
209
  }[keyof F] & string;
210
+ /**
211
+ * A planned change of one field (`planned`): from the date in the date field `from` on, `next` replaces `field`. `next`
212
+ * and `from` are optional and set together; every edit task that changes `field` lists them too. Field names only:
213
+ * a factory with generic fields could not name its date field otherwise, so `ginko check` reports the other rules.
214
+ */
215
+ type Planned<F> = {
216
+ field: keyof F & string;
217
+ next: keyof F & string;
218
+ from: keyof F & string;
219
+ };
179
220
  /** A collection's detail page per locale: a path with `[slug]` where the entry's slug goes, e.g. `/blog/[slug]`. */
180
221
  type RouteTemplate = `/${string}[slug]${string}`;
181
222
  declare function single<const F extends Fields>(input: {
@@ -185,6 +226,8 @@ declare function single<const F extends Fields>(input: {
185
226
  fields: F;
186
227
  layout?: 'sections' | 'tabs';
187
228
  tasks?: readonly Task<F>[];
229
+ planned?: Planned<F>;
230
+ status?: SingleStatus;
188
231
  }): SingleBrick<F>;
189
232
  declare function collection<const F extends Fields, const R extends Record<Locale, RouteTemplate> | undefined = undefined>(input: {
190
233
  label: Label;
@@ -193,6 +236,8 @@ declare function collection<const F extends Fields, const R extends Record<Local
193
236
  fields: F;
194
237
  layout?: 'sections' | 'tabs';
195
238
  tasks?: readonly Task<F>[];
239
+ /** A planned change of one field: from the date in `from` on, `next` replaces `field`. */
240
+ planned?: Planned<F>;
196
241
  max: number;
197
242
  order: 'manual' | {
198
243
  field: keyof F & string;
@@ -200,6 +245,10 @@ declare function collection<const F extends Fields, const R extends Record<Local
200
245
  };
201
246
  route?: R;
202
247
  visible?: Visibility<FieldsOfType<F, 'date' | 'daterange'>>;
248
+ /**
249
+ * A required translatable field is missing in a language: `'hide'` (default with a `route`) leaves the entry out
250
+ * there; `'fallback'` (default without) shows the main language and links to the main-language page.
251
+ */
203
252
  untranslated?: 'fallback' | 'hide';
204
253
  /** Entries plan weeks of the single brick `brick`; `field` (a date field of this collection) holds the first day of the week (`firstDayOfWeek`). */
205
254
  plans?: {
@@ -207,7 +256,9 @@ declare function collection<const F extends Fields, const R extends Record<Local
207
256
  field: FieldsOfType<F, 'date'>;
208
257
  };
209
258
  drafts?: boolean;
259
+ /** The status line on the Studio's Today page (see `CollectionStatus`). */
260
+ status?: CollectionStatus<FieldsOfType<F, 'date' | 'daterange'>, keyof F & string>;
210
261
  }): CollectionBrick<F, R>;
211
262
 
212
- export { checkLinks as d, collection as e, defineSite as f, entryRedirects as g, externalLinks as h, findRedirect as i, resolveRouteLinks as j, linkTargets as l, matchRedirect as m, redirectProblems as r, single as s };
213
- export type { CheckLinksOptions as C, ExternalLink as E, LinkConfig as L, Migration as M, Redirect as R, SiteInput as S, LinkIssue as a, MigrationContext as b, RouteTemplate as c };
263
+ export { checkLinks as d, collection as e, defineSite as f, entryRedirects as g, externalLinks as h, findRedirect as i, resolveRouteLinks as j, siteForContent as k, linkTargets as l, matchRedirect as m, redirectProblems as r, single as s, withoutFiles as w };
264
+ export type { CheckLinksOptions as C, ExternalLink as E, LinkConfig as L, Migration as M, RouteTemplate as R, SiteInput as S, WithoutFiles as W, LinkIssue as a, MigrationContext as b, Redirect as c };