@relyio/sdk 0.10.0 → 0.12.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.
Files changed (37) hide show
  1. package/CHANGELOG.md +135 -0
  2. package/dist/chunk-3DRDHXLY.js +680 -0
  3. package/dist/{chunk-2HP566NI.cjs → chunk-3MSFWXQL.cjs} +37 -65
  4. package/dist/{chunk-DC4QLAI3.cjs → chunk-DXCVO6M5.cjs} +46 -59
  5. package/dist/chunk-FI5VIF4M.cjs +686 -0
  6. package/dist/{chunk-2L5JUW7O.js → chunk-FRJEUAAF.js} +46 -59
  7. package/dist/{chunk-IGHYC2WM.js → chunk-POEEDYFM.js} +38 -65
  8. package/dist/client/index.cjs +3 -3
  9. package/dist/client/index.d.cts +5 -4
  10. package/dist/client/index.d.ts +5 -4
  11. package/dist/client/index.js +1 -1
  12. package/dist/{define-schema-rbTrXxNN.d.ts → define-schema-DgctHlI2.d.cts} +50 -229
  13. package/dist/{define-schema-W-0u1Jg1.d.cts → define-schema-mrEZbY1h.d.ts} +50 -229
  14. package/dist/{define-content-type-DfnTu26S.d.cts → include-paths-CL4gHv6I.d.cts} +1691 -739
  15. package/dist/{define-content-type-DfnTu26S.d.ts → include-paths-CrJNTsMl.d.ts} +1691 -739
  16. package/dist/{index-B9hDmCho.d.ts → index-B9RfZVF1.d.ts} +3 -2
  17. package/dist/{index-KuYgc7eG.d.cts → index-BrvEg6-A.d.cts} +110 -49
  18. package/dist/{index-rLEBSFvK.d.ts → index-Pw0onkJe.d.ts} +110 -49
  19. package/dist/{index-NnLCwSqF.d.cts → index-b-AkfTUv.d.cts} +3 -2
  20. package/dist/index.cjs +102 -85
  21. package/dist/index.d.cts +14 -10
  22. package/dist/index.d.ts +14 -10
  23. package/dist/index.js +4 -3
  24. package/dist/rich-text/index.cjs +29 -0
  25. package/dist/rich-text/index.d.cts +148 -0
  26. package/dist/rich-text/index.d.ts +148 -0
  27. package/dist/rich-text/index.js +4 -0
  28. package/dist/rich-text.d-rKmpgdGd.d.cts +200 -0
  29. package/dist/rich-text.d-rKmpgdGd.d.ts +200 -0
  30. package/dist/schema/index.cjs +34 -38
  31. package/dist/schema/index.d.cts +86 -38
  32. package/dist/schema/index.d.ts +86 -38
  33. package/dist/schema/index.js +1 -1
  34. package/dist/types/index.d.cts +3 -2
  35. package/dist/types/index.d.ts +3 -2
  36. package/package.json +8 -2
  37. package/dist/.tsbuildinfo +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,140 @@
1
1
  # @relyio/sdk
2
2
 
3
+ ## 0.12.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 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`.
8
+
9
+ ```ts
10
+ const home = await relyio
11
+ .content('page')
12
+ .findById('home')
13
+ .include(['sections.features(name,slug)', 'sections.customers.logo', 'ogImage'])
14
+ .execute()
15
+ ```
16
+
17
+ 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.
18
+
19
+ - 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.
20
+
21
+ ```ts
22
+ const question = fieldset('question', {
23
+ displayName: 'Question',
24
+ fields: [
25
+ fields.shortText('question', { config: { required: true } }),
26
+ fields.richText('answer'),
27
+ ],
28
+ })
29
+
30
+ fields.list('faq', { config: { of: question } })
31
+ // a read types each item as { readonly _type: 'question'; readonly _key: string; question: string; answer?: string }
32
+ ```
33
+
34
+ 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.
35
+
36
+ - 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:
37
+
38
+ ```tsx
39
+ <RichText
40
+ value={post.body}
41
+ components={{
42
+ heading: ({ value, children }) => <Heading level={value.level}>{children}</Heading>,
43
+ }}
44
+ />
45
+ ```
46
+
47
+ `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`.
48
+
49
+ - 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.
50
+
51
+ **`_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`.**
52
+
53
+ **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.
54
+
55
+ - 60d416f: **A type's `fields` hold fields and fieldsets side by side, and a required field is always present.**
56
+
57
+ 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.
58
+
59
+ ```ts
60
+ const seo = fieldset('seo', {
61
+ displayName: 'SEO',
62
+ fields: [fields.shortText('metaTitle'), fields.boolean('noIndex')],
63
+ })
64
+
65
+ defineContentType('article', {
66
+ displayName: 'Article',
67
+ fields: [fields.shortText('title', { config: { required: true } }), seo],
68
+ })
69
+ // a read types an article as { title: string; metaTitle?: string; noIndex?: boolean }
70
+ ```
71
+
72
+ In YAML, declare a fieldset where it is used, or once under a top-level `fieldsets` and place it with `fieldset: <id>`.
73
+
74
+ 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`.
75
+
76
+ `relyio validate` and `relyio deploy` answer with every error at once, including the checks that run past a field config the parse refuses.
77
+
78
+ - 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.
79
+
80
+ `<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:
81
+
82
+ ```tsx
83
+ <RichText
84
+ value={showcase.snippet}
85
+ components={{ codeGroup: ({ children }) => <Tabs>{children}</Tabs> }}
86
+ />
87
+ ```
88
+
89
+ `renderRichText` takes it too, and its input type is exported as `RichTextRenderable`. The schema-modelling skill names both kinds.
90
+
91
+ ## 0.11.0
92
+
93
+ ### Minor Changes
94
+
95
+ - 84f4acb: **A `dateTime` field reads back in UTC.** A `dateTime` is stored as its instant in UTC at millisecond precision, whatever offset it was written in, and every read returns it that way: write `2026-09-30T23:30:00-05:00` and you read back `2026-10-01T04:30:00.000Z`. The offset you sent is not kept. A `date` field is unchanged: `YYYY-MM-DD`.
96
+
97
+ Comparing two values is now comparing two instants, and a `where` on a date field compares the way you would expect across offsets:
98
+
99
+ - `{ dried: { $lt: '2026-10-01' } }` keeps the instants before the UTC day of 1 October. It used to compare text, so `2026-09-30T23:30:00-05:00` — which is 04:30 UTC on the 1st — was listed.
100
+ - A day compares with an instant's UTC day: `{ dried: { $lte: '2026-10-01' } }` keeps every instant of that day, and `{ dried: '2026-10-01' }` matches any of them.
101
+ - An instant you compare with is read with its offset: `{ dried: { $gte: '2026-10-01T06:30:00+02:00' } }` is 04:30 UTC.
102
+ - `$inTheLast` and `$inTheNext` on a `dateTime` or `date` field match, counted in UTC from the moment of the read. They matched nothing before.
103
+
104
+ A value that names no date — a word, or a time with no offset such as `2026-10-01T10:00` — is refused with `400 REQUEST_INVALID`, as is `$inTheLast` on a field that holds no date. If you compared a stored `dateTime` with the text you wrote, compare instants instead:
105
+
106
+ ```ts
107
+ const sameInstant = Date.parse(entry.dried) === Date.parse('2026-09-30T23:30:00-05:00')
108
+ ```
109
+
110
+ - 8c5b215: **A change to shared fields is reported as `hasDraft`, not as the default locale's draft.** A read's `_metadata` reports unpublished work in two kinds, apart: `draftLocales` names the locales with unpublished edits of their own, and `hasDraft` says the shared (non-localized) fields have unpublished changes. Publishing any locale makes a shared change live, so it is pending in every locale, and no locale code is listed for it.
111
+
112
+ `draftLocales` used to list the default locale's code for a shared-field draft. It no longer does: an entry whose only pending change is a shared field now reads `draftLocales: []` with `hasDraft: true`, in every locale you read it in. A locale has unpublished work when `draftLocales` names it or `hasDraft` is `true`:
113
+
114
+ ```ts
115
+ const { draftLocales, hasDraft } = entry._metadata
116
+ const frenchHasPendingWork = hasDraft || draftLocales.includes('fr')
117
+ ```
118
+
119
+ A settings group's `_metadata` — on `GET /v1/settings/{groupId}` and on each written group of `GET /v1/settings` — now carries `hasDraft` too, as an entry's, a singleton's and a search hit's already did, and so does each entry a query returns. `LocalePublicationFields` includes it.
120
+
121
+ - 4614003: **A view filter names its field as the Studio's entry list does.** A field your schema declares is `fields.<id>` — `fields.featured`, `fields.seo.title` for a component's sub-field — and a system field is `_metadata.<id>`: `_metadata.status`, `_metadata.createdAt`, `_metadata.updatedAt`, the name the Content API already gives those values in an entry's `_metadata`. A field called `status` and the entry's own lifecycle are now both filterable on one schema, where deploy used to refuse a filter on either:
122
+
123
+ ```ts
124
+ defineView('openTickets', {
125
+ name: 'Open tickets',
126
+ scope: { type: 'contentType', contentType: 'ticket' },
127
+ filters: [
128
+ { field: 'fields.status', operator: 'is', value: 'open' },
129
+ { field: '_metadata.status', operator: 'is', value: 'active' },
130
+ ],
131
+ })
132
+ ```
133
+
134
+ A filter on a bare field id — `{ field: 'featured', … }` — is refused at `relyio deploy` and `relyio status` as a field the view's entries cannot be filtered by, at `views.<n>.filters.<n>.field`. Write `fields.featured`. A YAML schema's `views[].filters` take the same names.
135
+
136
+ **A view's `sort` names its field the same way**, because the Studio puts a view's order in the entries read beside its filters: `sort: { field: '_metadata.updatedAt', direction: 'desc' }`, or `fields.title`. Deploy now refuses a sort whose field is a bare id, or one the view's entries cannot be sorted by (a long text, for example), at `views.<n>.sort.field`. A view's `columns` and `metadataFields` are display settings and still name field ids as written.
137
+
3
138
  ## 0.10.0
4
139
 
5
140
  ### Minor Changes