@seamlet/astro 0.12.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -76,6 +76,15 @@ interface TimeField {
76
76
  readonly kind: "time";
77
77
  readonly label: string;
78
78
  }
79
+ /**
80
+ * Where a record's page is on the site: `latte` puts it at `/menu/latte` (see recordPages). Lower-case
81
+ * Latin letters, digits and hyphens, one of a kind in its collection; left empty, the page is at the
82
+ * record's id. A field of a record of a collection, one per record.
83
+ */
84
+ interface SlugField {
85
+ readonly kind: "slug";
86
+ readonly label: string;
87
+ }
79
88
  /**
80
89
  * A record of a collection of the schema, by its id: the teacher of a practice. `collection` is
81
90
  * where that collection is, its name or a path through groups: "teachers", "studio.teachers".
@@ -108,7 +117,7 @@ interface CollectionField<F extends RecordFields = RecordFields> {
108
117
  /** The field of a record that names it in the editor: a text field, or a link by its text. */
109
118
  readonly itemTitle?: keyof F & string;
110
119
  }
111
- type Field = TextField | RichTextField | NumberField | DateField | ImageField | LinkField | ToggleField | ChoiceField | ChoicesField | UrlField | TimeField | ReferenceField | ReferencesField | GroupField | CollectionField;
120
+ type Field = TextField | RichTextField | NumberField | DateField | ImageField | LinkField | ToggleField | ChoiceField | ChoicesField | UrlField | TimeField | SlugField | ReferenceField | ReferencesField | GroupField | CollectionField;
112
121
  type FieldKind = Field["kind"];
113
122
  /** Field names to fields; the order of the keys is the order of the form. */
114
123
  interface Fields {
@@ -133,6 +142,7 @@ declare function choice<const O extends readonly ChoiceOption[]>(options: FieldO
133
142
  declare function choices<const O extends readonly ChoiceOption[]>(options: FieldOptions<ChoicesField<O>>): ChoicesField<O>;
134
143
  declare function url(options: FieldOptions<UrlField>): UrlField;
135
144
  declare function time(options: FieldOptions<TimeField>): TimeField;
145
+ declare function slug(options: FieldOptions<SlugField>): SlugField;
136
146
  declare function reference(options: FieldOptions<ReferenceField>): ReferenceField;
137
147
  declare function references(options: FieldOptions<ReferencesField>): ReferencesField;
138
148
  declare function group<F extends Fields>(options: FieldOptions<GroupField<F>>): GroupField<F>;
@@ -191,7 +201,7 @@ interface LinkValue {
191
201
  * The value of each field. A text can be empty (""), the other scalar fields can be unset (null):
192
202
  * content is filled in by people, so a site must be ready for a missing price or date.
193
203
  */
194
- 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 ReferenceField ? string | null : F extends ReferencesField ? readonly string[] : F extends GroupField<infer Group> ? ContentOf<Group> : F extends CollectionField<infer Item> ? readonly RecordOf<Item>[] : never;
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;
195
205
  /**
196
206
  * A record of a collection: its fields and its id. The id is missing only in content no editor has
197
207
  * saved yet, such as a site's initial content (see withRecordIds).
@@ -256,6 +266,16 @@ type ContentIssue = {
256
266
  readonly path: FieldPath;
257
267
  readonly code: "missing-record";
258
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";
259
279
  };
260
280
  interface ParsedContent<C> {
261
281
  readonly content: C;
@@ -274,9 +294,10 @@ declare function countCharacters(text: string): number;
274
294
  * - a value of the right type that breaks a rule of its field (too long, below the minimum) is
275
295
  * kept, with an issue: nothing a person typed is lost;
276
296
  * - keys the schema does not know are dropped; a record keeps its id (see withRecordIds);
277
- * - a reference to a record its collection does not have (removed since) is kept, with an issue.
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.
278
299
  * Content with issues is valid to show and edit, but not to save.
279
300
  */
280
301
  declare function parseContent<F extends Fields>(schema: F, input: unknown): ParsedContent<ContentOf<F>>;
281
302
 
282
- export { plainTextOf as $, collection as A, countCharacters as B, type ContentOf 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, image as S, type TextField as T, type UrlField as U, type ValueOf as V, linesOf as W, link as X, normalizedLine as Y, number as Z, parseContent as _, type Content as a, readRichText as a0, reference as a1, references as a2, richText as a3, richTextOf as a4, text as a5, time as a6, toggle as a7, url as a8, type FieldPath as b, type ContentIssue as c, type ImageValue as d, type Field as e, type CollectionField as f, type RecordFields as g, type LinkField as h, type ChoicesField as i, type TimeField as j, type ChoiceField as k, type ChoiceOption as l, type FieldKind as m, type FieldValue as n, type ImageFocus as o, type RecordOf as p, type ReferenceField as q, type ReferencesField as r, type RichBlock as s, type RichLine as t, type RichSpan as u, type RichTextField as v, type ToggleField as w, asOneParagraph as x, choice as y, choices as z };
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 ChoiceField as l, type ChoiceOption as m, type FieldKind as n, type FieldValue as o, type ImageFocus as p, type ReferenceField as q, type ReferencesField as r, type RichBlock as s, type RichLine as t, type RichSpan as u, type RichTextField as v, type ToggleField as w, asOneParagraph as x, choice as y, choices as z };
@@ -1,3 +1,6 @@
1
+ import { C as Content } from './parse-content-IOJxgKLB.js';
2
+ import { S as SiteDescription } from './site-description-BuEpga8p.js';
3
+
1
4
  /** Far more than the content of a site ever takes; a bigger body is not a draft. */
2
5
  declare const MAX_DRAFT_BYTES: number;
3
6
  /**
@@ -17,7 +20,18 @@ type DraftReading = {
17
20
  * page's own bridge renders a draft.
18
21
  */
19
22
  declare function readDraft(request: Request): Promise<DraftReading>;
23
+ /**
24
+ * Whether the address may be a page of the site: one of its pages, or under its pages of records.
25
+ * The preview answers anything else at once, without asking the CMS or reading a draft.
26
+ */
27
+ declare function isPageAddress(description: SiteDescription, rest: string | undefined): boolean;
28
+ /**
29
+ * The published content, with a blank record when the address is the page of a record it does not
30
+ * have: one the editor added since the publication, at its id. The editor renders its draft on the
31
+ * page at once, so the blank record only brings the page with its bridge to the editor.
32
+ */
33
+ declare function previewContentAt(description: SiteDescription, content: Content, rest: string | undefined): Content;
20
34
  /** Whether a name can be a photo uploaded to the CMS: the hash of its bytes, as WebP. */
21
35
  declare function isUploadedPhoto(fileName: string | undefined): fileName is string;
22
36
 
23
- export { type DraftReading, MAX_DRAFT_BYTES, isUploadedPhoto, previewHeadersOf, readDraft };
37
+ export { type DraftReading, MAX_DRAFT_BYTES, isPageAddress, isUploadedPhoto, previewContentAt, previewHeadersOf, readDraft };
@@ -1,12 +1,18 @@
1
1
  import {
2
2
  MAX_DRAFT_BYTES,
3
+ isPageAddress,
3
4
  isUploadedPhoto,
5
+ previewContentAt,
4
6
  previewHeadersOf,
5
7
  readDraft
6
- } from "./chunk-QL4K4HR6.js";
8
+ } from "./chunk-QXPXIMIM.js";
9
+ import "./chunk-G6Z44LGA.js";
10
+ import "./chunk-I3HOJE3S.js";
7
11
  export {
8
12
  MAX_DRAFT_BYTES,
13
+ isPageAddress,
9
14
  isUploadedPhoto,
15
+ previewContentAt,
10
16
  previewHeadersOf,
11
17
  readDraft
12
18
  };
@@ -1,6 +1,6 @@
1
- import { S as Shortcut, T as TextFormats } from './messages-CvZhLRSE.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-CvZhLRSE.js';
3
- import { F as Fields, b as FieldPath, I as ImageField, R as RichText } from './parse-content-Eg8yRq_t.js';
1
+ import { S as Shortcut, T as TextFormats } from './messages-Bqgs_mic.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-Bqgs_mic.js';
3
+ import { F as Fields, a as FieldPath, I as ImageField, R as RichText } from './parse-content-IOJxgKLB.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-SSECNMAD.js";
26
- import "./chunk-ZS32CW5F.js";
25
+ } from "./chunk-PLNPM2D3.js";
27
26
  import "./chunk-QCVWHHJC.js";
27
+ import "./chunk-I3HOJE3S.js";
28
28
  export {
29
29
  OWN_PHOTOS_PATH,
30
30
  PROTOCOL_VERSION,
@@ -3,7 +3,9 @@ import {
3
3
  } from "../chunk-QCVWHHJC.js";
4
4
  import {
5
5
  isUploadedPhoto
6
- } from "../chunk-QL4K4HR6.js";
6
+ } from "../chunk-QXPXIMIM.js";
7
+ import "../chunk-G6Z44LGA.js";
8
+ import "../chunk-I3HOJE3S.js";
7
9
 
8
10
  // src/routes/media.ts
9
11
  import { cmsOrigin } from "virtual:seamlet/config";
package/dist/schema.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { d as ImageValue, L as LinkValue, b as FieldPath, e as Field, f as CollectionField, F as Fields, a as Content, g as RecordFields, C as ContentOf, G as GroupField, h as LinkField, T as TextField, U as UrlField, i as ChoicesField, j as TimeField, I as ImageField } from './parse-content-Eg8yRq_t.js';
2
- export { k as ChoiceField, l as ChoiceOption, c as ContentIssue, D as DateField, m as FieldKind, n as FieldValue, o as ImageFocus, N as NumberField, O as OptionValue, P as ParsedContent, p as RecordOf, q as ReferenceField, r as ReferencesField, s as RichBlock, t as RichLine, u as RichSpan, R as RichText, v as RichTextField, w as ToggleField, V as ValueOf, x as asOneParagraph, y as choice, z as choices, A as collection, B as countCharacters, E as date, H as defaultContent, J as defaultValue, K as defineSchema, M as formatFieldPath, Q as group, S as image, W as linesOf, X as link, Y as normalizedLine, Z as number, _ as parseContent, $ as plainTextOf, a0 as readRichText, a1 as reference, a2 as references, a3 as richText, a4 as richTextOf, a5 as text, a6 as time, a7 as toggle, a8 as url } from './parse-content-Eg8yRq_t.js';
3
- export { F as FieldsReading, H as HOME_PAGE_PATH, P as PageProblemReason, a as SITE_DESCRIPTION_PATH, b as SchemaProblem, c as SchemaProblemReason, S as SiteDescription, d as SiteDescriptionProblem, e as SiteDescriptionReading, f as SitePageDescription, g as checkFields, h as checkSiteDescription, i as isSearchTextsGroup, r as readFields, j as readSiteDescription } from './site-description-Cg5KNhhG.js';
1
+ import { h as ImageValue, L as LinkValue, d as CollectionField, C as Content, F as Fields, e as RecordFields, b as ContentOf, G as GroupField, i as LinkField, T as TextField, U as UrlField, j as ChoicesField, k as TimeField, I as ImageField } from './parse-content-IOJxgKLB.js';
2
+ export { l as ChoiceField, m as ChoiceOption, g as ContentIssue, D as DateField, c as Field, n as FieldKind, a as FieldPath, o as FieldValue, p as ImageFocus, N as NumberField, O as OptionValue, P as ParsedContent, f as RecordOf, q as ReferenceField, r as ReferencesField, s as RichBlock, t as RichLine, u as RichSpan, R as RichText, v as RichTextField, S as SlugField, w as ToggleField, V as ValueOf, x as asOneParagraph, y as choice, z as choices, A as collection, B as countCharacters, E as date, H as defaultContent, J as defaultValue, K as defineSchema, M as formatFieldPath, Q as group, W as image, X as linesOf, Y as link, Z as normalizedLine, _ as number, $ as parseContent, a0 as plainTextOf, a1 as readRichText, a2 as reference, a3 as references, a4 as richText, a5 as richTextOf, a6 as slug, a7 as text, a8 as time, a9 as toggle, aa as url } from './parse-content-IOJxgKLB.js';
3
+ export { C as CollectionPath, F as FIELD_PATH_SEPARATOR, b as FieldTarget, c as FieldsReading, H as HOME_PAGE_PATH, P as PAGE_PATH_SEPARATOR, d as PageProblemReason, e as PlacedRecord, R as RECORD_PLACEHOLDER, f as RecordFieldsAt, g as RecordPagePath, h as RecordPageProblemReason, i as RecordPages, j as SITE_DESCRIPTION_PATH, k as SchemaProblem, l as SchemaProblemReason, S as SiteDescription, m as SiteDescriptionProblem, n as SiteDescriptionReading, o as SitePageDescription, a as SiteRecordPageDescription, p as checkFields, q as checkSiteDescription, r as isRecordPagePath, s as isSearchTextsGroup, t as readFields, u as readSiteDescription, v as recordAddressOf, w as recordAt, x as recordPageAt, y as recordPagePath, z as recordPages, A as resolveFieldPath } from './site-description-BuEpga8p.js';
4
4
 
5
5
  declare const FIELD_PATH_ATTRIBUTE = "data-cms-path";
6
6
  interface FieldMarker {
@@ -21,24 +21,6 @@ type ContentPath<C> = C extends Leaf ? never : C extends readonly (infer Record)
21
21
  */
22
22
  declare function editable<C>(): (...path: ContentPath<C>) => FieldMarker;
23
23
 
24
- /** What a path written on a page points at: a field, or a whole record of a collection. */
25
- type FieldTarget = {
26
- readonly kind: "field";
27
- readonly path: FieldPath;
28
- readonly field: Field;
29
- } | {
30
- readonly kind: "record";
31
- readonly path: FieldPath;
32
- readonly collection: CollectionField;
33
- /** The place of the record in its collection, the last segment of its path. */
34
- readonly index: number;
35
- };
36
- /**
37
- * Reads a path from a field marker (`menu.2.price`) by the schema: names inside fields and
38
- * groups, indexes inside collections. A path the schema does not have gives undefined.
39
- */
40
- declare function resolveFieldPath(schema: Fields, text: string): FieldTarget | undefined;
41
-
42
24
  declare const RECORD_ID_KEY = "id";
43
25
  declare function isRecordId(value: unknown): value is string;
44
26
  /** A new record of a collection: its fields empty, its id the given one. */
@@ -57,6 +39,8 @@ interface CollectionRecords {
57
39
  * its records in the content; undefined when the schema has no collection there.
58
40
  */
59
41
  declare function collectionAt(schema: Fields, content: Content, path: string): CollectionRecords | undefined;
42
+ /** The collection at a path of the schema through groups (`"studio.teachers"`), if it has one there. */
43
+ declare function collectionFieldAt(schema: Fields, path: string): CollectionField | undefined;
60
44
  /** The record of the list with the id, if it is there. */
61
45
  declare function findRecord<R extends {
62
46
  readonly id?: string;
@@ -179,6 +163,11 @@ declare function openingHoursField(options?: {
179
163
  /** An object of named values: not null, not a list. */
180
164
  type JsonObject = Readonly<Record<string, unknown>>;
181
165
  declare function isJsonObject(value: unknown): value is JsonObject;
166
+ /**
167
+ * Whether the value can be the content of a group or a record: an object of named values. What
168
+ * the values are is checked by the fields (see parseContent).
169
+ */
170
+ declare function isContentObject(value: unknown): value is Content;
182
171
 
183
172
  /** The file at the root of a site's repository. */
184
173
  declare const SITE_CONFIG_FILE = "seamlet.json";
@@ -203,4 +192,17 @@ declare function isSiteId(value: unknown): value is string;
203
192
  /** The config the input gives (the parsed `seamlet.json`); fields it does not know are left out. */
204
193
  declare function readSiteConfig(input: unknown): SiteConfigReading;
205
194
 
206
- export { ChoicesField, CollectionField, type CollectionRecords, Content, ContentOf, type ContentPath, FIELD_PATH_ATTRIBUTE, Field, type FieldMarker, FieldPath, type FieldTarget, Fields, GroupField, ImageField, ImageValue, type JsonObject, LinkField, type LinkKind, type LinkTarget, LinkValue, RECORD_ID_KEY, REVEALED_ATTRIBUTE, REVEAL_ATTRIBUTE, REVEAL_EVENT, RecordFields, SITE_CONFIG_FILE, type SiteConfig, type SiteConfigField, type SiteConfigReading, TextField, TimeField, UrlField, WEEKDAYS, collectionAt, contactsFields, editable, findRecord, hrefOf, imagePosition, isJsonObject, isRecordId, isSiteId, linkTargetOf, newRecord, openingHoursField, readSiteConfig, resolveFieldPath, safeHref, seoFields, withRecordIds };
195
+ /** Long enough for a name of a product, short enough to read in an address. */
196
+ declare const MAX_SLUG_LENGTH = 80;
197
+ /** Whether the value can be the address of a record's page: `latte`, `banya-6x4`. */
198
+ declare function isSlug(value: string): boolean;
199
+ /**
200
+ * The address a record's page takes from words: «Баня из бруса 6×4» is `banya-iz-brusa-6x4`. Empty
201
+ * when the words have no letter or digit to keep.
202
+ */
203
+ declare function slugOf(words: string): string;
204
+
205
+ /** The name of the slug of a record of the collection, if its records have one. */
206
+ declare function slugFieldOf(item: RecordFields): string | undefined;
207
+
208
+ export { ChoicesField, CollectionField, type CollectionRecords, Content, ContentOf, type ContentPath, FIELD_PATH_ATTRIBUTE, type FieldMarker, Fields, GroupField, ImageField, ImageValue, type JsonObject, LinkField, type LinkKind, type LinkTarget, LinkValue, MAX_SLUG_LENGTH, RECORD_ID_KEY, REVEALED_ATTRIBUTE, REVEAL_ATTRIBUTE, REVEAL_EVENT, RecordFields, SITE_CONFIG_FILE, type SiteConfig, type SiteConfigField, type SiteConfigReading, TextField, TimeField, UrlField, WEEKDAYS, collectionAt, collectionFieldAt, contactsFields, editable, findRecord, hrefOf, imagePosition, isContentObject, isJsonObject, isRecordId, isSiteId, isSlug, linkTargetOf, newRecord, openingHoursField, readSiteConfig, safeHref, seoFields, slugFieldOf, slugOf, withRecordIds };
package/dist/schema.js CHANGED
@@ -1,7 +1,11 @@
1
1
  import {
2
2
  FIELD_PATH_ATTRIBUTE,
3
+ FIELD_PATH_SEPARATOR,
3
4
  HOME_PAGE_PATH,
5
+ MAX_SLUG_LENGTH,
6
+ PAGE_PATH_SEPARATOR,
4
7
  RECORD_ID_KEY,
8
+ RECORD_PLACEHOLDER,
5
9
  REVEALED_ATTRIBUTE,
6
10
  REVEAL_ATTRIBUTE,
7
11
  REVEAL_EVENT,
@@ -15,6 +19,7 @@ import {
15
19
  choices,
16
20
  collection,
17
21
  collectionAt,
22
+ collectionFieldAt,
18
23
  contactsFields,
19
24
  countCharacters,
20
25
  date,
@@ -28,10 +33,13 @@ import {
28
33
  hrefOf,
29
34
  image,
30
35
  imagePosition,
36
+ isContentObject,
31
37
  isJsonObject,
32
38
  isRecordId,
39
+ isRecordPagePath,
33
40
  isSearchTextsGroup,
34
41
  isSiteId,
42
+ isSlug,
35
43
  linesOf,
36
44
  link,
37
45
  linkTargetOf,
@@ -45,6 +53,11 @@ import {
45
53
  readRichText,
46
54
  readSiteConfig,
47
55
  readSiteDescription,
56
+ recordAddressOf,
57
+ recordAt,
58
+ recordPageAt,
59
+ recordPagePath,
60
+ recordPages,
48
61
  reference,
49
62
  references,
50
63
  resolveFieldPath,
@@ -52,16 +65,23 @@ import {
52
65
  richTextOf,
53
66
  safeHref,
54
67
  seoFields,
68
+ slug,
69
+ slugFieldOf,
70
+ slugOf,
55
71
  text,
56
72
  time,
57
73
  toggle,
58
74
  url,
59
75
  withRecordIds
60
- } from "./chunk-ZS32CW5F.js";
76
+ } from "./chunk-I3HOJE3S.js";
61
77
  export {
62
78
  FIELD_PATH_ATTRIBUTE,
79
+ FIELD_PATH_SEPARATOR,
63
80
  HOME_PAGE_PATH,
81
+ MAX_SLUG_LENGTH,
82
+ PAGE_PATH_SEPARATOR,
64
83
  RECORD_ID_KEY,
84
+ RECORD_PLACEHOLDER,
65
85
  REVEALED_ATTRIBUTE,
66
86
  REVEAL_ATTRIBUTE,
67
87
  REVEAL_EVENT,
@@ -75,6 +95,7 @@ export {
75
95
  choices,
76
96
  collection,
77
97
  collectionAt,
98
+ collectionFieldAt,
78
99
  contactsFields,
79
100
  countCharacters,
80
101
  date,
@@ -88,10 +109,13 @@ export {
88
109
  hrefOf,
89
110
  image,
90
111
  imagePosition,
112
+ isContentObject,
91
113
  isJsonObject,
92
114
  isRecordId,
115
+ isRecordPagePath,
93
116
  isSearchTextsGroup,
94
117
  isSiteId,
118
+ isSlug,
95
119
  linesOf,
96
120
  link,
97
121
  linkTargetOf,
@@ -105,6 +129,11 @@ export {
105
129
  readRichText,
106
130
  readSiteConfig,
107
131
  readSiteDescription,
132
+ recordAddressOf,
133
+ recordAt,
134
+ recordPageAt,
135
+ recordPagePath,
136
+ recordPages,
108
137
  reference,
109
138
  references,
110
139
  resolveFieldPath,
@@ -112,6 +141,9 @@ export {
112
141
  richTextOf,
113
142
  safeHref,
114
143
  seoFields,
144
+ slug,
145
+ slugFieldOf,
146
+ slugOf,
115
147
  text,
116
148
  time,
117
149
  toggle,
@@ -0,0 +1,245 @@
1
+ import { a as FieldPath, c as Field, d as CollectionField, F as Fields, G as GroupField, C as Content, e as RecordFields, f as RecordOf, g as ContentIssue } from './parse-content-IOJxgKLB.js';
2
+
3
+ /** What a path written on a page points at: a field, or a whole record of a collection. */
4
+ type FieldTarget = {
5
+ readonly kind: "field";
6
+ readonly path: FieldPath;
7
+ readonly field: Field;
8
+ } | {
9
+ readonly kind: "record";
10
+ readonly path: FieldPath;
11
+ readonly collection: CollectionField;
12
+ /** The place of the record in its collection, the last segment of its path. */
13
+ readonly index: number;
14
+ };
15
+ /**
16
+ * What separates the names of a path written as text: a field marker (`menu.2.price`) and the
17
+ * collection a reference names (`studio.teachers`). A field's name never holds it.
18
+ */
19
+ declare const FIELD_PATH_SEPARATOR = ".";
20
+ /**
21
+ * Reads a path from a field marker (`menu.2.price`) by the schema: names inside fields and
22
+ * groups, indexes inside collections. A path the schema does not have gives undefined.
23
+ */
24
+ declare function resolveFieldPath(schema: Fields, text: string): FieldTarget | undefined;
25
+
26
+ /** Why a schema cannot be read. */
27
+ type SchemaProblemReason =
28
+ /** Not an object of fields, or a field that is not an object. */
29
+ "not-fields"
30
+ /** A name that is empty, holds the separator of paths (`.`) or is a number, which a path reads as a record. */
31
+ | "invalid-name" | "invalid-label" | "unknown-kind"
32
+ /** An option of the field (`maxLength`, `inline`…) is there but not of its kind. */
33
+ | "invalid-option" | "invalid-choice-options"
34
+ /** A record has a field named `id`, which is the record's own. */
35
+ | "record-id-field"
36
+ /** `itemTitle` names no text or link field of the record. */
37
+ | "invalid-item-title"
38
+ /** A reference names a collection the schema does not have. */
39
+ | "unknown-collection"
40
+ /** A slug that is not the one slug of a record of a collection: only a record's page has one. */
41
+ | "misplaced-slug";
42
+ interface SchemaProblem {
43
+ /** The names of the fields down to the field with the problem; empty for the schema itself. */
44
+ readonly path: readonly string[];
45
+ readonly reason: SchemaProblemReason;
46
+ }
47
+ type FieldsReading = {
48
+ readonly status: "read";
49
+ readonly fields: Fields;
50
+ } | {
51
+ readonly status: "invalid";
52
+ readonly problem: SchemaProblem;
53
+ };
54
+ /** The schema the input describes, or undefined when it is not one. */
55
+ declare function readFields(input: unknown): Fields | undefined;
56
+ /** The schema the input describes, or the first problem that keeps it from being one. */
57
+ declare function checkFields(input: unknown): FieldsReading;
58
+
59
+ /** What stands for the record in the address of its page: `/menu/[record]`. */
60
+ declare const RECORD_PLACEHOLDER = "[record]";
61
+ /** The pages of the records of a collection, as a site describes them to the CMS. */
62
+ interface SiteRecordPageDescription {
63
+ /** The address of a record's page, its slug or id in place of `[record]`: `/menu/[record]`. */
64
+ readonly path: string;
65
+ /** The collection whose every record has a page: `menu`, or through groups, `catalog.products`. */
66
+ readonly collection: string;
67
+ /**
68
+ * Where a record holds what search engines show for its page: a group of the record with a
69
+ * `title` and a `description` text (`seo`).
70
+ */
71
+ readonly searchTexts?: string;
72
+ }
73
+ /** A record of a collection and its place there. */
74
+ interface PlacedRecord {
75
+ readonly record: Content;
76
+ readonly index: number;
77
+ }
78
+ /** The pages of the records of a collection, with the address of each record's page. */
79
+ interface RecordPages<R> extends SiteRecordPageDescription {
80
+ /** The address of the record's page: `/menu/latte`; none for a record with neither slug nor id. */
81
+ readonly pathOf: (record: R) => string | undefined;
82
+ }
83
+ /** The address of a record's page with `[record]` in place of the record. */
84
+ type RecordPagePath = `/${string}/${typeof RECORD_PLACEHOLDER}`;
85
+ /** The paths of the collections of a schema, through its groups: `menu`, `catalog.products`. */
86
+ type CollectionPath<F extends Fields> = {
87
+ [Name in keyof F & string]: F[Name] extends {
88
+ readonly kind: "collection";
89
+ } ? Name : F[Name] extends GroupField<infer Inner> ? `${Name}${typeof FIELD_PATH_SEPARATOR}${CollectionPath<Inner>}` : never;
90
+ }[keyof F & string];
91
+ /** The fields of a record of the collection at the path of the schema. */
92
+ type RecordFieldsAt<F extends Fields, P extends string> = P extends `${infer Name}${typeof FIELD_PATH_SEPARATOR}${infer Rest}` ? F[Name] extends GroupField<infer Inner> ? RecordFieldsAt<Inner, Rest> : never : F[P] extends CollectionField<infer Item extends RecordFields> ? Item : never;
93
+ /** What separates the segments of a page's address: `/menu/latte`. */
94
+ declare const PAGE_PATH_SEPARATOR = "/";
95
+ /**
96
+ * A page of every record of a collection of the schema at the address with `[record]`:
97
+ * `recordPages(schema, { collection: "menu", path: "/menu/[record]" })`. A site gives it to
98
+ * `defineSeamletSite` with the component that renders a record, and links to a record's page with
99
+ * its `pathOf`.
100
+ */
101
+ declare function recordPages<F extends Fields, const P extends CollectionPath<F>>(schema: F, options: {
102
+ readonly collection: P;
103
+ readonly path: RecordPagePath;
104
+ readonly searchTexts?: string;
105
+ }): RecordPages<RecordOf<RecordFieldsAt<F, P>>>;
106
+ /** Whether the path can be the address of the pages of records: `/menu/[record]`. */
107
+ declare function isRecordPagePath(path: unknown): path is string;
108
+ /** The address of a record's page: its slug, or its id while it has none. */
109
+ declare function recordAddressOf<F extends RecordFields>(collection: CollectionField<F>, record: Content): string | undefined;
110
+ /** The address of a record's page on the site: `/menu/latte` for `/menu/[record]` and `latte`. */
111
+ declare function recordPagePath(pagePath: string, address: string): string;
112
+ /**
113
+ * The pages of records the path is the address of, with the record's part of it: `/menu/latte` is
114
+ * `latte` of `/menu/[record]`.
115
+ */
116
+ declare function recordPageAt<P extends SiteRecordPageDescription>(pages: readonly P[], path: string): {
117
+ readonly page: P;
118
+ readonly address: string;
119
+ } | undefined;
120
+ /**
121
+ * The record at the address of its page: the record whose page it is, or the record with that id,
122
+ * as the editor opens a page, since a slug may change while the page is open.
123
+ */
124
+ declare function recordAt<F extends RecordFields>(collection: CollectionField<F>, records: readonly Content[], address: string): PlacedRecord | undefined;
125
+
126
+ /** Where a site hands out its description, on the origin of its preview. */
127
+ declare const SITE_DESCRIPTION_PATH = "/seamlet/site.json";
128
+ /** A page of a site, which the site renders itself. */
129
+ interface SitePageDescription {
130
+ /** The address of the page on the site: `/` for the first page, `/menu` for others. */
131
+ readonly path: string;
132
+ /** How the editor names the page in its list of pages. */
133
+ readonly title: string;
134
+ /**
135
+ * Where the content holds what search engines and link previews show for the page
136
+ * (`seo.menu`): a group of a `title` and a `description` text.
137
+ */
138
+ readonly searchTexts?: string;
139
+ }
140
+ interface SiteDescription {
141
+ /** The address of the site in the CMS (`/sites/<id>`). */
142
+ readonly id: string;
143
+ /** How the CMS names the site. */
144
+ readonly title: string;
145
+ readonly schema: Fields;
146
+ /** The content shown and edited until the first save. */
147
+ readonly initialContent: Content;
148
+ /** The pages of the site; the first is the home page at `/`. */
149
+ readonly pages: readonly [SitePageDescription, ...SitePageDescription[]];
150
+ /** The pages of the records of collections; left out when the site has none. */
151
+ readonly recordPages?: readonly SiteRecordPageDescription[];
152
+ }
153
+ /** Why a site's description cannot be read: what to fix in the site. */
154
+ type SiteDescriptionProblem = {
155
+ readonly code: "not-a-description";
156
+ } | {
157
+ readonly code: "invalid-id";
158
+ } | {
159
+ readonly code: "invalid-title";
160
+ } | {
161
+ readonly code: "invalid-schema";
162
+ readonly problem: SchemaProblem;
163
+ }
164
+ /** The initial content is not content the CMS could save. */
165
+ | {
166
+ readonly code: "invalid-initial-content";
167
+ readonly issues: readonly ContentIssue[];
168
+ } | {
169
+ readonly code: "no-pages";
170
+ }
171
+ /** The page at this place of the list is not a page, or its path or title is not one. */
172
+ | {
173
+ readonly code: "invalid-page";
174
+ readonly index: number;
175
+ readonly reason: PageProblemReason;
176
+ } | {
177
+ readonly code: "home-page-not-first";
178
+ } | {
179
+ readonly code: "repeated-page-path";
180
+ readonly path: string;
181
+ }
182
+ /** The pages of records are not a list. */
183
+ | {
184
+ readonly code: "invalid-record-pages";
185
+ }
186
+ /** The pages of records at this place of their list are not such pages, or not of this site. */
187
+ | {
188
+ readonly code: "invalid-record-page";
189
+ readonly index: number;
190
+ readonly reason: RecordPageProblemReason;
191
+ }
192
+ /** Two kinds of pages for the records of one collection: a record has one page. */
193
+ | {
194
+ readonly code: "repeated-record-pages";
195
+ readonly collection: string;
196
+ }
197
+ /** A page of the site where the pages of records are: `/menu/specials` beside `/menu/[record]`. */
198
+ | {
199
+ readonly code: "page-among-record-pages";
200
+ readonly path: string;
201
+ }
202
+ /** A record of the initial content whose page has no address: neither a slug nor an id. */
203
+ | {
204
+ readonly code: "record-without-address";
205
+ readonly collection: string;
206
+ readonly index: number;
207
+ }
208
+ /** Two records of the initial content whose pages have one address: the same id, or slug. */
209
+ | {
210
+ readonly code: "repeated-record-address";
211
+ readonly collection: string;
212
+ readonly address: string;
213
+ };
214
+ type PageProblemReason = "not-a-page"
215
+ /** Not `/`, or not lower-case segments without a trailing slash, such as `/menu`. */
216
+ | "invalid-path" | "invalid-title"
217
+ /** Not where the content holds a group of a `title` and a `description` text. */
218
+ | "invalid-search-texts";
219
+ type RecordPageProblemReason = "not-a-page"
220
+ /** Not the address of a page, then `/[record]`, such as `/menu/[record]`. */
221
+ | "invalid-path"
222
+ /** Not a collection of the schema. */
223
+ | "unknown-collection"
224
+ /** Not where a record holds a group of a `title` and a `description` text. */
225
+ | "invalid-search-texts";
226
+ type SiteDescriptionReading = {
227
+ readonly status: "read";
228
+ readonly description: SiteDescription;
229
+ } | {
230
+ readonly status: "invalid";
231
+ readonly problems: readonly SiteDescriptionProblem[];
232
+ };
233
+ /** The first page of every site. */
234
+ declare const HOME_PAGE_PATH = "/";
235
+ /**
236
+ * The description the input gives, or undefined when it is not a whole and sound one: a known
237
+ * schema, an initial content with nothing to fix, the home page first, each page once.
238
+ */
239
+ declare function readSiteDescription(input: unknown): SiteDescription | undefined;
240
+ /** The description the input gives, or every problem that keeps it from being whole and sound. */
241
+ declare function checkSiteDescription(input: unknown): SiteDescriptionReading;
242
+ /** Whether the path names a group of a `title` and a `description` text. */
243
+ declare function isSearchTextsGroup(schema: Fields, path: string): boolean;
244
+
245
+ export { resolveFieldPath as A, type CollectionPath as C, FIELD_PATH_SEPARATOR as F, HOME_PAGE_PATH as H, PAGE_PATH_SEPARATOR as P, RECORD_PLACEHOLDER as R, type SiteDescription as S, type SiteRecordPageDescription as a, type FieldTarget as b, type FieldsReading as c, type PageProblemReason as d, type PlacedRecord as e, type RecordFieldsAt as f, type RecordPagePath as g, type RecordPageProblemReason as h, type RecordPages as i, SITE_DESCRIPTION_PATH as j, type SchemaProblem as k, type SchemaProblemReason as l, type SiteDescriptionProblem as m, type SiteDescriptionReading as n, type SitePageDescription as o, checkFields as p, checkSiteDescription as q, isRecordPagePath as r, isSearchTextsGroup as s, readFields as t, readSiteDescription as u, recordAddressOf as v, recordAt as w, recordPageAt as x, recordPagePath as y, recordPages as z };