@quillmark/wasm 0.98.0 → 0.100.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 +208 -1
- package/LICENSE +13 -0
- package/README.md +116 -64
- package/backends/pdfform/wasm.d.ts +336 -146
- package/backends/pdfform/wasm_bg.js +223 -122
- package/backends/pdfform/wasm_bg.wasm +0 -0
- package/backends/pdfform/wasm_bg.wasm.d.ts +3 -1
- package/backends/typst/wasm.d.ts +336 -146
- package/backends/typst/wasm_bg.js +223 -122
- package/backends/typst/wasm_bg.wasm +0 -0
- package/backends/typst/wasm_bg.wasm.d.ts +3 -1
- package/core/wasm.d.ts +163 -96
- package/core/wasm_bg.js +206 -102
- package/core/wasm_bg.wasm +0 -0
- package/core/wasm_bg.wasm.d.ts +3 -1
- package/package.json +2 -2
- package/runtime/runtime.d.ts +151 -76
- package/runtime/runtime.js +381 -100
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,212 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v0.100.0 - 2026-08-03
|
|
4
|
+
|
|
5
|
+
A content field gets one resting form, and the last reserved `$` key with no
|
|
6
|
+
reader is removed. All breaking changes are covered by
|
|
7
|
+
`docs/migrations/0.99-to-0.100.md`. Stored documents load unchanged, but a row
|
|
8
|
+
read through the bound door converges once: read-repair, not a schema-version
|
|
9
|
+
event. One ordering matters, and the guide's "Legacy data" section states it —
|
|
10
|
+
conform a stored population before exporting markdown from it.
|
|
11
|
+
|
|
12
|
+
- refactor(core,wasm,python)!: content fields have one resting form, enforced at
|
|
13
|
+
load. `Quill::conform(&mut doc)` is the primitive and `Quill::parse(md)`
|
|
14
|
+
(parse, then conform) the convenience — the documented primary ingestion path,
|
|
15
|
+
`quill.parse` / `quill.conform` on both bindings. A `richtext` field rests as
|
|
16
|
+
the canonical content object, a `plaintext` field as its **literal string**, so
|
|
17
|
+
the stored shape is a property of the codec instead of the construction lane:
|
|
18
|
+
`equals` and content hashes stop separating semantically identical documents.
|
|
19
|
+
The typed writer commits `plaintext` as a string, and `revise_field` diffs it
|
|
20
|
+
through the literal codec (a byte-identical revise of `a \*b\*` used to commit
|
|
21
|
+
`a *b*`). `Document::parse` / `Document.fromMarkdown` stay exactly as they
|
|
22
|
+
were, demoted to the transport/repair door. Conform is idempotent, a byte no-op
|
|
23
|
+
on an already-canonical document, and reports a `conform::*` warning where the
|
|
24
|
+
strict write refuses rather than retyping or rejecting; a `$quill` naming
|
|
25
|
+
another quill errors before any mutation (#1160, #1162). See
|
|
26
|
+
`docs/migrations/0.99-to-0.100.md`
|
|
27
|
+
- fix(core)!: markdown exported from a `plaintext` field resting as a content
|
|
28
|
+
object is markdown-escaped. Emit is schema-free and cannot tell a `plaintext`
|
|
29
|
+
content from a `richtext` one, so `a *literal* line` leaves as
|
|
30
|
+
`a \*literal\* line` and re-parses with the backslashes as characters — one
|
|
31
|
+
more layer per save cycle. Only the typed writer produced that rest, and the
|
|
32
|
+
string rest above deletes it rather than managing it: load, conform, and
|
|
33
|
+
re-store a population before exporting markdown from it. Markdown already
|
|
34
|
+
exported under ≤0.99 is corrupt at rest, its escapes indistinguishable from
|
|
35
|
+
authored ones, so re-export it from the conformed rows (#1159). See
|
|
36
|
+
`docs/migrations/0.99-to-0.100.md`
|
|
37
|
+
- fix(core,wasm,python)!: a `plaintext` field resting as a string reads through
|
|
38
|
+
the **literal** codec, not markdown — `note: 'a *literal* line'` read back as
|
|
39
|
+
`a literal line` while render and validation kept the asterisks. Only the
|
|
40
|
+
string lane was wrong; the committed-object lane always decoded correctly, so
|
|
41
|
+
a consumer that pre-escaped a `plaintext` field to survive the read drops the
|
|
42
|
+
escaping. Alongside it, `reader.get_content` / `reader.getContent` returns a
|
|
43
|
+
content field's `Content` corpus whichever lane stored it, so a consumer
|
|
44
|
+
holding a corpus editor stops branching on the wire shape. `EditError` gains
|
|
45
|
+
`FieldNotContent` (`edit::field_not_content`) for a declared type that is not a
|
|
46
|
+
content leaf; core adds `Card::field_plaintext_content` (#1154). See
|
|
47
|
+
`docs/migrations/0.99-to-0.100.md`
|
|
48
|
+
- refactor(core,wasm,python)!: card `$id` is removed — the reserved key, its
|
|
49
|
+
resolver (`Document::find_card` / `doc.cardIndexById` / `doc.card_index_by_id`),
|
|
50
|
+
the uniqueness contract (`EditError::CardIdCollision` / `EmptyCardId`, the
|
|
51
|
+
`parse::card_id_*` warnings, the storage rejection), `Card::id` /
|
|
52
|
+
`Payload::{id, set_id, take_id}` / `Document::{set_card_id, remove_card_id}`,
|
|
53
|
+
the `PayloadItem::Id` and `CardWire.id` wire members, and the projected `id` on
|
|
54
|
+
both bindings' card shape. Nothing in the engine read it and it never reached a
|
|
55
|
+
backend, so what is left after removing the machinery that served the resolver
|
|
56
|
+
is `$ext` with a reserved name. A block declaring `$id` no longer parses and a
|
|
57
|
+
blob carrying an `id` payload item no longer loads: a hard cutover, no
|
|
58
|
+
tolerate-and-ignore window. Per-card consumer keys move to `$ext` under a
|
|
59
|
+
namespace you own, with no uniqueness, no collision check, and no repair
|
|
60
|
+
(#1151). See `docs/migrations/0.99-to-0.100.md`
|
|
61
|
+
- refactor(content)!: `Content`, `Line`, `Mark`, and `Island` take
|
|
62
|
+
`#[non_exhaustive]` — the four public structs the 0.99 sweep missed, that pass
|
|
63
|
+
having run as two issues split by crate. Their literals give way to `new` plus
|
|
64
|
+
the `with_*` setters on the same terms as the rest of the API; every field stays
|
|
65
|
+
`pub`, so reading and assigning are unchanged. `Delta`, `Segment`, and
|
|
66
|
+
`BaseLengthMismatch` stay open deliberately and now say so in their rustdoc.
|
|
67
|
+
A Rust source break only: nothing about the wire, the canonical bytes, or the
|
|
68
|
+
bindings moves (#1146). See `docs/migrations/0.99-to-0.100.md`
|
|
69
|
+
- feat(core,wasm,python): `Diagnostic.args` — the facts `message` interpolates,
|
|
70
|
+
keyed by name, so a consumer with its own string table selects a sentence by
|
|
71
|
+
`code` and fills it itself. Values keep their JSON shape (a list arrives as a
|
|
72
|
+
list, a count as a number), engine prose never rides under a key, and a
|
|
73
|
+
formatter missing a key falls back to `message` wholesale. `prose/canon/ERROR.md`
|
|
74
|
+
§ "Diagnostic args" tabulates the keys per code and a test fails when code and
|
|
75
|
+
canon disagree (#1130)
|
|
76
|
+
- fix(core): the `$quill` mismatch message and hint name the pairing rather than
|
|
77
|
+
the verb. `check_quill_reference` gates every schema-bound door now, not the
|
|
78
|
+
render path alone, so a `quill.parse` failure no longer reads "was rendered
|
|
79
|
+
with". The codes (`quill::name_mismatch` / `quill::version_mismatch`) are
|
|
80
|
+
unchanged
|
|
81
|
+
- test(fuzz): the resting-form invariant gains a target, stated as three
|
|
82
|
+
properties — conform is a fixed point, parse-then-conform equals typed-write
|
|
83
|
+
per content field, and a document through the markdown surface and back settles
|
|
84
|
+
after one pass (exactly, for `plaintext`, whose codec is lossless both ways)
|
|
85
|
+
- docs: the cycle's stale pages are repaired. Both binding READMEs gain the bound
|
|
86
|
+
door and the corpus read, `revise_field` is documented per declared type on all
|
|
87
|
+
four surfaces instead of as a markdown-only richtext verb, and four canon claims
|
|
88
|
+
that outran the tree are corrected
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
## v0.99.0 - 2026-08-01
|
|
92
|
+
|
|
93
|
+
The 1.0.0 API freeze lands ahead of the tag, and the content codec closes its
|
|
94
|
+
last open gaps. All breaking changes are covered by
|
|
95
|
+
`docs/migrations/0.98-to-0.99.md`. Stored documents are unaffected: a `0.98` blob
|
|
96
|
+
loads byte-identically and `0.99` writes the same bytes for the same content.
|
|
97
|
+
|
|
98
|
+
- refactor(core,content,pdf,pdfform)!: the public API opens. 75 public types take
|
|
99
|
+
`#[non_exhaustive]` — nothing in the workspace carried it before — so an
|
|
100
|
+
exhaustive `match` needs a `_` arm and a struct literal gives way to `new` plus
|
|
101
|
+
`with_*` setters. `Backend` is sealed, `OutputFormat::ALL` and
|
|
102
|
+
`Content::RESERVED_{MARK_TYPES,LINE_KINDS,CONTAINERS}` become slices, and
|
|
103
|
+
`RenderOptions { .., ..Default::default() }` becomes
|
|
104
|
+
`RenderOptions::default().with_output_format(fmt)`. Four stay exhaustive and
|
|
105
|
+
say so: the storage DTOs, frozen per schema version, plus
|
|
106
|
+
`quillmark_pdf::FieldType`, `KnownIslandType`, and `Fidelity`, where an
|
|
107
|
+
out-of-crate `_` arm is silent (a field that draws nothing, an island dropped
|
|
108
|
+
from the projection, a fidelity rung nothing warns about). The rules are
|
|
109
|
+
canonized in `prose/canon/COMPATIBILITY.md` (#1090, #1103). See
|
|
110
|
+
`docs/migrations/0.98-to-0.99.md`
|
|
111
|
+
- refactor(core)!: the YAML engine leaves the public API. `QuillValue::from_yaml_str`
|
|
112
|
+
and `QuillConfig::schema_yaml` return `quillmark_core::YamlError` instead of
|
|
113
|
+
`serde_saphyr` types, so a `0.0.x` dependency release is no longer a break to
|
|
114
|
+
`quillmark-core`. The message is sanitized (the engine's own Rust API names are
|
|
115
|
+
stripped), and `from_yaml_str` gains the `MAX_YAML_DEPTH` budget its siblings
|
|
116
|
+
already carried (#1099, #1101). See `docs/migrations/0.98-to-0.99.md`
|
|
117
|
+
- fix(content)!: the reserved-name rule reaches the wire. `attrs` beside a
|
|
118
|
+
built-in discriminator resolved to the built-in and dropped the payload in
|
|
119
|
+
silence; the authored lane now refuses it on all four axes, where a host writes
|
|
120
|
+
it. Reading never got stricter — a blob from before a promotion still opens.
|
|
121
|
+
A table cell keeps its own unknown keys too, canonicalization now rewriting it
|
|
122
|
+
in place rather than minting a fresh `{text, marks}` (#1084, #1085, #1086,
|
|
123
|
+
#1092). See `docs/migrations/0.98-to-0.99.md`
|
|
124
|
+
- fix(content)!: opaque payload depth is bounded at `MAX_JSON_DEPTH` (128) on the
|
|
125
|
+
`Value` lane, where an unbounded one took the WASM module down with a
|
|
126
|
+
stack-overflow trap rather than a catchable error. The WASM guard sits on the
|
|
127
|
+
JS side of the boundary, since `serde_wasm_bindgen` recurses while building the
|
|
128
|
+
value, and covers every door that takes opaque host JSON: `install` and
|
|
129
|
+
`applyChange`, plus `makeCard`'s field values and `insertCard`'s payload items
|
|
130
|
+
(#1093). See `docs/migrations/0.98-to-0.99.md`
|
|
131
|
+
- fix(content)!: island `loss` becomes the fifth open set. An unrecognized class
|
|
132
|
+
round-trips verbatim instead of being rewritten to `unrepresentable`, so merely
|
|
133
|
+
opening a document no longer moves its content hash. `Loss` opens on the island
|
|
134
|
+
`type` axis' terms rather than the block axes': it becomes an opaque string
|
|
135
|
+
wrapper with `LOSSLESS` / `DEGRADED` / `UNREPRESENTABLE` consts, one value per
|
|
136
|
+
wire string, so a built-in's name has no second spelling and needs no
|
|
137
|
+
reserved-name rule. `Fidelity` is the closed view `Loss::fidelity` returns, and
|
|
138
|
+
is where a consumer switches; `Loss` consequently loses its `Copy` derive
|
|
139
|
+
(#1091, #1142). See `docs/migrations/0.98-to-0.99.md`
|
|
140
|
+
- refactor(core,typst,pdf)!: workspace-internal seams leave the published
|
|
141
|
+
surface. `quillmark-pdf`'s `reader`/`writer` modules and `quillmark_typst::emit`
|
|
142
|
+
become `#[doc(hidden)]`; the op-wire encoders emit an unknown's `attrs` in
|
|
143
|
+
caller key order, the redundant per-encoder sort having been dropped (canonical
|
|
144
|
+
content bytes are unchanged, the terminal sort still running) (#1095). See
|
|
145
|
+
`docs/migrations/0.98-to-0.99.md`
|
|
146
|
+
- fix(wasm)!: a `Quill` or `Document` from a second copy of `@quillmark/wasm` is
|
|
147
|
+
refused everywhere, as a `QuillmarkError` coded `runtime::foreign_handle` that
|
|
148
|
+
names the cause and hints `npm ls @quillmark/wasm`. 0.98 half-worked there:
|
|
149
|
+
`Engine` was duck-typed, so a quill from copy A rendered on an engine from copy
|
|
150
|
+
B at a per-copy clone cache nobody could see, while `Document.equals`,
|
|
151
|
+
`Quill.validate`, `Quill.resolve` and the typed writer met wasm-bindgen's bare
|
|
152
|
+
`expected instance of Document` at a value that *is* a `Document`. The check
|
|
153
|
+
covers `Engine` (`render`, `open`, `supportedFormats`, `supportsCanvas`),
|
|
154
|
+
`LiveSession.apply`, the writer and reader binds, and the three by-reference
|
|
155
|
+
core methods; a value that is not a handle at all keeps its own
|
|
156
|
+
`runtime::not_a_document` / `runtime::not_a_quill`. Nothing changes for a
|
|
157
|
+
one-copy install (#1132, #1136). See `docs/migrations/0.98-to-0.99.md`
|
|
158
|
+
- feat(wasm): `isUnknownLine` / `isUnknownContainer` / `isUnknownMark` /
|
|
159
|
+
`isUnknownIsland` answer known-vs-unknown on each open set, so a consumer no
|
|
160
|
+
longer enumerates built-in names in its own source. `ContentLineKind` is
|
|
161
|
+
re-exported from the package entry point, so a `setKind` op type-checks without
|
|
162
|
+
a cast
|
|
163
|
+
- feat(python): the Tier-1 gaps close. `doc.card(i)`, `doc.card_index_by_id(id)`,
|
|
164
|
+
and `doc.seed_overlay(kind)` are the single-card, `$id`, and seed reads WASM
|
|
165
|
+
already had, and the wheel ships `py.typed` plus stubs, so mypy and Pyright see
|
|
166
|
+
real signatures where the surface used to resolve to `Any` (#1011)
|
|
167
|
+
- fix(typst): four quill-load defects — a skipped asset, an unparseable
|
|
168
|
+
`typst.toml`, a skipped package file, a declared-but-absent entrypoint —
|
|
169
|
+
become `RenderResult` warnings (`typst::path_skipped`,
|
|
170
|
+
`typst::package_manifest`, `typst::package_entrypoint_missing`) instead of
|
|
171
|
+
`eprintln!` that wasm32 has nowhere to print (#1102)
|
|
172
|
+
- fix(wasm): the npm package states the license the workspace actually grants.
|
|
173
|
+
`package.json` declared `MIT OR Apache-2.0` where every Rust crate, the
|
|
174
|
+
workspace manifest, and the only `LICENSE` file in the tree are `Apache-2.0`,
|
|
175
|
+
and the package shipped no license text at all: `build-wasm.sh` copied
|
|
176
|
+
`LICENSE-MIT` and `LICENSE-APACHE`, neither of which exists. It now copies
|
|
177
|
+
`LICENSE`, or refuses to produce a package
|
|
178
|
+
- ci: the release gates the tag actually needs. New `package` (builds every
|
|
179
|
+
publishable crate from its own archive and asserts each ships its `LICENSE`),
|
|
180
|
+
`msrv` (holds `rust-version` to something true), and `audit` (bare `cargo
|
|
181
|
+
audit` over the lockfile) jobs; the workspace moves to edition 2024 and
|
|
182
|
+
declares MSRV 1.92. The `semver` job is dropped — it compared the tree's
|
|
183
|
+
unbumped version against itself — and `COMPATIBILITY.md` names the writer and
|
|
184
|
+
reviewer as what holds the promise instead (#1105, #1106, #1107, #1108)
|
|
185
|
+
- ci: the rustdoc gate covers the whole workspace. A bare `cargo doc` walks
|
|
186
|
+
default-members and never lints a crate outside it — the blind spot that let
|
|
187
|
+
the `Delta` links rot on the WASM surface and four more in the published
|
|
188
|
+
`quillmark-content`. `--workspace` needs no `--exclude` and covers the next
|
|
189
|
+
such crate on the day it lands
|
|
190
|
+
- test(fuzz): the four JSON decode lanes the bindings expose gain coverage
|
|
191
|
+
(#1104)
|
|
192
|
+
- docs(canon): `COMPATIBILITY.md` states the crate-API promise — what
|
|
193
|
+
`#[non_exhaustive]` does and does not buy, when to mark an enum, and what no
|
|
194
|
+
attribute sweep catches
|
|
195
|
+
- docs(all): the em-dash leaves comments and prose, folded to a colon, comma,
|
|
196
|
+
semicolon, or parentheses across ~2900 sites. `dense-prose` banned it while
|
|
197
|
+
every exemplar it named used it; the corpus now matches the rule. A handful of
|
|
198
|
+
diagnostic and CLI message strings repunctuate with it (`edit::body_only`,
|
|
199
|
+
`validation::must_fill`, the pdfform bind errors, `--help`); codes, severities,
|
|
200
|
+
and paths are unchanged. The character stays where it is the subject rather
|
|
201
|
+
than punctuation: the WinAnsi encoding table, the YAML en/em-dash fixtures, and
|
|
202
|
+
`docs/migrations/` (#1135)
|
|
203
|
+
- chore(core): `serde-saphyr` moves to `1.0`. The two call sites that built
|
|
204
|
+
`Options`/`SerializerOptions` with struct-literal-plus-`..Default::default()`
|
|
205
|
+
now go through the crate's own `options!`/`ser_options!`/`budget!` macros,
|
|
206
|
+
which the 1.0 release requires since both structs are `#[non_exhaustive]`.
|
|
207
|
+
`serde_saphyr` types stay out of `quillmark-core`'s public API (see the YAML
|
|
208
|
+
engine entry above), so nothing downstream moves
|
|
209
|
+
|
|
3
210
|
## v0.98.0 - 2026-07-28
|
|
4
211
|
|
|
5
212
|
Five breaking changes, all covered by `docs/migrations/0.97-to-0.98.md`.
|
|
@@ -148,7 +355,7 @@ Stored documents are unaffected: a `0.97` blob loads byte-identically and
|
|
|
148
355
|
- **breaking** core,wasm,python: a schema-bound read view — `Quill::view(&doc)` / `quill.view(doc)`, the read twin of `quill.writer(doc)`. `view.get(addr)` interprets each field by its declared type (a `richtext` field → markdown, a `plaintext` field → its literal text via the plaintext codec, every other type → its canonical value verbatim), returns absent as `undefined` / `None`, and — the authority the quill-free `getMarkdown` lacks — throws `UnknownField` for a name the schema does not declare and `FieldRichtextDecode` for a content field holding an undecodable value. Core `TypedReader::get` returns a `ReadValue` (`Markdown`/`Plaintext`/`Value`); `view.card(i)` is the card cursor; core adds `Card::field_plaintext` (the `to_plaintext` twin of `field_markdown`). **`getMarkdown`'s field half retires**: `getMarkdown` / `get_markdown` / `get_card_markdown` are now body-only (WASM `getMarkdown` takes a `CardAddr`, a present `field` throws; Python drops the `name` parameter) — a field's markdown is read through `view.get`. The quill-free body projection stays on `Document` (#978)
|
|
149
356
|
- **breaking** content: one delta-application contract — implicit trailing retain is `try_apply`'s semantics (a short delta retains the untouched remainder; the error is over-consumption only), `apply` panics on an over-long delta instead of clamping (clamping is silent corruption), and `extend_to_base` is removed. `split_line` / `join_line` rebase marks through their one-char `\n` splice with `map_pos` — the same mapping the text-delta channel uses — so marks no longer drift across line ops and `apply_field_change` canonicalizes once (a single terminal normalize instead of one per stage); line sync rebuilds in one forward pass instead of per-`\n` `Vec` splices. Mark ops are specified in final-text coordinates (post-delta, post-line-op — the frame they validate against) (#926, #987)
|
|
150
357
|
- **breaking** core: storage blobs tagged `@0.81.0` / `@0.82.0` fail as an unknown schema version — the read-only `V0_81_0` / `V0_82_0` DTO trees and their forward migrations are retired (nothing persisted on this lineage predates `@0.92.0`; `0.82.0` was yanked). `V0_92_0` stays the oldest shape read, and its payload types back the current write path. DOCUMENT_STORAGE.md records variant retirement as the policy when no stored population remains (#929)
|
|
151
|
-
- **breaking** core,wasm,python: the markdown projection stops appending a trailing newline — `to_markdown` projects a *value*, not a file, so `field_markdown` / `body_markdown` (WASM `getMarkdown` / `exportMarkdown`, Python `export_markdown` / `get_markdown`) no longer grow a `\n`; `writer.set("subject", "Hello")` reads back as `"Hello"`, not `"Hello\n"`.
|
|
358
|
+
- **breaking** core,wasm,python: the markdown projection stops appending a trailing newline — `to_markdown` projects a *value*, not a file, so `field_markdown` / `body_markdown` (WASM `getMarkdown` / `exportMarkdown`, Python `export_markdown` / `get_markdown`) no longer grow a `\n`; `writer.set("subject", "Hello")` reads back as `"Hello"`, not `"Hello\n"`. The content fixed point is unchanged (import is newline-insensitive) (#965)
|
|
152
359
|
- **breaking** all: rename the content genus off its codec's name — crate `quillmark-richtext` → `quillmark-content`, type `RichText` → `Content` (and `RichTextLine`/`RichTextContainer`/`RichTextMark`/`RichTextIsland` → `ContentLine`/…), const `RICHTEXT_MEDIA_TYPE` → `CONTENT_MEDIA_TYPE` and its wire string `application/quillmark-richtext+json` → `application/quillmark-content+json`, `#[serde(skip)]` companion caches `FieldSchema::{default,example}_corpus` → `_content`, `SegmentMap.corpus: Range<usize>` → `.content`, Typst-emitter `EmittedContent` → `Emission` (it is markup + source map, not a Typst `content` value). Schema tokens `richtext` / `plaintext`, `FieldType::{RichText,PlainText}` variants, and the codec-specific `field_richtext` / `FieldRichtext*` / `apply_field_richtext_change` / `richtext(inline)` surface are unchanged — those name codecs, not the model. Canonical body JSON is nameless, so stored documents don't migrate; `contentMediaType` consumers pin to the new spelling. Retires the informal "corpus" noun to end the code/prose split (#976)
|
|
153
360
|
- **breaking** core,wasm,python: `getMarkdown` / `get_markdown` / `get_card_markdown` stop conflating an absent field with a present-but-not-richtext one — a present field that does not decode as richtext (a scalar/array/object a `storeField` wrote) now throws `FieldRichtextDecode` instead of reading back `undefined` / `""`; absence still returns the absent shape. Core `Card::field_markdown` becomes `Option<Result<String, RichtextDecodeError>>` (the projection twin of `field_richtext`). Rule: absence returns, mismatch raises; read the raw value with `get` (#968)
|
|
154
361
|
- feat(core,wasm): typed, anchor-preserving field revise — `TypedWriter::revise_field` / `CardWriter::revise_field` and `writer.reviseField` / `writer.card(i).reviseField` wrap core `Card::revise_field_checked` (diff-rebase surviving anchors, then schema-conform the result); the schema-bound verb lives on the writer, where the schema is (#957, #966)
|
package/LICENSE
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Copyright (c) 2025 Nibs
|
|
2
|
+
|
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
you may not use this file except in compliance with the License.
|
|
5
|
+
You may obtain a copy of the License at
|
|
6
|
+
|
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
|
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
See the License for the specific language governing permissions and
|
|
13
|
+
limitations under the License.
|