@seamlet/astro 1.5.0 → 2.0.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 CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.0.0
4
+
5
+ - The consent of a form links to the business's own policy, a page of the site (`policyHref`, `/privacy` by default, `PRIVACY_PAGE_PATH`), not to texts kept by the CMS: the business is the operator of its visitors' data, and the CMS only passes requests on. `privacyPolicyFields()` is the policy as content and `privacyPolicyTextOf({ siteTitle, operator, forms })` a template to start it with.
6
+ - Breaking: `RequestForm.astro` has no `consentHref` and no separate paragraph of the policy (`seamlet-form-policy`); `legalPagePathOf` and `LegalDocument` are gone. The reason `closed` of `seamlet:request-failed` now means the CMS has nobody to mail the site's requests to.
7
+
3
8
  ## 1.5.0
4
9
 
5
10
  - 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.
package/README.md CHANGED
@@ -375,7 +375,7 @@ What was opened stays open while the page is rendered anew after every edit: a p
375
375
 
376
376
  ## Requests
377
377
 
378
- A visitor sends the business a request from the site: «Обратный звонок», «Рассчитать стоимость», «Бронь столика». The site declares its forms, gives them to `defineSeamletSite` and renders them; the CMS keeps every request a year, writes to the people who edit the site and passes it on where the site's developer says.
378
+ A visitor sends the business a request from the site: «Обратный звонок», «Рассчитать стоимость», «Бронь столика». The site declares its forms, gives them to `defineSeamletSite` and renders them; the CMS passes every request on by mail to the people who edit the site and keeps nothing once it is delivered.
379
379
 
380
380
  ```ts
381
381
  // src/forms.ts
@@ -420,15 +420,15 @@ import { estimateForm } from "../forms";
420
420
 
421
421
  The fields are `requestText` (a line), `requestLongText` (several lines), `requestPhone`, `requestEmail` and `requestChoice` (one of `options`); each may be `required`, and a text has a `maxLength` (200 for a line, 2000 for several by default). A field's name is a name such as `phone` or `preferredTime`. The form's `title` names its requests in the CMS and in the letters.
422
422
 
423
- `RequestForm.astro` renders plain elements with classes (`seamlet-form`, `seamlet-form-field`, `seamlet-form-issue`, `seamlet-form-consent`, `seamlet-form-policy`, `seamlet-form-submit`, `seamlet-form-sent`, `seamlet-form-failed`), which the site styles from around it: `.contacts :global(.seamlet-form-field)`. Its props are the `form`, the words of the button (`submitLabel`), what the visitor reads once the request is sent or could not be (`sentText`, `failedText`), the addresses of the texts of consent and of the policy (`consentHref`, `policyHref`, the CMS's pages of them by default) and `instance`, which tells apart two forms of the same id on one page.
423
+ `RequestForm.astro` renders plain elements with classes (`seamlet-form`, `seamlet-form-field`, `seamlet-form-issue`, `seamlet-form-consent`, `seamlet-form-submit`, `seamlet-form-sent`, `seamlet-form-failed`), which the site styles from around it: `.contacts :global(.seamlet-form-field)`. Its props are the `form`, the words of the button (`submitLabel`), what the visitor reads once the request is sent or could not be (`sentText`, `failedText`), the page of the business's policy the consent links to (`policyHref`, the site's own `/privacy` by default, `PRIVACY_PAGE_PATH`) and `instance`, which tells apart two forms of the same id on one page.
424
424
 
425
- The visitor consents with a checkbox of its own, never checked beforehand, to the texts the business confirmed in the CMS: until then the CMS takes no requests of the site, and the form says it could not send. Without a script the form is posted to the CMS, which answers with a page of its own; with the package's script (on every page, loaded only where a form is) the visitor stays on the page. The script checks what was typed with the code the CMS checks it with (`readRequestValues`), says what to fix under each field and sends the request.
425
+ The visitor consents with a checkbox of its own, never checked beforehand, on the terms of the business's policy, a page of the site: `/privacy` unless `policyHref` names another. The business is the operator of its visitors' data and publishes its policy; Seamlet only passes a request on by its commission. `privacyPolicyFields()` is the policy as content the owner edits, and `privacyPolicyTextOf({ siteTitle, operator, forms })` starts it with the business's details and what its forms ask (152-ФЗ): put it in the initial content and render it with `RichText` at `/privacy`. While the CMS has nobody to mail a site's requests to, it takes none, and the form says it could not send. Without a script the form is posted to the CMS, which answers with a page of its own; with the package's script (on every page, loaded only where a form is) the visitor stays on the page. The script checks what was typed with the code the CMS checks it with (`readRequestValues`), says what to fix under each field and sends the request.
426
426
 
427
427
  The site's own code hears every step from events on the form element, which bubble:
428
428
 
429
429
  - `seamlet:request-send` (`REQUEST_SEND_EVENT`), before the request is sent, with `{ form, entries }`. Cancelable: after `event.preventDefault()` the package sends nothing, and the site sends the request its own way.
430
430
  - `seamlet:request-sent` (`REQUEST_SENT_EVENT`), once the CMS has kept it, with `{ form, entries, id }`: a goal of the site's analytics, its own message.
431
- - `seamlet:request-failed` (`REQUEST_FAILED_EVENT`), with `{ form, reason, issues }`; `reason` is `invalid`, `closed` (the business takes no requests yet), `too-many` or `unavailable`.
431
+ - `seamlet:request-failed` (`REQUEST_FAILED_EVENT`), with `{ form, reason, issues }`; `reason` is `invalid`, `closed` (the CMS has nobody to mail the site's requests to), `too-many` or `unavailable`.
432
432
 
433
433
  ```ts
434
434
  document.addEventListener(REQUEST_SENT_EVENT, (event) => {
@@ -3,11 +3,11 @@
3
3
  // (`.contacts :global(.seamlet-form-field)`). Without a script it is a form posted to the CMS, and
4
4
  // the CMS answers with a page of its own; the package's script sends it in place. The checkbox of
5
5
  // consent stands apart from the fields and is never checked beforehand, as the law asks: the
6
- // visitor consents to the texts of the business, which the CMS shows at their addresses.
6
+ // visitor consents on the terms of the business's policy, a page of the site itself.
7
7
  import { cmsOrigin, siteId } from "virtual:seamlet/config";
8
8
  import {
9
- legalPagePathOf,
10
9
  maxLengthOf,
10
+ PRIVACY_PAGE_PATH,
11
11
  REQUEST_CONSENT_KEY,
12
12
  REQUEST_FORM_KEY,
13
13
  REQUEST_PAGE_KEY,
@@ -25,9 +25,7 @@ interface Props {
25
25
  readonly sentText?: string;
26
26
  /** What the visitor reads when the request could not be sent. */
27
27
  readonly failedText?: string;
28
- /** Where the text of consent is: the CMS's page of it unless the site has its own. */
29
- readonly consentHref?: string;
30
- /** Where the business's policy is: the CMS's page of it unless the site has its own. */
28
+ /** Where the business's policy is: the site's `/privacy` unless it says another page. */
31
29
  readonly policyHref?: string;
32
30
  /** Tells two forms of the same id on one page apart, in the ids of their elements. */
33
31
  readonly instance?: string;
@@ -39,8 +37,7 @@ const {
39
37
  submitLabel = "Отправить",
40
38
  sentText = "Спасибо! Заявка отправлена, мы скоро свяжемся с вами.",
41
39
  failedText = "Не получилось отправить заявку. Попробуйте ещё раз чуть позже.",
42
- consentHref = new URL(legalPagePathOf(siteId, "consent"), cmsOrigin).href,
43
- policyHref = new URL(legalPagePathOf(siteId, "privacy"), cmsOrigin).href,
40
+ policyHref = PRIVACY_PAGE_PATH,
44
41
  instance = form.id,
45
42
  class: className,
46
43
  } = Astro.props;
@@ -145,9 +142,9 @@ const isLineField = (field: RequestField): field is LineField =>
145
142
  aria-describedby={issueIdOf(REQUEST_CONSENT_KEY)}
146
143
  />
147
144
  <label for={idOf(REQUEST_CONSENT_KEY)}>
148
- Даю{" "}
149
- <a href={consentHref} target="_blank" rel="noopener">
150
- согласие на обработку персональных данных
145
+ Даю согласие на обработку персональных данных на условиях{" "}
146
+ <a href={policyHref} target="_blank" rel="noopener">
147
+ политики
151
148
  </a>
152
149
  </label>
153
150
  <p
@@ -157,11 +154,6 @@ const isLineField = (field: RequestField): field is LineField =>
157
154
  hidden
158
155
  />
159
156
  </div>
160
- <p class="seamlet-form-policy">
161
- <a href={policyHref} target="_blank" rel="noopener">
162
- Политика обработки персональных данных
163
- </a>
164
- </p>
165
157
  <button class="seamlet-form-submit" type="submit">
166
158
  {submitLabel}
167
159
  </button>
package/dist/bridge.js CHANGED
@@ -18,7 +18,7 @@ import {
18
18
  shortcutOf,
19
19
  toggleList,
20
20
  toggleMark
21
- } from "./chunk-WABVCYGZ.js";
21
+ } from "./chunk-XZDRTVJH.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-53O34DN3.js";
31
+ } from "./chunk-PAGIPRTS.js";
32
32
 
33
33
  // src/bridge/typed-fields.ts
34
34
  function typedKindAt(schema, path) {
@@ -6,7 +6,7 @@ import {
6
6
  recordAt,
7
7
  recordPageAt,
8
8
  recordPagePath
9
- } from "./chunk-53O34DN3.js";
9
+ } from "./chunk-PAGIPRTS.js";
10
10
 
11
11
  // src/site-pages.ts
12
12
  function pagePathOf(rest) {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  pagePathOf
3
- } from "./chunk-62XHZAPK.js";
3
+ } from "./chunk-CTS7Q45A.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-53O34DN3.js";
12
+ } from "./chunk-PAGIPRTS.js";
13
13
 
14
14
  // src/preview-requests.ts
15
15
  var MAX_DRAFT_BYTES = 1024 * 1024;
@@ -1827,8 +1827,59 @@ function isFilledValue(reading) {
1827
1827
  function requestsPathOf(siteId) {
1828
1828
  return `/api/sites/${siteId}/requests`;
1829
1829
  }
1830
- function legalPagePathOf(siteId, document) {
1831
- return `/legal/${siteId}/${document}`;
1830
+
1831
+ // src/schema/privacy-policy.ts
1832
+ var PRIVACY_PAGE_PATH = "/privacy";
1833
+ var WITHDRAWAL_DAYS = 30;
1834
+ var ANSWER_WORKING_DAYS = 10;
1835
+ function privacyPolicyFields(options = {}) {
1836
+ return group({
1837
+ label: options.label ?? "\u041F\u043E\u043B\u0438\u0442\u0438\u043A\u0430 \u043E\u0431\u0440\u0430\u0431\u043E\u0442\u043A\u0438 \u043F\u0435\u0440\u0441\u043E\u043D\u0430\u043B\u044C\u043D\u044B\u0445 \u0434\u0430\u043D\u043D\u044B\u0445",
1838
+ fields: {
1839
+ title: text({ label: "\u0417\u0430\u0433\u043E\u043B\u043E\u0432\u043E\u043A", maxLength: 120 }),
1840
+ text: richText({ label: "\u0422\u0435\u043A\u0441\u0442" })
1841
+ }
1842
+ });
1843
+ }
1844
+ function privacyPolicyTextOf(source) {
1845
+ const { operator, siteTitle } = source;
1846
+ const operatorName = `${operator.name} (\u0418\u041D\u041D ${operator.inn}, \u0430\u0434\u0440\u0435\u0441: ${operator.address})`;
1847
+ const sentData = sentDataOf(source.forms);
1848
+ return richTextOf(
1849
+ [
1850
+ "1. \u041E\u0431\u0449\u0438\u0435 \u043F\u043E\u043B\u043E\u0436\u0435\u043D\u0438\u044F",
1851
+ `1.1. \u042D\u0442\u0430 \u043F\u043E\u043B\u0438\u0442\u0438\u043A\u0430 \u043E\u043F\u0438\u0441\u044B\u0432\u0430\u0435\u0442, \u043A\u0430\u043A ${operatorName} \u2014 \u0434\u0430\u043B\u0435\u0435 \xAB\u041E\u043F\u0435\u0440\u0430\u0442\u043E\u0440\xBB \u2014 \u043E\u0431\u0440\u0430\u0431\u0430\u0442\u044B\u0432\u0430\u0435\u0442 \u043F\u0435\u0440\u0441\u043E\u043D\u0430\u043B\u044C\u043D\u044B\u0435 \u0434\u0430\u043D\u043D\u044B\u0435 \u043F\u043E\u0441\u0435\u0442\u0438\u0442\u0435\u043B\u0435\u0439 \u0441\u0430\u0439\u0442\u0430 \xAB${siteTitle}\xBB, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u044F\u044E\u0442 \u0437\u0430\u044F\u0432\u043A\u0438 \u0447\u0435\u0440\u0435\u0437 \u0435\u0433\u043E \u0444\u043E\u0440\u043C\u044B.`,
1852
+ "1.2. \u041F\u043E\u043B\u0438\u0442\u0438\u043A\u0430 \u0441\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u0430 \u043F\u043E \u0424\u0435\u0434\u0435\u0440\u0430\u043B\u044C\u043D\u043E\u043C\u0443 \u0437\u0430\u043A\u043E\u043D\u0443 \u043E\u0442 27.07.2006 \u2116 152-\u0424\u0417 \xAB\u041E \u043F\u0435\u0440\u0441\u043E\u043D\u0430\u043B\u044C\u043D\u044B\u0445 \u0434\u0430\u043D\u043D\u044B\u0445\xBB.",
1853
+ "2. \u041A\u0430\u043A\u0438\u0435 \u0434\u0430\u043D\u043D\u044B\u0435 \u043E\u0431\u0440\u0430\u0431\u0430\u0442\u044B\u0432\u0430\u044E\u0442\u0441\u044F",
1854
+ `2.1. \u0421\u0432\u0435\u0434\u0435\u043D\u0438\u044F, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u043F\u043E\u0441\u0435\u0442\u0438\u0442\u0435\u043B\u044C \u0441\u0430\u043C \u0443\u043A\u0430\u0437\u044B\u0432\u0430\u0435\u0442 \u0432 \u0444\u043E\u0440\u043C\u0435 \u0437\u0430\u044F\u0432\u043A\u0438: ${sentData}.`,
1855
+ "2.2. \u0410\u0434\u0440\u0435\u0441 \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u044B, \u0441 \u043A\u043E\u0442\u043E\u0440\u043E\u0439 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0430 \u0437\u0430\u044F\u0432\u043A\u0430, \u0438 \u0432\u0440\u0435\u043C\u044F \u0435\u0451 \u043E\u0442\u043F\u0440\u0430\u0432\u043A\u0438.",
1856
+ "2.3. \u041E\u043F\u0435\u0440\u0430\u0442\u043E\u0440 \u043D\u0435 \u043E\u0431\u0440\u0430\u0431\u0430\u0442\u044B\u0432\u0430\u0435\u0442 \u0441\u043F\u0435\u0446\u0438\u0430\u043B\u044C\u043D\u044B\u0435 \u043A\u0430\u0442\u0435\u0433\u043E\u0440\u0438\u0438 \u043F\u0435\u0440\u0441\u043E\u043D\u0430\u043B\u044C\u043D\u044B\u0445 \u0434\u0430\u043D\u043D\u044B\u0445 \u0438 \u0431\u0438\u043E\u043C\u0435\u0442\u0440\u0438\u0447\u0435\u0441\u043A\u0438\u0435 \u0434\u0430\u043D\u043D\u044B\u0435 \u0438 \u043F\u0440\u043E\u0441\u0438\u0442 \u043D\u0435 \u0443\u043A\u0430\u0437\u044B\u0432\u0430\u0442\u044C \u0438\u0445 \u0432 \u0437\u0430\u044F\u0432\u043A\u0435.",
1857
+ "3. \u0426\u0435\u043B\u044C, \u043E\u0441\u043D\u043E\u0432\u0430\u043D\u0438\u0435 \u0438 \u0441\u043E\u0433\u043B\u0430\u0441\u0438\u0435",
1858
+ "3.1. \u0426\u0435\u043B\u044C \u2014 \u0440\u0430\u0441\u0441\u043C\u043E\u0442\u0440\u0435\u0442\u044C \u0437\u0430\u044F\u0432\u043A\u0443 \u043F\u043E\u0441\u0435\u0442\u0438\u0442\u0435\u043B\u044F \u0438 \u0441\u0432\u044F\u0437\u0430\u0442\u044C\u0441\u044F \u0441 \u043D\u0438\u043C \u043F\u043E \u043D\u0435\u0439.",
1859
+ "3.2. \u041E\u0441\u043D\u043E\u0432\u0430\u043D\u0438\u0435 \u2014 \u0441\u043E\u0433\u043B\u0430\u0441\u0438\u0435 \u043F\u043E\u0441\u0435\u0442\u0438\u0442\u0435\u043B\u044F. \u041E\u0442\u043C\u0435\u0447\u0430\u044F \u0441\u043E\u0433\u043B\u0430\u0441\u0438\u0435 \u0432 \u0444\u043E\u0440\u043C\u0435 \u043F\u0435\u0440\u0435\u0434 \u043E\u0442\u043F\u0440\u0430\u0432\u043A\u043E\u0439 \u0437\u0430\u044F\u0432\u043A\u0438, \u043F\u043E\u0441\u0435\u0442\u0438\u0442\u0435\u043B\u044C \u0441\u0432\u043E\u0431\u043E\u0434\u043D\u043E, \u0441\u0432\u043E\u0435\u0439 \u0432\u043E\u043B\u0435\u0439 \u0438 \u0432 \u0441\u0432\u043E\u0451\u043C \u0438\u043D\u0442\u0435\u0440\u0435\u0441\u0435 \u0434\u0430\u0451\u0442 \u041E\u043F\u0435\u0440\u0430\u0442\u043E\u0440\u0443 \u0441\u043E\u0433\u043B\u0430\u0441\u0438\u0435 \u043D\u0430 \u043E\u0431\u0440\u0430\u0431\u043E\u0442\u043A\u0443 \u0443\u043A\u0430\u0437\u0430\u043D\u043D\u044B\u0445 \u0434\u0430\u043D\u043D\u044B\u0445 \u043D\u0430 \u0443\u0441\u043B\u043E\u0432\u0438\u044F\u0445 \u044D\u0442\u043E\u0439 \u043F\u043E\u043B\u0438\u0442\u0438\u043A\u0438.",
1860
+ "4. \u041A\u0430\u043A \u043E\u0431\u0440\u0430\u0431\u0430\u0442\u044B\u0432\u0430\u044E\u0442\u0441\u044F \u0434\u0430\u043D\u043D\u044B\u0435",
1861
+ "4.1. \u041E\u043F\u0435\u0440\u0430\u0442\u043E\u0440 \u0441\u043E\u0431\u0438\u0440\u0430\u0435\u0442, \u0437\u0430\u043F\u0438\u0441\u044B\u0432\u0430\u0435\u0442, \u0445\u0440\u0430\u043D\u0438\u0442, \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0435\u0442, \u0443\u0434\u0430\u043B\u044F\u0435\u0442 \u0438 \u0443\u043D\u0438\u0447\u0442\u043E\u0436\u0430\u0435\u0442 \u0434\u0430\u043D\u043D\u044B\u0435 \u0441 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D\u0438\u0435\u043C \u0441\u0440\u0435\u0434\u0441\u0442\u0432 \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u0438.",
1862
+ "4.2. \u041F\u0440\u0438\u0451\u043C \u0437\u0430\u044F\u0432\u043E\u043A \u0441 \u0444\u043E\u0440\u043C \u0441\u0430\u0439\u0442\u0430 \u043F\u043E \u043F\u043E\u0440\u0443\u0447\u0435\u043D\u0438\u044E \u041E\u043F\u0435\u0440\u0430\u0442\u043E\u0440\u0430 \u043E\u0431\u0435\u0441\u043F\u0435\u0447\u0438\u0432\u0430\u0435\u0442 \u0441\u0435\u0440\u0432\u0438\u0441 Seamlet: \u043E\u043D \u043F\u0435\u0440\u0435\u0434\u0430\u0451\u0442 \u0437\u0430\u044F\u0432\u043A\u0443 \u041E\u043F\u0435\u0440\u0430\u0442\u043E\u0440\u0443 \u0438 \u043D\u0435 \u0445\u0440\u0430\u043D\u0438\u0442 \u0435\u0451 \u0434\u043E\u043B\u044C\u0448\u0435, \u0447\u0435\u043C \u043D\u0443\u0436\u043D\u043E \u0434\u043B\u044F \u0434\u043E\u0441\u0442\u0430\u0432\u043A\u0438, \u0441\u043E\u0431\u043B\u044E\u0434\u0430\u044F \u043A\u043E\u043D\u0444\u0438\u0434\u0435\u043D\u0446\u0438\u0430\u043B\u044C\u043D\u043E\u0441\u0442\u044C \u0438 \u0442\u0440\u0435\u0431\u043E\u0432\u0430\u043D\u0438\u044F \u0441\u0442\u0430\u0442\u044C\u0438 19 \u0424\u0435\u0434\u0435\u0440\u0430\u043B\u044C\u043D\u043E\u0433\u043E \u0437\u0430\u043A\u043E\u043D\u0430 \u2116 152-\u0424\u0417. \u0414\u0430\u043D\u043D\u044B\u0435 \u0445\u0440\u0430\u043D\u044F\u0442\u0441\u044F \u0432 \u0420\u043E\u0441\u0441\u0438\u0439\u0441\u043A\u043E\u0439 \u0424\u0435\u0434\u0435\u0440\u0430\u0446\u0438\u0438.",
1863
+ "4.3. \u041E\u043F\u0435\u0440\u0430\u0442\u043E\u0440 \u043D\u0435 \u0440\u0430\u0441\u043F\u0440\u043E\u0441\u0442\u0440\u0430\u043D\u044F\u0435\u0442 \u0434\u0430\u043D\u043D\u044B\u0435 \u0438 \u043D\u0435 \u043F\u0435\u0440\u0435\u0434\u0430\u0451\u0442 \u0438\u0445 \u0442\u0440\u0435\u0442\u044C\u0438\u043C \u043B\u0438\u0446\u0430\u043C, \u043A\u0440\u043E\u043C\u0435 \u0441\u043B\u0443\u0447\u0430\u0435\u0432, \u043F\u0440\u0435\u0434\u0443\u0441\u043C\u043E\u0442\u0440\u0435\u043D\u043D\u044B\u0445 \u0437\u0430\u043A\u043E\u043D\u043E\u043C.",
1864
+ "5. \u0421\u0440\u043E\u043A\u0438",
1865
+ "5.1. \u0414\u0430\u043D\u043D\u044B\u0435 \u0445\u0440\u0430\u043D\u044F\u0442\u0441\u044F, \u043F\u043E\u043A\u0430 \u044D\u0442\u043E \u043D\u0443\u0436\u043D\u043E \u0434\u043B\u044F \u0440\u0430\u0441\u0441\u043C\u043E\u0442\u0440\u0435\u043D\u0438\u044F \u0437\u0430\u044F\u0432\u043A\u0438, \u043D\u043E \u043D\u0435 \u0434\u043E\u043B\u044C\u0448\u0435 \u043E\u0434\u043D\u043E\u0433\u043E \u0433\u043E\u0434\u0430 \u0441\u043E \u0434\u043D\u044F \u0435\u0451 \u043E\u0442\u043F\u0440\u0430\u0432\u043A\u0438, \u043F\u043E\u0441\u043B\u0435 \u0447\u0435\u0433\u043E \u0443\u0434\u0430\u043B\u044F\u044E\u0442\u0441\u044F.",
1866
+ `5.2. \u041F\u043E\u0441\u0435\u0442\u0438\u0442\u0435\u043B\u044C \u043C\u043E\u0436\u0435\u0442 \u043E\u0442\u043E\u0437\u0432\u0430\u0442\u044C \u0441\u043E\u0433\u043B\u0430\u0441\u0438\u0435, \u043D\u0430\u043F\u0438\u0441\u0430\u0432 \u041E\u043F\u0435\u0440\u0430\u0442\u043E\u0440\u0443 \u043D\u0430 ${operator.email}. \u0422\u043E\u0433\u0434\u0430 \u041E\u043F\u0435\u0440\u0430\u0442\u043E\u0440 \u043F\u0440\u0435\u043A\u0440\u0430\u0442\u0438\u0442 \u043E\u0431\u0440\u0430\u0431\u043E\u0442\u043A\u0443 \u0438 \u0443\u0434\u0430\u043B\u0438\u0442 \u0434\u0430\u043D\u043D\u044B\u0435 \u0432 \u0442\u0435\u0447\u0435\u043D\u0438\u0435 ${String(WITHDRAWAL_DAYS)} \u0434\u043D\u0435\u0439, \u0435\u0441\u043B\u0438 \u0438\u043D\u043E\u0435 \u043D\u0435 \u043F\u0440\u0435\u0434\u0443\u0441\u043C\u043E\u0442\u0440\u0435\u043D\u043E \u0437\u0430\u043A\u043E\u043D\u043E\u043C.`,
1867
+ "6. \u041F\u0440\u0430\u0432\u0430 \u043F\u043E\u0441\u0435\u0442\u0438\u0442\u0435\u043B\u044F",
1868
+ `6.1. \u041F\u043E\u0441\u0435\u0442\u0438\u0442\u0435\u043B\u044C \u0432\u043F\u0440\u0430\u0432\u0435 \u0443\u0437\u043D\u0430\u0442\u044C, \u043A\u0430\u043A \u043E\u0431\u0440\u0430\u0431\u0430\u0442\u044B\u0432\u0430\u044E\u0442\u0441\u044F \u0435\u0433\u043E \u0434\u0430\u043D\u043D\u044B\u0435, \u043F\u043E\u0442\u0440\u0435\u0431\u043E\u0432\u0430\u0442\u044C \u0443\u0442\u043E\u0447\u043D\u0438\u0442\u044C, \u0437\u0430\u0431\u043B\u043E\u043A\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0438\u043B\u0438 \u0443\u0434\u0430\u043B\u0438\u0442\u044C \u0438\u0445, \u043E\u0442\u043E\u0437\u0432\u0430\u0442\u044C \u0441\u043E\u0433\u043B\u0430\u0441\u0438\u0435 \u0438 \u043E\u0431\u0436\u0430\u043B\u043E\u0432\u0430\u0442\u044C \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044F \u041E\u043F\u0435\u0440\u0430\u0442\u043E\u0440\u0430 \u0432 \u0420\u043E\u0441\u043A\u043E\u043C\u043D\u0430\u0434\u0437\u043E\u0440\u0435 \u0438\u043B\u0438 \u0432 \u0441\u0443\u0434\u0435. \u0414\u043B\u044F \u044D\u0442\u043E\u0433\u043E \u0434\u043E\u0441\u0442\u0430\u0442\u043E\u0447\u043D\u043E \u043D\u0430\u043F\u0438\u0441\u0430\u0442\u044C \u041E\u043F\u0435\u0440\u0430\u0442\u043E\u0440\u0443 \u043D\u0430 ${operator.email}; \u041E\u043F\u0435\u0440\u0430\u0442\u043E\u0440 \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0432 \u0442\u0435\u0447\u0435\u043D\u0438\u0435 ${String(ANSWER_WORKING_DAYS)} \u0440\u0430\u0431\u043E\u0447\u0438\u0445 \u0434\u043D\u0435\u0439.`,
1869
+ "7. \u0418\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F \u043F\u043E\u043B\u0438\u0442\u0438\u043A\u0438",
1870
+ "7.1. \u041E\u043F\u0435\u0440\u0430\u0442\u043E\u0440 \u043C\u043E\u0436\u0435\u0442 \u0438\u0437\u043C\u0435\u043D\u044F\u0442\u044C \u043F\u043E\u043B\u0438\u0442\u0438\u043A\u0443. \u041D\u043E\u0432\u0430\u044F \u0440\u0435\u0434\u0430\u043A\u0446\u0438\u044F \u0434\u0435\u0439\u0441\u0442\u0432\u0443\u0435\u0442 \u0441 \u043C\u043E\u043C\u0435\u043D\u0442\u0430 \u043F\u0443\u0431\u043B\u0438\u043A\u0430\u0446\u0438\u0438 \u043D\u0430 \u044D\u0442\u043E\u0439 \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u0435."
1871
+ ].join("\n\n")
1872
+ );
1873
+ }
1874
+ function sentDataOf(forms) {
1875
+ const labels = forms.flatMap(
1876
+ (form) => Object.values(form.fields).map((field) => lowercaseFirst(field.label))
1877
+ );
1878
+ const unique = [...new Set(labels)];
1879
+ return unique.length === 0 ? "\u0438\u043C\u044F \u0438 \u043A\u043E\u043D\u0442\u0430\u043A\u0442\u044B" : unique.join(", ");
1880
+ }
1881
+ function lowercaseFirst(label) {
1882
+ return `${label.charAt(0).toLocaleLowerCase("ru")}${label.slice(1)}`;
1832
1883
  }
1833
1884
 
1834
1885
  // src/schema/request-events.ts
@@ -1941,7 +1992,9 @@ export {
1941
1992
  isPhoneNumber,
1942
1993
  isEmailAddress,
1943
1994
  requestsPathOf,
1944
- legalPagePathOf,
1995
+ PRIVACY_PAGE_PATH,
1996
+ privacyPolicyFields,
1997
+ privacyPolicyTextOf,
1945
1998
  REQUEST_SEND_EVENT,
1946
1999
  REQUEST_SENT_EVENT,
1947
2000
  REQUEST_FAILED_EVENT
@@ -6,7 +6,7 @@ import {
6
6
  readRichText,
7
7
  resolveFieldPath,
8
8
  safeHref
9
- } from "./chunk-53O34DN3.js";
9
+ } from "./chunk-PAGIPRTS.js";
10
10
 
11
11
  // src/bridge/editor-shortcuts.ts
12
12
  function shortcutOf(event) {
package/dist/cli.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  SITE_CONFIG_FILE,
4
4
  isSiteId,
5
5
  readSiteConfig
6
- } from "./chunk-53O34DN3.js";
6
+ } from "./chunk-PAGIPRTS.js";
7
7
  import {
8
8
  SITE_MODULE_FILE
9
9
  } from "./chunk-QCVWHHJC.js";
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-PW6OB4W2.js").then(({ connectRequestForms }) => {
19
+ void import("./request-sending-WCEHXPWV.js").then(({ connectRequestForms }) => {
20
20
  connectRequestForms(document, { isPreview });
21
21
  });
22
22
  }
package/dist/feeds.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  plainTextOf
3
- } from "./chunk-53O34DN3.js";
3
+ } from "./chunk-PAGIPRTS.js";
4
4
 
5
5
  // src/feeds/xml.ts
6
6
  var ESCAPES = {
package/dist/index.js CHANGED
@@ -1,16 +1,16 @@
1
1
  import {
2
2
  isUploadedFile
3
- } from "./chunk-JN6XVHCW.js";
3
+ } from "./chunk-NOAAAA5T.js";
4
4
  import {
5
5
  OWN_PHOTOS_PATH
6
6
  } from "./chunk-W2LIOR2C.js";
7
- import "./chunk-62XHZAPK.js";
7
+ import "./chunk-CTS7Q45A.js";
8
8
  import {
9
9
  SITE_CONFIG_FILE,
10
10
  SITE_DESCRIPTION_PATH,
11
11
  isJsonObject,
12
12
  readSiteConfig
13
- } from "./chunk-53O34DN3.js";
13
+ } from "./chunk-PAGIPRTS.js";
14
14
  import {
15
15
  PREVIEW_PATH,
16
16
  SITE_MODULE_FILE,
@@ -6,9 +6,9 @@ import {
6
6
  previewContentAt,
7
7
  previewHeadersOf,
8
8
  readDraft
9
- } from "./chunk-JN6XVHCW.js";
10
- import "./chunk-62XHZAPK.js";
11
- import "./chunk-53O34DN3.js";
9
+ } from "./chunk-NOAAAA5T.js";
10
+ import "./chunk-CTS7Q45A.js";
11
+ import "./chunk-PAGIPRTS.js";
12
12
  export {
13
13
  MAX_DRAFT_BYTES,
14
14
  isPageAddress,
package/dist/protocol.js CHANGED
@@ -22,8 +22,8 @@ import {
22
22
  shortcutOf,
23
23
  toggleList,
24
24
  toggleMark
25
- } from "./chunk-WABVCYGZ.js";
26
- import "./chunk-53O34DN3.js";
25
+ } from "./chunk-XZDRTVJH.js";
26
+ import "./chunk-PAGIPRTS.js";
27
27
  import "./chunk-QCVWHHJC.js";
28
28
  export {
29
29
  OWN_PHOTOS_PATH,
@@ -12,7 +12,7 @@ import {
12
12
  isJsonObject,
13
13
  readRequestForm,
14
14
  readRequestValues
15
- } from "./chunk-53O34DN3.js";
15
+ } from "./chunk-PAGIPRTS.js";
16
16
 
17
17
  // src/request-sending.ts
18
18
  var STATUS_CREATED = 201;
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  isUploadedFile
3
- } from "../chunk-JN6XVHCW.js";
4
- import "../chunk-62XHZAPK.js";
5
- import "../chunk-53O34DN3.js";
3
+ } from "../chunk-NOAAAA5T.js";
4
+ import "../chunk-CTS7Q45A.js";
5
+ import "../chunk-PAGIPRTS.js";
6
6
  import {
7
7
  uploadedPhotoPath
8
8
  } from "../chunk-QCVWHHJC.js";
package/dist/schema.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { j as ImageValue, k as LinkValue, a as CollectionField, C as Content, F as Fields, i as RecordFields, e as ContentOf, g as RecordOf, T as TextField, N as NumberField } from './content-ZG6F3MPA.js';
2
- export { l as ChoiceField, m as ChoiceOption, c as ChoicesField, D as DateField, h as Field, n as FieldKind, o as FieldValue, p as FileField, q as FileValue, G as GroupField, I as ImageField, r as ImageFocus, L as LinkField, O as OptionValue, s as ReferenceField, t as ReferencesField, u as RichBlock, v as RichLine, w as RichSpan, f as RichText, R as RichTextField, S as SlugField, d as TimeField, b as ToggleField, U as UrlField, V as ValueOf, x as asOneParagraph, y as choice, z as choices, A as collection, B as date, E as defaultContent, H as defaultValue, J as defineSchema, K as file, M as group, P as image, Q as linesOf, W as link, X as normalizedLine, Y as number, Z as plainTextOf, _ as readRichText, $ as reference, a0 as references, a1 as richText, a2 as richTextOf, a3 as slug, a4 as text, a5 as time, a6 as toggle, a7 as url } from './content-ZG6F3MPA.js';
1
+ import { j as ImageValue, k as LinkValue, a as CollectionField, C as Content, F as Fields, i as RecordFields, e as ContentOf, g as RecordOf, T as TextField, N as NumberField, G as GroupField, R as RichTextField, f as RichText } from './content-ZG6F3MPA.js';
2
+ export { l as ChoiceField, m as ChoiceOption, c as ChoicesField, D as DateField, h as Field, n as FieldKind, o as FieldValue, p as FileField, q as FileValue, I as ImageField, r as ImageFocus, L as LinkField, O as OptionValue, s as ReferenceField, t as ReferencesField, u as RichBlock, v as RichLine, w as RichSpan, S as SlugField, d as TimeField, b as ToggleField, U as UrlField, V as ValueOf, x as asOneParagraph, y as choice, z as choices, A as collection, B as date, E as defaultContent, H as defaultValue, J as defineSchema, K as file, M as group, P as image, Q as linesOf, W as link, X as normalizedLine, Y as number, Z as plainTextOf, _ as readRichText, $ as reference, a0 as references, a1 as richText, a2 as richTextOf, a3 as slug, a4 as text, a5 as time, a6 as toggle, a7 as url } from './content-ZG6F3MPA.js';
3
3
  export { C as ContentIssue, F as FieldPath, P as ParsedContent, c as countCharacters, f as formatFieldPath, p as parseContent } from './parse-content-Dtec5MKj.js';
4
4
  import { R as RequestForm } from './site-description-0UU5Wukx.js';
5
5
  export { C as CollectionPath, D as DEFAULT_LONG_TEXT_LENGTH, b as DEFAULT_TEXT_LENGTH, F as FIELD_PATH_SEPARATOR, c as FieldTarget, d as FieldsReading, H as HOME_PAGE_PATH, M as MAX_REQUEST_FIELD_LENGTH, P as PAGE_PATH_SEPARATOR, e as PageProblemReason, f as PlacedRecord, g as RECORD_PLACEHOLDER, h as RecordFieldsAt, i as RecordPagePath, j as RecordPageProblemReason, k as RecordPages, l as RequestChoiceField, m as RequestEmailField, n as RequestField, o as RequestFieldKind, p as RequestFields, q as RequestFormProblemReason, r as RequestFormReading, s as RequestLongTextField, t as RequestPhoneField, u as RequestTextField, v as SITE_DESCRIPTION_PATH, w as SchemaProblem, x as SchemaProblemReason, S as SiteDescription, y as SiteDescriptionProblem, z as SiteDescriptionReading, A as SitePageDescription, B as SiteRecordPageDescription, a as SiteRecordPages, E as checkFields, G as checkSiteDescription, I as isFormId, J as isRecordPagePath, K as isSearchTextsGroup, L as maxLengthOf, N as readFields, O as readRequestForm, Q as readSiteDescription, T as recordAddressOf, U as recordAt, V as recordPageAt, W as recordPagePath, X as recordPages, Y as requestChoice, Z as requestEmail, _ as requestForm, $ as requestLongText, a0 as requestPhone, a1 as requestText, a2 as resolveFieldPath } from './site-description-0UU5Wukx.js';
@@ -208,12 +208,35 @@ declare function requestPageOf(entries: RequestEntries): string;
208
208
  declare function isPhoneNumber(typed: string): boolean;
209
209
  declare function isEmailAddress(typed: string): boolean;
210
210
 
211
- /** The two texts a form's visitor reads before sending: the consent and the operator's policy. */
212
- type LegalDocument = "consent" | "privacy";
213
211
  /** Where the site's forms send their requests, on the CMS's origin. */
214
212
  declare function requestsPathOf(siteId: string): string;
215
- /** Where the CMS shows a text of the business that owns the site, to anyone. */
216
- declare function legalPagePathOf(siteId: string, document: LegalDocument): string;
213
+
214
+ /** The business that collects the data: as its documents name it. */
215
+ interface PolicyOperator {
216
+ /** «ООО «Лесная мастерская»» or «ИП Иванов Иван Иванович». */
217
+ readonly name: string;
218
+ readonly inn: string;
219
+ readonly address: string;
220
+ /** Where a visitor writes about their data: to withdraw the consent, to ask what is kept. */
221
+ readonly email: string;
222
+ }
223
+ interface PolicySource {
224
+ readonly siteTitle: string;
225
+ readonly operator: PolicyOperator;
226
+ /** The site's forms, whose fields the policy names. */
227
+ readonly forms: readonly RequestForm[];
228
+ }
229
+ /** The page of the policy on a site, which a form's consent links to unless the site says another. */
230
+ declare const PRIVACY_PAGE_PATH = "/privacy";
231
+ /** The policy as content: its title and its text, which the owner edits like any other text. */
232
+ declare function privacyPolicyFields(options?: {
233
+ readonly label?: string;
234
+ }): GroupField<{
235
+ title: TextField;
236
+ text: RichTextField;
237
+ }>;
238
+ /** A policy to start with, made from the business's details and what the site's forms ask. */
239
+ declare function privacyPolicyTextOf(source: PolicySource): RichText;
217
240
 
218
241
  /**
219
242
  * Before the request is sent, once the form takes what was typed. Cancelable: a site that calls
@@ -250,4 +273,4 @@ interface RequestFailedDetail {
250
273
  readonly issues: readonly RequestIssue[];
251
274
  }
252
275
 
253
- export { CONSENT_REQUIRED_TEXT, CollectionField, type CollectionRecords, Content, ContentOf, type ContentPath, FIELD_PATH_ATTRIBUTE, type FieldMarker, Fields, ImageValue, type JsonObject, type LegalDocument, type LinkKind, type LinkTarget, LinkValue, MAX_SLUG_LENGTH, NumberField, RECORD_ID_KEY, REQUEST_CONSENT_KEY, REQUEST_FAILED_EVENT, REQUEST_FORM_KEY, REQUEST_ISSUE_TEXTS, REQUEST_PAGE_KEY, REQUEST_SEND_EVENT, REQUEST_SENT_EVENT, REQUEST_TRAP_KEY, REVEALED_ATTRIBUTE, REVEAL_ATTRIBUTE, REVEAL_EVENT, type Rate, RecordFields, RecordOf, type RequestEntries, type RequestFailedDetail, type RequestFailure, RequestForm, type RequestIssue, type RequestIssueCode, type RequestReading, type RequestSendDetail, type RequestSentDetail, type RequestValue, SITE_CONFIG_FILE, type SiteConfig, type SiteConfigField, type SiteConfigReading, TextField, collectionAt, collectionFieldAt, editable, estimateOf, findRecord, hrefOf, imagePosition, isConsentGiven, isContentObject, isEmailAddress, isJsonObject, isPhoneNumber, isRecordId, isSiteId, isSlug, isTrapFilled, legalPagePathOf, linkTargetOf, newRecord, ratesField, readRequestEntries, readRequestValues, readSiteConfig, requestPageOf, requestsPathOf, safeHref, slugFieldOf, slugOf, withRecordIds };
276
+ export { CONSENT_REQUIRED_TEXT, CollectionField, type CollectionRecords, Content, ContentOf, type ContentPath, FIELD_PATH_ATTRIBUTE, type FieldMarker, Fields, GroupField, ImageValue, type JsonObject, type LinkKind, type LinkTarget, LinkValue, MAX_SLUG_LENGTH, NumberField, PRIVACY_PAGE_PATH, type PolicyOperator, type PolicySource, RECORD_ID_KEY, REQUEST_CONSENT_KEY, REQUEST_FAILED_EVENT, REQUEST_FORM_KEY, REQUEST_ISSUE_TEXTS, REQUEST_PAGE_KEY, REQUEST_SEND_EVENT, REQUEST_SENT_EVENT, REQUEST_TRAP_KEY, REVEALED_ATTRIBUTE, REVEAL_ATTRIBUTE, REVEAL_EVENT, type Rate, RecordFields, RecordOf, type RequestEntries, type RequestFailedDetail, type RequestFailure, RequestForm, type RequestIssue, type RequestIssueCode, type RequestReading, type RequestSendDetail, type RequestSentDetail, type RequestValue, RichText, RichTextField, SITE_CONFIG_FILE, type SiteConfig, type SiteConfigField, type SiteConfigReading, TextField, collectionAt, collectionFieldAt, editable, estimateOf, findRecord, hrefOf, imagePosition, isConsentGiven, isContentObject, isEmailAddress, isJsonObject, isPhoneNumber, isRecordId, isSiteId, isSlug, isTrapFilled, linkTargetOf, newRecord, privacyPolicyFields, privacyPolicyTextOf, ratesField, readRequestEntries, readRequestValues, readSiteConfig, requestPageOf, requestsPathOf, safeHref, slugFieldOf, slugOf, withRecordIds };
package/dist/schema.js CHANGED
@@ -8,6 +8,7 @@ import {
8
8
  MAX_REQUEST_FIELD_LENGTH,
9
9
  MAX_SLUG_LENGTH,
10
10
  PAGE_PATH_SEPARATOR,
11
+ PRIVACY_PAGE_PATH,
11
12
  RECORD_ID_KEY,
12
13
  RECORD_PLACEHOLDER,
13
14
  REQUEST_CONSENT_KEY,
@@ -59,7 +60,6 @@ import {
59
60
  isSiteId,
60
61
  isSlug,
61
62
  isTrapFilled,
62
- legalPagePathOf,
63
63
  linesOf,
64
64
  link,
65
65
  linkTargetOf,
@@ -70,6 +70,8 @@ import {
70
70
  openingHoursField,
71
71
  parseContent,
72
72
  plainTextOf,
73
+ privacyPolicyFields,
74
+ privacyPolicyTextOf,
73
75
  productFields,
74
76
  ratesField,
75
77
  readFields,
@@ -107,7 +109,7 @@ import {
107
109
  toggle,
108
110
  url,
109
111
  withRecordIds
110
- } from "./chunk-53O34DN3.js";
112
+ } from "./chunk-PAGIPRTS.js";
111
113
  export {
112
114
  CONSENT_REQUIRED_TEXT,
113
115
  DEFAULT_LONG_TEXT_LENGTH,
@@ -118,6 +120,7 @@ export {
118
120
  MAX_REQUEST_FIELD_LENGTH,
119
121
  MAX_SLUG_LENGTH,
120
122
  PAGE_PATH_SEPARATOR,
123
+ PRIVACY_PAGE_PATH,
121
124
  RECORD_ID_KEY,
122
125
  RECORD_PLACEHOLDER,
123
126
  REQUEST_CONSENT_KEY,
@@ -169,7 +172,6 @@ export {
169
172
  isSiteId,
170
173
  isSlug,
171
174
  isTrapFilled,
172
- legalPagePathOf,
173
175
  linesOf,
174
176
  link,
175
177
  linkTargetOf,
@@ -180,6 +182,8 @@ export {
180
182
  openingHoursField,
181
183
  parseContent,
182
184
  plainTextOf,
185
+ privacyPolicyFields,
186
+ privacyPolicyTextOf,
183
187
  productFields,
184
188
  ratesField,
185
189
  readFields,
package/dist/site.js CHANGED
@@ -3,12 +3,12 @@ import {
3
3
  previewedPageAt,
4
4
  renderedPageAt,
5
5
  staticPagesOf
6
- } from "./chunk-62XHZAPK.js";
6
+ } from "./chunk-CTS7Q45A.js";
7
7
  import {
8
8
  checkSiteDescription,
9
9
  formatFieldPath,
10
10
  parseContent
11
- } from "./chunk-53O34DN3.js";
11
+ } from "./chunk-PAGIPRTS.js";
12
12
  import {
13
13
  SITE_MODULE_FILE
14
14
  } from "./chunk-QCVWHHJC.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seamlet/astro",
3
- "version": "1.5.0",
3
+ "version": "2.0.0",
4
4
  "description": "Connects an Astro site to the Seamlet CMS: the schema of its content, the preview the editor frames, and its published content.",
5
5
  "license": "MIT",
6
6
  "type": "module",