@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.
@@ -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 = /^(\/[a-z0-9][a-z0-9-]*)+$/u;
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
- if (!isNamed || pages.pages === void 0 || problems.length > 0) {
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,
@@ -6,7 +6,7 @@ import {
6
6
  readRichText,
7
7
  resolveFieldPath,
8
8
  safeHref
9
- } from "./chunk-ZS32CW5F.js";
9
+ } from "./chunk-I3HOJE3S.js";
10
10
 
11
11
  // src/bridge/editor-shortcuts.ts
12
12
  function shortcutOf(event) {
@@ -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-ZS32CW5F.js";
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-QL4K4HR6.js";
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";
@@ -1,4 +1,4 @@
1
- import { R as RichText, F as Fields, a as Content, b as FieldPath } from './parse-content-Eg8yRq_t.js';
1
+ import { R as RichText, F as Fields, C as Content, a as FieldPath } from './parse-content-IOJxgKLB.js';
2
2
 
3
3
  interface Box {
4
4
  readonly top: number;