@seamlet/astro 1.2.1 → 1.4.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 +14 -0
- package/README.md +76 -0
- package/components/RequestForm.astro +192 -0
- package/dist/bridge.d.ts +2 -2
- package/dist/bridge.js +3 -2
- package/dist/chunk-2IK7D2AA.js +6 -0
- package/dist/{chunk-Y4R4YV5O.js → chunk-BCJUH3ZD.js} +1 -1
- package/dist/{chunk-OCA2WYK3.js → chunk-JQ4YD7MM.js} +8 -3
- package/dist/{chunk-UL2C2IB2.js → chunk-TMN7KG5M.js} +294 -4
- package/dist/{chunk-HFPZVDST.js → chunk-ULOKDC6D.js} +1 -1
- package/dist/cli.js +1 -1
- package/dist/client.js +8 -0
- package/dist/index.js +5 -5
- package/dist/{messages-C2OjqFmy.d.ts → messages-Dwjthhrk.d.ts} +1 -1
- package/dist/{parse-content-Bnn6Wa-l.d.ts → parse-content-CT2cUVL-.d.ts} +23 -4
- package/dist/preview-requests.d.ts +5 -3
- package/dist/preview-requests.js +5 -3
- package/dist/protocol.d.ts +3 -3
- package/dist/protocol.js +2 -2
- package/dist/request-sending-ZBTK5L7U.js +251 -0
- package/dist/routes/media.js +5 -5
- package/dist/schema.d.ts +103 -4
- package/dist/schema.js +63 -1
- package/dist/{site-description-BnHrODQX.d.ts → site-description-cKiFtNHW.d.ts} +100 -2
- package/dist/site.d.ts +7 -2
- package/dist/site.js +19 -3
- package/package.json +3 -2
|
@@ -17,6 +17,9 @@ function date(options) {
|
|
|
17
17
|
function image(options) {
|
|
18
18
|
return { kind: "image", ...options };
|
|
19
19
|
}
|
|
20
|
+
function file(options) {
|
|
21
|
+
return { kind: "file", ...options };
|
|
22
|
+
}
|
|
20
23
|
function link(options) {
|
|
21
24
|
return { kind: "link", ...options };
|
|
22
25
|
}
|
|
@@ -66,6 +69,7 @@ function defaultValue(field) {
|
|
|
66
69
|
case "number":
|
|
67
70
|
case "date":
|
|
68
71
|
case "image":
|
|
72
|
+
case "file":
|
|
69
73
|
case "link":
|
|
70
74
|
case "choice":
|
|
71
75
|
case "url":
|
|
@@ -116,6 +120,7 @@ function resolveInsideField(field, segments, path) {
|
|
|
116
120
|
case "number":
|
|
117
121
|
case "date":
|
|
118
122
|
case "image":
|
|
123
|
+
case "file":
|
|
119
124
|
case "link":
|
|
120
125
|
case "toggle":
|
|
121
126
|
case "choice":
|
|
@@ -352,6 +357,7 @@ function collectionIn(fields, content, names) {
|
|
|
352
357
|
case "number":
|
|
353
358
|
case "date":
|
|
354
359
|
case "image":
|
|
360
|
+
case "file":
|
|
355
361
|
case "link":
|
|
356
362
|
case "toggle":
|
|
357
363
|
case "choice":
|
|
@@ -379,6 +385,7 @@ function collectionFieldAt(schema, path) {
|
|
|
379
385
|
case "number":
|
|
380
386
|
case "date":
|
|
381
387
|
case "image":
|
|
388
|
+
case "file":
|
|
382
389
|
case "link":
|
|
383
390
|
case "toggle":
|
|
384
391
|
case "choice":
|
|
@@ -413,6 +420,7 @@ function withIdsIn(field, value, newId) {
|
|
|
413
420
|
case "number":
|
|
414
421
|
case "date":
|
|
415
422
|
case "image":
|
|
423
|
+
case "file":
|
|
416
424
|
case "link":
|
|
417
425
|
case "toggle":
|
|
418
426
|
case "choice":
|
|
@@ -469,6 +477,7 @@ function issuesInField(field, value, path, idsOf) {
|
|
|
469
477
|
case "number":
|
|
470
478
|
case "date":
|
|
471
479
|
case "image":
|
|
480
|
+
case "file":
|
|
472
481
|
case "link":
|
|
473
482
|
case "toggle":
|
|
474
483
|
case "choice":
|
|
@@ -521,6 +530,7 @@ function issuesInField2(field, value, path) {
|
|
|
521
530
|
case "number":
|
|
522
531
|
case "date":
|
|
523
532
|
case "image":
|
|
533
|
+
case "file":
|
|
524
534
|
case "link":
|
|
525
535
|
case "toggle":
|
|
526
536
|
case "choice":
|
|
@@ -685,6 +695,8 @@ function parseField(field, input, path) {
|
|
|
685
695
|
return parseDate(field, input, path);
|
|
686
696
|
case "image":
|
|
687
697
|
return parseImage(field, input, path);
|
|
698
|
+
case "file":
|
|
699
|
+
return parseFile(field, input, path);
|
|
688
700
|
case "link":
|
|
689
701
|
return parseLink(field, input, path);
|
|
690
702
|
case "toggle":
|
|
@@ -798,6 +810,21 @@ function parseImage(field, input, path) {
|
|
|
798
810
|
}
|
|
799
811
|
return valid(image2);
|
|
800
812
|
}
|
|
813
|
+
function parseFile(field, input, path) {
|
|
814
|
+
if (input === null) {
|
|
815
|
+
return valid(null);
|
|
816
|
+
}
|
|
817
|
+
const src = isJsonObject(input) ? input["src"] : void 0;
|
|
818
|
+
const name = isJsonObject(input) ? input["name"] : void 0;
|
|
819
|
+
if (typeof src !== "string" || typeof name !== "string") {
|
|
820
|
+
return invalidType(defaultValue(field), path);
|
|
821
|
+
}
|
|
822
|
+
const file2 = { src, name };
|
|
823
|
+
if (src.trim() === "") {
|
|
824
|
+
return { value: file2, issues: [{ path, code: "missing-file-src" }] };
|
|
825
|
+
}
|
|
826
|
+
return valid(file2);
|
|
827
|
+
}
|
|
801
828
|
function parseLink(field, input, path) {
|
|
802
829
|
if (input === null) {
|
|
803
830
|
return valid(null);
|
|
@@ -1105,6 +1132,7 @@ function readFieldOfKind(field) {
|
|
|
1105
1132
|
]);
|
|
1106
1133
|
case "date":
|
|
1107
1134
|
case "image":
|
|
1135
|
+
case "file":
|
|
1108
1136
|
case "link":
|
|
1109
1137
|
case "toggle":
|
|
1110
1138
|
case "url":
|
|
@@ -1184,6 +1212,7 @@ function unknownCollectionIn(schema, field, path) {
|
|
|
1184
1212
|
case "number":
|
|
1185
1213
|
case "date":
|
|
1186
1214
|
case "image":
|
|
1215
|
+
case "file":
|
|
1187
1216
|
case "link":
|
|
1188
1217
|
case "toggle":
|
|
1189
1218
|
case "choice":
|
|
@@ -1214,6 +1243,7 @@ function misplacedSlugIn(field, path) {
|
|
|
1214
1243
|
case "number":
|
|
1215
1244
|
case "date":
|
|
1216
1245
|
case "image":
|
|
1246
|
+
case "file":
|
|
1217
1247
|
case "link":
|
|
1218
1248
|
case "toggle":
|
|
1219
1249
|
case "choice":
|
|
@@ -1255,6 +1285,7 @@ var FIELD_KINDS = {
|
|
|
1255
1285
|
number: true,
|
|
1256
1286
|
date: true,
|
|
1257
1287
|
image: true,
|
|
1288
|
+
file: true,
|
|
1258
1289
|
link: true,
|
|
1259
1290
|
toggle: true,
|
|
1260
1291
|
choice: true,
|
|
@@ -1325,6 +1356,106 @@ function originOf(value) {
|
|
|
1325
1356
|
return WEB_PROTOCOLS.has(url2.protocol) ? url2.origin : void 0;
|
|
1326
1357
|
}
|
|
1327
1358
|
|
|
1359
|
+
// src/schema/request-forms.ts
|
|
1360
|
+
var DEFAULT_TEXT_LENGTH = 200;
|
|
1361
|
+
var DEFAULT_LONG_TEXT_LENGTH = 2e3;
|
|
1362
|
+
var MAX_REQUEST_FIELD_LENGTH = 5e3;
|
|
1363
|
+
var MAX_OPTIONS = 50;
|
|
1364
|
+
var MAX_FORM_ID_LENGTH = 40;
|
|
1365
|
+
var FORM_ID = /^[a-z][a-z0-9-]*$/u;
|
|
1366
|
+
var FIELD_NAME = /^[a-z][a-zA-Z0-9]*$/u;
|
|
1367
|
+
var LINE_BREAK = /[\r\n]/u;
|
|
1368
|
+
var REQUEST_FIELD_KINDS = [
|
|
1369
|
+
"text",
|
|
1370
|
+
"longText",
|
|
1371
|
+
"phone",
|
|
1372
|
+
"email",
|
|
1373
|
+
"choice"
|
|
1374
|
+
];
|
|
1375
|
+
function requestForm(form) {
|
|
1376
|
+
return form;
|
|
1377
|
+
}
|
|
1378
|
+
function requestText(options) {
|
|
1379
|
+
return { kind: "text", ...options };
|
|
1380
|
+
}
|
|
1381
|
+
function requestLongText(options) {
|
|
1382
|
+
return { kind: "longText", ...options };
|
|
1383
|
+
}
|
|
1384
|
+
function requestPhone(options) {
|
|
1385
|
+
return { kind: "phone", ...options };
|
|
1386
|
+
}
|
|
1387
|
+
function requestEmail(options) {
|
|
1388
|
+
return { kind: "email", ...options };
|
|
1389
|
+
}
|
|
1390
|
+
function requestChoice(options) {
|
|
1391
|
+
return { kind: "choice", ...options };
|
|
1392
|
+
}
|
|
1393
|
+
function readRequestForm(input) {
|
|
1394
|
+
if (!isJsonObject(input)) return invalidForm("not-a-form");
|
|
1395
|
+
const { id, title, fields } = input;
|
|
1396
|
+
if (!isFormId(id)) return invalidForm("invalid-id");
|
|
1397
|
+
if (!isText(title)) return invalidForm("invalid-title");
|
|
1398
|
+
if (!isJsonObject(fields)) return invalidForm("no-fields");
|
|
1399
|
+
const entries = Object.entries(fields);
|
|
1400
|
+
if (entries.length === 0) return invalidForm("no-fields");
|
|
1401
|
+
const hasInvalidName = entries.some(([name]) => !FIELD_NAME.test(name));
|
|
1402
|
+
if (hasInvalidName) return invalidForm("invalid-field-name");
|
|
1403
|
+
const readFields2 = entries.map(
|
|
1404
|
+
([name, field]) => [name, readRequestField(field)]
|
|
1405
|
+
);
|
|
1406
|
+
const isEveryFieldRead = readFields2.every(([, field]) => field !== void 0);
|
|
1407
|
+
if (!isEveryFieldRead) return invalidForm("invalid-field");
|
|
1408
|
+
const form = { id, title, fields: Object.fromEntries(readFields2) };
|
|
1409
|
+
return { status: "read", form };
|
|
1410
|
+
}
|
|
1411
|
+
function isFormId(value) {
|
|
1412
|
+
return typeof value === "string" && value.length <= MAX_FORM_ID_LENGTH && FORM_ID.test(value);
|
|
1413
|
+
}
|
|
1414
|
+
function maxLengthOf(field) {
|
|
1415
|
+
const fallback = field.kind === "text" ? DEFAULT_TEXT_LENGTH : DEFAULT_LONG_TEXT_LENGTH;
|
|
1416
|
+
return field.maxLength ?? fallback;
|
|
1417
|
+
}
|
|
1418
|
+
function readRequestField(input) {
|
|
1419
|
+
if (!isJsonObject(input)) return void 0;
|
|
1420
|
+
const { kind, label, required } = input;
|
|
1421
|
+
if (!isRequestFieldKind(kind) || !isText(label)) return void 0;
|
|
1422
|
+
if (required !== void 0 && typeof required !== "boolean") {
|
|
1423
|
+
return void 0;
|
|
1424
|
+
}
|
|
1425
|
+
const common = { label, ...required === void 0 ? {} : { required } };
|
|
1426
|
+
switch (kind) {
|
|
1427
|
+
case "text":
|
|
1428
|
+
case "longText":
|
|
1429
|
+
return withMaxLength(kind, common, input["maxLength"]);
|
|
1430
|
+
case "phone":
|
|
1431
|
+
case "email":
|
|
1432
|
+
return { kind, ...common };
|
|
1433
|
+
case "choice":
|
|
1434
|
+
return withOptions2(common, input["options"]);
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
function withMaxLength(kind, common, maxLength) {
|
|
1438
|
+
if (maxLength === void 0) return { kind, ...common };
|
|
1439
|
+
const isSoundLength = Number.isInteger(maxLength) && typeof maxLength === "number" && maxLength > 0 && maxLength <= MAX_REQUEST_FIELD_LENGTH;
|
|
1440
|
+
return isSoundLength ? { kind, ...common, maxLength } : void 0;
|
|
1441
|
+
}
|
|
1442
|
+
function withOptions2(common, options) {
|
|
1443
|
+
if (!Array.isArray(options)) return void 0;
|
|
1444
|
+
const values = options;
|
|
1445
|
+
const texts = values.filter(isOptionText);
|
|
1446
|
+
const isSoundList = texts.length === values.length && texts.length > 0 && texts.length <= MAX_OPTIONS && new Set(texts).size === texts.length;
|
|
1447
|
+
return isSoundList ? { kind: "choice", ...common, options: texts } : void 0;
|
|
1448
|
+
}
|
|
1449
|
+
function isRequestFieldKind(value) {
|
|
1450
|
+
return REQUEST_FIELD_KINDS.some((kind) => kind === value);
|
|
1451
|
+
}
|
|
1452
|
+
function invalidForm(reason) {
|
|
1453
|
+
return { status: "invalid", reason };
|
|
1454
|
+
}
|
|
1455
|
+
function isOptionText(value) {
|
|
1456
|
+
return isText(value) && value === value.trim() && !LINE_BREAK.test(value);
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1328
1459
|
// src/schema/record-pages.ts
|
|
1329
1460
|
var RECORD_PLACEHOLDER = "[record]";
|
|
1330
1461
|
var RECORD_PAGE_PATH = new RegExp(
|
|
@@ -1407,16 +1538,18 @@ function checkSiteDescription(input) {
|
|
|
1407
1538
|
const initial = parseContent(schema.fields, input["initialContent"]);
|
|
1408
1539
|
const pages = checkPages(schema.fields, input["pages"]);
|
|
1409
1540
|
const recordPages2 = checkRecordPages(schema.fields, input["recordPages"]);
|
|
1541
|
+
const forms = checkForms(input["forms"]);
|
|
1410
1542
|
const problems = [
|
|
1411
1543
|
...nameProblems,
|
|
1412
1544
|
...initialContentProblems(initial.issues),
|
|
1413
1545
|
...pages.problems,
|
|
1414
1546
|
...recordPages2.problems,
|
|
1415
1547
|
...placementProblems(pages.pages ?? [], recordPages2.pages ?? []),
|
|
1416
|
-
...addressProblems(schema.fields, initial.content, recordPages2.pages ?? [])
|
|
1548
|
+
...addressProblems(schema.fields, initial.content, recordPages2.pages ?? []),
|
|
1549
|
+
...forms.problems
|
|
1417
1550
|
];
|
|
1418
1551
|
const isNamed = isSiteId(id) && isText(title);
|
|
1419
|
-
const isWhole = pages.pages !== void 0 && recordPages2.pages !== void 0;
|
|
1552
|
+
const isWhole = pages.pages !== void 0 && recordPages2.pages !== void 0 && forms.forms !== void 0;
|
|
1420
1553
|
if (!isNamed || !isWhole || problems.length > 0) {
|
|
1421
1554
|
return invalid(problems);
|
|
1422
1555
|
}
|
|
@@ -1426,7 +1559,8 @@ function checkSiteDescription(input) {
|
|
|
1426
1559
|
schema: schema.fields,
|
|
1427
1560
|
initialContent: initial.content,
|
|
1428
1561
|
pages: pages.pages,
|
|
1429
|
-
...recordPages2.pages.length === 0 ? {} : { recordPages: recordPages2.pages }
|
|
1562
|
+
...recordPages2.pages.length === 0 ? {} : { recordPages: recordPages2.pages },
|
|
1563
|
+
...forms.forms.length === 0 ? {} : { forms: forms.forms }
|
|
1430
1564
|
};
|
|
1431
1565
|
return { status: "read", description };
|
|
1432
1566
|
}
|
|
@@ -1520,6 +1654,27 @@ function readRecordPage(schema, input) {
|
|
|
1520
1654
|
const isSearchTexts = typeof searchTexts === "string" && isSearchTextsGroup(field.item, searchTexts);
|
|
1521
1655
|
return isSearchTexts ? { path, collection: collection2, searchTexts } : "invalid-search-texts";
|
|
1522
1656
|
}
|
|
1657
|
+
function checkForms(input) {
|
|
1658
|
+
if (input === void 0) return { forms: [], problems: [] };
|
|
1659
|
+
if (!Array.isArray(input)) {
|
|
1660
|
+
return { forms: void 0, problems: [{ code: "invalid-forms" }] };
|
|
1661
|
+
}
|
|
1662
|
+
const entries = input;
|
|
1663
|
+
const readings = entries.map((entry) => readRequestForm(entry));
|
|
1664
|
+
const formProblems = readings.flatMap(
|
|
1665
|
+
(reading, index) => reading.status === "invalid" ? [{ code: "invalid-form", index, reason: reading.reason }] : []
|
|
1666
|
+
);
|
|
1667
|
+
const forms = readings.flatMap(
|
|
1668
|
+
(reading) => reading.status === "read" ? [reading.form] : []
|
|
1669
|
+
);
|
|
1670
|
+
const ids = forms.map((form) => form.id);
|
|
1671
|
+
const repeated = ids.filter((id, index) => ids.indexOf(id) !== index);
|
|
1672
|
+
const repeatProblems = [...new Set(repeated)].map(
|
|
1673
|
+
(id) => ({ code: "repeated-form-id", id })
|
|
1674
|
+
);
|
|
1675
|
+
const problems = [...formProblems, ...repeatProblems];
|
|
1676
|
+
return { forms: problems.length === 0 ? forms : void 0, problems };
|
|
1677
|
+
}
|
|
1523
1678
|
function placementProblems(pages, recordPages2) {
|
|
1524
1679
|
const misplaced = pages.filter(
|
|
1525
1680
|
(page) => recordPageAt(recordPages2, page.path) !== void 0
|
|
@@ -1555,6 +1710,110 @@ function invalid(problems) {
|
|
|
1555
1710
|
return { status: "invalid", problems };
|
|
1556
1711
|
}
|
|
1557
1712
|
|
|
1713
|
+
// src/schema/request-values.ts
|
|
1714
|
+
var REQUEST_FORM_KEY = "seamlet-form";
|
|
1715
|
+
var REQUEST_PAGE_KEY = "seamlet-page";
|
|
1716
|
+
var REQUEST_CONSENT_KEY = "seamlet-consent";
|
|
1717
|
+
var REQUEST_TRAP_KEY = "seamlet-check";
|
|
1718
|
+
var CHECKED_VALUES = /* @__PURE__ */ new Set(["on", "true"]);
|
|
1719
|
+
var MAX_PAGE_LENGTH = 300;
|
|
1720
|
+
var PAGE_PATH2 = /^\/(?![/\\])[^\s\\]*$/u;
|
|
1721
|
+
var PHONE_SEPARATORS = /[\s().-]/gu;
|
|
1722
|
+
var PHONE_DIGITS = /^\+?\d{5,15}$/u;
|
|
1723
|
+
var EMAIL_ADDRESS2 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/u;
|
|
1724
|
+
var MAX_EMAIL_LENGTH = 254;
|
|
1725
|
+
var LINE_BREAKS = /\r\n?/gu;
|
|
1726
|
+
var ANY_LINE_BREAK = /[\r\n]+/gu;
|
|
1727
|
+
var REQUEST_ISSUE_TEXTS = {
|
|
1728
|
+
required: "\u0417\u0430\u043F\u043E\u043B\u043D\u0438\u0442\u0435 \u044D\u0442\u043E \u043F\u043E\u043B\u0435",
|
|
1729
|
+
"too-long": "\u0417\u0434\u0435\u0441\u044C \u0441\u043B\u0438\u0448\u043A\u043E\u043C \u0434\u043B\u0438\u043D\u043D\u044B\u0439 \u0442\u0435\u043A\u0441\u0442",
|
|
1730
|
+
"invalid-phone": "\u041F\u0440\u043E\u0432\u0435\u0440\u044C\u0442\u0435 \u043D\u043E\u043C\u0435\u0440 \u0442\u0435\u043B\u0435\u0444\u043E\u043D\u0430",
|
|
1731
|
+
"invalid-email": "\u041F\u0440\u043E\u0432\u0435\u0440\u044C\u0442\u0435 \u0430\u0434\u0440\u0435\u0441 \u043F\u043E\u0447\u0442\u044B",
|
|
1732
|
+
"unknown-option": "\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u043E\u0434\u0438\u043D \u0438\u0437 \u0432\u0430\u0440\u0438\u0430\u043D\u0442\u043E\u0432"
|
|
1733
|
+
};
|
|
1734
|
+
var CONSENT_REQUIRED_TEXT = "\u041E\u0442\u043C\u0435\u0442\u044C\u0442\u0435 \u0441\u043E\u0433\u043B\u0430\u0441\u0438\u0435 \u043D\u0430 \u043E\u0431\u0440\u0430\u0431\u043E\u0442\u043A\u0443 \u043F\u0435\u0440\u0441\u043E\u043D\u0430\u043B\u044C\u043D\u044B\u0445 \u0434\u0430\u043D\u043D\u044B\u0445";
|
|
1735
|
+
function readRequestValues(form, entries) {
|
|
1736
|
+
const readings = Object.entries(form.fields).map(
|
|
1737
|
+
([name, field]) => readValue(name, field, entryOf(entries, name))
|
|
1738
|
+
);
|
|
1739
|
+
const issues = readings.filter(isIssue);
|
|
1740
|
+
if (issues.length > 0) return { status: "invalid", issues };
|
|
1741
|
+
const values = readings.filter(isFilledValue);
|
|
1742
|
+
return { status: "read", values };
|
|
1743
|
+
}
|
|
1744
|
+
function readRequestEntries(input) {
|
|
1745
|
+
if (!isJsonObject(input)) return {};
|
|
1746
|
+
const pairs = Object.entries(input).filter(
|
|
1747
|
+
(pair) => typeof pair[1] === "string"
|
|
1748
|
+
);
|
|
1749
|
+
return Object.fromEntries(pairs);
|
|
1750
|
+
}
|
|
1751
|
+
function isConsentGiven(entries) {
|
|
1752
|
+
return CHECKED_VALUES.has(entryOf(entries, REQUEST_CONSENT_KEY));
|
|
1753
|
+
}
|
|
1754
|
+
function isTrapFilled(entries) {
|
|
1755
|
+
return entryOf(entries, REQUEST_TRAP_KEY).trim() !== "";
|
|
1756
|
+
}
|
|
1757
|
+
function requestPageOf(entries) {
|
|
1758
|
+
const page = entryOf(entries, REQUEST_PAGE_KEY);
|
|
1759
|
+
const isPage = page.length <= MAX_PAGE_LENGTH && PAGE_PATH2.test(page);
|
|
1760
|
+
return isPage ? page : "/";
|
|
1761
|
+
}
|
|
1762
|
+
function isPhoneNumber(typed) {
|
|
1763
|
+
return PHONE_DIGITS.test(typed.replaceAll(PHONE_SEPARATORS, ""));
|
|
1764
|
+
}
|
|
1765
|
+
function isEmailAddress(typed) {
|
|
1766
|
+
return typed.length <= MAX_EMAIL_LENGTH && EMAIL_ADDRESS2.test(typed);
|
|
1767
|
+
}
|
|
1768
|
+
function entryOf(entries, name) {
|
|
1769
|
+
const entry = Object.hasOwn(entries, name) ? entries[name] : "";
|
|
1770
|
+
return typeof entry === "string" ? entry : "";
|
|
1771
|
+
}
|
|
1772
|
+
function readValue(name, field, typed) {
|
|
1773
|
+
const words = wordsOf(field, typed);
|
|
1774
|
+
if (words === "") {
|
|
1775
|
+
return field.required === true ? { field: name, code: "required" } : { field: name, label: field.label, value: "" };
|
|
1776
|
+
}
|
|
1777
|
+
const code = issueOf(field, words);
|
|
1778
|
+
return code === void 0 ? { field: name, label: field.label, value: words } : { field: name, code };
|
|
1779
|
+
}
|
|
1780
|
+
function wordsOf(field, typed) {
|
|
1781
|
+
const trimmed = typed.trim();
|
|
1782
|
+
return field.kind === "longText" ? trimmed.replaceAll(LINE_BREAKS, "\n") : trimmed.replaceAll(ANY_LINE_BREAK, " ");
|
|
1783
|
+
}
|
|
1784
|
+
function issueOf(field, words) {
|
|
1785
|
+
switch (field.kind) {
|
|
1786
|
+
case "text":
|
|
1787
|
+
case "longText":
|
|
1788
|
+
return countCharacters(words) > maxLengthOf(field) ? "too-long" : void 0;
|
|
1789
|
+
case "phone":
|
|
1790
|
+
return isPhoneNumber(words) ? void 0 : "invalid-phone";
|
|
1791
|
+
case "email":
|
|
1792
|
+
return isEmailAddress(words) ? void 0 : "invalid-email";
|
|
1793
|
+
case "choice":
|
|
1794
|
+
return field.options.includes(words) ? void 0 : "unknown-option";
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
function isIssue(reading) {
|
|
1798
|
+
return "code" in reading;
|
|
1799
|
+
}
|
|
1800
|
+
function isFilledValue(reading) {
|
|
1801
|
+
return "value" in reading && reading.value !== "";
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1804
|
+
// src/schema/request-addresses.ts
|
|
1805
|
+
function requestsPathOf(siteId) {
|
|
1806
|
+
return `/api/sites/${siteId}/requests`;
|
|
1807
|
+
}
|
|
1808
|
+
function legalPagePathOf(siteId, document) {
|
|
1809
|
+
return `/legal/${siteId}/${document}`;
|
|
1810
|
+
}
|
|
1811
|
+
|
|
1812
|
+
// src/schema/request-events.ts
|
|
1813
|
+
var REQUEST_SEND_EVENT = "seamlet:request-send";
|
|
1814
|
+
var REQUEST_SENT_EVENT = "seamlet:request-sent";
|
|
1815
|
+
var REQUEST_FAILED_EVENT = "seamlet:request-failed";
|
|
1816
|
+
|
|
1558
1817
|
export {
|
|
1559
1818
|
defineSchema,
|
|
1560
1819
|
text,
|
|
@@ -1562,6 +1821,7 @@ export {
|
|
|
1562
1821
|
number,
|
|
1563
1822
|
date,
|
|
1564
1823
|
image,
|
|
1824
|
+
file,
|
|
1565
1825
|
link,
|
|
1566
1826
|
toggle,
|
|
1567
1827
|
choice,
|
|
@@ -1618,6 +1878,18 @@ export {
|
|
|
1618
1878
|
SITE_CONFIG_FILE,
|
|
1619
1879
|
isSiteId,
|
|
1620
1880
|
readSiteConfig,
|
|
1881
|
+
DEFAULT_TEXT_LENGTH,
|
|
1882
|
+
DEFAULT_LONG_TEXT_LENGTH,
|
|
1883
|
+
MAX_REQUEST_FIELD_LENGTH,
|
|
1884
|
+
requestForm,
|
|
1885
|
+
requestText,
|
|
1886
|
+
requestLongText,
|
|
1887
|
+
requestPhone,
|
|
1888
|
+
requestEmail,
|
|
1889
|
+
requestChoice,
|
|
1890
|
+
readRequestForm,
|
|
1891
|
+
isFormId,
|
|
1892
|
+
maxLengthOf,
|
|
1621
1893
|
RECORD_PLACEHOLDER,
|
|
1622
1894
|
PAGE_PATH_SEPARATOR,
|
|
1623
1895
|
recordPages,
|
|
@@ -1630,5 +1902,23 @@ export {
|
|
|
1630
1902
|
HOME_PAGE_PATH,
|
|
1631
1903
|
readSiteDescription,
|
|
1632
1904
|
checkSiteDescription,
|
|
1633
|
-
isSearchTextsGroup
|
|
1905
|
+
isSearchTextsGroup,
|
|
1906
|
+
REQUEST_FORM_KEY,
|
|
1907
|
+
REQUEST_PAGE_KEY,
|
|
1908
|
+
REQUEST_CONSENT_KEY,
|
|
1909
|
+
REQUEST_TRAP_KEY,
|
|
1910
|
+
REQUEST_ISSUE_TEXTS,
|
|
1911
|
+
CONSENT_REQUIRED_TEXT,
|
|
1912
|
+
readRequestValues,
|
|
1913
|
+
readRequestEntries,
|
|
1914
|
+
isConsentGiven,
|
|
1915
|
+
isTrapFilled,
|
|
1916
|
+
requestPageOf,
|
|
1917
|
+
isPhoneNumber,
|
|
1918
|
+
isEmailAddress,
|
|
1919
|
+
requestsPathOf,
|
|
1920
|
+
legalPagePathOf,
|
|
1921
|
+
REQUEST_SEND_EVENT,
|
|
1922
|
+
REQUEST_SENT_EVENT,
|
|
1923
|
+
REQUEST_FAILED_EVENT
|
|
1634
1924
|
};
|
package/dist/cli.js
CHANGED
package/dist/client.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
REQUEST_FORM_SELECTOR
|
|
3
|
+
} from "./chunk-2IK7D2AA.js";
|
|
1
4
|
import {
|
|
2
5
|
PREVIEW_PATH
|
|
3
6
|
} from "./chunk-QCVWHHJC.js";
|
|
@@ -12,3 +15,8 @@ if (isFramed && isPreview) {
|
|
|
12
15
|
connectToEditor({ editorOrigin: cmsOrigin });
|
|
13
16
|
});
|
|
14
17
|
}
|
|
18
|
+
if (document.querySelector(REQUEST_FORM_SELECTOR) !== null) {
|
|
19
|
+
void import("./request-sending-ZBTK5L7U.js").then(({ connectRequestForms }) => {
|
|
20
|
+
connectRequestForms(document, { isPreview });
|
|
21
|
+
});
|
|
22
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -7,15 +7,15 @@ import {
|
|
|
7
7
|
UPLOADS_PATH
|
|
8
8
|
} from "./chunk-QCVWHHJC.js";
|
|
9
9
|
import {
|
|
10
|
-
|
|
11
|
-
} from "./chunk-
|
|
12
|
-
import "./chunk-
|
|
10
|
+
isUploadedFile
|
|
11
|
+
} from "./chunk-JQ4YD7MM.js";
|
|
12
|
+
import "./chunk-ULOKDC6D.js";
|
|
13
13
|
import {
|
|
14
14
|
SITE_CONFIG_FILE,
|
|
15
15
|
SITE_DESCRIPTION_PATH,
|
|
16
16
|
isJsonObject,
|
|
17
17
|
readSiteConfig
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-TMN7KG5M.js";
|
|
19
19
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
import process from "process";
|
|
@@ -33,7 +33,7 @@ var PHOTOS_COPIED_AT_ONCE = 6;
|
|
|
33
33
|
var PHOTO_TIMEOUT_MS = 6e4;
|
|
34
34
|
function uploadedPhotoAddresses(html, siteId) {
|
|
35
35
|
const addresses = [...html.matchAll(PHOTO_ADDRESS)].flatMap(
|
|
36
|
-
([address, site, fileName]) => site === siteId &&
|
|
36
|
+
([address, site, fileName]) => site === siteId && isUploadedFile(fileName) ? [address] : []
|
|
37
37
|
);
|
|
38
38
|
return [...new Set(addresses)];
|
|
39
39
|
}
|
|
@@ -34,6 +34,14 @@ interface ImageField {
|
|
|
34
34
|
readonly kind: "image";
|
|
35
35
|
readonly label: string;
|
|
36
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* A document a visitor opens or downloads: a price list, a menu, a contract, uploaded in the CMS as
|
|
39
|
+
* a PDF. Its value is the file's address and the name the site shows for it.
|
|
40
|
+
*/
|
|
41
|
+
interface FileField {
|
|
42
|
+
readonly kind: "file";
|
|
43
|
+
readonly label: string;
|
|
44
|
+
}
|
|
37
45
|
/**
|
|
38
46
|
* A link: its text and where it leads (a page of the site, another site, a phone or an email).
|
|
39
47
|
* The site decides how it looks; a button «Записаться» stays a button.
|
|
@@ -117,7 +125,7 @@ interface CollectionField<F extends RecordFields = RecordFields> {
|
|
|
117
125
|
/** The field of a record that names it in the editor: a text field, or a link by its text. */
|
|
118
126
|
readonly itemTitle?: keyof F & string;
|
|
119
127
|
}
|
|
120
|
-
type Field = TextField | RichTextField | NumberField | DateField | ImageField | LinkField | ToggleField | ChoiceField | ChoicesField | UrlField | TimeField | SlugField | ReferenceField | ReferencesField | GroupField | CollectionField;
|
|
128
|
+
type Field = TextField | RichTextField | NumberField | DateField | ImageField | FileField | LinkField | ToggleField | ChoiceField | ChoicesField | UrlField | TimeField | SlugField | ReferenceField | ReferencesField | GroupField | CollectionField;
|
|
121
129
|
type FieldKind = Field["kind"];
|
|
122
130
|
/** Field names to fields; the order of the keys is the order of the form. */
|
|
123
131
|
interface Fields {
|
|
@@ -135,6 +143,7 @@ declare function richText(options: FieldOptions<RichTextField>): RichTextField;
|
|
|
135
143
|
declare function number(options: FieldOptions<NumberField>): NumberField;
|
|
136
144
|
declare function date(options: FieldOptions<DateField>): DateField;
|
|
137
145
|
declare function image(options: FieldOptions<ImageField>): ImageField;
|
|
146
|
+
declare function file(options: FieldOptions<FileField>): FileField;
|
|
138
147
|
declare function link(options: FieldOptions<LinkField>): LinkField;
|
|
139
148
|
declare function toggle(options: FieldOptions<ToggleField>): ToggleField;
|
|
140
149
|
/** `const` keeps the values of the options literal, so the content has their exact type. */
|
|
@@ -192,6 +201,13 @@ interface ImageValue {
|
|
|
192
201
|
/** Unset: the middle of the photo (see imagePosition). */
|
|
193
202
|
readonly focus?: ImageFocus;
|
|
194
203
|
}
|
|
204
|
+
/** A document uploaded in the CMS: where it is and the name the site shows for it. */
|
|
205
|
+
interface FileValue {
|
|
206
|
+
/** `/media/<site>/<file>.pdf`, or an address of the site's own files. */
|
|
207
|
+
readonly src: string;
|
|
208
|
+
/** «Прайс-лист», «Меню банкета»: the words of the link to it. */
|
|
209
|
+
readonly name: string;
|
|
210
|
+
}
|
|
195
211
|
interface LinkValue {
|
|
196
212
|
readonly text: string;
|
|
197
213
|
/** A path of the site (`/menu`), a web address, `tel:` or `mailto:`; see linkTargetOf. */
|
|
@@ -201,7 +217,7 @@ interface LinkValue {
|
|
|
201
217
|
* The value of each field. A text can be empty (""), the other scalar fields can be unset (null):
|
|
202
218
|
* content is filled in by people, so a site must be ready for a missing price or date.
|
|
203
219
|
*/
|
|
204
|
-
type ValueOf<F extends Field> = F extends TextField ? string : F extends RichTextField ? RichText : F extends NumberField ? number | null : F extends DateField ? string | null : F extends ImageField ? ImageValue | null : F extends LinkField ? LinkValue | null : F extends ToggleField ? boolean : F extends ChoiceField<infer Options> ? OptionValue<Options> | null : F extends ChoicesField<infer Options> ? readonly OptionValue<Options>[] : F extends UrlField ? string | null : F extends TimeField ? string | null : F extends SlugField ? string : F extends ReferenceField ? string | null : F extends ReferencesField ? readonly string[] : F extends GroupField<infer Group> ? ContentOf<Group> : F extends CollectionField<infer Item> ? readonly RecordOf<Item>[] : never;
|
|
220
|
+
type ValueOf<F extends Field> = F extends TextField ? string : F extends RichTextField ? RichText : F extends NumberField ? number | null : F extends DateField ? string | null : F extends ImageField ? ImageValue | null : F extends FileField ? FileValue | null : F extends LinkField ? LinkValue | null : F extends ToggleField ? boolean : F extends ChoiceField<infer Options> ? OptionValue<Options> | null : F extends ChoicesField<infer Options> ? readonly OptionValue<Options>[] : F extends UrlField ? string | null : F extends TimeField ? string | null : F extends SlugField ? string : F extends ReferenceField ? string | null : F extends ReferencesField ? readonly string[] : F extends GroupField<infer Group> ? ContentOf<Group> : F extends CollectionField<infer Item> ? readonly RecordOf<Item>[] : never;
|
|
205
221
|
/**
|
|
206
222
|
* A record of a collection: its fields and its id. The id is missing only in content no editor has
|
|
207
223
|
* saved yet, such as a site's initial content (see withRecordIds).
|
|
@@ -219,7 +235,7 @@ type ContentOf<F extends Fields> = {
|
|
|
219
235
|
interface Content {
|
|
220
236
|
readonly [name: string]: FieldValue;
|
|
221
237
|
}
|
|
222
|
-
type FieldValue = string | number | boolean | null | readonly string[] | ImageValue | LinkValue | RichText | Content | readonly Content[];
|
|
238
|
+
type FieldValue = string | number | boolean | null | readonly string[] | ImageValue | FileValue | LinkValue | RichText | Content | readonly Content[];
|
|
223
239
|
declare function defaultValue(field: Field): FieldValue;
|
|
224
240
|
declare function defaultContent(fields: Fields): Content;
|
|
225
241
|
|
|
@@ -249,6 +265,9 @@ type ContentIssue = {
|
|
|
249
265
|
} | {
|
|
250
266
|
readonly path: FieldPath;
|
|
251
267
|
readonly code: "missing-image-src";
|
|
268
|
+
} | {
|
|
269
|
+
readonly path: FieldPath;
|
|
270
|
+
readonly code: "missing-file-src";
|
|
252
271
|
} | {
|
|
253
272
|
readonly path: FieldPath;
|
|
254
273
|
readonly code: "missing-link-href";
|
|
@@ -300,4 +319,4 @@ declare function countCharacters(text: string): number;
|
|
|
300
319
|
*/
|
|
301
320
|
declare function parseContent<F extends Fields>(schema: F, input: unknown): ParsedContent<ContentOf<F>>;
|
|
302
321
|
|
|
303
|
-
export {
|
|
322
|
+
export { link as $, choice as A, choices as B, type Content as C, type DateField as D, collection as E, type Fields as F, type GroupField as G, countCharacters as H, type ImageField as I, date as J, defaultContent as K, type LinkValue as L, defaultValue as M, type NumberField as N, type OptionValue as O, type ParsedContent as P, defineSchema as Q, type RichText as R, type SlugField as S, type TextField as T, type UrlField as U, type ValueOf as V, file as W, formatFieldPath as X, group as Y, image as Z, linesOf as _, type FieldPath as a, normalizedLine as a0, number as a1, parseContent as a2, plainTextOf as a3, readRichText as a4, reference as a5, references as a6, richText as a7, richTextOf as a8, slug as a9, text as aa, time as ab, toggle as ac, url as ad, type ContentOf as b, type Field as c, type CollectionField as d, type RecordFields as e, type RecordOf as f, type ContentIssue as g, type ImageValue as h, type LinkField as i, type ChoicesField as j, type TimeField as k, type RichTextField as l, type ToggleField as m, type ChoiceField as n, type ChoiceOption as o, type FieldKind as p, type FieldValue as q, type FileField as r, type FileValue as s, type ImageFocus as t, type ReferenceField as u, type ReferencesField as v, type RichBlock as w, type RichLine as x, type RichSpan as y, asOneParagraph as z };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { C as Content } from './parse-content-
|
|
2
|
-
import { S as SiteDescription } from './site-description-
|
|
1
|
+
import { C as Content } from './parse-content-CT2cUVL-.js';
|
|
2
|
+
import { S as SiteDescription } from './site-description-cKiFtNHW.js';
|
|
3
3
|
|
|
4
4
|
/** Far more than the content of a site ever takes; a bigger body is not a draft. */
|
|
5
5
|
declare const MAX_DRAFT_BYTES: number;
|
|
@@ -33,5 +33,7 @@ declare function isPageAddress(description: SiteDescription, rest: string | unde
|
|
|
33
33
|
declare function previewContentAt(description: SiteDescription, content: Content, rest: string | undefined): Content;
|
|
34
34
|
/** Whether a name can be a photo uploaded to the CMS: the hash of its bytes, as WebP. */
|
|
35
35
|
declare function isUploadedPhoto(fileName: string | undefined): fileName is string;
|
|
36
|
+
/** Whether a name can be a file uploaded to the CMS: a photo or a document. */
|
|
37
|
+
declare function isUploadedFile(fileName: string | undefined): fileName is string;
|
|
36
38
|
|
|
37
|
-
export { type DraftReading, MAX_DRAFT_BYTES, isPageAddress, isUploadedPhoto, previewContentAt, previewHeadersOf, readDraft };
|
|
39
|
+
export { type DraftReading, MAX_DRAFT_BYTES, isPageAddress, isUploadedFile, isUploadedPhoto, previewContentAt, previewHeadersOf, readDraft };
|
package/dist/preview-requests.js
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
2
|
MAX_DRAFT_BYTES,
|
|
3
3
|
isPageAddress,
|
|
4
|
+
isUploadedFile,
|
|
4
5
|
isUploadedPhoto,
|
|
5
6
|
previewContentAt,
|
|
6
7
|
previewHeadersOf,
|
|
7
8
|
readDraft
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import "./chunk-
|
|
10
|
-
import "./chunk-
|
|
9
|
+
} from "./chunk-JQ4YD7MM.js";
|
|
10
|
+
import "./chunk-ULOKDC6D.js";
|
|
11
|
+
import "./chunk-TMN7KG5M.js";
|
|
11
12
|
export {
|
|
12
13
|
MAX_DRAFT_BYTES,
|
|
13
14
|
isPageAddress,
|
|
15
|
+
isUploadedFile,
|
|
14
16
|
isUploadedPhoto,
|
|
15
17
|
previewContentAt,
|
|
16
18
|
previewHeadersOf,
|
package/dist/protocol.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { S as Shortcut, T as TextFormats } from './messages-
|
|
2
|
-
export { B as Box, a as EditorKey, E as EditorMessage, I as InlineEditingOutcome, L as LinkBox, M as MarkerBox, b as PROTOCOL_VERSION, c as PageLayout, P as PageMessage, d as Point, e as TextFormat, f as TypedSelection, V as Viewport, r as readEditorMessage, g as readPageMessage } from './messages-
|
|
3
|
-
import { F as Fields, a as FieldPath, I as ImageField, R as RichText } from './parse-content-
|
|
1
|
+
import { S as Shortcut, T as TextFormats } from './messages-Dwjthhrk.js';
|
|
2
|
+
export { B as Box, a as EditorKey, E as EditorMessage, I as InlineEditingOutcome, L as LinkBox, M as MarkerBox, b as PROTOCOL_VERSION, c as PageLayout, P as PageMessage, d as Point, e as TextFormat, f as TypedSelection, V as Viewport, r as readEditorMessage, g as readPageMessage } from './messages-Dwjthhrk.js';
|
|
3
|
+
import { F as Fields, a as FieldPath, I as ImageField, R as RichText } from './parse-content-CT2cUVL-.js';
|
|
4
4
|
|
|
5
5
|
declare function shortcutOf(event: KeyboardEvent): Shortcut | undefined;
|
|
6
6
|
|
package/dist/protocol.js
CHANGED
|
@@ -22,9 +22,9 @@ import {
|
|
|
22
22
|
shortcutOf,
|
|
23
23
|
toggleList,
|
|
24
24
|
toggleMark
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-BCJUH3ZD.js";
|
|
26
26
|
import "./chunk-QCVWHHJC.js";
|
|
27
|
-
import "./chunk-
|
|
27
|
+
import "./chunk-TMN7KG5M.js";
|
|
28
28
|
export {
|
|
29
29
|
OWN_PHOTOS_PATH,
|
|
30
30
|
PROTOCOL_VERSION,
|