@seamlet/astro 1.0.0 → 1.1.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 +9 -0
- package/README.md +84 -2
- package/dist/bridge.d.ts +2 -2
- package/dist/bridge.js +3 -2
- package/dist/chunk-G6Z44LGA.js +67 -0
- package/dist/{chunk-ZS32CW5F.js → chunk-I3HOJE3S.js} +365 -6
- package/dist/{chunk-SSECNMAD.js → chunk-PLNPM2D3.js} +1 -1
- package/dist/{chunk-QL4K4HR6.js → chunk-QXPXIMIM.js} +41 -0
- package/dist/cli.js +4 -4
- package/dist/index.js +8 -7
- package/dist/{messages-CvZhLRSE.d.ts → messages-Bqgs_mic.d.ts} +1 -1
- package/dist/{parse-content-Eg8yRq_t.d.ts → parse-content-IOJxgKLB.d.ts} +25 -4
- package/dist/preview-requests.d.ts +15 -1
- package/dist/preview-requests.js +7 -1
- package/dist/protocol.d.ts +3 -3
- package/dist/protocol.js +2 -2
- package/dist/routes/media.js +3 -1
- package/dist/schema.d.ts +24 -22
- package/dist/schema.js +33 -1
- package/dist/site-description-BuEpga8p.d.ts +245 -0
- package/dist/site.d.ts +49 -4
- package/dist/site.js +53 -9
- package/package.json +1 -1
- package/routes/preview.astro +23 -9
- package/dist/site-description-Cg5KNhhG.d.ts +0 -112
|
@@ -35,6 +35,9 @@ function url(options) {
|
|
|
35
35
|
function time(options) {
|
|
36
36
|
return { kind: "time", ...options };
|
|
37
37
|
}
|
|
38
|
+
function slug(options) {
|
|
39
|
+
return { kind: "slug", ...options };
|
|
40
|
+
}
|
|
38
41
|
function reference(options) {
|
|
39
42
|
return { kind: "reference", ...options };
|
|
40
43
|
}
|
|
@@ -52,6 +55,7 @@ function collection(options) {
|
|
|
52
55
|
function defaultValue(field) {
|
|
53
56
|
switch (field.kind) {
|
|
54
57
|
case "text":
|
|
58
|
+
case "slug":
|
|
55
59
|
return "";
|
|
56
60
|
case "toggle":
|
|
57
61
|
return false;
|
|
@@ -118,6 +122,7 @@ function resolveInsideField(field, segments, path) {
|
|
|
118
122
|
case "choices":
|
|
119
123
|
case "url":
|
|
120
124
|
case "time":
|
|
125
|
+
case "slug":
|
|
121
126
|
case "reference":
|
|
122
127
|
case "references":
|
|
123
128
|
return void 0;
|
|
@@ -353,6 +358,7 @@ function collectionIn(fields, content, names) {
|
|
|
353
358
|
case "choices":
|
|
354
359
|
case "url":
|
|
355
360
|
case "time":
|
|
361
|
+
case "slug":
|
|
356
362
|
case "reference":
|
|
357
363
|
case "references":
|
|
358
364
|
return void 0;
|
|
@@ -379,6 +385,7 @@ function collectionFieldAt(schema, path) {
|
|
|
379
385
|
case "choices":
|
|
380
386
|
case "url":
|
|
381
387
|
case "time":
|
|
388
|
+
case "slug":
|
|
382
389
|
case "reference":
|
|
383
390
|
case "references":
|
|
384
391
|
return void 0;
|
|
@@ -412,6 +419,7 @@ function withIdsIn(field, value, newId) {
|
|
|
412
419
|
case "choices":
|
|
413
420
|
case "url":
|
|
414
421
|
case "time":
|
|
422
|
+
case "slug":
|
|
415
423
|
case "reference":
|
|
416
424
|
case "references":
|
|
417
425
|
return value;
|
|
@@ -467,6 +475,7 @@ function issuesInField(field, value, path, idsOf) {
|
|
|
467
475
|
case "choices":
|
|
468
476
|
case "url":
|
|
469
477
|
case "time":
|
|
478
|
+
case "slug":
|
|
470
479
|
return [];
|
|
471
480
|
}
|
|
472
481
|
}
|
|
@@ -488,6 +497,146 @@ function stringsOf(value) {
|
|
|
488
497
|
return list.filter((id) => typeof id === "string");
|
|
489
498
|
}
|
|
490
499
|
|
|
500
|
+
// src/schema/slug-checks.ts
|
|
501
|
+
function slugFieldOf(item) {
|
|
502
|
+
const named = Object.entries(item).find(([, field]) => field.kind === "slug");
|
|
503
|
+
return named?.[0];
|
|
504
|
+
}
|
|
505
|
+
function takenSlugIssues(schema, content) {
|
|
506
|
+
return issuesInFields2(schema, content, []);
|
|
507
|
+
}
|
|
508
|
+
function issuesInFields2(fields, content, path) {
|
|
509
|
+
return Object.entries(fields).flatMap(
|
|
510
|
+
([name, field]) => issuesInField2(field, content[name], [...path, name])
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
function issuesInField2(field, value, path) {
|
|
514
|
+
switch (field.kind) {
|
|
515
|
+
case "group":
|
|
516
|
+
return isContentObject(value) ? issuesInFields2(field.fields, value, path) : [];
|
|
517
|
+
case "collection":
|
|
518
|
+
return issuesInCollection(field, recordsOf2(value), path);
|
|
519
|
+
case "text":
|
|
520
|
+
case "richText":
|
|
521
|
+
case "number":
|
|
522
|
+
case "date":
|
|
523
|
+
case "image":
|
|
524
|
+
case "link":
|
|
525
|
+
case "toggle":
|
|
526
|
+
case "choice":
|
|
527
|
+
case "choices":
|
|
528
|
+
case "url":
|
|
529
|
+
case "time":
|
|
530
|
+
case "slug":
|
|
531
|
+
case "reference":
|
|
532
|
+
case "references":
|
|
533
|
+
return [];
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
function issuesInCollection(collection2, records, path) {
|
|
537
|
+
const ownIssues = takenSlugsOf(collection2, records, path);
|
|
538
|
+
const innerIssues = records.flatMap(
|
|
539
|
+
(record, index) => issuesInFields2(collection2.item, record, [...path, index])
|
|
540
|
+
);
|
|
541
|
+
return [...ownIssues, ...innerIssues];
|
|
542
|
+
}
|
|
543
|
+
function takenSlugsOf(collection2, records, path) {
|
|
544
|
+
const slugName = slugFieldOf(collection2.item);
|
|
545
|
+
if (slugName === void 0) return [];
|
|
546
|
+
const ids = records.map((record) => record[RECORD_ID_KEY]);
|
|
547
|
+
const slugs = records.map((record) => record[slugName]);
|
|
548
|
+
return slugs.flatMap((slug2, index) => {
|
|
549
|
+
if (typeof slug2 !== "string" || slug2 === "") return [];
|
|
550
|
+
const isAnEarlierSlug = slugs.indexOf(slug2) < index;
|
|
551
|
+
const isAnotherId = ids.some(
|
|
552
|
+
(id, idIndex) => id === slug2 && idIndex !== index
|
|
553
|
+
);
|
|
554
|
+
const isTaken = isAnEarlierSlug || isAnotherId;
|
|
555
|
+
return isTaken ? [{ path: [...path, index, slugName], code: "taken-slug" }] : [];
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
function recordsOf2(value) {
|
|
559
|
+
return Array.isArray(value) ? value.filter(isContentObject) : [];
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
// src/schema/slugs.ts
|
|
563
|
+
var PAGE_SEGMENT = "[a-z0-9][a-z0-9-]*";
|
|
564
|
+
var MAX_SLUG_LENGTH = 80;
|
|
565
|
+
var SLUG_PATTERN = new RegExp(`^${PAGE_SEGMENT}$`, "u");
|
|
566
|
+
var NOT_SLUG_CHARACTERS = /[^a-z0-9]+/gu;
|
|
567
|
+
var EDGE_HYPHENS = /^-+|-+$/gu;
|
|
568
|
+
var COMBINING_MARKS = new RegExp("\\p{M}", "gu");
|
|
569
|
+
var HYPHEN = "-";
|
|
570
|
+
var TRANSLITERATION = new Map(
|
|
571
|
+
Object.entries({
|
|
572
|
+
\u0430: "a",
|
|
573
|
+
\u0431: "b",
|
|
574
|
+
\u0432: "v",
|
|
575
|
+
\u0433: "g",
|
|
576
|
+
\u0434: "d",
|
|
577
|
+
\u0435: "e",
|
|
578
|
+
\u0451: "e",
|
|
579
|
+
\u0436: "zh",
|
|
580
|
+
\u0437: "z",
|
|
581
|
+
\u0438: "i",
|
|
582
|
+
\u0439: "y",
|
|
583
|
+
\u043A: "k",
|
|
584
|
+
\u043B: "l",
|
|
585
|
+
\u043C: "m",
|
|
586
|
+
\u043D: "n",
|
|
587
|
+
\u043E: "o",
|
|
588
|
+
\u043F: "p",
|
|
589
|
+
\u0440: "r",
|
|
590
|
+
\u0441: "s",
|
|
591
|
+
\u0442: "t",
|
|
592
|
+
\u0443: "u",
|
|
593
|
+
\u0444: "f",
|
|
594
|
+
\u0445: "h",
|
|
595
|
+
\u0446: "ts",
|
|
596
|
+
\u0447: "ch",
|
|
597
|
+
\u0448: "sh",
|
|
598
|
+
\u0449: "sch",
|
|
599
|
+
\u044A: "",
|
|
600
|
+
\u044B: "y",
|
|
601
|
+
\u044C: "",
|
|
602
|
+
\u044D: "e",
|
|
603
|
+
\u044E: "yu",
|
|
604
|
+
\u044F: "ya",
|
|
605
|
+
// Letters of Ukrainian and Belarusian words a Russian text may quote.
|
|
606
|
+
\u0456: "i",
|
|
607
|
+
\u0457: "yi",
|
|
608
|
+
\u0454: "ye",
|
|
609
|
+
\u0491: "g",
|
|
610
|
+
\u045E: "u",
|
|
611
|
+
// «Баня 6×4» is `banya-6x4`, the way its size is said.
|
|
612
|
+
"\xD7": "x"
|
|
613
|
+
})
|
|
614
|
+
);
|
|
615
|
+
var TRANSLITERATED = new RegExp(
|
|
616
|
+
`[${[...TRANSLITERATION.keys()].join("")}]`,
|
|
617
|
+
"gu"
|
|
618
|
+
);
|
|
619
|
+
function isSlug(value) {
|
|
620
|
+
return SLUG_PATTERN.test(value);
|
|
621
|
+
}
|
|
622
|
+
function slugOf(words) {
|
|
623
|
+
const latin = words.normalize("NFC").toLowerCase().replace(
|
|
624
|
+
TRANSLITERATED,
|
|
625
|
+
(character) => TRANSLITERATION.get(character) ?? character
|
|
626
|
+
).normalize("NFKD").replace(COMBINING_MARKS, "");
|
|
627
|
+
const hyphenated = latin.toLowerCase().replace(NOT_SLUG_CHARACTERS, HYPHEN).replace(EDGE_HYPHENS, "");
|
|
628
|
+
return shortened(hyphenated);
|
|
629
|
+
}
|
|
630
|
+
function shortened(slug2) {
|
|
631
|
+
if (slug2.length <= MAX_SLUG_LENGTH) return slug2;
|
|
632
|
+
const cut = slug2.slice(0, MAX_SLUG_LENGTH);
|
|
633
|
+
const isWordWhole = slug2[MAX_SLUG_LENGTH] === HYPHEN;
|
|
634
|
+
if (isWordWhole) return cut;
|
|
635
|
+
const lastHyphen = cut.lastIndexOf(HYPHEN);
|
|
636
|
+
const whole = lastHyphen > 0 ? cut.slice(0, lastHyphen) : cut;
|
|
637
|
+
return whole.replace(EDGE_HYPHENS, "");
|
|
638
|
+
}
|
|
639
|
+
|
|
491
640
|
// src/schema/parse-content.ts
|
|
492
641
|
var DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/u;
|
|
493
642
|
var TIME_PATTERN = /^(?:[01]\d|2[0-3]):[0-5]\d$/u;
|
|
@@ -501,9 +650,10 @@ function countCharacters(text2) {
|
|
|
501
650
|
function parseContent(schema, input) {
|
|
502
651
|
const parsed = parseFields(schema, input, []);
|
|
503
652
|
const missingRecords = missingRecordIssues(schema, parsed.value);
|
|
653
|
+
const takenSlugs = takenSlugIssues(schema, parsed.value);
|
|
504
654
|
return {
|
|
505
655
|
content: parsed.value,
|
|
506
|
-
issues: [...parsed.issues, ...missingRecords]
|
|
656
|
+
issues: [...parsed.issues, ...missingRecords, ...takenSlugs]
|
|
507
657
|
};
|
|
508
658
|
}
|
|
509
659
|
function parseFields(fields, input, path) {
|
|
@@ -547,6 +697,8 @@ function parseField(field, input, path) {
|
|
|
547
697
|
return parseUrl(field, input, path);
|
|
548
698
|
case "time":
|
|
549
699
|
return parseTime(field, input, path);
|
|
700
|
+
case "slug":
|
|
701
|
+
return parseSlug(field, input, path);
|
|
550
702
|
case "reference":
|
|
551
703
|
return parseReference(field, input, path);
|
|
552
704
|
case "references":
|
|
@@ -724,6 +876,17 @@ function parseTime(field, input, path) {
|
|
|
724
876
|
}
|
|
725
877
|
return valid(input);
|
|
726
878
|
}
|
|
879
|
+
function parseSlug(field, input, path) {
|
|
880
|
+
if (typeof input !== "string") {
|
|
881
|
+
return invalidType(defaultValue(field), path);
|
|
882
|
+
}
|
|
883
|
+
if (input === "") {
|
|
884
|
+
return valid(input);
|
|
885
|
+
}
|
|
886
|
+
const formatIssues = isSlug(input) ? [] : [{ path, code: "invalid-slug" }];
|
|
887
|
+
const lengthIssues = tooLongIssues(MAX_SLUG_LENGTH, input, path);
|
|
888
|
+
return { value: input, issues: [...formatIssues, ...lengthIssues] };
|
|
889
|
+
}
|
|
727
890
|
function parseCollection(field, input, path) {
|
|
728
891
|
if (!Array.isArray(input)) {
|
|
729
892
|
return invalidType(defaultValue(field), path);
|
|
@@ -876,7 +1039,7 @@ function readFields(input) {
|
|
|
876
1039
|
function checkFields(input) {
|
|
877
1040
|
const reading = readFieldsAt(input, []);
|
|
878
1041
|
if (!reading.isRead) return { status: "invalid", problem: reading.problem };
|
|
879
|
-
const problem = firstUnknownCollection(reading.value, reading.value, []);
|
|
1042
|
+
const problem = firstUnknownCollection(reading.value, reading.value, []) ?? firstMisplacedSlug(reading.value, []);
|
|
880
1043
|
return problem === void 0 ? { status: "read", fields: reading.value } : { status: "invalid", problem };
|
|
881
1044
|
}
|
|
882
1045
|
function readFieldsAt(input, path) {
|
|
@@ -922,6 +1085,7 @@ function readFieldOfKind(field) {
|
|
|
922
1085
|
case "toggle":
|
|
923
1086
|
case "url":
|
|
924
1087
|
case "time":
|
|
1088
|
+
case "slug":
|
|
925
1089
|
return read({ kind, label });
|
|
926
1090
|
case "choice":
|
|
927
1091
|
case "choices":
|
|
@@ -1002,9 +1166,51 @@ function unknownCollectionIn(schema, field, path) {
|
|
|
1002
1166
|
case "choices":
|
|
1003
1167
|
case "url":
|
|
1004
1168
|
case "time":
|
|
1169
|
+
case "slug":
|
|
1005
1170
|
return void 0;
|
|
1006
1171
|
}
|
|
1007
1172
|
}
|
|
1173
|
+
function firstMisplacedSlug(fields, path) {
|
|
1174
|
+
for (const [name, field] of Object.entries(fields)) {
|
|
1175
|
+
const problem = misplacedSlugIn(field, [...path, name]);
|
|
1176
|
+
if (problem !== void 0) return problem;
|
|
1177
|
+
}
|
|
1178
|
+
return void 0;
|
|
1179
|
+
}
|
|
1180
|
+
function misplacedSlugIn(field, path) {
|
|
1181
|
+
switch (field.kind) {
|
|
1182
|
+
case "slug":
|
|
1183
|
+
return { path, reason: "misplaced-slug" };
|
|
1184
|
+
case "group":
|
|
1185
|
+
return firstMisplacedSlug(field.fields, path);
|
|
1186
|
+
case "collection":
|
|
1187
|
+
return misplacedSlugInRecord(field.item, path);
|
|
1188
|
+
case "text":
|
|
1189
|
+
case "richText":
|
|
1190
|
+
case "number":
|
|
1191
|
+
case "date":
|
|
1192
|
+
case "image":
|
|
1193
|
+
case "link":
|
|
1194
|
+
case "toggle":
|
|
1195
|
+
case "choice":
|
|
1196
|
+
case "choices":
|
|
1197
|
+
case "url":
|
|
1198
|
+
case "time":
|
|
1199
|
+
case "reference":
|
|
1200
|
+
case "references":
|
|
1201
|
+
return void 0;
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
function misplacedSlugInRecord(item, path) {
|
|
1205
|
+
const fields = Object.entries(item);
|
|
1206
|
+
const slugNames = fields.filter(([, field]) => field.kind === "slug").map(([name]) => name);
|
|
1207
|
+
const [, secondSlug] = slugNames;
|
|
1208
|
+
if (secondSlug !== void 0) {
|
|
1209
|
+
return { path: [...path, secondSlug], reason: "misplaced-slug" };
|
|
1210
|
+
}
|
|
1211
|
+
const otherFields = fields.filter(([, field]) => field.kind !== "slug");
|
|
1212
|
+
return firstMisplacedSlug(Object.fromEntries(otherFields), path);
|
|
1213
|
+
}
|
|
1008
1214
|
function referencesCollection(schema, field) {
|
|
1009
1215
|
return collectionFieldAt(schema, field.collection) !== void 0;
|
|
1010
1216
|
}
|
|
@@ -1031,6 +1237,7 @@ var FIELD_KINDS = {
|
|
|
1031
1237
|
choices: true,
|
|
1032
1238
|
url: true,
|
|
1033
1239
|
time: true,
|
|
1240
|
+
slug: true,
|
|
1034
1241
|
reference: true,
|
|
1035
1242
|
references: true,
|
|
1036
1243
|
group: true,
|
|
@@ -1094,9 +1301,69 @@ function originOf(value) {
|
|
|
1094
1301
|
return WEB_PROTOCOLS.has(url2.protocol) ? url2.origin : void 0;
|
|
1095
1302
|
}
|
|
1096
1303
|
|
|
1304
|
+
// src/schema/record-pages.ts
|
|
1305
|
+
var RECORD_PLACEHOLDER = "[record]";
|
|
1306
|
+
var RECORD_PAGE_PATH = new RegExp(
|
|
1307
|
+
`^(/${PAGE_SEGMENT})+/${escaped(RECORD_PLACEHOLDER)}$`,
|
|
1308
|
+
"u"
|
|
1309
|
+
);
|
|
1310
|
+
var PAGE_PATH_SEPARATOR = "/";
|
|
1311
|
+
var NOT_FOUND = -1;
|
|
1312
|
+
function recordPages(schema, options) {
|
|
1313
|
+
const collection2 = collectionFieldAt(schema, options.collection);
|
|
1314
|
+
if (collection2 === void 0) {
|
|
1315
|
+
throw new Error(
|
|
1316
|
+
`@seamlet/astro: the schema has no collection "${options.collection}" for the pages at "${options.path}"`
|
|
1317
|
+
);
|
|
1318
|
+
}
|
|
1319
|
+
const pathOf = (record) => {
|
|
1320
|
+
const address = recordAddressOf(collection2, record);
|
|
1321
|
+
return address === void 0 ? void 0 : recordPagePath(options.path, address);
|
|
1322
|
+
};
|
|
1323
|
+
return { ...options, pathOf };
|
|
1324
|
+
}
|
|
1325
|
+
function isRecordPagePath(path) {
|
|
1326
|
+
return typeof path === "string" && RECORD_PAGE_PATH.test(path);
|
|
1327
|
+
}
|
|
1328
|
+
function recordAddressOf(collection2, record) {
|
|
1329
|
+
const slugName = slugFieldOf(collection2.item);
|
|
1330
|
+
const slug2 = slugName === void 0 ? void 0 : record[slugName];
|
|
1331
|
+
if (typeof slug2 === "string" && isSlug(slug2)) return slug2;
|
|
1332
|
+
const id = record[RECORD_ID_KEY];
|
|
1333
|
+
return isRecordId(id) ? id : void 0;
|
|
1334
|
+
}
|
|
1335
|
+
function recordPagePath(pagePath, address) {
|
|
1336
|
+
return pagePath.replace(RECORD_PLACEHOLDER, address);
|
|
1337
|
+
}
|
|
1338
|
+
function recordPageAt(pages, path) {
|
|
1339
|
+
for (const page of pages) {
|
|
1340
|
+
const address = addressUnder(page.path, path);
|
|
1341
|
+
if (address !== void 0) return { page, address };
|
|
1342
|
+
}
|
|
1343
|
+
return void 0;
|
|
1344
|
+
}
|
|
1345
|
+
function recordAt(collection2, records, address) {
|
|
1346
|
+
const pageIndex = records.findIndex(
|
|
1347
|
+
(record2) => recordAddressOf(collection2, record2) === address
|
|
1348
|
+
);
|
|
1349
|
+
const index = pageIndex === NOT_FOUND ? records.findIndex((record2) => record2[RECORD_ID_KEY] === address) : pageIndex;
|
|
1350
|
+
const record = records[index];
|
|
1351
|
+
return record === void 0 ? void 0 : { record, index };
|
|
1352
|
+
}
|
|
1353
|
+
function addressUnder(recordPagePath2, path) {
|
|
1354
|
+
const prefix = recordPagePath2.slice(0, -RECORD_PLACEHOLDER.length);
|
|
1355
|
+
if (!path.startsWith(prefix)) return void 0;
|
|
1356
|
+
const address = path.slice(prefix.length);
|
|
1357
|
+
const isOneSegment = address !== "" && !address.includes(PAGE_PATH_SEPARATOR);
|
|
1358
|
+
return isOneSegment ? address : void 0;
|
|
1359
|
+
}
|
|
1360
|
+
function escaped(text2) {
|
|
1361
|
+
return text2.replace(/[[\]]/gu, "\\$&");
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1097
1364
|
// src/schema/site-description.ts
|
|
1098
1365
|
var SITE_DESCRIPTION_PATH = "/seamlet/site.json";
|
|
1099
|
-
var PAGE_PATH =
|
|
1366
|
+
var PAGE_PATH = new RegExp(`^(/${PAGE_SEGMENT})+$`, "u");
|
|
1100
1367
|
var HOME_PAGE_PATH = "/";
|
|
1101
1368
|
function readSiteDescription(input) {
|
|
1102
1369
|
const reading = checkSiteDescription(input);
|
|
@@ -1113,13 +1380,18 @@ function checkSiteDescription(input) {
|
|
|
1113
1380
|
}
|
|
1114
1381
|
const initial = parseContent(schema.fields, input["initialContent"]);
|
|
1115
1382
|
const pages = checkPages(schema.fields, input["pages"]);
|
|
1383
|
+
const recordPages2 = checkRecordPages(schema.fields, input["recordPages"]);
|
|
1116
1384
|
const problems = [
|
|
1117
1385
|
...nameProblems,
|
|
1118
1386
|
...initialContentProblems(initial.issues),
|
|
1119
|
-
...pages.problems
|
|
1387
|
+
...pages.problems,
|
|
1388
|
+
...recordPages2.problems,
|
|
1389
|
+
...placementProblems(pages.pages ?? [], recordPages2.pages ?? []),
|
|
1390
|
+
...addressProblems(schema.fields, initial.content, recordPages2.pages ?? [])
|
|
1120
1391
|
];
|
|
1121
1392
|
const isNamed = isSiteId(id) && isText(title);
|
|
1122
|
-
|
|
1393
|
+
const isWhole = pages.pages !== void 0 && recordPages2.pages !== void 0;
|
|
1394
|
+
if (!isNamed || !isWhole || problems.length > 0) {
|
|
1123
1395
|
return invalid(problems);
|
|
1124
1396
|
}
|
|
1125
1397
|
const description = {
|
|
@@ -1127,7 +1399,8 @@ function checkSiteDescription(input) {
|
|
|
1127
1399
|
title,
|
|
1128
1400
|
schema: schema.fields,
|
|
1129
1401
|
initialContent: initial.content,
|
|
1130
|
-
pages: pages.pages
|
|
1402
|
+
pages: pages.pages,
|
|
1403
|
+
...recordPages2.pages.length === 0 ? {} : { recordPages: recordPages2.pages }
|
|
1131
1404
|
};
|
|
1132
1405
|
return { status: "read", description };
|
|
1133
1406
|
}
|
|
@@ -1179,6 +1452,76 @@ function readPage(schema, input) {
|
|
|
1179
1452
|
const isSearchTexts = typeof searchTexts === "string" && isSearchTextsGroup(schema, searchTexts);
|
|
1180
1453
|
return isSearchTexts ? { path, title, searchTexts } : "invalid-search-texts";
|
|
1181
1454
|
}
|
|
1455
|
+
function checkRecordPages(schema, input) {
|
|
1456
|
+
if (input === void 0) return { pages: [], problems: [] };
|
|
1457
|
+
if (!Array.isArray(input)) {
|
|
1458
|
+
return { pages: void 0, problems: [{ code: "invalid-record-pages" }] };
|
|
1459
|
+
}
|
|
1460
|
+
const entries = input;
|
|
1461
|
+
const readings = entries.map((entry) => readRecordPage(schema, entry));
|
|
1462
|
+
const pageProblems = readings.flatMap(
|
|
1463
|
+
(reading, index) => typeof reading === "string" ? [{ code: "invalid-record-page", index, reason: reading }] : []
|
|
1464
|
+
);
|
|
1465
|
+
const pages = readings.filter((reading) => typeof reading !== "string");
|
|
1466
|
+
const problems = [...pageProblems, ...recordPageRepeats(pages)];
|
|
1467
|
+
return { pages: problems.length === 0 ? pages : void 0, problems };
|
|
1468
|
+
}
|
|
1469
|
+
function recordPageRepeats(pages) {
|
|
1470
|
+
const paths = pages.map((page) => page.path);
|
|
1471
|
+
const collections = pages.map((page) => page.collection);
|
|
1472
|
+
const repeatedPaths = paths.filter(
|
|
1473
|
+
(path, index) => paths.indexOf(path) !== index
|
|
1474
|
+
);
|
|
1475
|
+
const repeatedCollections = collections.filter(
|
|
1476
|
+
(collection2, index) => collections.indexOf(collection2) !== index
|
|
1477
|
+
);
|
|
1478
|
+
const pathProblems = [...new Set(repeatedPaths)].map(
|
|
1479
|
+
(path) => ({ code: "repeated-page-path", path })
|
|
1480
|
+
);
|
|
1481
|
+
const collectionProblems = [...new Set(repeatedCollections)].map(
|
|
1482
|
+
(collection2) => ({ code: "repeated-record-pages", collection: collection2 })
|
|
1483
|
+
);
|
|
1484
|
+
return [...pathProblems, ...collectionProblems];
|
|
1485
|
+
}
|
|
1486
|
+
function readRecordPage(schema, input) {
|
|
1487
|
+
if (!isJsonObject(input)) return "not-a-page";
|
|
1488
|
+
const { path, collection: collection2, searchTexts } = input;
|
|
1489
|
+
if (!isRecordPagePath(path)) return "invalid-path";
|
|
1490
|
+
if (typeof collection2 !== "string") return "unknown-collection";
|
|
1491
|
+
const field = collectionFieldAt(schema, collection2);
|
|
1492
|
+
if (field === void 0) return "unknown-collection";
|
|
1493
|
+
if (searchTexts === void 0) return { path, collection: collection2 };
|
|
1494
|
+
const isSearchTexts = typeof searchTexts === "string" && isSearchTextsGroup(field.item, searchTexts);
|
|
1495
|
+
return isSearchTexts ? { path, collection: collection2, searchTexts } : "invalid-search-texts";
|
|
1496
|
+
}
|
|
1497
|
+
function placementProblems(pages, recordPages2) {
|
|
1498
|
+
const misplaced = pages.filter(
|
|
1499
|
+
(page) => recordPageAt(recordPages2, page.path) !== void 0
|
|
1500
|
+
);
|
|
1501
|
+
return misplaced.map(
|
|
1502
|
+
(page) => ({ code: "page-among-record-pages", path: page.path })
|
|
1503
|
+
);
|
|
1504
|
+
}
|
|
1505
|
+
function addressProblems(schema, initialContent, recordPages2) {
|
|
1506
|
+
return recordPages2.flatMap((page) => {
|
|
1507
|
+
const found = collectionAt(schema, initialContent, page.collection);
|
|
1508
|
+
if (found === void 0) return [];
|
|
1509
|
+
const addresses = found.records.map(
|
|
1510
|
+
(record) => recordAddressOf(found.collection, record)
|
|
1511
|
+
);
|
|
1512
|
+
return addresses.flatMap(
|
|
1513
|
+
(_, index) => recordAddressProblems(page.collection, addresses, index)
|
|
1514
|
+
);
|
|
1515
|
+
});
|
|
1516
|
+
}
|
|
1517
|
+
function recordAddressProblems(collection2, addresses, index) {
|
|
1518
|
+
const address = addresses[index];
|
|
1519
|
+
if (address === void 0) {
|
|
1520
|
+
return [{ code: "record-without-address", collection: collection2, index }];
|
|
1521
|
+
}
|
|
1522
|
+
const isRepeated = addresses.indexOf(address) < index;
|
|
1523
|
+
return isRepeated ? [{ code: "repeated-record-address", collection: collection2, address }] : [];
|
|
1524
|
+
}
|
|
1182
1525
|
function isPagePath(path) {
|
|
1183
1526
|
return typeof path === "string" && (path === HOME_PAGE_PATH || PAGE_PATH.test(path));
|
|
1184
1527
|
}
|
|
@@ -1199,14 +1542,17 @@ export {
|
|
|
1199
1542
|
choices,
|
|
1200
1543
|
url,
|
|
1201
1544
|
time,
|
|
1545
|
+
slug,
|
|
1202
1546
|
reference,
|
|
1203
1547
|
references,
|
|
1204
1548
|
group,
|
|
1205
1549
|
collection,
|
|
1206
1550
|
defaultValue,
|
|
1207
1551
|
defaultContent,
|
|
1552
|
+
FIELD_PATH_SEPARATOR,
|
|
1208
1553
|
resolveFieldPath,
|
|
1209
1554
|
isJsonObject,
|
|
1555
|
+
isContentObject,
|
|
1210
1556
|
linkTargetOf,
|
|
1211
1557
|
safeHref,
|
|
1212
1558
|
hrefOf,
|
|
@@ -1221,7 +1567,12 @@ export {
|
|
|
1221
1567
|
newRecord,
|
|
1222
1568
|
withRecordIds,
|
|
1223
1569
|
collectionAt,
|
|
1570
|
+
collectionFieldAt,
|
|
1224
1571
|
findRecord,
|
|
1572
|
+
slugFieldOf,
|
|
1573
|
+
MAX_SLUG_LENGTH,
|
|
1574
|
+
isSlug,
|
|
1575
|
+
slugOf,
|
|
1225
1576
|
formatFieldPath,
|
|
1226
1577
|
countCharacters,
|
|
1227
1578
|
parseContent,
|
|
@@ -1240,6 +1591,14 @@ export {
|
|
|
1240
1591
|
SITE_CONFIG_FILE,
|
|
1241
1592
|
isSiteId,
|
|
1242
1593
|
readSiteConfig,
|
|
1594
|
+
RECORD_PLACEHOLDER,
|
|
1595
|
+
PAGE_PATH_SEPARATOR,
|
|
1596
|
+
recordPages,
|
|
1597
|
+
isRecordPagePath,
|
|
1598
|
+
recordAddressOf,
|
|
1599
|
+
recordPagePath,
|
|
1600
|
+
recordPageAt,
|
|
1601
|
+
recordAt,
|
|
1243
1602
|
SITE_DESCRIPTION_PATH,
|
|
1244
1603
|
HOME_PAGE_PATH,
|
|
1245
1604
|
readSiteDescription,
|
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
import {
|
|
2
|
+
pagePathOf
|
|
3
|
+
} from "./chunk-G6Z44LGA.js";
|
|
4
|
+
import {
|
|
5
|
+
FIELD_PATH_SEPARATOR,
|
|
6
|
+
collectionAt,
|
|
7
|
+
isContentObject,
|
|
8
|
+
isRecordId,
|
|
9
|
+
newRecord,
|
|
10
|
+
recordAt,
|
|
11
|
+
recordPageAt
|
|
12
|
+
} from "./chunk-I3HOJE3S.js";
|
|
13
|
+
|
|
1
14
|
// src/preview-requests.ts
|
|
2
15
|
var MAX_DRAFT_BYTES = 1024 * 1024;
|
|
3
16
|
var BAD_REQUEST = 400;
|
|
@@ -22,6 +35,25 @@ async function readDraft(request) {
|
|
|
22
35
|
return refused(BAD_REQUEST);
|
|
23
36
|
}
|
|
24
37
|
}
|
|
38
|
+
function isPageAddress(description, rest) {
|
|
39
|
+
const path = pagePathOf(rest);
|
|
40
|
+
const isOwnPage = description.pages.some((page) => page.path === path);
|
|
41
|
+
const recordPage = recordPageAt(description.recordPages ?? [], path);
|
|
42
|
+
return isOwnPage || recordPage !== void 0;
|
|
43
|
+
}
|
|
44
|
+
function previewContentAt(description, content, rest) {
|
|
45
|
+
const recordPages = description.recordPages ?? [];
|
|
46
|
+
const found = recordPageAt(recordPages, pagePathOf(rest));
|
|
47
|
+
if (found === void 0 || !isRecordId(found.address)) return content;
|
|
48
|
+
const { collection: path } = found.page;
|
|
49
|
+
const records = collectionAt(description.schema, content, path);
|
|
50
|
+
if (records === void 0) return content;
|
|
51
|
+
const isPublished = recordAt(records.collection, records.records, found.address) !== void 0;
|
|
52
|
+
if (isPublished) return content;
|
|
53
|
+
const blank = newRecord(records.collection, found.address);
|
|
54
|
+
const names = path.split(FIELD_PATH_SEPARATOR);
|
|
55
|
+
return withRecordsAt(content, names, [...records.records, blank]);
|
|
56
|
+
}
|
|
25
57
|
function isUploadedPhoto(fileName) {
|
|
26
58
|
return fileName !== void 0 && UPLOADED_PHOTO.test(fileName);
|
|
27
59
|
}
|
|
@@ -51,6 +83,13 @@ function concatenated(chunks, size) {
|
|
|
51
83
|
}
|
|
52
84
|
return bytes;
|
|
53
85
|
}
|
|
86
|
+
function withRecordsAt(content, names, records) {
|
|
87
|
+
const [name, ...rest] = names;
|
|
88
|
+
if (name === void 0) return content;
|
|
89
|
+
if (rest.length === 0) return { ...content, [name]: records };
|
|
90
|
+
const group = content[name];
|
|
91
|
+
return isContentObject(group) ? { ...content, [name]: withRecordsAt(group, rest, records) } : content;
|
|
92
|
+
}
|
|
54
93
|
function refused(status) {
|
|
55
94
|
return { isRead: false, response: new Response(null, { status }) };
|
|
56
95
|
}
|
|
@@ -59,5 +98,7 @@ export {
|
|
|
59
98
|
MAX_DRAFT_BYTES,
|
|
60
99
|
previewHeadersOf,
|
|
61
100
|
readDraft,
|
|
101
|
+
isPageAddress,
|
|
102
|
+
previewContentAt,
|
|
62
103
|
isUploadedPhoto
|
|
63
104
|
};
|
package/dist/cli.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
SITE_MODULE_FILE
|
|
4
|
+
} from "./chunk-QCVWHHJC.js";
|
|
2
5
|
import {
|
|
3
6
|
SITE_CONFIG_FILE,
|
|
4
7
|
isSiteId,
|
|
5
8
|
readSiteConfig
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import {
|
|
8
|
-
SITE_MODULE_FILE
|
|
9
|
-
} from "./chunk-QCVWHHJC.js";
|
|
9
|
+
} from "./chunk-I3HOJE3S.js";
|
|
10
10
|
|
|
11
11
|
// src/cli.ts
|
|
12
12
|
import process from "process";
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
OWN_PHOTOS_PATH
|
|
3
3
|
} from "./chunk-W2LIOR2C.js";
|
|
4
|
-
import {
|
|
5
|
-
SITE_CONFIG_FILE,
|
|
6
|
-
SITE_DESCRIPTION_PATH,
|
|
7
|
-
isJsonObject,
|
|
8
|
-
readSiteConfig
|
|
9
|
-
} from "./chunk-ZS32CW5F.js";
|
|
10
4
|
import {
|
|
11
5
|
PREVIEW_PATH,
|
|
12
6
|
SITE_MODULE_FILE,
|
|
@@ -14,7 +8,14 @@ import {
|
|
|
14
8
|
} from "./chunk-QCVWHHJC.js";
|
|
15
9
|
import {
|
|
16
10
|
isUploadedPhoto
|
|
17
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-QXPXIMIM.js";
|
|
12
|
+
import "./chunk-G6Z44LGA.js";
|
|
13
|
+
import {
|
|
14
|
+
SITE_CONFIG_FILE,
|
|
15
|
+
SITE_DESCRIPTION_PATH,
|
|
16
|
+
isJsonObject,
|
|
17
|
+
readSiteConfig
|
|
18
|
+
} from "./chunk-I3HOJE3S.js";
|
|
18
19
|
|
|
19
20
|
// src/index.ts
|
|
20
21
|
import process from "process";
|