@relyio/sdk 0.11.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 +172 -0
- package/dist/chunk-3NPPMGB6.js +1092 -0
- package/dist/{chunk-7GLDVHIH.js → chunk-6NGJXLOR.js} +65 -67
- package/dist/{chunk-4HTIU5YE.cjs → chunk-DEDIJKHY.cjs} +65 -67
- package/dist/{chunk-2TIJLV2X.js → chunk-EW23SZPX.js} +43 -65
- package/dist/{chunk-PO7CUR5U.cjs → chunk-FOSM4LMM.cjs} +42 -65
- package/dist/chunk-Z6GLWSSD.cjs +1106 -0
- package/dist/client/index.cjs +3 -3
- package/dist/client/index.d.cts +4 -4
- package/dist/client/index.d.ts +4 -4
- package/dist/client/index.js +1 -1
- package/dist/{define-schema-CVi8nwc7.d.ts → define-schema-UHViXKiv.d.ts} +33 -435
- package/dist/{define-schema-DqpipEHv.d.cts → define-schema-sWJ8Zn7C.d.cts} +33 -435
- package/dist/{define-content-type-ENAod5Ve.d.cts → include-paths-Bs5FhNw2.d.cts} +3505 -483
- package/dist/{define-content-type-ENAod5Ve.d.ts → include-paths-Cb7iPCW2.d.ts} +3505 -483
- package/dist/index--_u1nPHv.d.cts +507 -0
- package/dist/index-8DozAWsA.d.ts +507 -0
- package/dist/{index-MLAFUuco.d.cts → index-BT0L8xJj.d.cts} +139 -90
- package/dist/{index-BKEAYvvz.d.ts → index-CPjJ25R4.d.ts} +139 -90
- package/dist/index.cjs +134 -110
- package/dist/index.d.cts +44 -71
- package/dist/index.d.ts +44 -71
- package/dist/index.js +5 -28
- 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 +49 -0
- package/dist/rich-text/index.d.cts +3 -0
- package/dist/rich-text/index.d.ts +3 -0
- package/dist/rich-text/index.js +4 -0
- 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 -38
- package/dist/schema/index.d.cts +15 -137
- package/dist/schema/index.d.ts +15 -137
- package/dist/schema/index.js +1 -1
- package/dist/types/index.d.cts +2 -2
- package/dist/types/index.d.ts +2 -2
- package/package.json +19 -2
- package/dist/.tsbuildinfo +0 -1
- package/dist/index-BM42r4yg.d.cts +0 -860
- package/dist/index-C73cK2Tt.d.ts +0 -860
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,177 @@
|
|
|
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
|
+
|
|
87
|
+
## 0.12.0
|
|
88
|
+
|
|
89
|
+
### Minor Changes
|
|
90
|
+
|
|
91
|
+
- ec2d34d: **Include paths reach into structure, cross references and pick fields; a read is typed by them.** An include path names fields joined by dots, through lists, page builders, objects and references, and each hop across a reference is written out — nothing expands past where a path stops. Fields in parentheses are the only fields each expanded entry returns, beside its `_metadata`.
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
const home = await relyio
|
|
95
|
+
.content('page')
|
|
96
|
+
.findById('home')
|
|
97
|
+
.include(['sections.features(name,slug)', 'sections.customers.logo', 'ogImage'])
|
|
98
|
+
.execute()
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
A reference a path expands is typed as the entry — narrowed to the fields a path picked — and **every other reference is typed as its id**, where it was `string | <entry>` whatever the read included. A path that holds no reference does not compile, and the error names the paths that do. `include()` on `findById()`, `query()`, `singleton()`, `settings()` and an asset type, `{ include }` on `fetch`, `fetchOne` and `fetchOneWithTag`, and `@relyio/react`'s `useEntry`, `useEntries`, `useSingleton` and `relyioQueryOptions()` all take checked paths. Two `include()` calls now add up rather than the second replacing the first, and a `singleton()` or `settings()` read's `{ include }` adds to the paths `include()` named. An include list the compiler cannot see — a `string[]` — is taken as written and types every reference as either.
|
|
102
|
+
|
|
103
|
+
- 8df140c: **A list's items take a fieldset: `fields.list(id, { config: { of } })`.** `of` names the fieldset each item takes, and every item holds that fieldset's fields plus two keys: `_type`, the fieldset's id (present even when the list allows one shape), and `_key`, the item's key, created when the item is inserted and kept for its life. `subfields` is gone, with no alias: a deploy that sends it is refused.
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
const question = fieldset('question', {
|
|
107
|
+
displayName: 'Question',
|
|
108
|
+
fields: [
|
|
109
|
+
fields.shortText('question', { config: { required: true } }),
|
|
110
|
+
fields.richText('answer'),
|
|
111
|
+
],
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
fields.list('faq', { config: { of: question } })
|
|
115
|
+
// a read types each item as { readonly _type: 'question'; readonly _key: string; question: string; answer?: string }
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
In YAML, `of` names a fieldset declared under the top-level `fieldsets` (`of: question`), or declares one in place. `relyio generate-types` writes a list's `of` as a `fieldset(…)` call, and the Python target types each item with `type` and `key` beside its fields.
|
|
119
|
+
|
|
120
|
+
- d2d4e94: **Rich text renders with no code: `<RichText>` in React, and HTML or plain Markdown anywhere.** `<RichText value={post.body} />` from `@relyio/react` renders every built-in — paragraphs, headings, quotes, bullet, numbered and task lists, code and code groups, tables, dividers, line breaks and every mark — and `components` replaces any of them, keyed by `_type` and typed by the built-in it replaces:
|
|
121
|
+
|
|
122
|
+
```tsx
|
|
123
|
+
<RichText
|
|
124
|
+
value={post.body}
|
|
125
|
+
components={{
|
|
126
|
+
heading: ({ value, children }) => <Heading level={value.level}>{children}</Heading>,
|
|
127
|
+
}}
|
|
128
|
+
/>
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
`richTextToHtml(value)` from `@relyio/sdk` writes the exact markup `<RichText>` renders with no components, text and attributes escaped, with no DOM — for Node, a browser or a Worker. `richTextToMarkdown(value)` writes GitHub Flavored Markdown and drops what it cannot hold (underline, highlight, sub- and superscript, a code block's filename), keeping the text. A link to a scheme other than `http:`, `https:`, `mailto:` or `tel:` — `javascript:`, `data:` — renders its text with no anchor in all three. A node or mark from a newer RelyIO that this version does not know renders nothing, or its text, and never throws. All three share one walk, `renderRichText`, also exported for other frameworks; the functions are on `@relyio/sdk` and `@relyio/sdk/rich-text`.
|
|
132
|
+
|
|
133
|
+
- a97ccc4: **Rich text is a tree, not HTML.** A `richText` field's value is `{ _type: 'richText', version: 1, children }`: paragraphs, headings, quotes, bullet, numbered and task lists whose items hold blocks, code blocks and code groups, tables, dividers and line breaks, with text runs carrying named marks. Every node but a text run carries `_type` and a `_key`. The SDK exports the tree's types (`RichTextValue` and each node's), types a read of a `richText` field by them, and types `RichTextConfig.defaultValue` as a tree. A field narrows what it allows with `structure`, `marks` and `multiline`; `allowedFormats` and `embedTypes` are gone. A write of an HTML string is refused.
|
|
134
|
+
|
|
135
|
+
**`_metadata.refs` names a reference inside structure by its include path** (`sections.customers`, `sections.media.image`), with no shape segment. **A url field takes a site-relative path** (`/sign-up`) as well as an absolute URL. **A multi reference takes `minItems` and `maxItems`.**
|
|
136
|
+
|
|
137
|
+
**A saved query's string includes are include paths**, read as `?include=` reads them (`sections.customers.logo`, `sections.features(name,slug)`); the forward object form and its type `IncludeSelection` are removed, and `{ relationship, … }` stays for reverse joins, filters and counts.
|
|
138
|
+
|
|
139
|
+
- 60d416f: **A type's `fields` hold fields and fieldsets side by side, and a required field is always present.**
|
|
140
|
+
|
|
141
|
+
A fieldset among a type's fields is a section of the form: its fields are the record's own, so grouping the form never changes a read. The type-level `fieldsets` key is gone, with no alias. A fieldset id names one shape across the whole schema, so a deploy refuses two different shapes under one id, a fieldset that contains itself, and a field id used twice across a type's sections.
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
const seo = fieldset('seo', {
|
|
145
|
+
displayName: 'SEO',
|
|
146
|
+
fields: [fields.shortText('metaTitle'), fields.boolean('noIndex')],
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
defineContentType('article', {
|
|
150
|
+
displayName: 'Article',
|
|
151
|
+
fields: [fields.shortText('title', { config: { required: true } }), seo],
|
|
152
|
+
})
|
|
153
|
+
// a read types an article as { title: string; metaTitle?: string; noIndex?: boolean }
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
In YAML, declare a fieldset where it is used, or once under a top-level `fieldsets` and place it with `fieldset: <id>`.
|
|
157
|
+
|
|
158
|
+
A required field is present in every read's type. Text, lists, rich text and booleans may be empty in a draft: a draft read fills the kind's empty value and lists the field in `_metadata.emptyRequiredFields`. Numbers, dates, single choices and single references have no empty value, so they are required from creation: a create without them is refused, naming the fields, and a required single reference is never `null`.
|
|
159
|
+
|
|
160
|
+
`relyio validate` and `relyio deploy` answer with every error at once, including the checks that run past a field config the parse refuses.
|
|
161
|
+
|
|
162
|
+
- b1fd6f6: **`table` and `code` fields hold exactly a rich-text table's and code group's value.** `fields.table('comparison')` and `fields.code('snippet', { config: { languages: ['typescript', 'bash'], maxTabs: 4 } })` declare a field whose value is one built-in of the rich-text tree — a read types it `RichTextTable` or `RichTextCodeGroup`, the very type of the same block inside a `richText` field — so a comparison table on a page and a table in a doc are one thing, and a value copied between them is accepted unchanged. A code group holds one tab or more, each a code block with its text and an optional `label`, `language`, `filename` and `lines`; `languages` makes every tab name one of them and `maxTabs` bounds the tabs. Neither has an empty value, so a required one is given when the entry is created.
|
|
163
|
+
|
|
164
|
+
`<RichText value={page.comparison} />`, `richTextToHtml` and `richTextToMarkdown` take a table or code field's value as they take a tree, and draw it as the same block — a replacement in `components` included:
|
|
165
|
+
|
|
166
|
+
```tsx
|
|
167
|
+
<RichText
|
|
168
|
+
value={showcase.snippet}
|
|
169
|
+
components={{ codeGroup: ({ children }) => <Tabs>{children}</Tabs> }}
|
|
170
|
+
/>
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
`renderRichText` takes it too, and its input type is exported as `RichTextRenderable`. The schema-modelling skill names both kinds.
|
|
174
|
+
|
|
3
175
|
## 0.11.0
|
|
4
176
|
|
|
5
177
|
### Minor Changes
|