leptris 1.9.292.0-ppc64le-linux → 1.9.304.0-ppc64le-linux
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +44 -0
- data/Rakefile +1 -1
- data/lib/leptris/version.rb +1 -1
- data/lib/leptris/xml/element.rb +112 -4
- data/lib/libleptris.so +0 -0
- data/vendor-src/README.md +1 -1
- data/vendor-src/libleptris/CHANGELOG.md +398 -0
- data/vendor-src/libleptris/CMakeLists.txt +6 -1
- data/vendor-src/libleptris/benchmarks/modification/bench_append_attr_pugixml.cpp +36 -0
- data/vendor-src/libleptris/bindings/rust/Cargo.lock +1 -1
- data/vendor-src/libleptris/bindings/rust/Cargo.toml +3 -1
- data/vendor-src/libleptris/cli/CLI_ARCHITECTURE.md +1 -1
- data/vendor-src/libleptris/cli/CMakeLists.txt +1 -0
- data/vendor-src/libleptris/cli/cli.c +36 -1
- data/vendor-src/libleptris/cli/cli.h +39 -2
- data/vendor-src/libleptris/cli/commands/xquery.c +1 -31
- data/vendor-src/libleptris/cli/commands/xslt.c +118 -0
- data/vendor-src/libleptris/cli/main.c +24 -22
- data/vendor-src/libleptris/cli/man/leptris-diff.1.adoc +40 -0
- data/vendor-src/libleptris/cli/man/leptris-validate.1.adoc +43 -0
- data/vendor-src/libleptris/cli/man/leptris-xquery.1.adoc +44 -0
- data/vendor-src/libleptris/cli/man/leptris-xslt.1.adoc +49 -0
- data/vendor-src/libleptris/src/CMakeLists.txt +26 -0
- data/vendor-src/libleptris/src/leptris/dom/compact.c +4 -0
- data/vendor-src/libleptris/src/leptris/dom/element_modify.c +83 -18
- data/vendor-src/libleptris/src/leptris/dom/mut_recycle.c +112 -0
- data/vendor-src/libleptris/src/leptris/dom/mut_recycle.h +50 -0
- data/vendor-src/libleptris/src/leptris/leptris.c +10 -4
- data/vendor-src/libleptris/src/leptris/leptris_internal.h +7 -0
- data/vendor-src/libleptris/src/leptris/xpath/collation.c +494 -0
- data/vendor-src/libleptris/src/leptris/xpath/collation.h +66 -0
- data/vendor-src/libleptris/src/leptris/xpath/ducet_table.h +21994 -0
- data/vendor-src/libleptris/src/leptris/xpath/evaluator_internal.h +6 -0
- data/vendor-src/libleptris/src/leptris/xpath/evaluator_operators.c +82 -5
- data/vendor-src/libleptris/src/leptris/xpath/evaluator_types.c +53 -8
- data/vendor-src/libleptris/src/leptris/xpath/functions.c +605 -68
- data/vendor-src/libleptris/src/leptris/xpath/functions_ext31.c +636 -65
- data/vendor-src/libleptris/src/leptris/xpath/vm.c +20 -4
- data/vendor-src/libleptris/src/leptris/xpath/xpath_public.c +3 -1
- data/vendor-src/libleptris/src/leptris/xquery/xquery.c +23 -1
- data/vendor-src/libleptris/test/CMakeLists.txt +14 -0
- data/vendor-src/libleptris/test/abi/test_dom_mutation_bugs.cpp +127 -0
- data/vendor-src/libleptris/test/cli/test_cli.cpp +61 -0
- data/vendor-src/libleptris/test/memory/test_mut_recycle.cpp +64 -0
- data/vendor-src/libleptris/test/xpath/test_collation.cpp +147 -0
- data/vendor-src/libleptris/test/xpath/test_xslt.cpp +114 -0
- data/vendor-src/libleptris/test/xquery/qt3/fn/abs.xml +2085 -0
- data/vendor-src/libleptris/test/xquery/qt3/fn/apply.xml +214 -0
- data/vendor-src/libleptris/test/xquery/qt3/fn/avg.xml +2199 -0
- data/vendor-src/libleptris/test/xquery/qt3/fn/ceiling.xml +961 -0
- data/vendor-src/libleptris/test/xquery/qt3/fn/collation-key.xml +331 -0
- data/vendor-src/libleptris/test/xquery/qt3/fn/filter.xml +387 -0
- data/vendor-src/libleptris/test/xquery/qt3/fn/floor.xml +968 -0
- data/vendor-src/libleptris/test/xquery/qt3/fn/fold-left.xml +426 -0
- data/vendor-src/libleptris/test/xquery/qt3/fn/fold-right.xml +363 -0
- data/vendor-src/libleptris/test/xquery/qt3/fn/for-each-pair.xml +585 -0
- data/vendor-src/libleptris/test/xquery/qt3/fn/for-each.xml +170 -0
- data/vendor-src/libleptris/test/xquery/qt3/fn/max.xml +2018 -0
- data/vendor-src/libleptris/test/xquery/qt3/fn/min.xml +1973 -0
- data/vendor-src/libleptris/test/xquery/qt3/fn/round-half-to-even.xml +1336 -0
- data/vendor-src/libleptris/test/xquery/qt3/fn/round.xml +2539 -0
- data/vendor-src/libleptris/test/xquery/qt3/fn/sum.xml +2043 -0
- data/vendor-src/libleptris/test/xquery/qt3/misc/UCACollation.xml +1120 -0
- data/vendor-src/libleptris/test/xquery/test_qt3.cpp +196 -14
- data/vendor-src/libleptris/test/xquery/test_xquery.cpp +293 -0
- data/vendor-src/libleptris/tools/gen_ducet_table.py +110 -0
- data/vendor-src/libleptris/vcpkg.json +1 -1
- data/vendor-src/libleptris/vendor/ducet/allkeys.txt +39435 -0
- metadata +33 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 785d9b7e66ab4d337f9002a5d4bc39d67c681a74e1dc04967990279e866e034c
|
|
4
|
+
data.tar.gz: e804fd26e2b9ab2d42cbdcd1e0af3972f5d1fc8dda4c6c690c0308bb9b0030ae
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 36067b9300f15de58055ce88d04ba035410585c79134270ce26fb54ff177f6366e904e248554548c23c7670666f6e90045cf6f756c51a037e5ccddb334770de4
|
|
7
|
+
data.tar.gz: 37873702eb98d9aa34dddbd8b876e650a0b1a9abb6a84798922161007a548953df1c3565bf624476862540bd76ff9716b6374ee73a424005182cf71ef79512eb
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,47 @@
|
|
|
1
|
+
## [1.9.304.0] - 2026-10-05
|
|
2
|
+
|
|
3
|
+
### Fixed
|
|
4
|
+
|
|
5
|
+
- **Cross-document appends adopt by copy — the xpath.rb:39 crash
|
|
6
|
+
class (engine #1528, libleptris 1.9.304)**: the engine's four
|
|
7
|
+
splice seams (append/prepend/insert_before/insert_after) no
|
|
8
|
+
longer splice foreign-document pointers raw — a scratch-doc
|
|
9
|
+
node appended into a live tree then freed with its document
|
|
10
|
+
dangled every adopted node and detonated the next compiled-XPath
|
|
11
|
+
eval (the metanorma sectioned-cleanup crash). The binding now
|
|
12
|
+
completes the contract: cross-document appends return the
|
|
13
|
+
INSTALLED in-tree wrapper (add_child, append_children, children=,
|
|
14
|
+
prepend_child, add_previous_sibling, add_next_sibling, wrap),
|
|
15
|
+
the #178 namespace lift runs before the splice, and the source
|
|
16
|
+
original is removed after (the historical move semantics —
|
|
17
|
+
source loses the node). Same-document appends stay
|
|
18
|
+
pointer-identical on the native fast path. The wrap face moves
|
|
19
|
+
self into the INSTALLED wrapper. Adoption specs include the
|
|
20
|
+
scratch-free + compiled-XPath crash shape.
|
|
21
|
+
|
|
22
|
+
## [1.9.302.0] - 2026-10-05
|
|
23
|
+
|
|
24
|
+
### Engine sync (libleptris 1.9.293-1.9.302)
|
|
25
|
+
|
|
26
|
+
Ten upstream releases ride in this pin — internal fixes and
|
|
27
|
+
performance with NO binding-surface changes (zero header deltas
|
|
28
|
+
292..302; the audit gate confirms 363/362 unchanged). Highlights:
|
|
29
|
+
|
|
30
|
+
- **XQuery correctness**: a false() bound anywhere read as truthy;
|
|
31
|
+
fn:not uses the effective boolean value; fn:abs(()) is empty;
|
|
32
|
+
string lt/gt are codepoint comparisons; fn:avg edge shapes;
|
|
33
|
+
aggregates atomize array arguments; `instance of xs:integer`
|
|
34
|
+
demands integrality; xs:byte/unsignedByte/unsignedInt
|
|
35
|
+
constructors; fn:sum#2's zero argument.
|
|
36
|
+
- **Collation**: UCA over the vendored DUCET table (slices 1-4) —
|
|
37
|
+
fn:normalize-unicode F&O conformance, collation-aware string
|
|
38
|
+
functions, 71+1078 QT3 cases adopted, implicit weights and
|
|
39
|
+
alternate=blanked.
|
|
40
|
+
- **Performance**: per-thread recycle of mutation blocks (DOM
|
|
41
|
+
create/build/free).
|
|
42
|
+
- **Rust bindings**: first tokenless crates.io release (leptris
|
|
43
|
+
v1.4.1) from CI.
|
|
44
|
+
|
|
1
45
|
## [1.9.292.0] - 2026-10-03
|
|
2
46
|
|
|
3
47
|
### Fixed
|
data/Rakefile
CHANGED
|
@@ -8,7 +8,7 @@ RSpec::Core::RakeTask.new(:spec)
|
|
|
8
8
|
# Pin for `rake compile` and the platform-gem builds. Keep in lockstep
|
|
9
9
|
# with .github/workflows/build.yml (which calls `rake compile`) and the
|
|
10
10
|
# CHANGELOG when libleptris releases.
|
|
11
|
-
LIBLEPTRIS_VERSION = "1.9.
|
|
11
|
+
LIBLEPTRIS_VERSION = "1.9.304"
|
|
12
12
|
# Vendored alongside libleptris for fn:normalize-unicode (TODO
|
|
13
13
|
# .restructure/20): built per platform with a RELOCATABLE @rpath
|
|
14
14
|
# install name, loaded by ffi.rb before libleptris so the
|
data/lib/leptris/version.rb
CHANGED
data/lib/leptris/xml/element.rb
CHANGED
|
@@ -419,6 +419,21 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
419
419
|
# C-bound insert (TODO.perf/14): gates + predicate + version
|
|
420
420
|
# bump + engine insert in one dispatch; Qnil = the child needs
|
|
421
421
|
# the namespace lift — the full path below handles it.
|
|
422
|
+
# Cross-document nodes skip the fast path entirely (1.9.304,
|
|
423
|
+
# #1528): the engine deep-copies them at the splice, so the
|
|
424
|
+
# full path's lift must run FIRST and the source original is
|
|
425
|
+
# removed after (move semantics).
|
|
426
|
+
if node.document && !node.document.equal?(@document)
|
|
427
|
+
ensure_writable!
|
|
428
|
+
unless Leptris::XML::Element.skip_adoption_lift?(node)
|
|
429
|
+
Leptris::XML::Element.lift_namespaces_for_adoption(node, namespaces)
|
|
430
|
+
end
|
|
431
|
+
Leptris::XML::FFI.check_status(
|
|
432
|
+
Leptris::XML::FFI.leptris_element_prepend_child(c_ptr, node.c_ptr))
|
|
433
|
+
Leptris::XML::Node.invalidate_cross_document!(node, @document)
|
|
434
|
+
node.parent&.remove_child(node)
|
|
435
|
+
return children.to_a.first
|
|
436
|
+
end
|
|
422
437
|
if native_fast_children?
|
|
423
438
|
unless Leptris::XML::Native.insert_binding_child(
|
|
424
439
|
@document, @c_address, node.c_ptr.address, 1).nil?
|
|
@@ -440,6 +455,20 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
440
455
|
# C-bound insert (TODO.perf/14): gates + predicate + version
|
|
441
456
|
# bump + engine insert in one dispatch; Qnil = the child needs
|
|
442
457
|
# the namespace lift — the full path below handles it.
|
|
458
|
+
# Cross-document: full path first (lift + engine copy), then
|
|
459
|
+
# the source original is removed; returns the installed copy
|
|
460
|
+
# (self's new next sibling).
|
|
461
|
+
if node.document && !node.document.equal?(@document)
|
|
462
|
+
ensure_writable!
|
|
463
|
+
unless Leptris::XML::Element.skip_adoption_lift?(node)
|
|
464
|
+
Leptris::XML::Element.lift_namespaces_for_adoption(node, namespaces)
|
|
465
|
+
end
|
|
466
|
+
Leptris::XML::FFI.check_status(
|
|
467
|
+
Leptris::XML::FFI.leptris_element_insert_after(c_ptr, node.c_ptr))
|
|
468
|
+
Leptris::XML::Node.invalidate_cross_document!(node, @document)
|
|
469
|
+
node.parent&.remove_child(node)
|
|
470
|
+
return self.next_sibling
|
|
471
|
+
end
|
|
443
472
|
if native_fast_children?
|
|
444
473
|
unless Leptris::XML::Native.insert_binding_child(
|
|
445
474
|
@document, @c_address, node.c_ptr.address, 2).nil?
|
|
@@ -461,6 +490,20 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
461
490
|
# C-bound insert (TODO.perf/14): gates + predicate + version
|
|
462
491
|
# bump + engine insert in one dispatch; Qnil = the child needs
|
|
463
492
|
# the namespace lift — the full path below handles it.
|
|
493
|
+
# Cross-document: full path first (lift + engine copy), then
|
|
494
|
+
# the source original is removed; returns the installed copy
|
|
495
|
+
# (self's new previous sibling).
|
|
496
|
+
if node.document && !node.document.equal?(@document)
|
|
497
|
+
ensure_writable!
|
|
498
|
+
unless Leptris::XML::Element.skip_adoption_lift?(node)
|
|
499
|
+
Leptris::XML::Element.lift_namespaces_for_adoption(node, namespaces)
|
|
500
|
+
end
|
|
501
|
+
Leptris::XML::FFI.check_status(
|
|
502
|
+
Leptris::XML::FFI.leptris_element_insert_before(c_ptr, node.c_ptr))
|
|
503
|
+
Leptris::XML::Node.invalidate_cross_document!(node, @document)
|
|
504
|
+
node.parent&.remove_child(node)
|
|
505
|
+
return self.previous_sibling
|
|
506
|
+
end
|
|
464
507
|
if native_fast_children?
|
|
465
508
|
unless Leptris::XML::Native.insert_binding_child(
|
|
466
509
|
@document, @c_address, node.c_ptr.address, 3).nil?
|
|
@@ -508,6 +551,9 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
508
551
|
nodes.each { |n| add_child(n) }
|
|
509
552
|
return
|
|
510
553
|
end
|
|
554
|
+
cross = nodes.any? do |n|
|
|
555
|
+
n.document && !n.document.equal?(@document)
|
|
556
|
+
end
|
|
511
557
|
if native_fast_children?
|
|
512
558
|
addrs = nodes.map { |n| n.c_ptr.address }
|
|
513
559
|
done = Leptris::XML::Native.append_binding_children(
|
|
@@ -516,14 +562,25 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
516
562
|
if done >= nodes.size
|
|
517
563
|
nodes.each do |n|
|
|
518
564
|
Leptris::XML::Node.invalidate_cross_document!(n, @document)
|
|
565
|
+
remove_from_source_original(n) if cross
|
|
519
566
|
end
|
|
520
|
-
|
|
567
|
+
# 1.9.304 (#1528): foreign children were adopted by COPY —
|
|
568
|
+
# hand back the installed in-tree wrappers.
|
|
569
|
+
return cross ? children.to_a.last(nodes.size) : nodes
|
|
521
570
|
end
|
|
522
571
|
nodes[0, done].each do |n|
|
|
523
572
|
Leptris::XML::Node.invalidate_cross_document!(n, @document)
|
|
573
|
+
remove_from_source_original(n) if cross
|
|
524
574
|
end
|
|
525
575
|
nodes = nodes[done..]
|
|
526
576
|
end
|
|
577
|
+
if cross
|
|
578
|
+
nodes.each do |n|
|
|
579
|
+
append_installed(n)
|
|
580
|
+
remove_from_source_original(n)
|
|
581
|
+
end
|
|
582
|
+
return children.to_a.last(nodes.size)
|
|
583
|
+
end
|
|
527
584
|
nodes.each { |n| add_child(n) }
|
|
528
585
|
nodes
|
|
529
586
|
end
|
|
@@ -565,9 +622,12 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
565
622
|
# add_child moves self (unlinks from old parent first), so no explicit
|
|
566
623
|
# remove_child needed — and trying to remove after the move corrupts
|
|
567
624
|
# the C tree (libleptris silently handles non-child args badly).
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
self
|
|
625
|
+
# The wrapper may be cross-document (parsed into a scratch doc,
|
|
626
|
+
# 1.9.304 #1528) — add_next_sibling returns the INSTALLED copy;
|
|
627
|
+
# move self into THAT.
|
|
628
|
+
installed = add_next_sibling(wrapper)
|
|
629
|
+
installed.add_child(self)
|
|
630
|
+
installed
|
|
571
631
|
end
|
|
572
632
|
|
|
573
633
|
# Deep copy in a NEW document via Document.copy_of (the single
|
|
@@ -608,6 +668,13 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
608
668
|
ensure_writable!
|
|
609
669
|
case node_or_markup
|
|
610
670
|
when Leptris::XML::Node
|
|
671
|
+
# Cross-document adoption (1.9.304, #1528): the engine
|
|
672
|
+
# deep-copies foreign nodes at the splice — the returned
|
|
673
|
+
# wrapper must be the INSTALLED copy, not the detached
|
|
674
|
+
# source. Route those through the re-resolving path.
|
|
675
|
+
cross = node_or_markup.document &&
|
|
676
|
+
!node_or_markup.document.equal?(@document)
|
|
677
|
+
return append_installed(node_or_markup) if cross
|
|
611
678
|
# C-bound append (TODO.perf/08-09): one dispatch runs the
|
|
612
679
|
# readonly/liveness gates, the provable no-op lift
|
|
613
680
|
# predicate, the version bump, and the engine append. Qnil
|
|
@@ -659,6 +726,47 @@ class Leptris::XML::Element < Leptris::XML::Node
|
|
|
659
726
|
raise ArgumentError, "add_child expects a Node or String, got #{node_or_markup.class}"
|
|
660
727
|
end
|
|
661
728
|
end
|
|
729
|
+
# Cross-document append (1.9.304, #1528): the engine deep-copies
|
|
730
|
+
# the foreign node at the splice, so the installed node is this
|
|
731
|
+
# element's LAST child — re-wrap it and return that. The binding
|
|
732
|
+
# keeps its historical MOVE semantics on top: namespaces are
|
|
733
|
+
# lifted BEFORE the copy (declarations survive), and the source
|
|
734
|
+
# original is removed after (the source loses the node, as
|
|
735
|
+
# every pre-304 caller observed).
|
|
736
|
+
def append_installed(node)
|
|
737
|
+
unless Leptris::XML::Element.skip_adoption_lift?(node)
|
|
738
|
+
Leptris::XML::Element.lift_namespaces_for_adoption(node, namespaces)
|
|
739
|
+
end
|
|
740
|
+
Leptris::XML::FFI.check_status(
|
|
741
|
+
Leptris::XML::FFI.leptris_element_append_child(c_ptr, node.c_ptr))
|
|
742
|
+
Leptris::XML::Node.invalidate_cross_document!(node, @document)
|
|
743
|
+
remove_from_source_original(node)
|
|
744
|
+
last_installed
|
|
745
|
+
end
|
|
746
|
+
private :append_installed
|
|
747
|
+
|
|
748
|
+
# 1.9.304 (#1528): a foreign child appended to this document was
|
|
749
|
+
# adopted by COPY — remove the ORIGINAL from its source document
|
|
750
|
+
# (the binding's move semantics). The wrapper's @parent may be
|
|
751
|
+
# unset for lazily minted wrappers, so ask the ENGINE for the
|
|
752
|
+
# source parent and remove through a wrapped handle.
|
|
753
|
+
def remove_from_source_original(node)
|
|
754
|
+
return unless node.document && !node.document.equal?(@document)
|
|
755
|
+
|
|
756
|
+
srcp = Leptris::XML::FFI.leptris_node_parent(node.c_ptr)
|
|
757
|
+
return if srcp.nil? || srcp.null?
|
|
758
|
+
|
|
759
|
+
Leptris::XML::Node.wrap(srcp, node.document).remove_child(node)
|
|
760
|
+
end
|
|
761
|
+
private :remove_from_source_original
|
|
762
|
+
|
|
763
|
+
# The last child, wrapped fresh through the document's identity
|
|
764
|
+
# cache (bypasses any stale self-side wrapper of the same node).
|
|
765
|
+
def last_installed kids = children.to_a
|
|
766
|
+
kids.last
|
|
767
|
+
end
|
|
768
|
+
private :last_installed
|
|
769
|
+
|
|
662
770
|
# The markup-append face appends in fragment order; the added
|
|
663
771
|
# nodes are this element's last +count+ children.
|
|
664
772
|
def last_added(count)
|
data/lib/libleptris.so
CHANGED
|
Binary file
|
data/vendor-src/README.md
CHANGED
|
@@ -1,5 +1,403 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [1.9.304] - 2026-10-05
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **DOM: nodes from a different document are adopted by copy, never
|
|
8
|
+
spliced raw (#1528)** — `leptris_element_append_child`,
|
|
9
|
+
`prepend_child`, `insert_before` and `insert_after` accepted a
|
|
10
|
+
pointer from ANY document and spliced it in as-is. A node parsed
|
|
11
|
+
into a scratch document (the cleanup pipelines' replacement
|
|
12
|
+
markup) left the live tree holding nodes and text content from
|
|
13
|
+
the scratch pool; freeing that scratch document dangled every
|
|
14
|
+
adopted node, and the next native walk crashed — the
|
|
15
|
+
`leptris_xpath_compiled_eval` segfault in the metanorma
|
|
16
|
+
sectioned-cleanup path, same family as #358/#362/#371. The four
|
|
17
|
+
seams now adopt foreign nodes through `leptris_element_copy`
|
|
18
|
+
(the `set_root` adoption contract since #371). Same-document
|
|
19
|
+
moves stay pointer-identical and zero-copy; detached chains keep
|
|
20
|
+
their link-only contract; the mutation hot path reuses its
|
|
21
|
+
existing document resolution. RED spec under ASAN
|
|
22
|
+
(`CrossDocumentAdoption` in the mutation suite: splice a parsed
|
|
23
|
+
scratch root, free the scratch document, then compiled-XPath and
|
|
24
|
+
serialize the live tree).
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
## [1.9.303] - 2026-10-05
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
|
|
32
|
+
- **XQuery: `fn:min`/`fn:max` coerce NaN** — any numeric NaN in the
|
|
33
|
+
input forces the NaN result; the comparison operators never select
|
|
34
|
+
it (`d > best` is false for NaN), so it is tracked explicitly
|
|
35
|
+
(K-SeqMAXFunc-16/27/28/31 and the MIN twins).
|
|
36
|
+
- **XQuery: duration comparisons in `fn:min`/`fn:max`/`fn:sum`/
|
|
37
|
+
`fn:avg`** — `xs:yearMonthDuration` items compare by months,
|
|
38
|
+
`xs:dayTimeDuration` by seconds, through the shared duration
|
|
39
|
+
formatters (`max(1 to 10 months)` = `P10M`, `cbcl-max-015` =
|
|
40
|
+
`P1Y1M`). Duration results of the four aggregates carry their
|
|
41
|
+
atomic type, so `instance of xs:*Duration` now holds
|
|
42
|
+
(fn-sum-1/3/4/6, fn-max/min-4/6/7, K-SeqSUMFunc-30, fn-avg-3/5).
|
|
43
|
+
- **XQuery: `fn:min`/`fn:max` over `xs:boolean`** — false < true,
|
|
44
|
+
and the result is a boolean, not a string (a string "false" would
|
|
45
|
+
read truthy at every assert-false site; cbcl-max-001, cbcl-min-002).
|
|
46
|
+
|
|
47
|
+
### Fixed
|
|
48
|
+
|
|
49
|
+
- **XQuery: `fn:sum#2` zero semantics** — the zero is the
|
|
50
|
+
EMPTY-sequence answer only, never an addend:
|
|
51
|
+
`sum((1 to 3), 17)` is 6 (was 23, fn-sum-12); an empty zero
|
|
52
|
+
leaves the empty result empty (`sum((), ())`, K-SeqSUMFunc-5);
|
|
53
|
+
a non-integer numeric zero adds back only for the empty sequence.
|
|
54
|
+
The libxslt suite (221/221) pins the XPath 1.0 side unchanged.
|
|
55
|
+
- **instance of the duration types** — the `'d'`-marked member check
|
|
56
|
+
recovers the flavor from the lexical shape (months-only is
|
|
57
|
+
yearMonth; dayTime never spells months) and the scalar check
|
|
58
|
+
requires the matching atomic type; both branches sit ahead of the
|
|
59
|
+
`is_num_ty` catch-all that would otherwise swallow every
|
|
60
|
+
non-string/boolean/node base name.
|
|
61
|
+
- **fn:sum atomization leak** — the duration prescan freed the
|
|
62
|
+
flattened array-carrier members' container but not the member
|
|
63
|
+
strings (Linux ASAN caught it: 10 bytes per `sum([1,2,3,4,5])`).
|
|
64
|
+
- QT3 re-pins from one verified run: fn/avg 173→175, fn/max
|
|
65
|
+
167→176, fn/min 167→175, fn/sum 204→209 (+24 adopted cases;
|
|
66
|
+
total pins 1148 → 1172; 61/61 suites green).
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
## [1.9.302] - 2026-10-04
|
|
71
|
+
|
|
72
|
+
### Added
|
|
73
|
+
|
|
74
|
+
- **XQuery: aggregates atomize array arguments** — `fn:avg`,
|
|
75
|
+
`fn:sum`, `fn:min` and `fn:max` take `xs:anyAtomicType*`, so a
|
|
76
|
+
dense array argument now contributes its members instead of
|
|
77
|
+
leaking the internal carrier: `avg([1,2,3,4,5])` = 3,
|
|
78
|
+
`sum([1,2,3,4,5])` = 15 (fn-avg-10, fn-sum-11, fn-min-19,
|
|
79
|
+
fn-max-19). A new shared decoder turns carriers keyed exactly
|
|
80
|
+
1..n into positional members, recursing into nested arrays
|
|
81
|
+
(`[[1,2],[3]]` → 1,2,3); maps and sparse carriers stay single
|
|
82
|
+
items, and `item()`-typed functions such as `fn:count` still see
|
|
83
|
+
an array as one item, per XDM. `fn:sum`'s per-item accumulation
|
|
84
|
+
moved into a helper so atomized members feed the existing
|
|
85
|
+
int64-exact and xs:float channels unchanged.
|
|
86
|
+
- **XQuery: `instance of` over the integer family demands
|
|
87
|
+
integrality** — `xs:integer` and its derived types
|
|
88
|
+
(int/long/short/byte/unsigned*/nonNegative…) now require an
|
|
89
|
+
integral value on both the sequence-member and scalar paths:
|
|
90
|
+
`fn:abs(1.1) instance of xs:integer` is false,
|
|
91
|
+
`fn:abs(-3) instance of xs:integer` is true.
|
|
92
|
+
|
|
93
|
+
### Fixed
|
|
94
|
+
|
|
95
|
+
- **XQuery: `fn:avg` edge shapes** — `avg(())` returns the empty
|
|
96
|
+
sequence under XQuery (XPath 1.0 parity untouched), and averages
|
|
97
|
+
over `xs:yearMonthDuration` format through the shared months
|
|
98
|
+
formatter (cbcl-avg-006: `P1Y`). Also freed a nested-array
|
|
99
|
+
carrier on an allocation-failure path in the atomizing decoder.
|
|
100
|
+
- QT3 re-pins from one verified run: fn/avg 164→173, fn/max
|
|
101
|
+
165→167, fn/min 165→167, fn/sum 203→204 (+14 adopted cases;
|
|
102
|
+
total pins 1134 → 1148; 61/61 suites green).
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
## [1.9.301] - 2026-10-04
|
|
107
|
+
|
|
108
|
+
### Fixed
|
|
109
|
+
|
|
110
|
+
- **XQuery: a false() bound anywhere read as truthy** — values
|
|
111
|
+
stringified to a marker-less "false" that every effective-boolean-
|
|
112
|
+
value site saw as a non-empty string, so `let $x := false() return
|
|
113
|
+
if ($x)...` took the then-branch and fold accumulators ignored a
|
|
114
|
+
false() zero (fold-left-004). Variable bindings, local:function
|
|
115
|
+
thunk arguments and the fold accumulators/items now carry the
|
|
116
|
+
boolean type marker; the EBV read honors it on singleton synthetic
|
|
117
|
+
members (XML 1.0 forbids raw control characters in text content,
|
|
118
|
+
so the marker cannot collide with document data); named-reference
|
|
119
|
+
closure calls spell marked booleans back to literals; both string
|
|
120
|
+
consumers render them as true/false. (PR #1522)
|
|
121
|
+
|
|
122
|
+
- **fn:not uses the effective boolean value** — not(false()) is
|
|
123
|
+
true while fn:empty((false())) stays false; real nodes keep the
|
|
124
|
+
XPath 1.0 existence rule, so libxml2 parity is unchanged.
|
|
125
|
+
(PR #1522)
|
|
126
|
+
|
|
127
|
+
- **fn:abs(()) is the empty sequence** under the XQuery surface;
|
|
128
|
+
**fn:sum((), $zero) returns a non-numeric zero verbatim**
|
|
129
|
+
(sum((), xs:dayTimeDuration("PT0S")) = PT0S); **fn:avg over
|
|
130
|
+
dayTimeDurations** divides the total seconds and formats
|
|
131
|
+
(fn-avg-4: PT5H30M); and the kept lexical zero in fn:sum is freed
|
|
132
|
+
on every result path (a Linux ASAN catch — macOS has no leak
|
|
133
|
+
sanitizer). (PR #1522)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
## [1.9.300] - 2026-10-04
|
|
138
|
+
|
|
139
|
+
### Added
|
|
140
|
+
|
|
141
|
+
- **XQuery: the xs:float and int64 typed-atom channels survive the
|
|
142
|
+
numeric function hop (+49 QT3 agreements; the fifteen-family batch
|
|
143
|
+
pins rise 1078→1127 and fn:round is fully green at 220/220)**.
|
|
144
|
+
fn:abs, fn:round, fn:round-half-to-even and the avg/min/max
|
|
145
|
+
aggregates copy the float tag onto their results (round over
|
|
146
|
+
xs:float("3.4028235E38") spells 3.4028235E38, not the widened
|
|
147
|
+
double digits); fn:sum keeps an exact long-long total when every
|
|
148
|
+
member is an integer lexical (fn:sum#2's zero seeds it); fn:floor,
|
|
149
|
+
fn:ceiling, fn:round and fn:abs pass int64 atoms through unchanged
|
|
150
|
+
(floor(xs:integer("999999999999999999")) stays exact, not 1e18).
|
|
151
|
+
(PR #1520)
|
|
152
|
+
|
|
153
|
+
- **xs:byte, xs:unsignedByte and xs:unsignedInt constructors** —
|
|
154
|
+
the three integer subtypes were unregistered and errored as
|
|
155
|
+
unknown functions. (PR #1520)
|
|
156
|
+
|
|
157
|
+
### Fixed
|
|
158
|
+
|
|
159
|
+
- **is_int results now also carry the double field** — eq and
|
|
160
|
+
boolean() comparisons read number_value, which the int64 path
|
|
161
|
+
left unset (sum((3,4,5)) eq 12 was false). (PR #1520)
|
|
162
|
+
|
|
163
|
+
- **The float-precision speller snaps into the float domain before
|
|
164
|
+
spelling** — double arithmetic can leave a non-float-representable
|
|
165
|
+
value whose shortest float round-trip never terminates, spelling
|
|
166
|
+
18-digit mantissas. (PR #1520)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
## [1.9.299] - 2026-10-04
|
|
171
|
+
|
|
172
|
+
### Added
|
|
173
|
+
|
|
174
|
+
- **XQuery: 1078 QT3 cases adopted across fifteen fn families** —
|
|
175
|
+
the numeric aggregates and rounding set (abs, avg, ceiling, floor,
|
|
176
|
+
max, min, round, round-half-to-even, sum) plus the higher-order
|
|
177
|
+
combiners (apply, filter, for-each, for-each-pair, fold-left,
|
|
178
|
+
fold-right), vendored verbatim from the upstream QT3 mirror. The
|
|
179
|
+
named per-suite excludes document the typed-atom lane's remaining
|
|
180
|
+
levers (xs:float spelling through aggregates, int64-exact
|
|
181
|
+
multi-argument sums, instance-of results). (PR #1518)
|
|
182
|
+
|
|
183
|
+
- **fn:sum#2** — the zero argument returns its value for the empty
|
|
184
|
+
sequence: fn:sum((1,2,3), 10) = 16, fn:sum((), 5) = 5.
|
|
185
|
+
(PR #1518)
|
|
186
|
+
|
|
187
|
+
### Fixed
|
|
188
|
+
|
|
189
|
+
- **Number spelling destroyed integral magnitudes** — the shared
|
|
190
|
+
trailing-zero trim ran on dot-free integer renderings, so 1e18
|
|
191
|
+
spelled "1"; it now runs only when a decimal point is present.
|
|
192
|
+
Negative zero also spells "-0" (fn:round(-0.01) = -0), and
|
|
193
|
+
fn:round preserves -0 for inputs in (-0.5, 0). (PR #1518)
|
|
194
|
+
|
|
195
|
+
- **Value carriers overflowed at DBL_MAX** — the ext31 string
|
|
196
|
+
carrier used the libxml2-parity printer, which renders DBL_MAX
|
|
197
|
+
with 18 significant digits; strtod read-back of that string
|
|
198
|
+
overflows to infinity, so fn:avg((1.7976931348623157E308)) was
|
|
199
|
+
INF. Carriers now use the injective shortest-round-trip spelling.
|
|
200
|
+
(PR #1518)
|
|
201
|
+
|
|
202
|
+
- **fn:sum rejected non-nodeset arguments** — single-item sequences
|
|
203
|
+
(passed as scalars by the XQuery adapter) errored out. (PR #1518)
|
|
204
|
+
|
|
205
|
+
- **fn:round()/fn:round-half-to-even(()) now return the empty
|
|
206
|
+
sequence** under the XQuery surface; XPath 1.0 coercion stays NaN
|
|
207
|
+
(libxml2 parity unchanged). (PR #1518)
|
|
208
|
+
|
|
209
|
+
- **fn:max/fn:min over non-numeric items** (xs:string, xs:anyURI)
|
|
210
|
+
compare in codepoint order and return the extreme item instead of
|
|
211
|
+
coercing to zero. (PR #1518)
|
|
212
|
+
|
|
213
|
+
- **QT3 runner: the standard `emptydoc`/`empty` environments
|
|
214
|
+
resolve to the empty context** — cases carrying them were
|
|
215
|
+
silently skipped (the abs/ceiling/floor/fold-* families adopted
|
|
216
|
+
zero cases before this). (PR #1518)
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
## [1.9.298] - 2026-10-04
|
|
221
|
+
|
|
222
|
+
### Added
|
|
223
|
+
|
|
224
|
+
- **XPath/XQuery: the six remaining string functions honor UCA
|
|
225
|
+
collations (DUCET lane closed)** — starts-with, ends-with,
|
|
226
|
+
contains, substring-before, substring-after and deep-equal now
|
|
227
|
+
route `http://www.w3.org/2013/collation/UCA` URIs through the
|
|
228
|
+
collation module. Matching scans codepoint-aligned windows under
|
|
229
|
+
the collation compare (every collation-unit boundary falls
|
|
230
|
+
between codepoints, so the scan is exact across contractions and
|
|
231
|
+
normalization); substring-before/after slice the leftmost-shortest
|
|
232
|
+
occurrence. deep-equal threads the collation into plain string
|
|
233
|
+
atoms and map values, while \x03-marked carriers keep their
|
|
234
|
+
value-space equality. **QT3 adoption rises by 73 cases**:
|
|
235
|
+
contains 46→63, starts-with 43→56, ends-with 34→48,
|
|
236
|
+
substring-before 12→26, substring-after 16→29, deep-equal
|
|
237
|
+
221→223. (PR #1515)
|
|
238
|
+
|
|
239
|
+
- **Collation: UCA 7.1.1 implicit weights** — codepoints with no
|
|
240
|
+
DUCET mapping sort by the two-CE algorithm: the registered
|
|
241
|
+
@implicitweights ranges (Tangut/Tangut-Supplement base FB00,
|
|
242
|
+
Khitan FB02, Nushu FB01) order ahead of the unassigned groups
|
|
243
|
+
(base 0xFB80 + 2*(cp>>15)), each codepoint emitting an
|
|
244
|
+
even/odd CE pair that orders within its group by codepoint.
|
|
245
|
+
(PR #1515)
|
|
246
|
+
|
|
247
|
+
- **Collation: `alternate=blanked`** (fn-contains-35..38) — the
|
|
248
|
+
third W3C UCA variable-weighting value is accepted, and variable
|
|
249
|
+
collation elements now leave every ordinary level under
|
|
250
|
+
shifted/blanked weighting (shifted retains its quaternary
|
|
251
|
+
re-add; blanked drops them wholly — ICU variable semantics).
|
|
252
|
+
(PR #1515)
|
|
253
|
+
|
|
254
|
+
- **CLI: `leptris xslt` command** — apply a stylesheet to a
|
|
255
|
+
document and print the serialized result tree:
|
|
256
|
+
`leptris xslt -s style.xsl input.xml`. Conventions match
|
|
257
|
+
xsltproc: INPUT is a file path, `-` or absent reads stdin. The
|
|
258
|
+
stylesheet compiles via leptris_xslt_parse_file, so
|
|
259
|
+
xsl:include/xsl:import resolve relative to the sheet. New
|
|
260
|
+
CLI_ERROR_XSLT exit code (7). (PR #1516)
|
|
261
|
+
|
|
262
|
+
- **CLI: man pages for every command** — new leptris-xslt.1 plus
|
|
263
|
+
the missing leptris-xquery.1, leptris-diff.1 and
|
|
264
|
+
leptris-validate.1, all registered for generation. (PR #1516)
|
|
265
|
+
|
|
266
|
+
### Fixed
|
|
267
|
+
|
|
268
|
+
- **CLI: `--help` was missing registered commands** — the usage
|
|
269
|
+
text hand-maintained its own command list, so the registered
|
|
270
|
+
`diff` and `xquery` commands were invisible. The help listing
|
|
271
|
+
now iterates the command registry (the descriptors are the
|
|
272
|
+
single source of truth), and adding a command can no longer
|
|
273
|
+
leave help stale. xquery's private stream reader is promoted to
|
|
274
|
+
shared cli_read_stream/cli_read_file. (PR #1516)
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
## [1.9.297] - 2026-10-03
|
|
279
|
+
|
|
280
|
+
### Fixed
|
|
281
|
+
|
|
282
|
+
- **XQuery: string lt/gt are codepoint value comparisons** — the
|
|
283
|
+
shared XPath 1.0 comparison numeric-coerced strings, so two
|
|
284
|
+
collation-keys that both spell numbers compared as equal numbers.
|
|
285
|
+
Both comparison sites now codepoint-compare string operands when
|
|
286
|
+
the evaluation is XQuery-flavored (the evaluator's string branch
|
|
287
|
+
and the VM twin); XPath 1.0 evaluation is unchanged.
|
|
288
|
+
(PR #1513)
|
|
289
|
+
|
|
290
|
+
- **XQuery: fn:codepoints-to-string encodes every item of its
|
|
291
|
+
sequence argument** — the collector collapsed a parenthesized
|
|
292
|
+
sequence to one scalar (its space-joined spelling), so
|
|
293
|
+
(37, 65500, 37) encoded as just the first codepoint. Collation-key
|
|
294
|
+
adoption rises to 12/56 run-and-agree. (PR #1513)
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
## [1.9.296] - 2026-10-03
|
|
299
|
+
|
|
300
|
+
### Added
|
|
301
|
+
|
|
302
|
+
- **XPath/XQuery: 71 QT3 collation cases adopted (DUCET slice 4)** —
|
|
303
|
+
the misc/UCACollation corpus (62 cases: the UCA ordering battery
|
|
304
|
+
over DUCET weights) and fn/collation-key (9 cases). The collation
|
|
305
|
+
URI grammar now parses the W3C semicolon-separated parameter form,
|
|
306
|
+
accepts lang= (DUCET is the root locale), and applies
|
|
307
|
+
caseFirst=upper|lower by swapping the DUCET case tertiaries.
|
|
308
|
+
fn:collation-key gains its 1-argument form (the static default
|
|
309
|
+
collation; codepoint keys are the UTF-8 bytes) and its keys are
|
|
310
|
+
lowercase hex — injective AND order-preserving, so lt/gt on two
|
|
311
|
+
keys is byte-wise key order. The QT3 runner now credits an
|
|
312
|
+
error alternative inside any-of for a failed evaluation, gates the
|
|
313
|
+
optional simple-uca-fallback feature (this engine raises FOCH0002
|
|
314
|
+
per the strict F&O reading), and lifts the pre-DUCET
|
|
315
|
+
collation/UCA exclusion under a DUCET build. (PR #1511)
|
|
316
|
+
|
|
317
|
+
### Fixed
|
|
318
|
+
|
|
319
|
+
- the DUCET implicit-weight fallback dropped a mask that broke
|
|
320
|
+
ordering between high codepoints. (PR #1511)
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
## [1.9.295] - 2026-10-03
|
|
325
|
+
|
|
326
|
+
### Added
|
|
327
|
+
|
|
328
|
+
- **XPath: collation-aware functions over the vendored DUCET
|
|
329
|
+
(slices 2-3)** — fn:compare's 3-argument form honors its collation
|
|
330
|
+
URI (unknown needed collations raise FOCH0002; unknown-but-unneeded
|
|
331
|
+
ones are ignored per the QT3 any-of expectations);
|
|
332
|
+
fn:collation-key($key, $collation) is new, returning the UCA sort
|
|
333
|
+
key as base64 text (the result-type enum is ABI-frozen with no
|
|
334
|
+
binary kind; base64 is injective, so key equality is byte equality);
|
|
335
|
+
fn:index-of, fn:distinct-values and fn:sort route plain-string
|
|
336
|
+
comparisons through the collation while typed atoms keep their
|
|
337
|
+
value-space equality. DUCET-gated: builds without utf8proc keep the
|
|
338
|
+
previous behavior. (PRs #1508, #1509)
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
## [1.9.294] - 2026-10-03
|
|
343
|
+
|
|
344
|
+
### Added
|
|
345
|
+
|
|
346
|
+
- **XPath: UCA collation over the vendored DUCET table (slice 1)** —
|
|
347
|
+
`http://www.w3.org/2013/collation/UCA` collation URIs parse
|
|
348
|
+
`strength` and `alternate` parameters per F&O 3.x (defaults:
|
|
349
|
+
tertiary, non-ignorable) and compare through multi-level sort keys:
|
|
350
|
+
NFD (utf8proc) → DUCET weights (Unicode 16.0.0, 38,443 single
|
|
351
|
+
codepoints + 964 contractions, vendored with an offline table
|
|
352
|
+
generator) → primary/secondary/tertiary levels with 0x00
|
|
353
|
+
separators; `alternate=shifted` demotes variable primaries to the
|
|
354
|
+
quaternary level. Codepoint and html-ascii-case-insensitive
|
|
355
|
+
collations keep their semantics. Unmapped codepoints get a coarse
|
|
356
|
+
deterministic fallback (the full UCA 7.1.1 implicit-weight
|
|
357
|
+
algorithm is a later slice). New CMake option
|
|
358
|
+
`LEPTRIS_ENABLE_DUCET` (default on, requires utf8proc; builds
|
|
359
|
+
without utf8proc degrade to codepoint collation). Function
|
|
360
|
+
wiring (fn:compare, fn:collation-key, default collation) and
|
|
361
|
+
QT3 UCA case adoption are the next slices. (PR #1506)
|
|
362
|
+
|
|
363
|
+
- **XQuery: fn:normalize-unicode F&O conformance** — the
|
|
364
|
+
single-argument form normalizes with NFC instead of returning the
|
|
365
|
+
input unchanged; unknown normalization forms raise FOCH0003
|
|
366
|
+
instead of silently falling back to NFC; the form argument is
|
|
367
|
+
case-insensitive with surrounding whitespace tolerated
|
|
368
|
+
(FULLY-NORMALIZED is gone from F&O 3.x; its satisfied="false"
|
|
369
|
+
twins run). utf8proc-gated end to end; the 29-run
|
|
370
|
+
fn/normalize-unicode QT3 family is adopted. (PR #1505)
|
|
371
|
+
|
|
372
|
+
### Fixed
|
|
373
|
+
|
|
374
|
+
- DUCET builds without utf8proc (cppcheck leg, Rust lane, ILP32,
|
|
375
|
+
Windows static) degrade to codepoint collation with a warning
|
|
376
|
+
instead of failing the configure. (PR #1506)
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
## [1.9.293] - 2026-10-03
|
|
381
|
+
|
|
382
|
+
### Performance
|
|
383
|
+
|
|
384
|
+
- DOM: per-thread recycle of mutation blocks. Document create/build/free
|
|
385
|
+
cycles now park their elem (45KB), name (4KB) and attr (5KB) blocks on
|
|
386
|
+
thread-local free-lists instead of returning them to malloc — the next
|
|
387
|
+
cycle carves from hot pages instead of re-faulting fresh ones. Byte-capped
|
|
388
|
+
(8MB elem, 4MB name/attr); POSIX threads drain via a pthread_key
|
|
389
|
+
destructor at thread exit, and `leptris_explicit_cleanup()` drains
|
|
390
|
+
synchronously. Mutation doc-cycle row: 19.4 → 15.1 ns/child (−22%), gap
|
|
391
|
+
to pugixml on that shape narrows 1.66x → 1.29x. Single-document append
|
|
392
|
+
throughput is unchanged. (PR #1503)
|
|
393
|
+
|
|
394
|
+
### Rust bindings
|
|
395
|
+
|
|
396
|
+
- First fully tokenless crates.io release: leptris v1.4.1 published from CI
|
|
397
|
+
via crates.io Trusted Publishing (`rust-lang/crates-io-auth-action` —
|
|
398
|
+
cargo itself has no native OIDC support). The Rust release lane now needs
|
|
399
|
+
no registry token of any kind. (PRs #1497, #1499–#1502)
|
|
400
|
+
|
|
3
401
|
## [1.9.292] - 2026-10-02
|
|
4
402
|
|
|
5
403
|
### Added
|