@relyio/sdk 0.12.0 → 0.13.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 +84 -0
- package/dist/{chunk-3DRDHXLY.js → chunk-3NPPMGB6.js} +435 -23
- package/dist/{chunk-FRJEUAAF.js → chunk-6NGJXLOR.js} +22 -11
- package/dist/{chunk-DXCVO6M5.cjs → chunk-DEDIJKHY.cjs} +22 -11
- package/dist/{chunk-POEEDYFM.js → chunk-EW23SZPX.js} +7 -2
- package/dist/{chunk-3MSFWXQL.cjs → chunk-FOSM4LMM.cjs} +7 -2
- package/dist/{chunk-FI5VIF4M.cjs → chunk-Z6GLWSSD.cjs} +442 -22
- package/dist/client/index.cjs +3 -3
- package/dist/client/index.d.cts +4 -5
- package/dist/client/index.d.ts +4 -5
- package/dist/client/index.js +1 -1
- package/dist/{define-schema-mrEZbY1h.d.ts → define-schema-UHViXKiv.d.ts} +2 -219
- package/dist/{define-schema-DgctHlI2.d.cts → define-schema-sWJ8Zn7C.d.cts} +2 -219
- package/dist/{include-paths-CL4gHv6I.d.cts → include-paths-Bs5FhNw2.d.cts} +2191 -108
- package/dist/{include-paths-CrJNTsMl.d.ts → include-paths-Cb7iPCW2.d.ts} +2191 -108
- package/dist/index--_u1nPHv.d.cts +507 -0
- package/dist/index-8DozAWsA.d.ts +507 -0
- package/dist/{index-BrvEg6-A.d.cts → index-BT0L8xJj.d.cts} +41 -53
- package/dist/{index-Pw0onkJe.d.ts → index-CPjJ25R4.d.ts} +41 -53
- package/dist/index.cjs +119 -112
- package/dist/index.d.cts +39 -70
- package/dist/index.d.ts +39 -70
- package/dist/index.js +5 -29
- package/dist/markdown/index.cjs +13750 -0
- package/dist/markdown/index.d.cts +111 -0
- package/dist/markdown/index.d.ts +111 -0
- package/dist/markdown/index.js +13742 -0
- package/dist/rich-text/index.cjs +26 -6
- package/dist/rich-text/index.d.cts +3 -148
- package/dist/rich-text/index.d.ts +3 -148
- package/dist/rich-text/index.js +1 -1
- package/dist/rich-text.d-BWc-5gX1.d.cts +326 -0
- package/dist/rich-text.d-BWc-5gX1.d.ts +326 -0
- package/dist/schema/index.cjs +34 -34
- package/dist/schema/index.d.cts +9 -178
- package/dist/schema/index.d.ts +9 -178
- package/dist/schema/index.js +1 -1
- package/dist/types/index.d.cts +2 -3
- package/dist/types/index.d.ts +2 -3
- package/package.json +14 -3
- package/dist/index-B9RfZVF1.d.ts +0 -861
- package/dist/index-b-AkfTUv.d.cts +0 -861
- package/dist/rich-text.d-rKmpgdGd.d.cts +0 -200
- package/dist/rich-text.d-rKmpgdGd.d.ts +0 -200
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,89 @@
|
|
|
1
1
|
# @relyio/sdk
|
|
2
2
|
|
|
3
|
+
## 0.13.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- e678fb8: **`update()` and `restore()` say which entry links they left pointing nowhere.** `update()` returns the entry with `_metadata.brokenLinks`: each link another entry publishes whose anchor names a heading the revision took away, empty when it took none. `restore()` returns `_metadata.brokenLinks` beside `_metadata.leftOut`, both now in its type. A batch revision (`PATCH`, `PUT`) answers the same list under `data._metadata.brokenLinks`, with `returnData` or without, and a request retried with the same `Idempotency-Key` answers the list the first attempt did.
|
|
8
|
+
- c2fe14d: **Filter by a reference inside blocks and rich text.** A typed client's `where()` and `fetchFirst()` accept a reference inside a list, a page builder or an object (`'sections.features'`) and a rich-text field that links to entries or declares a block holding a reference, each compared with an entry id: "pages that feature X". `$eq`/`$in` match an entry where some block holds it, `$ne`/`$nin` one where no block does, and `$isNull` asks whether any block holds a reference at all. Any other value inside a block, and a `json` field, is refused by name.
|
|
9
|
+
- c2fe14d: **Heading anchors are unique per entry.** Headings are numbered across an entry's top-level rich-text fields in the order its type declares them, so `intro`'s "Overview" is `#overview` and `body`'s is `#overview-1`. `entryHeadingAnchors(entry)` computes an entry's anchors once; hand them to `<RichText anchors={…}>`, `renderRichText` or `richTextToHtml` (`anchors` option) and every field writes the ids an entry link's anchor names. A field rendered without `anchors` numbers its own headings, as before. A publish that takes away a heading other entries link to still lands, and the Content API's update answer lists the links it left pointing nowhere in `_metadata.brokenLinks`.
|
|
10
|
+
- 39ea8c6: **A `json` field holds data nobody models.** `fields.json('chart', { config: { schema } })` declares a field whose value is any JSON value — an object, an array, a string, a number, a boolean or `null` — held to an optional JSON Schema on every write, a draft's included: a value the schema refuses is refused (`JSON_INVALID`), never stored. A read is typed by the schema — `type`, `properties`, `required`, `additionalProperties`, `items`, `enum`, `const`, `anyOf` and `oneOf` — and is `JsonValue` without one, never `any`:
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
fields.json('chart', {
|
|
14
|
+
config: {
|
|
15
|
+
schema: {
|
|
16
|
+
type: 'object',
|
|
17
|
+
properties: { points: { type: 'array', items: { type: 'number' } } },
|
|
18
|
+
required: ['points'],
|
|
19
|
+
} as const,
|
|
20
|
+
},
|
|
21
|
+
})
|
|
22
|
+
// a read of `chart`: { points: number[] }
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Write the schema `as const` so `required`, `enum` and `const` keep their literals; `relyio generate-types` writes it so, and its Python target writes the same subset inline. A `json` field takes no `defaultValue`, and is neither searched nor filterable. The schema-modelling skill names the kind and when to reach for it.
|
|
26
|
+
|
|
27
|
+
- c2d8071: **A preview can take an editor from any block to that block.** `relyioField` binds a value inside a field by its key path — `relyioField(page, 'sections[_key=="f2k6ub9s"].heading')` — the spelling a refused write already names a value by, and continues a binding it returned: `relyioField(binding, 'heading')` binds a field below it, `relyioField(binding, item)` an item of the list it binds. The attributes are the same five, version 1. `formatKeyPath` writes a key path and `parseKeyPath` reads one back, refusing an index, which names another item once a list is reordered.
|
|
28
|
+
|
|
29
|
+
`<Blocks>` and `<RichText>` take the field's binding as `field` and stamp each block, and each declared block and inline element, with its key path, on the element its component returns:
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
<Blocks
|
|
33
|
+
value={page.sections ?? []}
|
|
34
|
+
field={relyioField(page, 'sections')}
|
|
35
|
+
components={{
|
|
36
|
+
hero: (block, field) => (
|
|
37
|
+
<header>
|
|
38
|
+
<h1 {...relyioField(field, 'headline')}>{block.headline}</h1>
|
|
39
|
+
<RichText
|
|
40
|
+
value={block.body}
|
|
41
|
+
field={relyioField(field, 'body')}
|
|
42
|
+
components={{ callout: Callout }}
|
|
43
|
+
/>
|
|
44
|
+
</header>
|
|
45
|
+
),
|
|
46
|
+
}}
|
|
47
|
+
/>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Each component is handed its block's binding — a `<Blocks>` component as its second argument, a declared component as `field` — so a block inside a block carries its full path, `sections[_key=="f2k6ub9s"].body[_key=="c1"]`. A component that returns a fragment or an element of a component of its own spreads its binding itself. With no `field`, nothing is stamped and the markup is unchanged. The preview toolbar badges each stamped block and links it to its place in the Studio.
|
|
51
|
+
|
|
52
|
+
- e0b742d: **`richTextToHtml` stamps each declared block with its key path, as `<RichText>` does.** Handed the field's binding as `field`, each declared Block and Inline element's writer is handed that node's binding as its second argument, and the one element it returns carries it — `data-relyio-field="body[_key=="c1"]"` after the element's own attributes — so a site rendering HTML without React still lets the preview toolbar take an editor from a block to the Studio:
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
richTextToHtml(post.body, {
|
|
56
|
+
field: relyioField(post, 'body'),
|
|
57
|
+
declared: {
|
|
58
|
+
callout: (block, field) =>
|
|
59
|
+
`<aside>${richTextToHtml(block.body, { field: relyioField(field, 'body') })}</aside>`,
|
|
60
|
+
},
|
|
61
|
+
})
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
A writer that returns text, several elements or an element it bound itself is written as it returns it; it holds the binding to write where it belongs. With no `field`, nothing is stamped and every writer is handed `undefined`.
|
|
65
|
+
|
|
66
|
+
- 6f0ea50: **Rich text comes in and goes out as RelyIO Markdown: `@relyio/sdk/markdown`.** `richTextFromMarkdown(markdown)` reads a Markdown file into the rich-text tree, every node keyed and ready to write; `richTextToRelyIOMarkdown(value)` writes any rich-text value back as the same Markdown, and importing it again gives the same tree, keys aside. RelyIO Markdown is CommonMark with GitHub's tables, task lists and strikethrough, and directives for what Markdown has no syntax for — declared blocks and inline elements, entry links, code tabs, underline, highlight, sub- and superscript.
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
import { richTextFromMarkdown, richTextToRelyIOMarkdown } from '@relyio/sdk/markdown'
|
|
70
|
+
|
|
71
|
+
const body = richTextFromMarkdown(await readFile('getting-started.md', 'utf8'))
|
|
72
|
+
await relyio.content('docPage').create({ title: 'Getting started', body })
|
|
73
|
+
|
|
74
|
+
richTextToRelyIOMarkdown(page.body) // the same Markdown, ready to edit and import again
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Markdown the tree cannot hold — HTML, an image, a footnote, a link's title — throws `RichTextMarkdownImportError`, whose `unplaced` names each one with its line and column; `readRichTextMarkdown` returns the tree and that list instead of throwing. A `table` or `code` field takes one block: `richTextFieldBlockFromMarkdown(markdown, 'table')` or `'codeGroup'`. Export throws `RichTextMarkdownExportError` for a node it cannot name. The parser has its own entry point, so a page that only renders rich text never loads it; `richTextToMarkdown` on the root, which writes plain Markdown and drops what it cannot hold, is unchanged.
|
|
78
|
+
|
|
79
|
+
- 9873ca5: **A typed `where` takes what the API filters by everywhere it is offered, and an expanded singleton is typed as it is served.** `useEntries` and `relyioQueryOptions().entries()` take the `where` `client.content(type).query().where()` takes, so an object's field, a reference inside a block and `$isNull` compile there, and a `json` field or a whole list or object does not. A typed `asset(type)` client's `where()` and `fetchFirst()` are held to the same paths. `WhereOperator` gains `$inTheLast` and `$inTheNext` (`{ amount, unit }`), which a typed read offers on a `date` or `dateTime` field only, as the API asks them of nothing else. `children(id)` no longer takes `depth`: the route lists a parent's direct children, and refused every request that sent one with `400`; a nested tree is a list or entry read's `children()`. A singleton an include expands carries an entry's `_metadata` (`EntryMetadata`), not `SingletonMetadata`, and is typed so — in Python as `Expanded<Name>`, which a reference to the singleton now names.
|
|
80
|
+
- 6c3d471: **A typed `where()` takes exactly what the API filters by.** `where()`, `fetchFirst()` and `fetch({ where })` name a field inside an object by its path — `{ 'pricing.basePrice': { $gte: 100 } }` — and take `$isNull` on any path. A condition the API refuses no longer compiles: a `json` field or a key inside one; a list, an object, a table or code as a whole; rich text that can point at no entry; and a reference or rich text a shared list translates item by item. `$regex`, which the API never read, is gone from `WhereOperator`. `fields.reference`, `fields.richText`, `fields.list` and `fields.object` keep the `localization` they were written with in their type, which is how a typed `where` tells a list translated whole from one translated item by item.
|
|
81
|
+
|
|
82
|
+
### Patch Changes
|
|
83
|
+
|
|
84
|
+
- 9ccfff0: **Filter by a reference or rich text a shared list translates item by item.** A typed client's `where()` now accepts these paths (`'sections.logo'` where each section's logo is translated): each item is matched in the read's locale by its own translation, or by that item's fallback, as the read shows it.
|
|
85
|
+
- c2fe14d: **A slug may have segments.** `guide/schema/text-fields` is a valid slug; a leading, trailing or doubled slash is refused, naming which. Read an entry by such a slug with the slug encoded in the path (`guide%2Fschema%2Ftext-fields`), as the SDK already does.
|
|
86
|
+
|
|
3
87
|
## 0.12.0
|
|
4
88
|
|
|
5
89
|
### Minor Changes
|
|
@@ -2,20 +2,252 @@ import { __name } from './chunk-XQ4QHWZE.js';
|
|
|
2
2
|
|
|
3
3
|
/* RelyIO SDK - Built with ❤️ by the RelyIO Team */
|
|
4
4
|
|
|
5
|
+
// src/rich-text/anchors.ts
|
|
6
|
+
function richTextHeadingSlug(text) {
|
|
7
|
+
return text.trim().toLowerCase().replace(/[^\p{L}\p{M}\p{N}\s_-]/gu, "").replace(/\s/gu, "-");
|
|
8
|
+
}
|
|
9
|
+
__name(richTextHeadingSlug, "richTextHeadingSlug");
|
|
10
|
+
function isRecord(value) {
|
|
11
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
12
|
+
}
|
|
13
|
+
__name(isRecord, "isRecord");
|
|
14
|
+
var BUILT_IN_CONTAINERS = /* @__PURE__ */ new Set([
|
|
15
|
+
"paragraph",
|
|
16
|
+
"heading",
|
|
17
|
+
"quote",
|
|
18
|
+
"list",
|
|
19
|
+
"listItem",
|
|
20
|
+
"codeGroup",
|
|
21
|
+
"table",
|
|
22
|
+
"tableRow",
|
|
23
|
+
"tableCell"
|
|
24
|
+
]);
|
|
25
|
+
function headingText(heading) {
|
|
26
|
+
if (!Array.isArray(heading.children)) return "";
|
|
27
|
+
let text = "";
|
|
28
|
+
for (const child of heading.children) {
|
|
29
|
+
if (!isRecord(child)) continue;
|
|
30
|
+
if (child._type === "lineBreak") text += " ";
|
|
31
|
+
else if (child._type === "text" && typeof child.text === "string") text += child.text;
|
|
32
|
+
}
|
|
33
|
+
return text;
|
|
34
|
+
}
|
|
35
|
+
__name(headingText, "headingText");
|
|
36
|
+
function richTextHeadingAnchors(value) {
|
|
37
|
+
return anchorsAcross([value]);
|
|
38
|
+
}
|
|
39
|
+
__name(richTextHeadingAnchors, "richTextHeadingAnchors");
|
|
40
|
+
function entryHeadingAnchors(entry) {
|
|
41
|
+
return anchorsAcross(isRecord(entry) ? Object.values(entry) : []);
|
|
42
|
+
}
|
|
43
|
+
__name(entryHeadingAnchors, "entryHeadingAnchors");
|
|
44
|
+
function anchorsAcross(values) {
|
|
45
|
+
const anchors = /* @__PURE__ */ new Map();
|
|
46
|
+
const taken = /* @__PURE__ */ new Map();
|
|
47
|
+
const unique = /* @__PURE__ */ __name((slug) => {
|
|
48
|
+
let anchor = slug;
|
|
49
|
+
const seen = taken.get(slug);
|
|
50
|
+
if (seen !== void 0) {
|
|
51
|
+
let count = seen;
|
|
52
|
+
do {
|
|
53
|
+
count += 1;
|
|
54
|
+
anchor = `${slug}-${count}`;
|
|
55
|
+
} while (taken.has(anchor));
|
|
56
|
+
taken.set(slug, count);
|
|
57
|
+
}
|
|
58
|
+
taken.set(anchor, 0);
|
|
59
|
+
return anchor;
|
|
60
|
+
}, "unique");
|
|
61
|
+
const visit = /* @__PURE__ */ __name((nodes) => {
|
|
62
|
+
for (const node of nodes) {
|
|
63
|
+
if (!isRecord(node) || typeof node._type !== "string") continue;
|
|
64
|
+
if (node._type === "heading") {
|
|
65
|
+
const slug = richTextHeadingSlug(headingText(node));
|
|
66
|
+
if (slug !== "") anchors.set(node, unique(slug));
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (BUILT_IN_CONTAINERS.has(node._type) && Array.isArray(node.children)) visit(node.children);
|
|
70
|
+
}
|
|
71
|
+
}, "visit");
|
|
72
|
+
for (const value of values) {
|
|
73
|
+
if (isRecord(value) && value._type === "richText" && Array.isArray(value.children)) {
|
|
74
|
+
visit(value.children);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return anchors;
|
|
78
|
+
}
|
|
79
|
+
__name(anchorsAcross, "anchorsAcross");
|
|
80
|
+
|
|
81
|
+
// src/binding/key-path.ts
|
|
82
|
+
var ITEM_SELECTOR = "[_key==";
|
|
83
|
+
function formatKeyPath(listPath, key, field) {
|
|
84
|
+
const item = `${listPath}${ITEM_SELECTOR}${JSON.stringify(key)}]`;
|
|
85
|
+
return field === void 0 ? item : `${item}.${field}`;
|
|
86
|
+
}
|
|
87
|
+
__name(formatKeyPath, "formatKeyPath");
|
|
88
|
+
function parseKeyPath(path) {
|
|
89
|
+
const segments = [];
|
|
90
|
+
let at = 0;
|
|
91
|
+
let expectField = true;
|
|
92
|
+
while (at < path.length) {
|
|
93
|
+
if (path.startsWith(ITEM_SELECTOR, at)) {
|
|
94
|
+
const previous = segments[segments.length - 1];
|
|
95
|
+
if (expectField || previous === void 0 || !("field" in previous)) return null;
|
|
96
|
+
const quoted = readQuoted(path, at + ITEM_SELECTOR.length);
|
|
97
|
+
if (quoted === null || path[quoted.end] !== "]") return null;
|
|
98
|
+
segments.push({ key: quoted.value });
|
|
99
|
+
at = quoted.end + 1;
|
|
100
|
+
if (at < path.length && path[at] !== "." && !path.startsWith(ITEM_SELECTOR, at)) return null;
|
|
101
|
+
if (path[at] === ".") {
|
|
102
|
+
at += 1;
|
|
103
|
+
expectField = true;
|
|
104
|
+
}
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
let end = at;
|
|
108
|
+
while (end < path.length && path[end] !== "." && path[end] !== "[") end += 1;
|
|
109
|
+
const field = path.slice(at, end);
|
|
110
|
+
if (field.length === 0) return null;
|
|
111
|
+
segments.push({ field });
|
|
112
|
+
expectField = false;
|
|
113
|
+
at = end;
|
|
114
|
+
if (path[at] === ".") {
|
|
115
|
+
at += 1;
|
|
116
|
+
expectField = true;
|
|
117
|
+
if (at === path.length) return null;
|
|
118
|
+
} else if (at < path.length && !path.startsWith(ITEM_SELECTOR, at)) {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return segments.length === 0 || expectField ? null : segments;
|
|
123
|
+
}
|
|
124
|
+
__name(parseKeyPath, "parseKeyPath");
|
|
125
|
+
function readQuoted(path, start) {
|
|
126
|
+
if (path[start] !== '"') return null;
|
|
127
|
+
for (let end = start + 1; end < path.length; end += 1) {
|
|
128
|
+
if (path[end] === "\\") {
|
|
129
|
+
end += 1;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (path[end] !== '"') continue;
|
|
133
|
+
try {
|
|
134
|
+
const value = JSON.parse(path.slice(start, end + 1));
|
|
135
|
+
return typeof value === "string" && value.length > 0 ? { value, end: end + 1 } : null;
|
|
136
|
+
} catch {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
__name(readQuoted, "readQuoted");
|
|
143
|
+
|
|
144
|
+
// src/binding/relyio-field.ts
|
|
145
|
+
var BINDING_VERSION = "1";
|
|
146
|
+
function relyioField(subject, path) {
|
|
147
|
+
if (subject === void 0) return void 0;
|
|
148
|
+
if (isBindingShaped(subject)) return continued(subject, path);
|
|
149
|
+
const field = typeof path === "string" ? path : String(path);
|
|
150
|
+
const metadata = subject._metadata;
|
|
151
|
+
const attributes = {
|
|
152
|
+
"data-relyio-entry": requireMetadata(metadata, "id", field),
|
|
153
|
+
"data-relyio-type": requireMetadata(metadata, "type", field),
|
|
154
|
+
"data-relyio-field": field,
|
|
155
|
+
"data-relyio-locale": requireMetadata(metadata, "locale", field),
|
|
156
|
+
"data-relyio-v": BINDING_VERSION
|
|
157
|
+
};
|
|
158
|
+
requireKeyPath("entry", field, field);
|
|
159
|
+
return attributes;
|
|
160
|
+
}
|
|
161
|
+
__name(relyioField, "relyioField");
|
|
162
|
+
function isBindingShaped(value) {
|
|
163
|
+
return "data-relyio-field" in value;
|
|
164
|
+
}
|
|
165
|
+
__name(isBindingShaped, "isBindingShaped");
|
|
166
|
+
function continued(binding, path) {
|
|
167
|
+
const spelled = typeof path === "string" ? path : `{ _key: ${JSON.stringify(path._key)} }`;
|
|
168
|
+
const complete = binding["data-relyio-v"] === BINDING_VERSION && [
|
|
169
|
+
binding["data-relyio-entry"],
|
|
170
|
+
binding["data-relyio-type"],
|
|
171
|
+
binding["data-relyio-field"],
|
|
172
|
+
binding["data-relyio-locale"]
|
|
173
|
+
].every((value) => typeof value === "string" && value.length > 0);
|
|
174
|
+
if (!complete) {
|
|
175
|
+
throw new TypeError(
|
|
176
|
+
`relyioField(binding, '${spelled}'): the binding is not one relyioField returned \u2014 continue the attributes relyioField(entry, field) gave, or a renderer handed you.`
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
const field = typeof path === "string" ? `${binding["data-relyio-field"]}.${path}` : formatKeyPath(binding["data-relyio-field"], path._key);
|
|
180
|
+
requireKeyPath("binding", spelled, field);
|
|
181
|
+
return {
|
|
182
|
+
"data-relyio-entry": binding["data-relyio-entry"],
|
|
183
|
+
"data-relyio-type": binding["data-relyio-type"],
|
|
184
|
+
"data-relyio-field": field,
|
|
185
|
+
"data-relyio-locale": binding["data-relyio-locale"],
|
|
186
|
+
"data-relyio-v": BINDING_VERSION
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
__name(continued, "continued");
|
|
190
|
+
function requireKeyPath(form, given, field) {
|
|
191
|
+
if (parseKeyPath(field) !== null) return;
|
|
192
|
+
throw new TypeError(
|
|
193
|
+
`relyioField(${form}, '${given}'): '${field}' is not a field or a key path. Name a field (\`title\`), or a value inside one by its items\u2019 keys (\`sections[_key=="k1"].heading\`).`
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
__name(requireKeyPath, "requireKeyPath");
|
|
197
|
+
function requireMetadata(metadata, key, field) {
|
|
198
|
+
const value = metadata?.[key];
|
|
199
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
200
|
+
throw new TypeError(
|
|
201
|
+
`relyioField(entry, '${field}'): the entry has no _metadata.${key}, so the field cannot be bound. Pass an entry as the RelyIO API returned it.`
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
return value;
|
|
205
|
+
}
|
|
206
|
+
__name(requireMetadata, "requireMetadata");
|
|
207
|
+
|
|
5
208
|
// src/rich-text/render.ts
|
|
6
209
|
var RICH_TEXT_VOID_TAGS = /* @__PURE__ */ new Set([
|
|
7
210
|
"hr",
|
|
8
211
|
"br",
|
|
9
212
|
"input"
|
|
10
213
|
]);
|
|
214
|
+
var BUILT_IN_NODES = {
|
|
215
|
+
paragraph: true,
|
|
216
|
+
heading: true,
|
|
217
|
+
quote: true,
|
|
218
|
+
list: true,
|
|
219
|
+
listItem: true,
|
|
220
|
+
codeBlock: true,
|
|
221
|
+
codeGroup: true,
|
|
222
|
+
table: true,
|
|
223
|
+
tableRow: true,
|
|
224
|
+
tableCell: true,
|
|
225
|
+
divider: true,
|
|
226
|
+
text: true,
|
|
227
|
+
lineBreak: true
|
|
228
|
+
};
|
|
229
|
+
function isDeclaredRichTextNode(node) {
|
|
230
|
+
if (typeof node !== "object" || node === null) return false;
|
|
231
|
+
const type = node._type;
|
|
232
|
+
return typeof type === "string" && type !== "" && !Object.prototype.hasOwnProperty.call(BUILT_IN_NODES, type);
|
|
233
|
+
}
|
|
234
|
+
__name(isDeclaredRichTextNode, "isDeclaredRichTextNode");
|
|
11
235
|
var PLAIN = Object.freeze({ header: false });
|
|
12
236
|
var HEADER = Object.freeze({ header: true });
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
237
|
+
var headingAnchors = /* @__PURE__ */ new Map();
|
|
238
|
+
function renderRichText(value, target, options = {}) {
|
|
239
|
+
const held = value;
|
|
240
|
+
if (held._type !== "richText") {
|
|
241
|
+
const drawn = block(held, target);
|
|
16
242
|
return drawn === void 0 ? [] : [drawn];
|
|
17
243
|
}
|
|
18
|
-
|
|
244
|
+
const outer = headingAnchors;
|
|
245
|
+
headingAnchors = options.anchors ?? richTextHeadingAnchors(held);
|
|
246
|
+
try {
|
|
247
|
+
return Array.isArray(held.children) ? blocks(held.children, target) : [];
|
|
248
|
+
} finally {
|
|
249
|
+
headingAnchors = outer;
|
|
250
|
+
}
|
|
19
251
|
}
|
|
20
252
|
__name(renderRichText, "renderRichText");
|
|
21
253
|
function blocks(nodes, target) {
|
|
@@ -36,6 +268,9 @@ var HEADING_TAGS = {
|
|
|
36
268
|
6: "h6"
|
|
37
269
|
};
|
|
38
270
|
function block(node, target) {
|
|
271
|
+
if (isDeclaredRichTextNode(node)) {
|
|
272
|
+
return target.declared(node, "block");
|
|
273
|
+
}
|
|
39
274
|
switch (node._type) {
|
|
40
275
|
case "paragraph": {
|
|
41
276
|
return target.node(
|
|
@@ -47,10 +282,16 @@ function block(node, target) {
|
|
|
47
282
|
}
|
|
48
283
|
case "heading": {
|
|
49
284
|
const tag = HEADING_TAGS[node.level] ?? "p";
|
|
285
|
+
const anchor = headingAnchors.get(node);
|
|
50
286
|
return target.node(
|
|
51
287
|
node,
|
|
52
288
|
inline(node.children, target),
|
|
53
|
-
(contents) => target.element(
|
|
289
|
+
(contents) => target.element(
|
|
290
|
+
tag,
|
|
291
|
+
anchor === void 0 ? void 0 : { id: anchor },
|
|
292
|
+
contents,
|
|
293
|
+
node._key
|
|
294
|
+
),
|
|
54
295
|
PLAIN
|
|
55
296
|
);
|
|
56
297
|
}
|
|
@@ -253,6 +494,12 @@ function inline(nodes, target) {
|
|
|
253
494
|
runs = [];
|
|
254
495
|
}, "flush");
|
|
255
496
|
for (const node of nodes) {
|
|
497
|
+
if (isDeclaredRichTextNode(node)) {
|
|
498
|
+
flush();
|
|
499
|
+
const drawn = target.declared(node, "inline");
|
|
500
|
+
if (drawn !== void 0) out.push(drawn);
|
|
501
|
+
continue;
|
|
502
|
+
}
|
|
256
503
|
switch (node._type) {
|
|
257
504
|
case "text": {
|
|
258
505
|
runs.push({ span: node, drawn: 0 });
|
|
@@ -301,9 +548,22 @@ function nextMark(run) {
|
|
|
301
548
|
__name(nextMark, "nextMark");
|
|
302
549
|
function sameMark(a, b) {
|
|
303
550
|
if (a?._type !== b._type) return false;
|
|
304
|
-
|
|
551
|
+
if (a._type === "link" && b._type === "link") return a.href === b.href;
|
|
552
|
+
if (a._type === "entryLink" && b._type === "entryLink") {
|
|
553
|
+
return linkedEntryId(a) === linkedEntryId(b) && a.anchor === b.anchor;
|
|
554
|
+
}
|
|
555
|
+
return true;
|
|
305
556
|
}
|
|
306
557
|
__name(sameMark, "sameMark");
|
|
558
|
+
function linkedEntryId(link) {
|
|
559
|
+
const to = link.to;
|
|
560
|
+
if (typeof to === "object" && to !== null && "_metadata" in to) {
|
|
561
|
+
const metadata = to._metadata;
|
|
562
|
+
return metadata?.id ?? to;
|
|
563
|
+
}
|
|
564
|
+
return to;
|
|
565
|
+
}
|
|
566
|
+
__name(linkedEntryId, "linkedEntryId");
|
|
307
567
|
var MARK_TAGS = {
|
|
308
568
|
bold: "strong",
|
|
309
569
|
italic: "em",
|
|
@@ -316,6 +576,9 @@ var MARK_TAGS = {
|
|
|
316
576
|
};
|
|
317
577
|
function applyMark(mark, inner, target) {
|
|
318
578
|
switch (mark._type) {
|
|
579
|
+
case "entryLink": {
|
|
580
|
+
return [target.entryLink(mark, inner)];
|
|
581
|
+
}
|
|
319
582
|
case "link": {
|
|
320
583
|
const href = safeRichTextHref(mark.href);
|
|
321
584
|
if (href === void 0) return inner;
|
|
@@ -389,28 +652,168 @@ function attributesOf(attributes) {
|
|
|
389
652
|
return written;
|
|
390
653
|
}
|
|
391
654
|
__name(attributesOf, "attributesOf");
|
|
392
|
-
var
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
655
|
+
var VOID_ELEMENTS = /* @__PURE__ */ new Set([
|
|
656
|
+
"area",
|
|
657
|
+
"base",
|
|
658
|
+
"br",
|
|
659
|
+
"col",
|
|
660
|
+
"embed",
|
|
661
|
+
"hr",
|
|
662
|
+
"img",
|
|
663
|
+
"input",
|
|
664
|
+
"link",
|
|
665
|
+
"meta",
|
|
666
|
+
"source",
|
|
667
|
+
"track",
|
|
668
|
+
"wbr"
|
|
669
|
+
]);
|
|
670
|
+
var RAW_TEXT_ELEMENTS = /* @__PURE__ */ new Set(["script", "style", "textarea", "title"]);
|
|
671
|
+
var BOUND = /\sdata-relyio-entry(?:[\s=/>]|$)/i;
|
|
672
|
+
function tagEnd(html, start) {
|
|
673
|
+
let quote;
|
|
674
|
+
for (let index = start + 1; index < html.length; index++) {
|
|
675
|
+
const character = html[index];
|
|
676
|
+
if (quote !== void 0) {
|
|
677
|
+
if (character === quote) quote = void 0;
|
|
678
|
+
} else if (character === '"' || character === "'") {
|
|
679
|
+
quote = character;
|
|
680
|
+
} else if (character === ">") {
|
|
681
|
+
return index + 1;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
return -1;
|
|
685
|
+
}
|
|
686
|
+
__name(tagEnd, "tagEnd");
|
|
687
|
+
var TAG_NAME = /^<\/?([A-Za-z][\w:-]*)/;
|
|
688
|
+
function soleElementStartTag(html) {
|
|
689
|
+
const from = html.length - html.trimStart().length;
|
|
690
|
+
const to = html.trimEnd().length;
|
|
691
|
+
const root = TAG_NAME.exec(html.slice(from));
|
|
692
|
+
if (root === null || html[from + 1] === "/") return void 0;
|
|
693
|
+
let depth = 0;
|
|
694
|
+
let index = from;
|
|
695
|
+
let startTag;
|
|
696
|
+
while (index < to) {
|
|
697
|
+
if (depth === 0 && startTag !== void 0) return void 0;
|
|
698
|
+
const open = html.indexOf("<", index);
|
|
699
|
+
if (open !== index) {
|
|
700
|
+
if (depth === 0) return void 0;
|
|
701
|
+
if (open === -1 || open >= to) return void 0;
|
|
702
|
+
index = open;
|
|
703
|
+
continue;
|
|
704
|
+
}
|
|
705
|
+
if (html.startsWith("<!--", index)) {
|
|
706
|
+
if (depth === 0) return void 0;
|
|
707
|
+
const close = html.indexOf("-->", index + 4);
|
|
708
|
+
if (close === -1) return void 0;
|
|
709
|
+
index = close + 3;
|
|
710
|
+
continue;
|
|
711
|
+
}
|
|
712
|
+
const name = TAG_NAME.exec(html.slice(index));
|
|
713
|
+
if (name === null) {
|
|
714
|
+
if (depth === 0) return void 0;
|
|
715
|
+
index += 1;
|
|
716
|
+
continue;
|
|
717
|
+
}
|
|
718
|
+
const end = tagEnd(html, index);
|
|
719
|
+
if (end === -1 || end > to) return void 0;
|
|
720
|
+
const tag = (name[1] ?? "").toLowerCase();
|
|
721
|
+
if (html[index + 1] === "/") {
|
|
722
|
+
depth -= 1;
|
|
723
|
+
if (depth < 0) return void 0;
|
|
724
|
+
index = end;
|
|
725
|
+
continue;
|
|
726
|
+
}
|
|
727
|
+
const selfClosing = html[end - 2] === "/";
|
|
728
|
+
if (startTag === void 0) {
|
|
729
|
+
let insertAt = selfClosing ? end - 2 : end - 1;
|
|
730
|
+
while (insertAt > index && /\s/.test(html[insertAt - 1] ?? "")) insertAt -= 1;
|
|
731
|
+
startTag = { insertAt, tag: html.slice(index, end) };
|
|
732
|
+
}
|
|
733
|
+
if (selfClosing || VOID_ELEMENTS.has(tag)) {
|
|
734
|
+
index = end;
|
|
735
|
+
continue;
|
|
736
|
+
}
|
|
737
|
+
if (RAW_TEXT_ELEMENTS.has(tag)) {
|
|
738
|
+
const close = html.toLowerCase().indexOf(`</${tag}`, end);
|
|
739
|
+
if (close === -1) return void 0;
|
|
740
|
+
depth += 1;
|
|
741
|
+
index = close;
|
|
742
|
+
continue;
|
|
743
|
+
}
|
|
744
|
+
depth += 1;
|
|
745
|
+
index = end;
|
|
746
|
+
}
|
|
747
|
+
return depth === 0 ? startTag : void 0;
|
|
748
|
+
}
|
|
749
|
+
__name(soleElementStartTag, "soleElementStartTag");
|
|
750
|
+
function stamped(html, binding) {
|
|
751
|
+
if (binding === void 0) return html;
|
|
752
|
+
const root = soleElementStartTag(html);
|
|
753
|
+
if (root === void 0) return html;
|
|
754
|
+
if (BOUND.test(root.tag)) return html;
|
|
755
|
+
return `${html.slice(0, root.insertAt)}${attributesOf(binding)}${html.slice(root.insertAt)}`;
|
|
756
|
+
}
|
|
757
|
+
__name(stamped, "stamped");
|
|
758
|
+
function htmlTarget(declared2, entryLink, field) {
|
|
759
|
+
return {
|
|
760
|
+
element(tag, attributes, children) {
|
|
761
|
+
return RICH_TEXT_VOID_TAGS.has(tag) ? `<${tag}${attributesOf(attributes)}/>` : `<${tag}${attributesOf(attributes)}>${children.join("")}</${tag}>`;
|
|
762
|
+
},
|
|
763
|
+
text: escapeHtml,
|
|
764
|
+
node: /* @__PURE__ */ __name((_node, contents, draw) => draw(contents), "node"),
|
|
765
|
+
mark: /* @__PURE__ */ __name((_mark, contents, draw) => draw(contents), "mark"),
|
|
766
|
+
// A value built by hand holding an entry link no writer draws: its text, with no anchor
|
|
767
|
+
entryLink: /* @__PURE__ */ __name((link, contents) => entryLink === void 0 ? contents.join("") : entryLink(link, contents.join("")), "entryLink"),
|
|
768
|
+
// A declared node's writer (REL-1807), handed the node's key path below the field's binding and
|
|
769
|
+
// stamped with it (REL-2142); no binding asks the core nothing
|
|
770
|
+
declared(node) {
|
|
771
|
+
const write = Object.prototype.hasOwnProperty.call(declared2, node._type) ? declared2[node._type] : void 0;
|
|
772
|
+
if (write === void 0) return void 0;
|
|
773
|
+
const binding = field === void 0 ? void 0 : relyioField(field, node);
|
|
774
|
+
return stamped(write(node, binding), binding);
|
|
775
|
+
}
|
|
776
|
+
};
|
|
777
|
+
}
|
|
778
|
+
__name(htmlTarget, "htmlTarget");
|
|
779
|
+
function richTextToHtml(value, ...[options]) {
|
|
401
780
|
if (value === void 0) return "";
|
|
402
|
-
|
|
781
|
+
const declared2 = options?.declared ?? {};
|
|
782
|
+
const entryLink = options?.entryLink;
|
|
783
|
+
return renderRichText(
|
|
784
|
+
value,
|
|
785
|
+
htmlTarget(declared2, entryLink, options?.field),
|
|
786
|
+
options?.anchors === void 0 ? {} : { anchors: options.anchors }
|
|
787
|
+
).join("");
|
|
403
788
|
}
|
|
404
789
|
__name(richTextToHtml, "richTextToHtml");
|
|
405
790
|
|
|
406
791
|
// src/rich-text/markdown.ts
|
|
407
|
-
|
|
792
|
+
var entryLinkWriter;
|
|
793
|
+
var writers = {};
|
|
794
|
+
function declared(node) {
|
|
795
|
+
const type = node._type;
|
|
796
|
+
const write = Object.prototype.hasOwnProperty.call(writers, type) ? writers[type] : void 0;
|
|
797
|
+
return write === void 0 ? void 0 : write(node);
|
|
798
|
+
}
|
|
799
|
+
__name(declared, "declared");
|
|
800
|
+
function richTextToMarkdown(value, ...[options]) {
|
|
408
801
|
if (value === void 0) return "";
|
|
409
|
-
const
|
|
802
|
+
const tree = value;
|
|
803
|
+
const held = tree._type === "richText" ? tree.children : [tree];
|
|
410
804
|
if (!Array.isArray(held)) return "";
|
|
411
|
-
const
|
|
412
|
-
|
|
805
|
+
const outer = writers;
|
|
806
|
+
const outerLink = entryLinkWriter;
|
|
807
|
+
writers = options?.declared ?? {};
|
|
808
|
+
entryLinkWriter = options?.entryLink;
|
|
809
|
+
try {
|
|
810
|
+
const written = blocks2(held);
|
|
811
|
+
return written.length === 0 ? "" : `${written.join("\n\n")}
|
|
413
812
|
`;
|
|
813
|
+
} finally {
|
|
814
|
+
writers = outer;
|
|
815
|
+
entryLinkWriter = outerLink;
|
|
816
|
+
}
|
|
414
817
|
}
|
|
415
818
|
__name(richTextToMarkdown, "richTextToMarkdown");
|
|
416
819
|
function blocks2(nodes) {
|
|
@@ -423,6 +826,7 @@ function blocks2(nodes) {
|
|
|
423
826
|
}
|
|
424
827
|
__name(blocks2, "blocks");
|
|
425
828
|
function block2(node) {
|
|
829
|
+
if (isDeclaredRichTextNode(node)) return declared(node);
|
|
426
830
|
switch (node._type) {
|
|
427
831
|
case "paragraph": {
|
|
428
832
|
return escapeLineStarts(inline2(node.children, "\\\n"));
|
|
@@ -566,6 +970,11 @@ function inline2(nodes, lineBreak) {
|
|
|
566
970
|
runs = [];
|
|
567
971
|
}, "flush");
|
|
568
972
|
for (const node of nodes) {
|
|
973
|
+
if (isDeclaredRichTextNode(node)) {
|
|
974
|
+
flush();
|
|
975
|
+
out += declared(node) ?? "";
|
|
976
|
+
continue;
|
|
977
|
+
}
|
|
569
978
|
switch (node._type) {
|
|
570
979
|
case "text": {
|
|
571
980
|
runs.push({ span: node, drawn: 0 });
|
|
@@ -616,6 +1025,9 @@ function marked2(runs) {
|
|
|
616
1025
|
__name(marked2, "marked");
|
|
617
1026
|
function applyMark2(mark, inner) {
|
|
618
1027
|
switch (mark._type) {
|
|
1028
|
+
case "entryLink": {
|
|
1029
|
+
return entryLinkWriter === void 0 || inner === "" ? inner : entryLinkWriter(mark, inner);
|
|
1030
|
+
}
|
|
619
1031
|
case "link": {
|
|
620
1032
|
const href = safeRichTextHref(mark.href);
|
|
621
1033
|
if (href === void 0 || inner === "") return inner;
|
|
@@ -675,6 +1087,6 @@ function escapeLineStarts(paragraph) {
|
|
|
675
1087
|
}
|
|
676
1088
|
__name(escapeLineStarts, "escapeLineStarts");
|
|
677
1089
|
|
|
678
|
-
export { RICH_TEXT_VOID_TAGS, renderRichText, richTextToHtml, richTextToMarkdown, safeRichTextHref };
|
|
679
|
-
//# sourceMappingURL=chunk-
|
|
680
|
-
//# sourceMappingURL=chunk-
|
|
1090
|
+
export { RICH_TEXT_VOID_TAGS, entryHeadingAnchors, escapeHtml, formatKeyPath, isDeclaredRichTextNode, parseKeyPath, relyioField, renderRichText, richTextHeadingAnchors, richTextHeadingSlug, richTextToHtml, richTextToMarkdown, safeRichTextHref };
|
|
1091
|
+
//# sourceMappingURL=chunk-3NPPMGB6.js.map
|
|
1092
|
+
//# sourceMappingURL=chunk-3NPPMGB6.js.map
|