@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
package/dist/internal.mjs CHANGED
@@ -1,11 +1,19 @@
1
- import { j as parseMarkdown, i as isTranslatable, o as labelOf, g as effectiveMax, q as describeProfile, r as effectiveProfile } from './shared/ginko.C-QjDn3O.mjs';
2
- export { A as ALT_MAX, G as GINKO_COMPONENTS_KEY, u as GINKO_TOC_KEY, M as MARKDOWN_PROFILES, v as MAX_LOCALES, U as UNSAFE_LINK, w as URL_PROPS, x as componentProblems, y as componentPropMessage, z as componentPropProblem, C as coreCatalogs, n as coreMessage, d as defineComponents, l as effectiveAlt, f, D as isCurrency, E as isLocale, F as isSafeHref, H as isShown, m as msg, p as plainText, I as profileCovers, s as safeHref, k as sha256, b as stableJson, t as textBlocks } from './shared/ginko.C-QjDn3O.mjs';
3
- export { B as BUNDLE_FORMAT, a as applyDefaults, b as blockedSlugs, c as buildBundle, d as bundleFits, e as defaultsFor, f as deletesExpired, g as diffSchema, h as fieldsChanged, i as isBundle, j as isVisible, k as knownBricks, m as markdownTrees, p as printOf, r as releaseData, l as resolveEntry, s as servedBundle, n as sortEntries, u as urlClashes, o as urlsFor, v as visibleUntil, w as withFileBricks } from './shared/ginko.C90fPpMP.mjs';
4
- export { D as DEFAULT_WEEK, a as addDays, r as resolveToken, s as startOfWeek, t as todayIn, w as weekInfoOf, b as workWeekOf } from './shared/ginko.CQvxhyez.mjs';
5
- export { P as PREFILL_TOKENS, f as SCHEMA_LIMITS, g as SITE_BUDGET, h as SLUG_PATTERN, U as UPLOAD_TYPES, i as acceptOf, c as checkLinks, j as checkSchema, a as checkSite, d as entryRedirects, e as externalLinks, k as fileProblem, l as findRedirect, m as formatMb, n as isDate, o as isPrefillToken, p as isTimeZone, q as limitOf, t as linkTargets, u as mainLocale, v as matchRedirect, w as migrationFingerprint, x as ordered, y as plannedWeeks, z as plansThisWeek, A as platformSchema, b as redirectProblems, r as resolveRouteLinks, s as serializeSchema, C as slugify, D as staleOf, E as uniqueSlug, G as validate, H as weeklyTask } from './shared/ginko.CrNRBkS5.mjs';
6
- import { m as minorDigits } from './shared/ginko.Mn30dmlm.mjs';
7
- export { c as collection, d as defineSite, f as formatMoney, s as single } from './shared/ginko.Mn30dmlm.mjs';
1
+ import { k as isVisible, v as visibleUntil, m as printOf } from './shared/ginko.t-gbwzxh.mjs';
2
+ export { S as SITE_BUDGET, n as applyDefaults, o as blockedSlugs, q as buildBundle, s as byteSize, j as defaultDir, d as defaultsFor, t as deletesExpired, b as diffSchema, e as entryBytes, f as fieldsChanged, a as fitsBudget, l as largestEntryBytes, c as largestFieldBytes, h as pathOf, r as releaseData, u as resolveEntry, w as routeUrls, x as servedBundle, y as sortEntries, z as untranslatedMode, A as urlClashes, B as urlsFor } from './shared/ginko.t-gbwzxh.mjs';
3
+ export { H as eventsBrick, I as hourPlansBrick, J as hoursBrick, q as hoursLine, K as noticesBrick, L as offerPrice, M as postalAddress, N as weekdays } from './shared/ginko.WAfoMhv1.mjs';
4
+ import { b as addDays } from './shared/ginko.C-oz8wwI.mjs';
5
+ export { D as DEFAULT_WEEK, c as collection, d as defineSite, f as firstDayShown, r as resolveToken, s as single, g as siteForContent, a as startOfWeek, t as todayIn, e as weekInfoOf, h as withoutFiles, w as workWeekOf, i as zoneOffset, z as zonedDateTime } from './shared/ginko.C-oz8wwI.mjs';
6
+ import { a as parseMarkdown, h as stableJson, u as labelOf, i as isTranslatable, g as effectiveMax, P as describeProfile, y as effectiveProfile } from './shared/ginko.BNVC-g9n.mjs';
7
+ export { v as ALT_INVISIBLE, A as ALT_MAX, Q as FILE_REF, R as GINKO_COMPONENTS_KEY, S as GINKO_TOC_KEY, J as MARKDOWN_PROFILES, M as MAX_LOCALES, U as UNSAFE_LINK, T as URL_PROPS, c as checkLinks, G as componentProblems, V as componentPropMessage, W as componentPropProblem, X as coreCatalogs, F as coreMessage, d as defineComponents, q as effectiveAlt, Y as entryLinkRefs, N as entryRedirects, e as externalLinks, f, Z as fileImagesWithoutAlt, _ as fileRefOf, z as fileRefUses, $ as fileRefsIn, a0 as findRedirect, a1 as imagePropsOf, E as isCurrency, C as isLocale, a2 as isSafeHref, x as isShown, a3 as labelsIn, a4 as linkTargets, a5 as mapMarkdownTexts, a6 as matchRedirect, m as msg, p as plainText, K as profileCovers, L as redirectProblems, a7 as remapFileIds, o as resolveFileRefs, r as resolveRouteLinks, a8 as rewriteEntryLinks, a9 as rewriteFileRefs, s as safeHref, n as sha256, O as studioLabel, t as textBlocks, w as wordsOf } from './shared/ginko.BNVC-g9n.mjs';
8
+ import { e as entryTitle$1 } from './shared/ginko.yKMIjJ68.mjs';
9
+ export { B as BUNDLE_FORMAT, b as bundleFits, i as isBundle, k as knownBricks, m as markdownTrees, a as withFileBricks } from './shared/ginko.yKMIjJ68.mjs';
10
+ export { C as CALENDAR_NAMES, S as STORED_HOLIDAYS, U as UPLOAD_TYPES, a as acceptOf, c as calendarOfCountry, f as formatMb, h as holidaysOf, l as limitOf } from './shared/ginko.D1PeQ3ds.mjs';
11
+ import { m as minorDigits } from './shared/ginko.DCHGkQ7P.mjs';
12
+ export { O as ORGANIZATION_TYPES, a as articleLd, b as breadcrumbJsonLd, d as faqLd, f as formatMoney, i as isPreviewHost, o as organizationLd, r as robotsTxt, s as seoHead, e as siteHeaders, w as websiteLd, g as withCountry } from './shared/ginko.DCHGkQ7P.mjs';
13
+ import { o as ordered, b as createAsks } from './shared/ginko.B1-DoOea.mjs';
14
+ export { P as PREFILL_TOKENS, d as SCHEMA_LIMITS, e as SHAPE_CODES, f as SLUG_PATTERN, a as checkSchema, c as checkSite, g as duePlan, h as fileProblem, i as grantGroups, j as isDate, k as isHourPlans, l as isPrefillToken, m as isTimeZone, n as liveFrom, q as mainLocale, r as migrationFingerprint, t as plannedWeeks, u as plansThisWeek, p as platformSchema, s as serializeSchema, w as slugBases, x as slugify, y as staleOf, z as uniqueSlug, v as validate, A as weekStartField, C as weeklyTask } from './shared/ginko.B1-DoOea.mjs';
8
15
  import { renderMarkdown } from 'comark/render';
16
+ export { f as fileMarkdownRef, p as platformMarkdownRef, w as withMarkdownRefs } from './shared/ginko.DIl7l-75.mjs';
9
17
  import 'comark';
10
18
  import 'comark/plugins/alert';
11
19
  import 'comark/plugins/attributes';
@@ -39,8 +47,8 @@ async function markdownBlocks(text, options = {}) {
39
47
  const body = full.body;
40
48
  const whole = () => ({ nodes, blocks: [{ nodes, source: body.trim() ? body.trimEnd() : "", gap: "" }], tail: body.slice(body.trimEnd().length) });
41
49
  if (!body.trim()) return { nodes, blocks: [], tail: body };
42
- const lead = /^\s*/.exec(body)[0];
43
- const tail = /\s*$/.exec(body)[0];
50
+ const lead = body.slice(0, body.length - body.trimStart().length);
51
+ const tail = body.slice(body.trimEnd().length);
44
52
  const core = body.slice(lead.length, body.length - tail.length);
45
53
  const parts = core.split(/(\r?\n(?:[ \t]*\r?\n)+)/);
46
54
  const pieceAt = (from, to) => parts.slice(from, to * 2 + 1).join("");
@@ -204,10 +212,238 @@ async function serializeMarkdown(nodes, options = {}) {
204
212
  }
205
213
  async function isStableMarkdown(text, options = {}) {
206
214
  const first = await parseMarkdown(text, options);
215
+ if (first.hidden) return false;
207
216
  const again = await parseMarkdown(await render(first.value.nodes, {}), { ...options, frontmatter: false });
208
217
  return JSON.stringify(first.value.nodes) === JSON.stringify(again.value.nodes);
209
218
  }
210
219
 
220
+ const isRow = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
221
+ const differs = (a, b) => stableJson(a ?? null) !== stableJson(b ?? null);
222
+ const rowsOf = (value) => (Array.isArray(value) ? value : []).filter(isRow);
223
+ const idOf = (row, i) => typeof row._id === "string" ? row._id : `#${i}`;
224
+ const byId = (rows) => new Map(rows.map((row, i) => [idOf(row, i), row]));
225
+ function rowDiffs(before, after) {
226
+ const oldById = byId(rowsOf(before));
227
+ const nextById = byId(rowsOf(after));
228
+ const rows = [];
229
+ for (const [id, row] of nextById) {
230
+ const was = oldById.get(id);
231
+ if (!was) rows.push({ id, change: "added", before: null, after: row });
232
+ else if (differs(was, row)) {
233
+ const keys = [.../* @__PURE__ */ new Set([...Object.keys(was), ...Object.keys(row)])].filter((k) => differs(was[k], row[k]));
234
+ rows.push({ id, change: "changed", before: was, after: row, fields: keys });
235
+ }
236
+ }
237
+ for (const [id, row] of oldById) if (!nextById.has(id)) rows.push({ id, change: "removed", before: row, after: null });
238
+ const kept = (map, other) => [...map.keys()].filter((id) => other.has(id));
239
+ const reordered = kept(oldById, nextById).join("\n") !== kept(nextById, oldById).join("\n");
240
+ return reordered ? { rows, reordered } : { rows };
241
+ }
242
+ function mergeRows(base, mine, theirs) {
243
+ const lists = [rowsOf(base), rowsOf(mine), rowsOf(theirs)];
244
+ const legacy = lists.some((rows2) => rows2.some((row) => typeof row._id !== "string"));
245
+ const [b, m, t] = [byId(lists[0]), byId(lists[1]), byId(lists[2])];
246
+ const reordered = legacy || !!rowDiffs(base, mine).reordered || !!rowDiffs(base, theirs).reordered;
247
+ const clashes = [];
248
+ const rows = [];
249
+ for (const [id, their] of t) {
250
+ const was = b.get(id);
251
+ const my = m.get(id);
252
+ if (!was) {
253
+ if (my && differs(my, their)) clashes.push(id);
254
+ rows.push(their);
255
+ } else if (!my) {
256
+ if (differs(was, their)) {
257
+ clashes.push(id);
258
+ rows.push(their);
259
+ }
260
+ } else if (differs(was, my) && differs(was, their) && differs(my, their)) {
261
+ clashes.push(id);
262
+ rows.push(their);
263
+ } else rows.push(differs(was, my) ? my : their);
264
+ }
265
+ for (const [id, was] of b) {
266
+ const my = m.get(id);
267
+ if (!t.has(id) && my && differs(was, my)) clashes.push(id);
268
+ }
269
+ const ids = rows.map(idOf);
270
+ let previous;
271
+ for (const [id, row] of m) {
272
+ if (!b.has(id) && !t.has(id)) {
273
+ let at = previous === void 0 ? 0 : ids.indexOf(previous) + 1;
274
+ while (at < ids.length && !b.has(ids[at]) && !m.has(ids[at])) at++;
275
+ rows.splice(at, 0, row);
276
+ ids.splice(at, 0, id);
277
+ }
278
+ if (ids.includes(id)) previous = id;
279
+ }
280
+ return { rows, clashes, reordered };
281
+ }
282
+
283
+ function textIn(value, locale) {
284
+ if (typeof value === "string") return value;
285
+ if (value && typeof value === "object" && !Array.isArray(value)) {
286
+ const v = value[locale];
287
+ return typeof v === "string" ? v : "";
288
+ }
289
+ return "";
290
+ }
291
+ function searchable(text) {
292
+ return text.normalize("NFD").replace(/\p{Diacritic}/gu, "").toLowerCase();
293
+ }
294
+ function entryTitle(brick, data, locale, label = (l) => labelOf(l, locale, locale)) {
295
+ if (brick.kind === "collection" && brick.plans && typeof data[brick.plans.field] === "string") {
296
+ const day = /* @__PURE__ */ new Date(`${data[brick.plans.field]}T00:00:00Z`);
297
+ const field = brick.fields[brick.plans.field];
298
+ if (field && !Number.isNaN(day.getTime())) return `${label(field.label)} ${new Intl.DateTimeFormat(locale, { day: "2-digit", month: "2-digit", year: "numeric", timeZone: "UTC" }).format(day)}`;
299
+ }
300
+ for (const [key, field] of ordered(brick.fields)) {
301
+ if (field.retired) continue;
302
+ if (field.type === "text" || field.type === "longtext") {
303
+ const text = textIn(data[key], locale).trim();
304
+ if (text) return text;
305
+ }
306
+ }
307
+ return void 0;
308
+ }
309
+ function listHaystack(brick, data, locales) {
310
+ const parts = [];
311
+ for (const [key, field] of ordered(brick.fields)) {
312
+ if (field.retired || field.type !== "text" && field.type !== "longtext") continue;
313
+ for (const locale of locales) parts.push(textIn(data[key], locale));
314
+ }
315
+ return searchable(parts.join(" "));
316
+ }
317
+ function sortKey(field, value, main) {
318
+ if (value === void 0 || value === null) return "";
319
+ switch (field.type) {
320
+ case "number":
321
+ return Number(value);
322
+ case "money":
323
+ return value.amount;
324
+ case "daterange":
325
+ return value.from;
326
+ case "date":
327
+ return String(value);
328
+ default:
329
+ return searchable(textIn(value, main));
330
+ }
331
+ }
332
+ function listSort(rows, brick, sort, main) {
333
+ const field = Object.hasOwn(brick.fields, sort.field) ? brick.fields[sort.field] : void 0;
334
+ if (!field) return [...rows];
335
+ const sign = sort.dir === "asc" ? 1 : -1;
336
+ return [...rows].sort((a, b) => {
337
+ const x = sortKey(field, a.data[sort.field], main);
338
+ const y = sortKey(field, b.data[sort.field], main);
339
+ if (x === "") return y === "" ? 0 : 1;
340
+ if (y === "") return -1;
341
+ return sign * (typeof x === "number" && typeof y === "number" ? x - y : String(x).localeCompare(String(y)));
342
+ });
343
+ }
344
+
345
+ const WINDOW = 7;
346
+ function brickStatus(brick, entries, site) {
347
+ const { today, locales } = site;
348
+ const main = locales[0];
349
+ const status = brick.status;
350
+ const live = entries.filter((e) => e.state !== "draft");
351
+ const title = (data) => entryTitle(brick, data, main) ?? null;
352
+ const soon = addDays(today, WINDOW);
353
+ if (!status) {
354
+ if (brick.kind !== "collection" || !brick.drafts) return { line: null, due: false };
355
+ const drafts = entries.filter((e) => e.state === "draft");
356
+ const scheduled = drafts.filter((e) => e.publishAt !== void 0).length;
357
+ return drafts.length ? { line: { kind: "drafts", drafts: drafts.length - scheduled, scheduled }, due: false } : { line: null, due: false };
358
+ }
359
+ if ("hours" in status) {
360
+ const hours = site.hours ?? null;
361
+ return hours ? { line: { kind: "hours", today: hours }, due: "unknown" in hours } : { line: null, due: false };
362
+ }
363
+ if ("count" in status) {
364
+ const online = live.filter((e) => isVisible(brick, e.data, today));
365
+ const ending = online.filter((e) => {
366
+ const until = visibleUntil(brick, e.data);
367
+ return until !== void 0 && until < soon;
368
+ }).length;
369
+ return { line: { kind: "count", ending, online: online.length }, due: ending > 0 };
370
+ }
371
+ if ("missing" in status) {
372
+ const field = brick.fields[status.missing];
373
+ const count = live.filter((e) => isEmpty(field, e.data[status.missing], main)).length;
374
+ return { line: { kind: "missing", count, field: status.missing }, due: count > 0 };
375
+ }
376
+ if ("next" in status) {
377
+ const dated2 = live.flatMap((e) => {
378
+ const span = spanOf(e.data[status.next]);
379
+ return span && span.to >= today ? [{ e, ...span }] : [];
380
+ }).sort((a, b) => a.from.localeCompare(b.from));
381
+ const next = dated2[0];
382
+ if (!next) return { line: null, due: false };
383
+ const due = next.from < soon && Object.entries(brick.fields).some(([name, field]) => !field.optional && !field.retired && field.type !== "slug" && name !== status.next && emptyInSomeLanguage(field, next.e.data[name], locales));
384
+ return { line: { kind: "next", title: title(next.e.data), date: next.from }, due };
385
+ }
386
+ const dated = live.flatMap((e) => {
387
+ const span = spanOf(e.data[status.age]);
388
+ return span ? [{ e, date: span.from }] : [];
389
+ }).sort((a, b) => b.date.localeCompare(a.date));
390
+ const newest = dated[0];
391
+ if (!newest) return { line: null, due: false };
392
+ return { line: { kind: "age", title: title(newest.e.data), date: newest.date, weeks: Math.max(0, Math.floor(daysBetween(newest.date, today) / 7)) }, due: false };
393
+ }
394
+ function spanOf(value) {
395
+ if (typeof value === "string" && value) return { from: value, to: value };
396
+ if (value && typeof value === "object" && !Array.isArray(value)) {
397
+ const { from, to } = value;
398
+ if (typeof from === "string" && from) return { from, to: typeof to === "string" && to ? to : from };
399
+ }
400
+ return void 0;
401
+ }
402
+ function daysBetween(from, to) {
403
+ return Math.round((Date.parse(`${to}T00:00:00Z`) - Date.parse(`${from}T00:00:00Z`)) / 864e5);
404
+ }
405
+ function isEmpty(field, value, locale) {
406
+ if (value === void 0 || value === null || value === "") return true;
407
+ if (Array.isArray(value)) return value.length === 0;
408
+ if (isTranslatable(field) && typeof value === "object") return textIn(value, locale).trim() === "";
409
+ return false;
410
+ }
411
+ const emptyInSomeLanguage = (field, value, locales) => isTranslatable(field) ? locales.some((locale) => isEmpty(field, value, locale)) : isEmpty(field, value, locales[0]);
412
+
413
+ function leavesOf(field, value, path, out = /* @__PURE__ */ new Map()) {
414
+ if (value === void 0 || value === null) return out;
415
+ if (isTranslatable(field)) out.set(path, asRecord(value));
416
+ else if (field.type === "image") out.set(`${path}.alt`, asRecord(asRecord(value).alt));
417
+ else if (field.type === "list" && Array.isArray(value)) {
418
+ for (const row of value) {
419
+ if (!isRecord$1(row)) continue;
420
+ for (const [key, inner] of Object.entries(field.of)) leavesOf(inner, row[key], `${path}.${String(row._id)}.${key}`, out);
421
+ }
422
+ }
423
+ return out;
424
+ }
425
+ function carryPrints(fields, before, after, prints, main) {
426
+ const out = Object.fromEntries(Object.entries(prints).map(([leaf, byLocale]) => [leaf, { ...byLocale }]));
427
+ for (const [root, field] of Object.entries(fields)) {
428
+ if (stableJson(before[root] ?? null) === stableJson(after[root] ?? null)) continue;
429
+ const old = leavesOf(field, before[root], root);
430
+ for (const [leaf, values] of leavesOf(field, after[root], root)) {
431
+ const was = old.get(leaf);
432
+ const kept = out[leaf];
433
+ if (!was || !kept) continue;
434
+ const current = printOf(was[main]);
435
+ for (const locale of Object.keys(kept)) if (kept[locale] === current) kept[locale] = printOf(values[main]);
436
+ }
437
+ }
438
+ return out;
439
+ }
440
+ function isRecord$1(v) {
441
+ return typeof v === "object" && v !== null && !Array.isArray(v);
442
+ }
443
+ function asRecord(v) {
444
+ return isRecord$1(v) ? v : {};
445
+ }
446
+
211
447
  const DATE = "^\\d{4}-\\d{2}-\\d{2}$";
212
448
  const TIME = "^\\d{2}:\\d{2}$";
213
449
  function taskInputSchema(brick, task, ctx) {
@@ -217,7 +453,7 @@ function taskInputSchema(brick, task, ctx) {
217
453
  if (field) fields[key] = field;
218
454
  }
219
455
  if (task.kind === "create") {
220
- for (const [key, field] of Object.entries(brick.fields)) if (field.type !== "slug") fields[key] = field;
456
+ for (const [key, field] of Object.entries(brick.fields)) if (createAsks(brick, key, task)) fields[key] = field;
221
457
  }
222
458
  const schema = objectOf(fields, ctx, ctx.prefill);
223
459
  const pick = task.kind === "remove" || task.kind === "edit" && task.pick === true;
@@ -243,7 +479,7 @@ function objectOf(fields, ctx, prefill) {
243
479
  if (value !== void 0) prop.default = value;
244
480
  }
245
481
  properties[key] = prop;
246
- if (!field.optional && field.default === void 0 && preset === void 0) required.push(key);
482
+ if (!field.optional && !field.showWhen && field.default === void 0 && preset === void 0) required.push(key);
247
483
  }
248
484
  return { type: "object", properties, required, additionalProperties: false };
249
485
  }
@@ -594,13 +830,14 @@ const treeOf = (value, trees) => {
594
830
  const ref = value.ref;
595
831
  return typeof ref === "string" ? trees?.get(ref) : void 0;
596
832
  };
833
+ const markdownText = (text, inline = false) => (inline ? text.replace(/\s+/g, (space) => space.includes("\n") ? " " : space) : text).replace(/[\\`*_[\]<>#|!~]/g, "\\$&");
597
834
  async function entryMarkdown(item, brick, options) {
598
- const title = typeof item.data.title === "string" ? item.data.title : item.id;
599
- const parts = [`# ${title}`];
835
+ const title = entryTitle$1(item, options.schema.bricks[brick]?.fields);
836
+ const parts = [`# ${markdownText(title, true)}`];
600
837
  for (const [name, field] of Object.entries(options.schema.bricks[brick]?.fields ?? {})) {
601
- if (name === "title") continue;
602
838
  const value = item.data[name];
603
- if ((field.type === "text" || field.type === "longtext") && typeof value === "string" && value.trim()) parts.push(value.trim());
839
+ if (name === "title" || value === title) continue;
840
+ if ((field.type === "text" || field.type === "longtext") && typeof value === "string" && value.trim()) parts.push(markdownText(value.trim()));
604
841
  if (field.type !== "markdown") continue;
605
842
  const tree = treeOf(value, options.trees);
606
843
  if (tree) {
@@ -611,15 +848,20 @@ async function entryMarkdown(item, brick, options) {
611
848
  return `${parts.join("\n\n")}
612
849
  `;
613
850
  }
851
+ const withoutTrailingSlashes = (text) => {
852
+ let end = text.length;
853
+ while (end > 0 && text[end - 1] === "/") end--;
854
+ return text.slice(0, end);
855
+ };
614
856
  const markdownPath = (url) => {
615
- const path = url.replace(/\/+$/, "");
857
+ const path = withoutTrailingSlashes(url);
616
858
  return path === "" ? "/index.md" : `${path}.md`;
617
859
  };
618
860
  function brickLabel(label, brick, bundle) {
619
861
  return label === void 0 ? brick : labelOf(label, bundle.locale, bundle.mainLocale);
620
862
  }
621
863
  function llmsTxt(bundle, options) {
622
- const origin = options.origin.replace(/\/+$/, "");
864
+ const origin = withoutTrailingSlashes(options.origin);
623
865
  const lines = [`# ${options.siteName}`];
624
866
  if (options.description) lines.push("", `> ${options.description}`);
625
867
  for (const [brick, entry] of Object.entries(bundle.bricks)) {
@@ -628,8 +870,8 @@ function llmsTxt(bundle, options) {
628
870
  for (const item of entry.items) {
629
871
  const url = item.urls?.[bundle.locale];
630
872
  if (!url) continue;
631
- const title = typeof item.data.title === "string" ? item.data.title : item.id;
632
- links.push(`- [${title}](${origin}${markdownPath(url)})`);
873
+ const title = entryTitle$1(item, options.schema.bricks[brick]?.fields);
874
+ links.push(`- [${markdownText(title, true)}](${origin}${markdownPath(url)})`);
633
875
  }
634
876
  if (links.length === 0) continue;
635
877
  lines.push("", `## ${brickLabel(options.schema.bricks[brick]?.label, brick, bundle)}`, "", ...links);
@@ -638,7 +880,7 @@ function llmsTxt(bundle, options) {
638
880
  `;
639
881
  }
640
882
  async function llmsFullTxt(bundle, options) {
641
- const origin = options.origin.replace(/\/+$/, "");
883
+ const origin = withoutTrailingSlashes(options.origin);
642
884
  const parts = [`# ${options.siteName}`];
643
885
  if (options.description) parts.push(`> ${options.description}`);
644
886
  for (const [brick, entry] of Object.entries(bundle.bricks)) {
@@ -659,15 +901,15 @@ ${markdown.trimEnd()}`);
659
901
  }
660
902
  const searchTokens = (path) => [...new Set(path.toLowerCase().replace(/\.md$/, "").split(/[/_.-]+/).filter((token) => token.length >= 3))];
661
903
  function markdownNotFound(bundle, path, options) {
662
- const origin = options.origin.replace(/\/+$/, "");
904
+ const origin = withoutTrailingSlashes(options.origin);
663
905
  const tokens = searchTokens(path);
664
906
  const matches = [];
665
- for (const entry of Object.values(bundle.bricks)) {
907
+ for (const [brick, entry] of Object.entries(bundle.bricks)) {
666
908
  if (entry.kind !== "collection") continue;
667
909
  for (const item of entry.items) {
668
910
  const url = item.urls?.[bundle.locale];
669
911
  if (!url) continue;
670
- const title = typeof item.data.title === "string" ? item.data.title : item.id;
912
+ const title = entryTitle$1(item, options.schema?.bricks[brick]?.fields);
671
913
  const haystack = `${url} ${title}`.toLowerCase();
672
914
  const score = tokens.filter((token) => haystack.includes(token)).length;
673
915
  if (score > 0) matches.push({ url, title, score });
@@ -676,7 +918,7 @@ function markdownNotFound(bundle, path, options) {
676
918
  const suggestions = matches.sort((a, b) => b.score - a.score).slice(0, options.limit ?? 5);
677
919
  const lines = ["# Not found", "", `There is no page at \`${path}\`.`];
678
920
  if (suggestions.length > 0) {
679
- lines.push("", "## Maybe you meant", "", ...suggestions.map((match) => `- [${match.title}](${origin}${markdownPath(match.url)})`));
921
+ lines.push("", "## Maybe you meant", "", ...suggestions.map((match) => `- [${markdownText(match.title, true)}](${origin}${markdownPath(match.url)})`));
680
922
  }
681
923
  lines.push("", `All pages: ${origin}/llms.txt`);
682
924
  return `${lines.join("\n")}
@@ -702,82 +944,6 @@ function findEntryByPath(bundle, path) {
702
944
  return void 0;
703
945
  }
704
946
 
705
- function seoHead(input) {
706
- const { siteName, description, image, noindex } = input;
707
- const title = input.title || void 0;
708
- const template = input.titleTemplate ?? `%s \xB7 ${siteName}`;
709
- const fullTitle = title ? template.replace("%s", () => title) : siteName;
710
- const meta = [
711
- { name: "description", content: description ?? "" },
712
- { name: "robots", content: noindex ? "noindex, nofollow" : "" },
713
- { property: "og:title", content: title ?? siteName },
714
- { property: "og:description", content: description ?? "" },
715
- { property: "og:type", content: input.type ?? "website" },
716
- { property: "og:site_name", content: siteName },
717
- { property: "og:image", content: image?.url ?? "" },
718
- { property: "og:image:width", content: image?.width === void 0 ? "" : String(image.width) },
719
- { property: "og:image:height", content: image?.height === void 0 ? "" : String(image.height) },
720
- { property: "og:image:alt", content: image?.alt ?? "" },
721
- { name: "twitter:card", content: image ? "summary_large_image" : "summary" }
722
- ];
723
- return { title: fullTitle, meta: meta.filter((m) => m.content !== "") };
724
- }
725
- function isPreviewHost(host, siteUrl) {
726
- const bare = (h) => h.toLowerCase().replace(/^www\./, "");
727
- return bare(host ?? "") !== bare(new URL(siteUrl).host);
728
- }
729
- function robotsTxt(options) {
730
- if (options.noindex) return "User-agent: *\nDisallow: /\n";
731
- const rules = options.disallow?.length ? options.disallow.map((path) => `Disallow: ${path}`) : ["Allow: /"];
732
- const lines = ["User-agent: *", ...rules];
733
- if (options.sitemap) lines.push("", `Sitemap: ${options.sitemap}`);
734
- return `${lines.join("\n")}
735
- `;
736
- }
737
- const compact = (o) => Object.fromEntries(Object.entries(o).filter(([, v]) => v !== void 0));
738
- function websiteLd(o) {
739
- return { "@context": "https://schema.org", "@type": "WebSite", name: o.name, url: o.url, inLanguage: o.lang };
740
- }
741
- function organizationLd(o) {
742
- return compact({ "@context": "https://schema.org", "@type": "Organization", name: o.name, url: o.url, logo: o.logo, sameAs: o.sameAs });
743
- }
744
- function breadcrumbLd(nodes, origin) {
745
- return {
746
- "@context": "https://schema.org",
747
- "@type": "BreadcrumbList",
748
- itemListElement: nodes.map((node, i) => ({ "@type": "ListItem", position: i + 1, name: node.title, item: origin + node.path }))
749
- };
750
- }
751
- function articleLd(o) {
752
- return compact({
753
- "@context": "https://schema.org",
754
- "@type": "Article",
755
- headline: o.title,
756
- url: o.url,
757
- description: o.description,
758
- image: o.image,
759
- datePublished: o.published,
760
- dateModified: o.modified,
761
- author: o.author === void 0 ? void 0 : { "@type": "Person", name: o.author },
762
- inLanguage: o.lang
763
- });
764
- }
765
- function faqLd(pairs) {
766
- return {
767
- "@context": "https://schema.org",
768
- "@type": "FAQPage",
769
- mainEntity: pairs.map((p) => ({ "@type": "Question", name: p.question, acceptedAnswer: { "@type": "Answer", text: p.answer } }))
770
- };
771
- }
772
- function siteHeaders(input) {
773
- return {
774
- "content-security-policy": input.preview && input.platformOrigin ? `frame-ancestors ${input.platformOrigin}` : `frame-ancestors 'self'`,
775
- "x-content-type-options": "nosniff",
776
- "referrer-policy": "strict-origin-when-cross-origin",
777
- ...input.noindex ? { "x-robots-tag": "noindex" } : {}
778
- };
779
- }
780
-
781
947
  function imageInfo(bytes) {
782
948
  return pngInfo(bytes) ?? gifInfo(bytes) ?? jpegInfo(bytes) ?? webpInfo(bytes) ?? avifInfo(bytes) ?? svgInfo(bytes);
783
949
  }
@@ -900,4 +1066,4 @@ function sized(type, width, height) {
900
1066
  return { type, width, height };
901
1067
  }
902
1068
 
903
- export { articleLd, breadcrumbLd, breadcrumbs, describeProfile, effectiveMax, effectiveProfile, entryMarkdown, faqLd, findEntryByPath, fromToolInput, imageInfo, isPreviewHost, isStableMarkdown, isTranslatable, labelOf, llmsFullTxt, llmsTxt, markdownBlocks, markdownNotFound, markdownRequestPath, minorDigits, navTree, neighbors, organizationLd, parseMarkdown, query, robotsTxt, seoHead, serializeMarkdown, siteHeaders, taskInputSchema, websiteLd };
1069
+ export { addDays, breadcrumbs, brickStatus, carryPrints, createAsks, describeProfile, effectiveMax, effectiveProfile, entryMarkdown, entryTitle, findEntryByPath, fromToolInput, imageInfo, isStableMarkdown, isTranslatable, isVisible, labelOf, leavesOf, listHaystack, listSort, llmsFullTxt, llmsTxt, markdownBlocks, markdownNotFound, markdownRequestPath, mergeRows, minorDigits, navTree, neighbors, ordered, parseMarkdown, printOf, query, rowDiffs, searchable, serializeMarkdown, stableJson, taskInputSchema, textIn, visibleUntil };
@@ -1,9 +1,9 @@
1
1
  import * as nuxt_schema from 'nuxt/schema';
2
2
  import { SiteInput } from '../index.mjs';
3
- import '../shared/ginko.D-fNojn9.mjs';
3
+ import '../shared/ginko.DrkUeYFP.mjs';
4
4
  import '@lupinum/ginko-i18n/core';
5
5
  import 'comark';
6
- import '../shared/ginko.Bwan3BB-.mjs';
6
+ import '../shared/ginko.6cI8KXZP.mjs';
7
7
 
8
8
  declare module '@nuxt/schema' {
9
9
  interface NuxtHooks {
@@ -1,9 +1,9 @@
1
1
  import * as nuxt_schema from 'nuxt/schema';
2
2
  import { SiteInput } from '../index.js';
3
- import '../shared/ginko.D-fNojn9.js';
3
+ import '../shared/ginko.DrkUeYFP.js';
4
4
  import '@lupinum/ginko-i18n/core';
5
5
  import 'comark';
6
- import '../shared/ginko.BBkZW2nW.js';
6
+ import '../shared/ginko.B_pACpVU.js';
7
7
 
8
8
  declare module '@nuxt/schema' {
9
9
  interface NuxtHooks {