@lupinum/ginko 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +150 -0
- package/README.md +2 -2
- package/dist/basics.d.mts +492 -0
- package/dist/basics.d.ts +492 -0
- package/dist/basics.mjs +416 -0
- package/dist/cli/index.mjs +54 -55
- package/dist/index.d.mts +5 -4
- package/dist/index.d.ts +5 -4
- package/dist/index.mjs +3 -3
- package/dist/internal/search.mjs +6 -3
- package/dist/internal.d.mts +387 -41
- package/dist/internal.d.ts +387 -41
- package/dist/internal.mjs +267 -101
- package/dist/nuxt/module.d.mts +2 -2
- package/dist/nuxt/module.d.ts +2 -2
- package/dist/nuxt/module.mjs +105 -13
- package/dist/nuxt/runtime/GinkoImage.d.mts +1 -1
- package/dist/nuxt/runtime/GinkoMarkdown.mjs +2 -2
- package/dist/nuxt/runtime/composables.d.mts +4 -3
- package/dist/nuxt/runtime/composables.mjs +19 -15
- package/dist/nuxt/runtime/fetch.d.mts +3 -1
- package/dist/nuxt/runtime/fetch.mjs +12 -6
- package/dist/nuxt/runtime/markdown.d.mts +3 -0
- package/dist/nuxt/runtime/markdown.mjs +15 -3
- package/dist/nuxt/runtime/plugin.mjs +6 -35
- package/dist/nuxt/runtime/preview-listener.d.mts +13 -0
- package/dist/nuxt/runtime/preview-listener.mjs +37 -0
- package/dist/nuxt/runtime/preview.vue +3 -1
- package/dist/nuxt/runtime/schema.d.ts +6 -0
- package/dist/nuxt/runtime/server/bundles.d.ts +4 -0
- package/dist/nuxt/runtime/server/files-md.d.ts +2 -0
- package/dist/nuxt/runtime/server/files.d.ts +2 -0
- package/dist/nuxt/runtime/server/headers.d.ts +2 -0
- package/dist/nuxt/runtime/server/items.d.mts +12 -0
- package/dist/nuxt/runtime/server/items.d.ts +12 -0
- package/dist/nuxt/runtime/server/items.mjs +17 -0
- package/dist/nuxt/runtime/server/llms-full.d.ts +2 -0
- package/dist/nuxt/runtime/server/llms.d.ts +2 -0
- package/dist/nuxt/runtime/server/markdown-trees.d.ts +2 -0
- package/dist/nuxt/runtime/server/markdown.d.ts +2 -0
- package/dist/nuxt/runtime/server/markdown.mjs +1 -1
- package/dist/nuxt/runtime/server/platform-md.d.mts +3 -0
- package/dist/nuxt/runtime/server/platform-md.d.ts +3 -0
- package/dist/nuxt/runtime/server/platform-md.mjs +71 -0
- package/dist/nuxt/runtime/server/redirects.d.ts +2 -0
- package/dist/nuxt/runtime/server/robots.d.ts +2 -0
- package/dist/nuxt/runtime/server/search.d.ts +2 -0
- package/dist/nuxt/runtime/server/site-config.d.ts +14 -0
- package/dist/nuxt/runtime/server/sitemap-source.d.ts +2 -0
- package/dist/nuxt/runtime/server/sitemap-xml.d.ts +24 -0
- package/dist/nuxt/runtime/server/sitemap.d.ts +2 -0
- package/dist/nuxt/runtime/shared.d.mts +10 -3
- package/dist/nuxt/runtime/shared.mjs +2 -2
- package/dist/shared/ginko.1wwZv4B_.d.ts +1622 -0
- package/dist/shared/{ginko.BBkZW2nW.d.ts → ginko.6cI8KXZP.d.mts} +2 -2
- package/dist/shared/{ginko.CQvxhyez.mjs → ginko.AprJymER.mjs} +30 -1
- package/dist/shared/ginko.BNVC-g9n.mjs +10389 -0
- package/dist/shared/{ginko.Bwan3BB-.d.mts → ginko.B_pACpVU.d.ts} +2 -2
- package/dist/shared/{ginko.CrNRBkS5.mjs → ginko.C4gcOovG.mjs} +253 -309
- package/dist/shared/{ginko.C90fPpMP.mjs → ginko.CXA2R3aS.mjs} +128 -101
- package/dist/shared/ginko.CjWB8N7f.d.ts +128 -0
- package/dist/shared/ginko.D1PeQ3ds.mjs +36 -0
- package/dist/shared/ginko.DIl7l-75.mjs +26 -0
- package/dist/shared/ginko.DY7RbCEw.mjs +601 -0
- package/dist/shared/ginko.Dmgixfpf.mjs +130 -0
- package/dist/shared/{ginko.D-fNojn9.d.mts → ginko.DrkUeYFP.d.mts} +824 -10
- package/dist/shared/{ginko.D-fNojn9.d.ts → ginko.DrkUeYFP.d.ts} +824 -10
- package/dist/shared/{ginko.akHewCGt.d.mts → ginko.DvPtlHIz.d.mts} +50 -5
- package/dist/shared/{ginko.BDF3bW0C.d.ts → ginko.EelR5dt9.d.ts} +50 -5
- package/dist/shared/ginko.ULKQnAWd.d.ts +91 -0
- package/dist/shared/ginko.loGIxGZg.d.mts +91 -0
- package/dist/shared/ginko.o1yIKlgj.d.mts +128 -0
- package/dist/shared/ginko.plW4dLmm.d.mts +1622 -0
- package/dist/shared/{ginko.Dht0xxE5.mjs → ginko.raCzOPYC.mjs} +1 -2
- package/dist/shared/ginko.yKMIjJ68.mjs +84 -0
- package/dist/vue.d.mts +5 -4
- package/dist/vue.d.ts +5 -4
- package/dist/vue.mjs +1 -1
- package/i18n/ar.json +212 -0
- package/i18n/de.json +9 -1
- package/i18n/en.json +9 -1
- package/i18n/es.json +12 -4
- package/i18n/fr.json +212 -0
- package/i18n/it.json +212 -0
- package/package.json +7 -3
- package/dist/shared/ginko.BTUfZCfa.d.mts +0 -172
- package/dist/shared/ginko.C-QjDn3O.mjs +0 -4944
- package/dist/shared/ginko.Dc0oNcDv.d.ts +0 -172
- package/dist/shared/ginko.Mn30dmlm.mjs +0 -20
|
@@ -1,9 +1,84 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { B as BRICK_KEYS, F as FIELD_KEYS, S as SCHEMA_KEYS
|
|
3
|
-
import { a as addDays } from './ginko.
|
|
1
|
+
import { i as isTranslatable, A as ALT_MAX, g as effectiveMax, m as msg, h as stableJson, j as bare, k as sameLabel, l as effectiveMin, n as sha256, r as resolveRouteLinks, o as resolveFileRefs, q as effectiveAlt } from './ginko.BNVC-g9n.mjs';
|
|
2
|
+
import { B as BRICK_KEYS, F as FIELD_KEYS, S as SCHEMA_KEYS } from './ginko.C4gcOovG.mjs';
|
|
3
|
+
import { a as addDays } from './ginko.AprJymER.mjs';
|
|
4
|
+
import { B as BUNDLE_FORMAT, w as withMarkdownTrees } from './ginko.yKMIjJ68.mjs';
|
|
5
|
+
|
|
6
|
+
class CliError extends Error {
|
|
7
|
+
code;
|
|
8
|
+
constructor(message, code = "INVALID") {
|
|
9
|
+
super(message);
|
|
10
|
+
this.code = code;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
const isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
14
|
+
function parseReleaseState(value) {
|
|
15
|
+
if (!isObject(value)) throw new CliError("The platform did not return a valid state.", "UNAVAILABLE");
|
|
16
|
+
const { site, schema, schemaRev, contentRev, migrations, entries } = value;
|
|
17
|
+
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));
|
|
18
|
+
if (!valid) throw new CliError("The platform returned an incomplete state.", "UNAVAILABLE");
|
|
19
|
+
return value;
|
|
20
|
+
}
|
|
21
|
+
const EXTENSIONS = {
|
|
22
|
+
"image/jpeg": ".jpg",
|
|
23
|
+
"image/png": ".png",
|
|
24
|
+
"image/webp": ".webp",
|
|
25
|
+
"image/avif": ".avif",
|
|
26
|
+
"application/pdf": ".pdf"
|
|
27
|
+
};
|
|
28
|
+
function defaultDir(site, now) {
|
|
29
|
+
return `ginko-export-${site}-${now.toISOString().slice(0, 10)}`;
|
|
30
|
+
}
|
|
31
|
+
function pathOf(file) {
|
|
32
|
+
return `files/${file.id}${EXTENSIONS[file.mime] ?? ""}`;
|
|
33
|
+
}
|
|
34
|
+
function parseExport(value, locator, problem) {
|
|
35
|
+
const optionalNumber = (v) => v === void 0 || typeof v === "number";
|
|
36
|
+
const optionalString = (v) => v === void 0 || typeof v === "string";
|
|
37
|
+
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.original === void 0 || f.original === true) && (f.alt === void 0 || isObject(f.alt) && Object.values(f.alt).every((text) => typeof text === "string")) && (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)));
|
|
38
|
+
if (!valid) throw new CliError(problem, "UNAVAILABLE" );
|
|
39
|
+
return value;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const SITE_BUDGET = { siteBytes: 4 * 1024 * 1024, entries: 2e3, entryBytes: 480 * 1024, treeBytes: 8 * 1024 * 1024, trashBytes: 4 * 1024 * 1024 };
|
|
43
|
+
function byteSize(value) {
|
|
44
|
+
return new TextEncoder().encode(JSON.stringify(value ?? null)).byteLength;
|
|
45
|
+
}
|
|
46
|
+
function entryBytes(data, prints = {}) {
|
|
47
|
+
return byteSize(data) + byteSize(prints);
|
|
48
|
+
}
|
|
49
|
+
function fitsBudget(after, before, limit) {
|
|
50
|
+
return after <= limit || after <= before;
|
|
51
|
+
}
|
|
52
|
+
const VALUE_JSON = 8;
|
|
53
|
+
const ID_BYTES = 40;
|
|
54
|
+
const SHORT_VALUE = 32;
|
|
55
|
+
function largestFieldBytes(field, locales) {
|
|
56
|
+
if (field.retired) return 0;
|
|
57
|
+
const values = isTranslatable(field) ? locales.length : 1;
|
|
58
|
+
switch (field.type) {
|
|
59
|
+
case "text":
|
|
60
|
+
case "longtext":
|
|
61
|
+
case "markdown":
|
|
62
|
+
return (effectiveMax(field) + VALUE_JSON) * values;
|
|
63
|
+
case "list":
|
|
64
|
+
return field.max * (largestEntryBytes(field.of, locales) + ID_BYTES);
|
|
65
|
+
case "choices":
|
|
66
|
+
return effectiveMax(field) * ID_BYTES;
|
|
67
|
+
case "ref":
|
|
68
|
+
return field.multiple ? (field.max ?? 0) * ID_BYTES : ID_BYTES;
|
|
69
|
+
case "image":
|
|
70
|
+
case "file":
|
|
71
|
+
return ID_BYTES + ALT_MAX * locales.length;
|
|
72
|
+
default:
|
|
73
|
+
return SHORT_VALUE * values;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function largestEntryBytes(fields, locales) {
|
|
77
|
+
return Object.values(fields).reduce((sum, field) => sum + largestFieldBytes(field, locales), 0);
|
|
78
|
+
}
|
|
4
79
|
|
|
5
80
|
const fx = (key) => msg(`diff.fix.${key}`);
|
|
6
|
-
const NEW_FIELD_FIX = fx("newField");
|
|
81
|
+
const NEW_FIELD_FIX = /* @__PURE__ */ fx("newField");
|
|
7
82
|
function diffSchema(prod, next) {
|
|
8
83
|
const changes = [];
|
|
9
84
|
const rejected = [];
|
|
@@ -28,6 +103,7 @@ function diffSchema(prod, next) {
|
|
|
28
103
|
}
|
|
29
104
|
for (const k of ["name", "url"]) if (prod[k] !== next[k]) ok(k, k, msg("diff.valueChanged", { name: k }));
|
|
30
105
|
if (stableJson(prod.week) !== stableJson(next.week)) ok("week", "week", msg("diff.valueChanged", { name: "week" }));
|
|
106
|
+
if ((prod.holidays ?? null) !== (next.holidays ?? null)) ok("holidays", "holidays", msg("diff.valueChanged", { name: "holidays" }));
|
|
31
107
|
if (stableJson(prod.pages) !== stableJson(next.pages)) ok("pages", "pages", msg("diff.pagesChanged"));
|
|
32
108
|
if (stableJson(prod.agents ?? null) !== stableJson(next.agents ?? null)) ok("agents", "agents", msg("diff.agentsChanged"));
|
|
33
109
|
for (const [path, rule] of Object.entries(prod.retired)) {
|
|
@@ -92,6 +168,8 @@ function diffBrick(key, before, after, newFills, newLocales, ok, no) {
|
|
|
92
168
|
if (!sameLabel(before.describe ?? "", after.describe ?? "")) ok(`${key}.describe`, "text", msg("diff.textChanged"));
|
|
93
169
|
if ((before.layout ?? "sections") !== (after.layout ?? "sections")) ok(`${key}.layout`, "text", msg("diff.textChanged"));
|
|
94
170
|
if (stableJson(before.tasks ?? []) !== stableJson(after.tasks ?? [])) ok(`${key}.tasks`, "tasks", msg("diff.tasksChanged"));
|
|
171
|
+
if (stableJson(before.planned ?? null) !== stableJson(after.planned ?? null)) ok(`${key}.planned`, "planned", msg("diff.valueChanged", { name: "planned" }));
|
|
172
|
+
if (stableJson(before.status ?? null) !== stableJson(after.status ?? null)) ok(`${key}.status`, "text", msg("diff.textChanged"));
|
|
95
173
|
if (before.kind === "collection" && after.kind === "collection") {
|
|
96
174
|
if (after.max < before.max) no(`${key}.max`, "maxLowered", msg("diff.maxLowered", { at: key }), fx("keepMax"));
|
|
97
175
|
else if (after.max > before.max) ok(`${key}.max`, "maxRaised", msg("diff.maxRaised"));
|
|
@@ -158,7 +236,7 @@ function diffField(at, old, next, newFills, ok, no) {
|
|
|
158
236
|
if (onGrid) ok(at, "minLowered", msg("diff.minLowered"));
|
|
159
237
|
else no(at, "minOffGrid", msg("diff.minOffGrid", { at, step }), fx("minOnGrid"));
|
|
160
238
|
}
|
|
161
|
-
for (const k of ["step", "format", "currency", "from", "accept", "to", "multiple"]) {
|
|
239
|
+
for (const k of ["step", "format", "currency", "from", "accept", "to", "multiple", "after"]) {
|
|
162
240
|
if (stableJson(o[k] ?? null) !== stableJson(n[k] ?? null)) no(at, `${k}Changed`, msg("diff.propChanged", { at, prop: k }));
|
|
163
241
|
}
|
|
164
242
|
if ((old.type === "choice" || old.type === "choices") && (next.type === "choice" || next.type === "choices")) {
|
|
@@ -179,7 +257,7 @@ function diffField(at, old, next, newFills, ok, no) {
|
|
|
179
257
|
}
|
|
180
258
|
}
|
|
181
259
|
|
|
182
|
-
const isRecord
|
|
260
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
183
261
|
function releaseData(stored, next, data, written) {
|
|
184
262
|
const out = { ...data };
|
|
185
263
|
for (const [name, field] of Object.entries(next)) {
|
|
@@ -193,7 +271,7 @@ function releaseData(stored, next, data, written) {
|
|
|
193
271
|
const columns = Object.entries(field.of).filter(([column, inner]) => !Object.hasOwn(old.of, column) && !inner.optional && !inner.retired && inner.default !== void 0);
|
|
194
272
|
if (!columns.length) continue;
|
|
195
273
|
out[name] = out[name].map((row) => {
|
|
196
|
-
if (!isRecord
|
|
274
|
+
if (!isRecord(row)) return row;
|
|
197
275
|
const filled = { ...row };
|
|
198
276
|
for (const [column, inner] of columns) if (filled[column] === void 0) filled[column] = inner.default;
|
|
199
277
|
return filled;
|
|
@@ -209,81 +287,9 @@ function fieldsChanged(stored, next, brick) {
|
|
|
209
287
|
return !!before && !!after && stableJson(before.fields) !== stableJson(after.fields);
|
|
210
288
|
}
|
|
211
289
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
if (!isRecord(value)) return false;
|
|
215
|
-
const b = value;
|
|
216
|
-
if (b.format !== BUNDLE_FORMAT || typeof b.site !== "string" || typeof b.locale !== "string") return false;
|
|
217
|
-
if (!Number.isInteger(b.schemaRev) || !Number.isInteger(b.contentRev) || !isRecord(b.bricks)) return false;
|
|
218
|
-
return Object.values(b.bricks).every(isBundleBrick);
|
|
219
|
-
}
|
|
220
|
-
function knownBricks(bundle, schema) {
|
|
221
|
-
return { ...bundle, bricks: Object.fromEntries(Object.entries(bundle.bricks).filter(([key]) => Object.hasOwn(schema.bricks, key))) };
|
|
222
|
-
}
|
|
223
|
-
function withFileBricks(platform, files, schema) {
|
|
224
|
-
const base = platform ?? files;
|
|
225
|
-
if (!base) throw new Error("withFileBricks: no bundle");
|
|
226
|
-
const fromPlatform = Object.entries(platform?.bricks ?? {}).filter(([key]) => schema.bricks[key]?.source !== "files");
|
|
227
|
-
return { ...base, bricks: { ...Object.fromEntries(fromPlatform), ...files?.bricks } };
|
|
228
|
-
}
|
|
229
|
-
async function markdownTrees(fields, data) {
|
|
230
|
-
const trees = {};
|
|
231
|
-
const parse = async (text) => (await parseMarkdown(text)).value;
|
|
232
|
-
const walk = async (inner, row, path) => {
|
|
233
|
-
for (const [name, field] of Object.entries(inner)) {
|
|
234
|
-
const value = row[name];
|
|
235
|
-
const leaf = path ? `${path}.${name}` : name;
|
|
236
|
-
if (field.type === "markdown" && typeof value === "string") trees[leaf] = await parse(value);
|
|
237
|
-
else if (field.type === "markdown" && isRecord(value)) {
|
|
238
|
-
const perLocale = {};
|
|
239
|
-
for (const [locale, text] of Object.entries(value)) if (typeof text === "string") perLocale[locale] = await parse(text);
|
|
240
|
-
trees[leaf] = perLocale;
|
|
241
|
-
} else if (field.type === "list" && Array.isArray(value)) {
|
|
242
|
-
for (const r of value) if (isRecord(r) && typeof r._id === "string") await walk(field.of, r, `${leaf}.${r._id}`);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
};
|
|
246
|
-
await walk(fields, data, "");
|
|
247
|
-
return trees;
|
|
248
|
-
}
|
|
249
|
-
async function withMarkdownTrees(bundle, schema) {
|
|
250
|
-
const fill = async (fields, data) => {
|
|
251
|
-
const out = { ...data };
|
|
252
|
-
for (const [key, field] of Object.entries(fields)) {
|
|
253
|
-
const value = out[key];
|
|
254
|
-
if (field.type === "markdown" && typeof value === "string") out[key] = (await parseMarkdown(value)).value;
|
|
255
|
-
else if (field.type === "list" && Array.isArray(value)) out[key] = await Promise.all(value.map((row) => isRecord(row) ? fill(field.of, row) : row));
|
|
256
|
-
}
|
|
257
|
-
return out;
|
|
258
|
-
};
|
|
259
|
-
const bricks = {};
|
|
260
|
-
for (const [key, brick] of Object.entries(bundle.bricks)) {
|
|
261
|
-
const fields = Object.hasOwn(schema.bricks, key) ? schema.bricks[key].fields : void 0;
|
|
262
|
-
if (!fields) bricks[key] = brick;
|
|
263
|
-
else if (brick.kind === "single") bricks[key] = { ...brick, data: await fill(fields, brick.data) };
|
|
264
|
-
else bricks[key] = { ...brick, items: await Promise.all(brick.items.map(async (item) => ({ ...item, data: await fill(fields, item.data) }))) };
|
|
265
|
-
}
|
|
266
|
-
return { ...bundle, bricks };
|
|
267
|
-
}
|
|
268
|
-
function bundleFits(bundle, schema) {
|
|
269
|
-
return Object.entries(schema.bricks).every(([key, brick]) => {
|
|
270
|
-
const have = Object.hasOwn(bundle.bricks, key) ? bundle.bricks[key] : void 0;
|
|
271
|
-
return have === void 0 || have.kind === (brick.kind === "single" ? "single" : "collection");
|
|
272
|
-
});
|
|
273
|
-
}
|
|
274
|
-
function isBundleBrick(brick) {
|
|
275
|
-
if (!isRecord(brick)) return false;
|
|
276
|
-
if (brick.kind === "single") return isRecord(brick.data) && isStrings(brick.fallback);
|
|
277
|
-
if (brick.kind !== "collection" || !Array.isArray(brick.items)) return false;
|
|
278
|
-
return brick.items.every((item) => isRecord(item) && typeof item.id === "string" && isRecord(item.data) && isStrings(item.fallback) && (item.urls === void 0 || isRecord(item.urls) && Object.values(item.urls).every((u) => typeof u === "string")) && (item.updatedAt === void 0 || typeof item.updatedAt === "string") && (item.source === void 0 || typeof item.source === "string"));
|
|
279
|
-
}
|
|
280
|
-
function isStrings(value) {
|
|
281
|
-
return Array.isArray(value) && value.every((v) => typeof v === "string");
|
|
282
|
-
}
|
|
283
|
-
function isRecord(value) {
|
|
284
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
290
|
+
function untranslatedMode(brick) {
|
|
291
|
+
return brick.untranslated ?? (brick.route ? "hide" : "fallback");
|
|
285
292
|
}
|
|
286
|
-
|
|
287
293
|
function printOf(mainValue) {
|
|
288
294
|
return sha256(stableJson(mainValue ?? null));
|
|
289
295
|
}
|
|
@@ -291,8 +297,9 @@ function resolveEntry(schema, brickKey, entry, ctx) {
|
|
|
291
297
|
const brick = schema.bricks[brickKey];
|
|
292
298
|
const main = schema.locales[0];
|
|
293
299
|
const fallback = [];
|
|
294
|
-
const
|
|
295
|
-
const
|
|
300
|
+
const outdated = [];
|
|
301
|
+
const blocking = [];
|
|
302
|
+
const state = { schema, brickKey, entry, ctx, main, fallback, outdated, blocking };
|
|
296
303
|
const data = resolveFields(brick.fields, entry.data, "", state);
|
|
297
304
|
for (const [name, field] of Object.entries(brick.fields)) {
|
|
298
305
|
if (!field.retired) continue;
|
|
@@ -303,12 +310,13 @@ function resolveEntry(schema, brickKey, entry, ctx) {
|
|
|
303
310
|
if (leaf === rule.from || leaf.startsWith(`${rule.from}.`)) fallback.push(name + leaf.slice(rule.from.length));
|
|
304
311
|
}
|
|
305
312
|
} else if (entry.data[name] !== void 0) {
|
|
306
|
-
data[name] = resolveValue(field, entry.data[name], name, { ...state,
|
|
313
|
+
data[name] = resolveValue(field, entry.data[name], name, { ...state, blocking: [], outdated: [] });
|
|
307
314
|
} else if ("value" in rule) {
|
|
308
|
-
data[name] = resolveValue(field, rule.value, name, { ...state,
|
|
315
|
+
data[name] = resolveValue(field, rule.value, name, { ...state, blocking: [], outdated: [] });
|
|
309
316
|
}
|
|
310
317
|
}
|
|
311
|
-
|
|
318
|
+
const hidden = blocking.length > 0 && brick.kind === "collection" && untranslatedMode(brick) === "hide";
|
|
319
|
+
return { data, fallback, outdated, blocking, hidden };
|
|
312
320
|
}
|
|
313
321
|
function resolveFields(fields, stored, path, state) {
|
|
314
322
|
const out = {};
|
|
@@ -362,8 +370,9 @@ function altOf(own, library, leaf, state, field) {
|
|
|
362
370
|
const fromLibrary = (l) => effectiveAlt(map, void 0, l) ? "" : effectiveAlt(void 0, library, l);
|
|
363
371
|
if (!markAllFallback && locale !== state.main && fromLibrary(locale)) return fromLibrary(locale);
|
|
364
372
|
const main = fromLibrary(state.main);
|
|
365
|
-
return localized(main ? { ...map, [state.main]: main } : map, leaf, state, field) ?? "";
|
|
373
|
+
return localized(main ? { ...map, [state.main]: main } : map, leaf, { ...state, blocking: [] }, field) ?? "";
|
|
366
374
|
}
|
|
375
|
+
const isEmpty = (value) => value === void 0 || value === null || value === "";
|
|
367
376
|
function localized(value, leaf, state, field) {
|
|
368
377
|
const map = value ?? {};
|
|
369
378
|
const mainValue = map[state.main];
|
|
@@ -374,14 +383,16 @@ function localized(value, leaf, state, field) {
|
|
|
374
383
|
}
|
|
375
384
|
if (locale === state.main) return mainValue;
|
|
376
385
|
const own = map[locale];
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
if (state.flags.canHide && !field.optional) state.flags.hidden = true;
|
|
386
|
+
if (isEmpty(own)) {
|
|
387
|
+
if (!isEmpty(mainValue)) {
|
|
388
|
+
state.fallback.push(leaf);
|
|
389
|
+
if (!field.optional && field.type !== "slug") state.blocking.push(leaf);
|
|
390
|
+
}
|
|
383
391
|
return mainValue;
|
|
384
392
|
}
|
|
393
|
+
if (field.type === "slug" || state.entry.prints?.[leaf]?.[locale] === printOf(mainValue)) return own;
|
|
394
|
+
if (isEmpty(mainValue)) return mainValue;
|
|
395
|
+
state.outdated.push(leaf);
|
|
385
396
|
return own;
|
|
386
397
|
}
|
|
387
398
|
function defaultsFor(fields) {
|
|
@@ -401,13 +412,19 @@ function visibleUntil(brick, data) {
|
|
|
401
412
|
return typeof last === "string" ? addDays(last, vis.days ?? 0) : void 0;
|
|
402
413
|
}
|
|
403
414
|
const deletesExpired = (brick) => brick.kind === "collection" && brick.visible?.mode === "until" && brick.visible.deleteExpired === true;
|
|
404
|
-
function sortEntries(brick, entries) {
|
|
415
|
+
function sortEntries(brick, entries, languages) {
|
|
405
416
|
const copy = [...entries];
|
|
406
417
|
if (brick.order === "manual") return copy.sort((a, b) => (a.order ?? 0) - (b.order ?? 0) || a.id.localeCompare(b.id));
|
|
407
418
|
const { field, dir } = brick.order;
|
|
408
419
|
const sign = dir === "desc" ? -1 : 1;
|
|
420
|
+
const def = brick.fields[field];
|
|
421
|
+
const translated = def !== void 0 && isTranslatable(def);
|
|
409
422
|
const key = (e) => {
|
|
410
|
-
|
|
423
|
+
let v = e.data[field];
|
|
424
|
+
if (translated && languages && typeof v === "object" && v !== null && !Array.isArray(v)) {
|
|
425
|
+
const map = v;
|
|
426
|
+
v = isEmpty(map[languages.locale]) ? map[languages.main] : map[languages.locale];
|
|
427
|
+
}
|
|
411
428
|
if (v === void 0 || v === null) return [];
|
|
412
429
|
if (typeof v === "number") return [v];
|
|
413
430
|
if (typeof v === "object" && !Array.isArray(v) && "amount" in v) return [Number(v.amount)];
|
|
@@ -426,7 +443,10 @@ function sortEntries(brick, entries) {
|
|
|
426
443
|
return copy.sort((a, b) => sign * compare(key(a), key(b)) || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
|
|
427
444
|
}
|
|
428
445
|
async function servedBundle(input) {
|
|
429
|
-
|
|
446
|
+
const bundle = buildBundle(input);
|
|
447
|
+
const main = bundle.locale === bundle.mainLocale ? void 0 : buildBundle({ ...input, requestedLocale: bundle.mainLocale });
|
|
448
|
+
const trees = await withMarkdownTrees(bundle, input.schema);
|
|
449
|
+
return resolveRouteLinks(resolveFileRefs(trees, input.schema, input.files), input.schema, main);
|
|
430
450
|
}
|
|
431
451
|
function buildBundle(input) {
|
|
432
452
|
const { schema } = input;
|
|
@@ -444,7 +464,7 @@ function buildBundle(input) {
|
|
|
444
464
|
continue;
|
|
445
465
|
}
|
|
446
466
|
const items = [];
|
|
447
|
-
for (const entry of sortEntries(brick, stored)) {
|
|
467
|
+
for (const entry of sortEntries(brick, stored, { locale, main })) {
|
|
448
468
|
const r = resolveEntry(schema, key, entry, ctx);
|
|
449
469
|
if (r.hidden) continue;
|
|
450
470
|
const item = { id: entry.id, data: r.data, fallback: r.fallback };
|
|
@@ -453,7 +473,7 @@ function buildBundle(input) {
|
|
|
453
473
|
if (entry.updatedAt !== void 0) item.updatedAt = entry.updatedAt;
|
|
454
474
|
const source = entry.sources?.[locale] ?? entry.sources?.[main];
|
|
455
475
|
if (source !== void 0) item.source = source;
|
|
456
|
-
if (brick.route && isVisible(brick, r.data, input.today)) item.urls = urlsFor(schema, key, brick, entry
|
|
476
|
+
if (brick.route && isVisible(brick, r.data, input.today)) item.urls = urlsFor(schema, key, brick, entry);
|
|
457
477
|
items.push(item);
|
|
458
478
|
}
|
|
459
479
|
bricks[key] = { kind: "collection", items };
|
|
@@ -472,20 +492,27 @@ function buildBundle(input) {
|
|
|
472
492
|
bricks
|
|
473
493
|
};
|
|
474
494
|
}
|
|
475
|
-
function
|
|
495
|
+
function routeUrls(schema, brick, entry) {
|
|
476
496
|
const slugField = Object.entries(brick.fields).find(([, field]) => field.type === "slug" && !field.retired)?.[0];
|
|
477
497
|
if (!slugField || !brick.route) return {};
|
|
478
498
|
const slugs = entry.data[slugField] ?? {};
|
|
479
499
|
const main = schema.locales[0];
|
|
480
500
|
const urls = {};
|
|
481
501
|
for (const locale of schema.locales) {
|
|
482
|
-
if (locale !== main && brick.untranslated === "hide" && resolveEntry(schema, key, entry, { locale, files }).hidden) continue;
|
|
483
502
|
const slug = slugs[locale] || slugs[main];
|
|
484
503
|
const route = brick.route[locale];
|
|
485
504
|
if (slug && route) urls[locale] = route.replace("[slug]", slug);
|
|
486
505
|
}
|
|
487
506
|
return urls;
|
|
488
507
|
}
|
|
508
|
+
function urlsFor(schema, key, brick, entry) {
|
|
509
|
+
const urls = routeUrls(schema, brick, entry);
|
|
510
|
+
const main = schema.locales[0];
|
|
511
|
+
for (const locale of schema.locales) {
|
|
512
|
+
if (locale !== main && urls[locale] && resolveEntry(schema, key, entry, { locale, files: () => void 0 }).blocking.length) delete urls[locale];
|
|
513
|
+
}
|
|
514
|
+
return urls;
|
|
515
|
+
}
|
|
489
516
|
function urlClashes(entries) {
|
|
490
517
|
const owner = /* @__PURE__ */ new Map();
|
|
491
518
|
const clashes = [];
|
|
@@ -541,4 +568,4 @@ function applyDefaults(bundle, schema) {
|
|
|
541
568
|
return { ...bundle, bricks };
|
|
542
569
|
}
|
|
543
570
|
|
|
544
|
-
export {
|
|
571
|
+
export { urlClashes as A, urlsFor as B, CliError as C, SITE_BUDGET as S, fitsBudget as a, diffSchema as b, largestFieldBytes as c, defaultsFor as d, entryBytes as e, fieldsChanged as f, parseReleaseState as g, pathOf as h, isObject as i, defaultDir as j, isVisible as k, largestEntryBytes as l, printOf as m, applyDefaults as n, blockedSlugs as o, parseExport as p, buildBundle as q, releaseData as r, byteSize as s, deletesExpired as t, resolveEntry as u, visibleUntil as v, routeUrls as w, servedBundle as x, sortEntries as y, untranslatedMode as z };
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { B as Bundle } from './ginko.B_pACpVU.js';
|
|
2
|
+
import { L as Locale } from './ginko.DrkUeYFP.js';
|
|
3
|
+
|
|
4
|
+
interface NavNode {
|
|
5
|
+
title: string;
|
|
6
|
+
path: string;
|
|
7
|
+
id?: string;
|
|
8
|
+
brick?: string;
|
|
9
|
+
children: NavNode[];
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Navigation tree of the collections `bricks` in `locale`, built from item URLs. An item is a child of the
|
|
13
|
+
* node at its nearest existing path prefix, else a root node. A string `parent` field (item id) overrides that.
|
|
14
|
+
* Siblings keep the item order of the bundle (bricks in the given order).
|
|
15
|
+
*/
|
|
16
|
+
declare function navTree(bundle: Bundle, bricks: string[], locale: Locale): NavNode[];
|
|
17
|
+
/** Nodes from the root down to the node at `path`; empty when the path is not in the tree. */
|
|
18
|
+
declare function breadcrumbs(tree: NavNode[], path: string): NavNode[];
|
|
19
|
+
|
|
20
|
+
interface SeoImage {
|
|
21
|
+
url: string;
|
|
22
|
+
width?: number;
|
|
23
|
+
height?: number;
|
|
24
|
+
alt?: string;
|
|
25
|
+
}
|
|
26
|
+
interface SeoInput {
|
|
27
|
+
siteName: string;
|
|
28
|
+
/** `%s` is replaced by the page title; default '%s · <siteName>'. A page without title gets siteName alone. */
|
|
29
|
+
titleTemplate?: string;
|
|
30
|
+
title?: string;
|
|
31
|
+
description?: string;
|
|
32
|
+
image?: SeoImage;
|
|
33
|
+
type?: 'website' | 'article';
|
|
34
|
+
noindex?: boolean;
|
|
35
|
+
}
|
|
36
|
+
interface SeoHead {
|
|
37
|
+
title: string;
|
|
38
|
+
meta: (({
|
|
39
|
+
name: string;
|
|
40
|
+
} | {
|
|
41
|
+
property: string;
|
|
42
|
+
}) & {
|
|
43
|
+
content: string;
|
|
44
|
+
})[];
|
|
45
|
+
}
|
|
46
|
+
declare function seoHead(input: SeoInput): SeoHead;
|
|
47
|
+
/** True when a request for `host` (with port, as in the Host header) is not for the site's own address: preview deployments, staging, localhost. Those must never be indexed. */
|
|
48
|
+
declare function isPreviewHost(host: string | undefined, siteUrl: string): boolean;
|
|
49
|
+
declare function robotsTxt(options: {
|
|
50
|
+
sitemap?: string;
|
|
51
|
+
noindex?: boolean;
|
|
52
|
+
disallow?: string[];
|
|
53
|
+
}): string;
|
|
54
|
+
declare function websiteLd(o: {
|
|
55
|
+
name: string;
|
|
56
|
+
url: string;
|
|
57
|
+
lang: string;
|
|
58
|
+
}): Record<string, unknown>;
|
|
59
|
+
/**
|
|
60
|
+
* `node` with the site's `country` (ISO 3166 code) where a builder left it out: `addressCountry` of every PostalAddress,
|
|
61
|
+
* and where remote applicants of a remote JobPosting must live. The Nuxt module applies it to a page's JSON-LD, so
|
|
62
|
+
* `siteJsonLd` and `jobJsonLd` need no `country` (an explicit one wins). An empty `country` changes nothing.
|
|
63
|
+
*/
|
|
64
|
+
declare function withCountry<T>(node: T, country: string): T;
|
|
65
|
+
/** The schema.org types `organizationLd` writes. A page whose JSON-LD has a node of one of them is the site's organisation node. */
|
|
66
|
+
declare const ORGANIZATION_TYPES: readonly ["Organization", "LocalBusiness", "NGO"];
|
|
67
|
+
interface OrganizationLdInput {
|
|
68
|
+
name: string;
|
|
69
|
+
url: string;
|
|
70
|
+
/** `Organization` (default), `LocalBusiness` for a shop or practice, `NGO` for a nonprofit. */
|
|
71
|
+
type?: (typeof ORGANIZATION_TYPES)[number];
|
|
72
|
+
legalName?: string;
|
|
73
|
+
/** Absolute URL of the logo. */
|
|
74
|
+
logo?: string;
|
|
75
|
+
email?: string;
|
|
76
|
+
telephone?: string;
|
|
77
|
+
address?: {
|
|
78
|
+
street?: string;
|
|
79
|
+
postalCode?: string;
|
|
80
|
+
city?: string;
|
|
81
|
+
country?: string;
|
|
82
|
+
};
|
|
83
|
+
vatID?: string;
|
|
84
|
+
/** Absolute URLs of the organisation's profiles elsewhere. */
|
|
85
|
+
sameAs?: string[];
|
|
86
|
+
}
|
|
87
|
+
declare function organizationLd(o: OrganizationLdInput): Record<string, unknown>;
|
|
88
|
+
/**
|
|
89
|
+
* A schema.org BreadcrumbList from the first step (usually the home page) to the page itself. Each step has a `title`
|
|
90
|
+
* and a `path` of the site, as the nodes of `useGinkoBreadcrumbs` do; `siteUrl` (`useGinkoSite().url`) makes the
|
|
91
|
+
* paths absolute.
|
|
92
|
+
*/
|
|
93
|
+
declare function breadcrumbJsonLd(steps: readonly Pick<NavNode, 'title' | 'path'>[], siteUrl: string): Record<string, unknown>;
|
|
94
|
+
interface ArticleLdInput {
|
|
95
|
+
/** `Article` (default) or `BlogPosting` for a post of a blog. */
|
|
96
|
+
type?: 'Article' | 'BlogPosting';
|
|
97
|
+
title: string;
|
|
98
|
+
url: string;
|
|
99
|
+
description?: string;
|
|
100
|
+
image?: string;
|
|
101
|
+
published?: string;
|
|
102
|
+
modified?: string;
|
|
103
|
+
/** One author by name, or a list of authors with an optional website each. */
|
|
104
|
+
author?: string | readonly {
|
|
105
|
+
name: string;
|
|
106
|
+
url?: string;
|
|
107
|
+
}[];
|
|
108
|
+
keywords?: readonly string[];
|
|
109
|
+
lang: string;
|
|
110
|
+
}
|
|
111
|
+
declare function articleLd(o: ArticleLdInput): Record<string, unknown>;
|
|
112
|
+
declare function faqLd(pairs: {
|
|
113
|
+
question: string;
|
|
114
|
+
answer: string;
|
|
115
|
+
}[]): Record<string, unknown>;
|
|
116
|
+
/**
|
|
117
|
+
* Headers every response of a Ginko site carries. Only the platform may frame `/__ginko` (the preview); nothing
|
|
118
|
+
* else is framed. `nosniff` stops a browser from reading user text (`.md`, `llms.txt`, JSON) as HTML, and the
|
|
119
|
+
* referrer policy keeps full URLs from leaving the site.
|
|
120
|
+
*/
|
|
121
|
+
declare function siteHeaders(input: {
|
|
122
|
+
preview: boolean;
|
|
123
|
+
platformOrigin: string;
|
|
124
|
+
noindex: boolean;
|
|
125
|
+
}): Record<string, string>;
|
|
126
|
+
|
|
127
|
+
export { ORGANIZATION_TYPES as a, articleLd as d, breadcrumbJsonLd as e, breadcrumbs as f, faqLd as g, siteHeaders as h, isPreviewHost as i, withCountry as j, navTree as n, organizationLd as o, robotsTxt as r, seoHead as s, websiteLd as w };
|
|
128
|
+
export type { ArticleLdInput as A, NavNode as N, OrganizationLdInput as O, SeoHead as S, SeoImage as b, SeoInput as c };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const MB = 1024 * 1024;
|
|
2
|
+
const UPLOAD_TYPES = {
|
|
3
|
+
"image/jpeg": { label: "JPEG", kind: "image", limit: 20 * MB },
|
|
4
|
+
"image/png": { label: "PNG", kind: "image", limit: 20 * MB },
|
|
5
|
+
"image/webp": { label: "WebP", kind: "image", limit: 20 * MB },
|
|
6
|
+
"application/pdf": { label: "PDF", kind: "file", limit: 10 * MB }
|
|
7
|
+
};
|
|
8
|
+
function acceptOf(kind) {
|
|
9
|
+
return typesOf(kind).join(",");
|
|
10
|
+
}
|
|
11
|
+
function limitOf(kind) {
|
|
12
|
+
return Math.max(...typesOf(kind).map((type) => UPLOAD_TYPES[type].limit));
|
|
13
|
+
}
|
|
14
|
+
function formatMb(bytes) {
|
|
15
|
+
return `${roundedMb(bytes)}`.replace(".", ",");
|
|
16
|
+
}
|
|
17
|
+
function roundedMb(bytes) {
|
|
18
|
+
return Math.round(bytes / MB * 10) / 10;
|
|
19
|
+
}
|
|
20
|
+
function typesOf(kind) {
|
|
21
|
+
return Object.keys(UPLOAD_TYPES).filter((type) => UPLOAD_TYPES[type].kind === kind);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const CALENDAR_NAMES = ["AT", "DE", "SG", "none"];
|
|
25
|
+
const STORED_HOLIDAYS = [...CALENDAR_NAMES, "custom"];
|
|
26
|
+
const COUNTRY_CALENDARS = { AT: "AT", DE: "DE", SG: "SG" };
|
|
27
|
+
function calendarOfCountry(country) {
|
|
28
|
+
return Object.hasOwn(COUNTRY_CALENDARS, country) ? COUNTRY_CALENDARS[country] : void 0;
|
|
29
|
+
}
|
|
30
|
+
function holidaysOf(site) {
|
|
31
|
+
if (site.calendar !== void 0) return typeof site.calendar === "function" ? "custom" : site.calendar;
|
|
32
|
+
if (site.country !== void 0) return calendarOfCountry(site.country) ?? "none";
|
|
33
|
+
return void 0;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export { CALENDAR_NAMES as C, STORED_HOLIDAYS as S, UPLOAD_TYPES as U, acceptOf as a, calendarOfCountry as c, formatMb as f, holidaysOf as h, limitOf as l, roundedMb as r };
|
|
@@ -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 };
|