@seamlet/astro 1.3.0 → 1.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 +11 -0
- package/README.md +46 -0
- package/dist/bridge.d.ts +3 -2
- package/dist/bridge.js +3 -2
- package/dist/{chunk-ENMUO442.js → chunk-53O34DN3.js} +56 -0
- package/dist/{chunk-VQKAWFCQ.js → chunk-62XHZAPK.js} +1 -1
- package/dist/{chunk-MFD4TJPV.js → chunk-JN6XVHCW.js} +8 -3
- package/dist/{chunk-2GZZHXP5.js → chunk-WABVCYGZ.js} +1 -1
- package/dist/cli.js +4 -4
- package/dist/client.js +1 -1
- package/dist/{parse-content-Bnn6Wa-l.d.ts → content-ZG6F3MPA.d.ts} +20 -81
- package/dist/feeds.d.ts +50 -0
- package/dist/feeds.js +174 -0
- package/dist/index.js +11 -11
- package/dist/{messages-C2OjqFmy.d.ts → messages-d-elsH_0.d.ts} +2 -1
- package/dist/parse-content-Dtec5MKj.d.ts +83 -0
- package/dist/presets-Ch54FeQK.d.ts +104 -0
- package/dist/preview-requests.d.ts +6 -3
- package/dist/preview-requests.js +5 -3
- package/dist/protocol.d.ts +4 -3
- package/dist/protocol.js +2 -2
- package/dist/{request-sending-Y6L55IIM.js → request-sending-PW6OB4W2.js} +1 -1
- package/dist/routes/media.js +6 -6
- package/dist/schema.d.ts +17 -100
- package/dist/schema.js +7 -1
- package/dist/{site-description-DSHyBrAL.d.ts → site-description-0UU5Wukx.d.ts} +2 -1
- package/dist/site.d.ts +3 -2
- package/dist/site.js +5 -5
- package/package.json +5 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.5.0
|
|
4
|
+
|
|
5
|
+
- Feeds of the catalog: `@seamlet/astro/feeds` with `yandexFeedOf` (YML for Яндекс), `avitoFeedOf` (an Avito autoload file, with the fields of its category from the site) and `productOffersOf`, the offers of `productFields` records.
|
|
6
|
+
- A calculator whose prices the owner keeps: `ratesField()` and `estimateOf(rates, amounts)`.
|
|
7
|
+
|
|
8
|
+
## 1.4.0
|
|
9
|
+
|
|
10
|
+
- Documents: `file({ label })` holds a PDF the owner uploads in the CMS, `{ src, name }` (`FileValue`): a price list, a menu, a contract. An empty name of a file asks for its file (`missing-file-src`). A static build copies the uploaded documents its pages link to, as it copies the photos, and the route of uploads serves them on request; `isUploadedFile` names both.
|
|
11
|
+
|
|
12
|
+
A site with a file field needs a CMS that reads 1.4 (cms.seamlet.ru from its release that takes documents). Code with an exhaustive `switch` over `FieldKind` or over the issues of `parseContent` gets the new member to handle.
|
|
13
|
+
|
|
3
14
|
## 1.3.0
|
|
4
15
|
|
|
5
16
|
- Requests: `requestForm({ id, title, fields })` with `requestText`, `requestLongText`, `requestPhone`, `requestEmail` and `requestChoice` declares a form visitors send the business requests with; `defineSeamletSite` takes them as `forms`, and the site's description tells the CMS about them. `RequestForm.astro` renders one, with a checkbox of consent of its own; it is posted to the CMS without a script, and sent in place with it.
|
package/README.md
CHANGED
|
@@ -128,6 +128,7 @@ The site's pages take their published content with `site.loadPublishedContent()`
|
|
|
128
128
|
| `time({ label })` | `HH:MM`, 00:00 to 23:59 | `null` |
|
|
129
129
|
| `slug({ label })` | the address of a record's page, `latte` in `/menu/latte` (below) | `""` |
|
|
130
130
|
| `image({ label })` | `{ src, alt }` | `null` |
|
|
131
|
+
| `file({ label })` | `{ src, name }`: a PDF uploaded in the CMS, a price list or a menu | `null` |
|
|
131
132
|
| `link({ label })` | `{ text, href }`: a page of the site, another site, a phone or an email | `null` |
|
|
132
133
|
| `url({ label })` | a web address without a text: a booking widget, a map | `null` |
|
|
133
134
|
| `toggle({ label })` | `true` or `false` | `false` |
|
|
@@ -138,6 +139,16 @@ The site's pages take their published content with `site.loadPublishedContent()`
|
|
|
138
139
|
| `group({ label, fields })` | named fields shown together | each field empty |
|
|
139
140
|
| `collection({ label, item, itemTitle? })` | a list of records of the same fields | `[]` |
|
|
140
141
|
|
|
142
|
+
A file is a document the owner uploads in the CMS as a PDF. The site links to it, and a static build copies it next to its pages as it copies the uploaded photos:
|
|
143
|
+
|
|
144
|
+
```astro
|
|
145
|
+
{content.priceList && (
|
|
146
|
+
<a href={safeHref(content.priceList.src)} download>
|
|
147
|
+
{content.priceList.name}
|
|
148
|
+
</a>
|
|
149
|
+
)}
|
|
150
|
+
```
|
|
151
|
+
|
|
141
152
|
Options are `{ value, label }`: the site reads `value`, the editor shows `label`, so a label can change without touching the site. The content has their exact type:
|
|
142
153
|
|
|
143
154
|
```ts
|
|
@@ -427,6 +438,41 @@ document.addEventListener(REQUEST_SENT_EVENT, (event) => {
|
|
|
427
438
|
|
|
428
439
|
A site whose `Content-Security-Policy` sets `connect-src` or `form-action` lets the CMS's origin in both. A request the policy stops on its way is posted by the browser as a plain form.
|
|
429
440
|
|
|
441
|
+
## A calculator
|
|
442
|
+
|
|
443
|
+
`ratesField()` is a collection of rates the owner keeps (what is counted, its unit, its price), and `estimateOf(rates, amounts)` sums what a visitor chose, the amounts known by the ids of their rates, in whole rubles. The site's own script asks the amounts and shows the sum; the page writes the rates for it, `data-rates={JSON.stringify(content.rates)}`. A new price reaches the calculator with the next publication.
|
|
444
|
+
|
|
445
|
+
## Feeds of the catalog
|
|
446
|
+
|
|
447
|
+
`@seamlet/astro/feeds` writes the catalog for the places that sell it, from the published content: `yandexFeedOf(shop, offers, madeAt)` in YML (Яндекс Директ, Бизнес, Маркет) and `avitoFeedOf(seller, offers, fieldsOf?)` as an Avito autoload file, `fieldsOf` giving the fields Avito's category asks. `productOffersOf(products, { siteUrl, pathOf })` makes the offers of `productFields` records, leaving the hidden ones out; a product without a price is left out of both feeds.
|
|
448
|
+
|
|
449
|
+
```ts
|
|
450
|
+
// src/pages/feeds/yandex.xml.ts
|
|
451
|
+
import { productOffersOf, yandexFeedOf } from "@seamlet/astro/feeds";
|
|
452
|
+
import site from "../../seamlet";
|
|
453
|
+
import { productPages } from "../../schema";
|
|
454
|
+
|
|
455
|
+
export async function GET() {
|
|
456
|
+
const content = await site.loadPublishedContent();
|
|
457
|
+
const offers = productOffersOf(content.products, {
|
|
458
|
+
siteUrl: "https://lesnaya.ru",
|
|
459
|
+
pathOf: productPages.pathOf,
|
|
460
|
+
});
|
|
461
|
+
const feed = yandexFeedOf(
|
|
462
|
+
{
|
|
463
|
+
name: "Лесная мастерская",
|
|
464
|
+
company: "ООО «Лесная мастерская»",
|
|
465
|
+
url: "https://lesnaya.ru",
|
|
466
|
+
},
|
|
467
|
+
offers,
|
|
468
|
+
new Date(),
|
|
469
|
+
);
|
|
470
|
+
return new Response(feed, {
|
|
471
|
+
headers: { "Content-Type": "application/xml; charset=utf-8" },
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
```
|
|
475
|
+
|
|
430
476
|
## What the integration adds
|
|
431
477
|
|
|
432
478
|
- `/preview` and `/preview/<page>`: the preview the CMS's editor frames, the pages of records too (at their address or at the record's id, a page the site does not show too). `GET` shows the published content; `POST` with the content as JSON shows that content without saving it, which is how the editor shows every edit at once. Only the CMS may frame it (`Content-Security-Policy: frame-ancestors`), and it is never indexed or cached; its refusals (an address of no page, a record a draft does not have, a draft that is not JSON or is over 1 MB) say the same.
|
package/dist/bridge.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { F as Fields } from './
|
|
2
|
-
import { P as PageMessage, E as EditorMessage } from './messages-
|
|
1
|
+
import { F as Fields } from './content-ZG6F3MPA.js';
|
|
2
|
+
import { P as PageMessage, E as EditorMessage } from './messages-d-elsH_0.js';
|
|
3
|
+
import './parse-content-Dtec5MKj.js';
|
|
3
4
|
|
|
4
5
|
interface BridgeOptions {
|
|
5
6
|
/** The origin of the CMS whose editor may frame the page, such as `https://cms.example.com`. */
|
package/dist/bridge.js
CHANGED
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
shortcutOf,
|
|
19
19
|
toggleList,
|
|
20
20
|
toggleMark
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-WABVCYGZ.js";
|
|
22
22
|
import {
|
|
23
23
|
FIELD_PATH_ATTRIBUTE,
|
|
24
24
|
REVEALED_ATTRIBUTE,
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
readRichText,
|
|
29
29
|
resolveFieldPath,
|
|
30
30
|
safeHref
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-53O34DN3.js";
|
|
32
32
|
|
|
33
33
|
// src/bridge/typed-fields.ts
|
|
34
34
|
function typedKindAt(schema, path) {
|
|
@@ -44,6 +44,7 @@ function typedKindAt(schema, path) {
|
|
|
44
44
|
case "time":
|
|
45
45
|
case "slug":
|
|
46
46
|
case "image":
|
|
47
|
+
case "file":
|
|
47
48
|
case "link":
|
|
48
49
|
case "url":
|
|
49
50
|
case "toggle":
|
|
@@ -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);
|
|
@@ -975,6 +1002,28 @@ function percentOf(fraction) {
|
|
|
975
1002
|
return `${String(rounded)}%`;
|
|
976
1003
|
}
|
|
977
1004
|
|
|
1005
|
+
// src/schema/rates.ts
|
|
1006
|
+
function ratesField(options = {}) {
|
|
1007
|
+
return collection({
|
|
1008
|
+
label: options.label ?? "\u0420\u0430\u0441\u0446\u0435\u043D\u043A\u0438 \u043A\u0430\u043B\u044C\u043A\u0443\u043B\u044F\u0442\u043E\u0440\u0430",
|
|
1009
|
+
itemTitle: "name",
|
|
1010
|
+
item: {
|
|
1011
|
+
name: text({ label: "\u0427\u0442\u043E \u0441\u0447\u0438\u0442\u0430\u0435\u043C", maxLength: 80 }),
|
|
1012
|
+
unit: text({ label: "\u0415\u0434\u0438\u043D\u0438\u0446\u0430", maxLength: 12 }),
|
|
1013
|
+
price: number({ label: "\u0426\u0435\u043D\u0430 \u0437\u0430 \u0435\u0434\u0438\u043D\u0438\u0446\u0443, \u20BD", min: 0 })
|
|
1014
|
+
}
|
|
1015
|
+
});
|
|
1016
|
+
}
|
|
1017
|
+
function estimateOf(rates, amounts) {
|
|
1018
|
+
const total = rates.reduce((sum, rate) => {
|
|
1019
|
+
const price = rate.price ?? 0;
|
|
1020
|
+
const amount = rate.id === void 0 ? 0 : amounts[rate.id] ?? 0;
|
|
1021
|
+
const isCounted = Number.isFinite(amount) && amount > 0;
|
|
1022
|
+
return isCounted ? sum + amount * price : sum;
|
|
1023
|
+
}, 0);
|
|
1024
|
+
return Math.round(total);
|
|
1025
|
+
}
|
|
1026
|
+
|
|
978
1027
|
// src/schema/presets.ts
|
|
979
1028
|
function seoFields(options = {}) {
|
|
980
1029
|
return group({
|
|
@@ -1105,6 +1154,7 @@ function readFieldOfKind(field) {
|
|
|
1105
1154
|
]);
|
|
1106
1155
|
case "date":
|
|
1107
1156
|
case "image":
|
|
1157
|
+
case "file":
|
|
1108
1158
|
case "link":
|
|
1109
1159
|
case "toggle":
|
|
1110
1160
|
case "url":
|
|
@@ -1184,6 +1234,7 @@ function unknownCollectionIn(schema, field, path) {
|
|
|
1184
1234
|
case "number":
|
|
1185
1235
|
case "date":
|
|
1186
1236
|
case "image":
|
|
1237
|
+
case "file":
|
|
1187
1238
|
case "link":
|
|
1188
1239
|
case "toggle":
|
|
1189
1240
|
case "choice":
|
|
@@ -1214,6 +1265,7 @@ function misplacedSlugIn(field, path) {
|
|
|
1214
1265
|
case "number":
|
|
1215
1266
|
case "date":
|
|
1216
1267
|
case "image":
|
|
1268
|
+
case "file":
|
|
1217
1269
|
case "link":
|
|
1218
1270
|
case "toggle":
|
|
1219
1271
|
case "choice":
|
|
@@ -1255,6 +1307,7 @@ var FIELD_KINDS = {
|
|
|
1255
1307
|
number: true,
|
|
1256
1308
|
date: true,
|
|
1257
1309
|
image: true,
|
|
1310
|
+
file: true,
|
|
1258
1311
|
link: true,
|
|
1259
1312
|
toggle: true,
|
|
1260
1313
|
choice: true,
|
|
@@ -1790,6 +1843,7 @@ export {
|
|
|
1790
1843
|
number,
|
|
1791
1844
|
date,
|
|
1792
1845
|
image,
|
|
1846
|
+
file,
|
|
1793
1847
|
link,
|
|
1794
1848
|
toggle,
|
|
1795
1849
|
choice,
|
|
@@ -1836,6 +1890,8 @@ export {
|
|
|
1836
1890
|
REVEALED_ATTRIBUTE,
|
|
1837
1891
|
REVEAL_EVENT,
|
|
1838
1892
|
imagePosition,
|
|
1893
|
+
ratesField,
|
|
1894
|
+
estimateOf,
|
|
1839
1895
|
seoFields,
|
|
1840
1896
|
contactsFields,
|
|
1841
1897
|
WEEKDAYS,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
pagePathOf
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-62XHZAPK.js";
|
|
4
4
|
import {
|
|
5
5
|
FIELD_PATH_SEPARATOR,
|
|
6
6
|
collectionAt,
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
newRecord,
|
|
10
10
|
recordAt,
|
|
11
11
|
recordPageAt
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-53O34DN3.js";
|
|
13
13
|
|
|
14
14
|
// src/preview-requests.ts
|
|
15
15
|
var MAX_DRAFT_BYTES = 1024 * 1024;
|
|
@@ -17,6 +17,7 @@ var BAD_REQUEST = 400;
|
|
|
17
17
|
var PAYLOAD_TOO_LARGE = 413;
|
|
18
18
|
var UNSUPPORTED_MEDIA_TYPE = 415;
|
|
19
19
|
var UPLOADED_PHOTO = /^[A-Za-z0-9_-]+\.webp$/u;
|
|
20
|
+
var UPLOADED_FILE = /^[A-Za-z0-9_-]+\.(?:webp|pdf)$/u;
|
|
20
21
|
function previewHeadersOf(cmsOrigin) {
|
|
21
22
|
return {
|
|
22
23
|
"Content-Security-Policy": `frame-ancestors ${cmsOrigin}`,
|
|
@@ -57,6 +58,9 @@ function previewContentAt(description, content, rest) {
|
|
|
57
58
|
function isUploadedPhoto(fileName) {
|
|
58
59
|
return fileName !== void 0 && UPLOADED_PHOTO.test(fileName);
|
|
59
60
|
}
|
|
61
|
+
function isUploadedFile(fileName) {
|
|
62
|
+
return fileName !== void 0 && UPLOADED_FILE.test(fileName);
|
|
63
|
+
}
|
|
60
64
|
async function readLimited(request) {
|
|
61
65
|
if (request.body === null) return "";
|
|
62
66
|
const reader = request.body.getReader();
|
|
@@ -100,5 +104,6 @@ export {
|
|
|
100
104
|
readDraft,
|
|
101
105
|
isPageAddress,
|
|
102
106
|
previewContentAt,
|
|
103
|
-
isUploadedPhoto
|
|
107
|
+
isUploadedPhoto,
|
|
108
|
+
isUploadedFile
|
|
104
109
|
};
|
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";
|
|
5
2
|
import {
|
|
6
3
|
SITE_CONFIG_FILE,
|
|
7
4
|
isSiteId,
|
|
8
5
|
readSiteConfig
|
|
9
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-53O34DN3.js";
|
|
7
|
+
import {
|
|
8
|
+
SITE_MODULE_FILE
|
|
9
|
+
} from "./chunk-QCVWHHJC.js";
|
|
10
10
|
|
|
11
11
|
// src/cli.ts
|
|
12
12
|
import process from "process";
|
package/dist/client.js
CHANGED
|
@@ -16,7 +16,7 @@ if (isFramed && isPreview) {
|
|
|
16
16
|
});
|
|
17
17
|
}
|
|
18
18
|
if (document.querySelector(REQUEST_FORM_SELECTOR) !== null) {
|
|
19
|
-
void import("./request-sending-
|
|
19
|
+
void import("./request-sending-PW6OB4W2.js").then(({ connectRequestForms }) => {
|
|
20
20
|
connectRequestForms(document, { isPreview });
|
|
21
21
|
});
|
|
22
22
|
}
|
|
@@ -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,85 +235,8 @@ 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
|
|
|
226
|
-
|
|
227
|
-
type FieldPath = readonly (string | number)[];
|
|
228
|
-
type ContentIssue = {
|
|
229
|
-
readonly path: FieldPath;
|
|
230
|
-
readonly code: "invalid-type";
|
|
231
|
-
} | {
|
|
232
|
-
readonly path: FieldPath;
|
|
233
|
-
readonly code: "invalid-date";
|
|
234
|
-
} | {
|
|
235
|
-
readonly path: FieldPath;
|
|
236
|
-
readonly code: "invalid-time";
|
|
237
|
-
} | {
|
|
238
|
-
readonly path: FieldPath;
|
|
239
|
-
readonly code: "too-long";
|
|
240
|
-
readonly maxLength: number;
|
|
241
|
-
} | {
|
|
242
|
-
readonly path: FieldPath;
|
|
243
|
-
readonly code: "below-min";
|
|
244
|
-
readonly min: number;
|
|
245
|
-
} | {
|
|
246
|
-
readonly path: FieldPath;
|
|
247
|
-
readonly code: "above-max";
|
|
248
|
-
readonly max: number;
|
|
249
|
-
} | {
|
|
250
|
-
readonly path: FieldPath;
|
|
251
|
-
readonly code: "missing-image-src";
|
|
252
|
-
} | {
|
|
253
|
-
readonly path: FieldPath;
|
|
254
|
-
readonly code: "missing-link-href";
|
|
255
|
-
} | {
|
|
256
|
-
readonly path: FieldPath;
|
|
257
|
-
readonly code: "unsupported-link-href";
|
|
258
|
-
} | {
|
|
259
|
-
readonly path: FieldPath;
|
|
260
|
-
readonly code: "unsupported-url";
|
|
261
|
-
} | {
|
|
262
|
-
readonly path: FieldPath;
|
|
263
|
-
readonly code: "unknown-choice";
|
|
264
|
-
readonly value: string;
|
|
265
|
-
} | {
|
|
266
|
-
readonly path: FieldPath;
|
|
267
|
-
readonly code: "missing-record";
|
|
268
|
-
readonly id: string;
|
|
269
|
-
}
|
|
270
|
-
/** A slug with a character an address does not have. */
|
|
271
|
-
| {
|
|
272
|
-
readonly path: FieldPath;
|
|
273
|
-
readonly code: "invalid-slug";
|
|
274
|
-
}
|
|
275
|
-
/** A slug another record of the collection has as its slug or its id. */
|
|
276
|
-
| {
|
|
277
|
-
readonly path: FieldPath;
|
|
278
|
-
readonly code: "taken-slug";
|
|
279
|
-
};
|
|
280
|
-
interface ParsedContent<C> {
|
|
281
|
-
readonly content: C;
|
|
282
|
-
readonly issues: readonly ContentIssue[];
|
|
283
|
-
}
|
|
284
|
-
declare function formatFieldPath(path: FieldPath): string;
|
|
285
|
-
/**
|
|
286
|
-
* The length that `maxLength` limits: characters as people see them (grapheme clusters), so
|
|
287
|
-
* "й" and "👍🏽" are one character each, whatever number of code units they take.
|
|
288
|
-
*/
|
|
289
|
-
declare function countCharacters(text: string): number;
|
|
290
|
-
/**
|
|
291
|
-
* Brings any input to the shape of the schema, so the result is always content of that schema:
|
|
292
|
-
* - a field missing from the input (the schema grew after the content was saved) gets its default;
|
|
293
|
-
* - a value of the wrong type or format gets the default too, and an issue;
|
|
294
|
-
* - a value of the right type that breaks a rule of its field (too long, below the minimum) is
|
|
295
|
-
* kept, with an issue: nothing a person typed is lost;
|
|
296
|
-
* - keys the schema does not know are dropped; a record keeps its id (see withRecordIds);
|
|
297
|
-
* - a reference to a record its collection does not have (removed since) is kept, with an issue;
|
|
298
|
-
* - a slug another record of the collection has is kept, with an issue.
|
|
299
|
-
* Content with issues is valid to show and edit, but not to save.
|
|
300
|
-
*/
|
|
301
|
-
declare function parseContent<F extends Fields>(schema: F, input: unknown): ParsedContent<ContentOf<F>>;
|
|
302
|
-
|
|
303
|
-
export { parseContent as $, collection as A, countCharacters as B, type Content as C, type DateField as D, date as E, type Fields as F, type GroupField as G, defaultContent as H, type ImageField as I, defaultValue as J, defineSchema as K, type LinkValue as L, formatFieldPath as M, type NumberField as N, type OptionValue as O, type ParsedContent as P, group as Q, type RichText as R, type SlugField as S, type TextField as T, type UrlField as U, type ValueOf as V, image as W, linesOf as X, link as Y, normalizedLine as Z, number as _, type FieldPath as a, plainTextOf as a0, readRichText as a1, reference as a2, references as a3, richText as a4, richTextOf as a5, slug as a6, text as a7, time as a8, toggle as a9, url as aa, 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 ImageFocus as r, type ReferenceField as s, type ReferencesField as t, type RichBlock as u, type RichLine as v, type RichSpan as w, asOneParagraph as x, choice as y, choices as z };
|
|
242
|
+
export { reference as $, collection as A, date as B, type Content as C, type DateField as D, defaultContent as E, type Fields as F, type GroupField as G, defaultValue as H, type ImageField as I, defineSchema as J, file as K, type LinkField as L, group as M, type NumberField as N, type OptionValue as O, image as P, linesOf as Q, type RichTextField as R, type SlugField as S, type TextField as T, type UrlField as U, type ValueOf as V, link as W, normalizedLine as X, number as Y, plainTextOf as Z, readRichText as _, type CollectionField as a, references as a0, richText as a1, richTextOf as a2, slug as a3, text as a4, time as a5, toggle as a6, url as a7, type ToggleField as b, type ChoicesField as c, type TimeField as d, type ContentOf as e, type RichText as f, type RecordOf as g, type Field as h, type RecordFields as i, type ImageValue as j, type LinkValue as k, type ChoiceField as l, type ChoiceOption as m, type FieldKind as n, type FieldValue as o, type FileField as p, type FileValue as q, type ImageFocus as r, type ReferenceField as s, type ReferencesField as t, type RichBlock as u, type RichLine as v, type RichSpan as w, asOneParagraph as x, choice as y, choices as z };
|
package/dist/feeds.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { g as RecordOf } from './content-ZG6F3MPA.js';
|
|
2
|
+
import { p as productFields } from './presets-Ch54FeQK.js';
|
|
3
|
+
|
|
4
|
+
interface FeedOffer {
|
|
5
|
+
/** Stays the same while the product lives: the record's id. */
|
|
6
|
+
readonly id: string;
|
|
7
|
+
readonly name: string;
|
|
8
|
+
/** The whole address of the product's page, or of the catalog when products have no pages. */
|
|
9
|
+
readonly url: string;
|
|
10
|
+
/** In rubles; a product without a price is left out of the feeds, which require one. */
|
|
11
|
+
readonly price: number | null;
|
|
12
|
+
readonly description: string;
|
|
13
|
+
/** Whole addresses of its photos, the main one first. */
|
|
14
|
+
readonly pictures: readonly string[];
|
|
15
|
+
/** The name of its category, if the catalog has them. */
|
|
16
|
+
readonly category?: string;
|
|
17
|
+
}
|
|
18
|
+
type ProductRecord = RecordOf<ReturnType<typeof productFields>>;
|
|
19
|
+
interface ProductOffersOptions {
|
|
20
|
+
/** The site's address: `https://lesnaya.ru`. */
|
|
21
|
+
readonly siteUrl: string;
|
|
22
|
+
/** The page of a product (`recordPages(...).pathOf`), or none: the catalog's page is used. */
|
|
23
|
+
readonly pathOf?: (product: ProductRecord) => string | undefined;
|
|
24
|
+
/** The page of the catalog, for products without pages of their own. */
|
|
25
|
+
readonly catalogPath?: string;
|
|
26
|
+
readonly categoryOf?: (product: ProductRecord) => string | undefined;
|
|
27
|
+
}
|
|
28
|
+
/** The offers of the products a visitor sees: hidden ones and ones without an id are left out. */
|
|
29
|
+
declare function productOffersOf(products: readonly ProductRecord[], options: ProductOffersOptions): readonly FeedOffer[];
|
|
30
|
+
|
|
31
|
+
interface AvitoSeller {
|
|
32
|
+
/** Where the goods are: the address of the shop or the workshop. */
|
|
33
|
+
readonly address: string;
|
|
34
|
+
readonly contactPhone: string;
|
|
35
|
+
/** The category of Avito the ads go to: «Ремонт и строительство». */
|
|
36
|
+
readonly category: string;
|
|
37
|
+
}
|
|
38
|
+
declare function avitoFeedOf(seller: AvitoSeller, offers: readonly FeedOffer[], fieldsOf?: (offer: FeedOffer) => Readonly<Record<string, string>>): string;
|
|
39
|
+
|
|
40
|
+
interface YandexShop {
|
|
41
|
+
/** The short name buyers know: «Лесная мастерская». */
|
|
42
|
+
readonly name: string;
|
|
43
|
+
/** The company's full name: «ООО «Лесная мастерская»». */
|
|
44
|
+
readonly company: string;
|
|
45
|
+
/** The site's address. */
|
|
46
|
+
readonly url: string;
|
|
47
|
+
}
|
|
48
|
+
declare function yandexFeedOf(shop: YandexShop, offers: readonly FeedOffer[], madeAt: Date): string;
|
|
49
|
+
|
|
50
|
+
export { type AvitoSeller, type FeedOffer, type ProductOffersOptions, type ProductRecord, type YandexShop, avitoFeedOf, productOffersOf, yandexFeedOf };
|