Repository: goccy/go-yaml
Pull request #642 (2025-02-07 20:55:15)

feat: support receiving ast.Node in custom unmarshalers
The `go-yaml` library in addition to custom unmarshalers, also allows partial unmarshaling into `ast.Node` struct members.\
This can be useful when working with dynamic documents.

Unfortunately this is not enough when there is a need to have a custom type which needs to be constructed out of `ast.Node` but you also would like to avoid inner unmarshaling inside `TextUnmarshaler`.

In my case, this is also useful in order to provide a detailed information about position in returned syntax error.

### Use-case demo

Let's assume you've an application which can accept some dynamic value which needs to be validated later.

Application accepts an input something like this:

```yaml
tasks:
  build:
    - action: foo
      timeout: 10s
      with:
        # Here is a dynamic content
        key: value
        items:
          - 1
          - ${foo.bar.baz} # App supports might want to pre-process some fields
          - true
```

Most of the content is static except `tasks.[*].with` which can have different body depending on context.\
Currently in order to process that, you would need to unmarshal whole document into a struct below and process `ast.Node` fields separately.

```go
type Doc struct {
	Tasks map[string]struct{
		Action string
		With ast.Node
	}
}
```

This PR introduces `NodeUnmarshaler` and `NodeUnmarshalerContext` interfaces that allow moving AST traversal into an unmarshaler.

### Testing

I've added tests for both new interfaces into `yaml_test.go`


codecov-commenter: :warning: Please install the !['codecov app svg image'](https://github.com/codecov/engineering-team/assets/152432831/e90313f4-9d3a-4b63-8b54-cfe14e7ec20d) to ensure uploads and comments are reliably processed by Codecov.

## [Codecov](https://app.codecov.io/gh/goccy/go-yaml/pull/642?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Masaaki+Goshima) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 77.36%. Comparing base [(`8d4b192`)](https://app.codecov.io/gh/goccy/go-yaml/commit/8d4b192f87ebaceb061adfe9bd3ff32f41d01f49?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Masaaki+Goshima) to head [(`423164a`)](https://app.codecov.io/gh/goccy/go-yaml/commit/423164a5f48ed0728bf0e589e6c6007e8b223095?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Masaaki+Goshima).

:exclamation: Your organization needs to install the [Codecov GitHub app](https://github.com/apps/codecov/installations/select_target) to enable full functionality.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master     #642      +/-   ##
==========================================
+ Coverage   77.34%   77.36%   +0.01%     
==========================================
  Files          22       22              
  Lines        7905     7903       -2     
==========================================
  Hits         6114     6114              
+ Misses       1372     1370       -2     
  Partials      419      419              
```

</details>

Patch:
diff --git a/.gitignore b/.gitignore
index 869fe9f..47952bd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
 bin/
+.idea/
 cover.out
diff --git a/ast/ast.go b/ast/ast.go
index 47c6dd2..3882850 100644
--- a/ast/ast.go
+++ b/ast/ast.go
@@ -51,6 +51,8 @@ const (
 	MappingValueType
 	// SequenceType type identifier for sequence node
 	SequenceType
+	// SequenceEntryType type identifier for sequence entry node
+	SequenceEntryType
 	// AnchorType type identifier for anchor node
 	AnchorType
 	// AliasType type identifier for alias node
@@ -98,6 +100,8 @@ func (t NodeType) String() string {
 		return "MappingValue"
 	case SequenceType:
 		return "Sequence"
+	case SequenceEntryType:
+		return "SequenceEntry"
 	case AnchorType:
 		return "Anchor"
 	case AliasType:
@@ -148,6 +152,8 @@ func (t NodeType) YAMLName() string {
 		return "value"
 	case SequenceType:
 		return "sequence"
+	case SequenceEntryType:
+		return "value"
 	case AnchorType:
 		return "anchor"
 	case AliasType:
@@ -1351,10 +1357,12 @@ func (n *MappingKeyNode) IsMergeKey() bool {
 // MappingValueNode type of mapping value
 type MappingValueNode struct {
 	*BaseNode
-	Start       *token.Token
-	Key         MapKeyNode
-	Value       Node
-	FootComment *CommentGroupNode
+	Start        *token.Token // delimiter token ':'.
+	CollectEntry *token.Token // collect entry token ','.
+	Key          MapKeyNode
+	Value        Node
+	FootComment  *CommentGroupNode
+	IsFlowStyle  bool
 }
 
 // Replace replace value node.
@@ -1391,6 +1399,7 @@ func (n *MappingValueNode) AddColumn(col int) {
 
 // SetIsFlowStyle set value to IsFlowStyle field recursively.
 func (n *MappingValueNode) SetIsFlowStyle(isFlow bool) {
+	n.IsFlowStyle = isFlow
 	switch value := n.Value.(type) {
 	case *MappingNode:
 		value.SetIsFlowStyle(isFlow)
@@ -1429,7 +1438,7 @@ func (n *MappingValueNode) toString() string {
 	keyComment := n.Key.GetComment()
 	if _, ok := n.Value.(ScalarNode); ok {
 		return fmt.Sprintf("%s%s: %s", space, n.Key.String(), n.Value.String())
-	} else if keyIndentLevel < valueIndentLevel {
+	} else if keyIndentLevel < valueIndentLevel && !n.IsFlowStyle {
 		if keyComment != nil {
 			return fmt.Sprintf(
 				"%s%s: %s\n%s",
@@ -1522,6 +1531,7 @@ type SequenceNode struct {
 	IsFlowStyle       bool
 	Values            []Node
 	ValueHeadComments []*CommentGroupNode
+	Entries           []*SequenceEntryNode
 	FootComment       *CommentGroupNode
 }
 
@@ -1667,6 +1677,65 @@ func (n *SequenceNode) MarshalYAML() ([]byte, error) {
 	return []byte(n.String()), nil
 }
 
+// SequenceEntryNode is the sequence entry.
+type SequenceEntryNode struct {
+	*BaseNode
+	HeadComment *CommentGroupNode // head comment.
+	LineComment *CommentGroupNode // line comment e.g.) - # comment.
+	Start       *token.Token      // entry token.
+	Value       Node              // value node.
+}
+
+// String node to text
+func (n *SequenceEntryNode) String() string {
+	return "" // TODO
+}
+
+// GetToken returns token instance
+func (n *SequenceEntryNode) GetToken() *token.Token {
+	return n.Start
+}
+
+// Type returns type of node
+func (n *SequenceEntryNode) Type() NodeType {
+	return SequenceEntryType
+}
+
+// AddColumn add column number to child nodes recursively
+func (n *SequenceEntryNode) AddColumn(col int) {
+	n.Start.AddColumn(col)
+}
+
+// SetComment set line comment.
+func (n *SequenceEntryNode) SetComment(cm *CommentGroupNode) error {
+	n.LineComment = cm
+	return nil
+}
+
+// Comment returns comment token instance
+func (n *SequenceEntryNode) GetComment() *CommentGroupNode {
+	return n.LineComment
+}
+
+// MarshalYAML
+func (n *SequenceEntryNode) MarshalYAML() ([]byte, error) {
+	return []byte(n.String()), nil
+}
+
+func (n *SequenceEntryNode) Read(p []byte) (int, error) {
+	return readNode(p, n)
+}
+
+// SequenceEntry creates SequenceEntryNode instance.
+func SequenceEntry(start *token.Token, value Node, headComment *CommentGroupNode) *SequenceEntryNode {
+	return &SequenceEntryNode{
+		BaseNode:    &BaseNode{},
+		HeadComment: headComment,
+		Start:       start,
+		Value:       value,
+	}
+}
+
 // SequenceMergeValue creates SequenceMergeValueNode instance.
 func SequenceMergeValue(values ...MapNode) *SequenceMergeValueNode {
 	return &SequenceMergeValueNode{
diff --git a/context.go b/context.go
index aa17d3c..133f05e 100644
--- a/context.go
+++ b/context.go
@@ -2,7 +2,10 @@ package yaml
 
 import "context"
 
-type ctxMergeKey struct{}
+type (
+	ctxMergeKey  struct{}
+	ctxAnchorKey struct{}
+)
 
 func withMerge(ctx context.Context) context.Context {
 	return context.WithValue(ctx, ctxMergeKey{}, true)
@@ -15,3 +18,20 @@ func isMerge(ctx context.Context) bool {
 	}
 	return v
 }
+
+func withAnchor(ctx context.Context, name string) context.Context {
+	anchorMap := getAnchorMap(ctx)
+	if anchorMap == nil {
+		anchorMap = make(map[string]struct{})
+	}
+	anchorMap[name] = struct{}{}
+	return context.WithValue(ctx, ctxAnchorKey{}, anchorMap)
+}
+
+func getAnchorMap(ctx context.Context) map[string]struct{} {
+	v, ok := ctx.Value(ctxAnchorKey{}).(map[string]struct{})
+	if !ok {
+		return nil
+	}
+	return v
+}
diff --git a/decode.go b/decode.go
index 0191a39..6bb2e72 100644
--- a/decode.go
+++ b/decode.go
@@ -19,6 +19,7 @@ import (
 
 	"github.com/goccy/go-yaml/ast"
 	"github.com/goccy/go-yaml/internal/errors"
+	"github.com/goccy/go-yaml/internal/format"
 	"github.com/goccy/go-yaml/parser"
 	"github.com/goccy/go-yaml/token"
 )
@@ -28,7 +29,6 @@ type Decoder struct {
 	reader               io.Reader
 	referenceReaders     []io.Reader
 	anchorNodeMap        map[string]ast.Node
-	aliasValueMap        map[*ast.AliasNode]any
 	anchorValueMap       map[string]reflect.Value
 	customUnmarshalerMap map[reflect.Type]func(interface{}, []byte) error
 	commentMaps          []CommentMap
@@ -53,7 +53,6 @@ func NewDecoder(r io.Reader, opts ...DecodeOption) *Decoder {
 	return &Decoder{
 		reader:               r,
 		anchorNodeMap:        map[string]ast.Node{},
-		aliasValueMap:        make(map[*ast.AliasNode]any),
 		anchorValueMap:       map[string]reflect.Value{},
 		customUnmarshalerMap: map[reflect.Type]func(interface{}, []byte) error{},
 		opts:                 opts,
@@ -116,8 +115,8 @@ func (d *Decoder) castToFloat(v interface{}) interface{} {
 	return 0
 }
 
-func (d *Decoder) mapKeyNodeToString(node ast.MapKeyNode) (string, error) {
-	key, err := d.nodeToValue(node)
+func (d *Decoder) mapKeyNodeToString(ctx context.Context, node ast.MapKeyNode) (string, error) {
+	key, err := d.nodeToValue(ctx, node)
 	if err != nil {
 		return "", err
 	}
@@ -130,7 +129,7 @@ func (d *Decoder) mapKeyNodeToString(node ast.MapKeyNode) (string, error) {
 	return fmt.Sprint(key), nil
 }
 
-func (d *Decoder) setToMapValue(node ast.Node, m map[string]interface{}) error {
+func (d *Decoder) setToMapValue(ctx context.Context, node ast.Node, m map[string]interface{}) error {
 	d.stepIn()
 	defer d.stepOut()
 	if d.isExceededMaxDepth() {
@@ -147,16 +146,16 @@ func (d *Decoder) setToMapValue(node ast.Node, m map[string]interface{}) error {
 			}
 			iter := value.MapRange()
 			for iter.Next() {
-				if err := d.setToMapValue(iter.KeyValue(), m); err != nil {
+				if err := d.setToMapValue(ctx, iter.KeyValue(), m); err != nil {
 					return err
 				}
 			}
 		} else {
-			key, err := d.mapKeyNodeToString(n.Key)
+			key, err := d.mapKeyNodeToString(ctx, n.Key)
 			if err != nil {
 				return err
 			}
-			v, err := d.nodeToValue(n.Value)
+			v, err := d.nodeToValue(ctx, n.Value)
 			if err != nil {
 				return err
 			}
@@ -164,7 +163,7 @@ func (d *Decoder) setToMapValue(node ast.Node, m map[string]interface{}) error {
 		}
 	case *ast.MappingNode:
 		for _, value := range n.Values {
-			if err := d.setToMapValue(value, m); err != nil {
+			if err := d.setToMapValue(ctx, value, m); err != nil {
 				return err
 			}
 		}
@@ -175,7 +174,7 @@ func (d *Decoder) setToMapValue(node ast.Node, m map[string]interface{}) error {
 	return nil
 }
 
-func (d *Decoder) setToOrderedMapValue(node ast.Node, m *MapSlice) error {
+func (d *Decoder) setToOrderedMapValue(ctx context.Context, node ast.Node, m *MapSlice) error {
 	d.stepIn()
 	defer d.stepOut()
 	if d.isExceededMaxDepth() {
@@ -192,16 +191,16 @@ func (d *Decoder) setToOrderedMapValue(node ast.Node, m *MapSlice) error {
 			}
 			iter := value.MapRange()
 			for iter.Next() {
-				if err := d.setToOrderedMapValue(iter.KeyValue(), m); err != nil {
+				if err := d.setToOrderedMapValue(ctx, iter.KeyValue(), m); err != nil {
 					return err
 				}
 			}
 		} else {
-			key, err := d.mapKeyNodeToString(n.Key)
+			key, err := d.mapKeyNodeToString(ctx, n.Key)
 			if err != nil {
 				return err
 			}
-			value, err := d.nodeToValue(n.Value)
+			value, err := d.nodeToValue(ctx, n.Value)
 			if err != nil {
 				return err
 			}
@@ -209,7 +208,7 @@ func (d *Decoder) setToOrderedMapValue(node ast.Node, m *MapSlice) error {
 		}
 	case *ast.MappingNode:
 		for _, value := range n.Values {
-			if err := d.setToOrderedMapValue(value, m); err != nil {
+			if err := d.setToOrderedMapValue(ctx, value, m); err != nil {
 				return err
 			}
 		}
@@ -340,7 +339,7 @@ func (d *Decoder) addCommentToMap(path string, comment *Comment) {
 	})
 }
 
-func (d *Decoder) nodeToValue(node ast.Node) (any, error) {
+func (d *Decoder) nodeToValue(ctx context.Context, node ast.Node) (any, error) {
 	d.stepIn()
 	defer d.stepOut()
 	if d.isExceededMaxDepth() {
@@ -365,7 +364,7 @@ func (d *Decoder) nodeToValue(node ast.Node) (any, error) {
 		return n.GetValue(), nil
 	case *ast.TagNode:
 		if n.Directive != nil {
-			v, err := d.nodeToValue(n.Value)
+			v, err := d.nodeToValue(ctx, n.Value)
 			if err != nil {
 				return nil, err
 			}
@@ -376,17 +375,17 @@ func (d *Decoder) nodeToValue(node ast.Node) (any, error) {
 		}
 		switch token.ReservedTagKeyword(n.Start.Value) {
 		case token.TimestampTag:
-			t, _ := d.castToTime(n.Value)
+			t, _ := d.castToTime(ctx, n.Value)
 			return t, nil
 		case token.IntegerTag:
-			v, err := d.nodeToValue(n.Value)
+			v, err := d.nodeToValue(ctx, n.Value)
 			if err != nil {
 				return nil, err
 			}
 			i, _ := strconv.Atoi(fmt.Sprint(v))
 			return i, nil
 		case token.FloatTag:
-			v, err := d.nodeToValue(n.Value)
+			v, err := d.nodeToValue(ctx, n.Value)
 			if err != nil {
 				return nil, err
 			}
@@ -394,7 +393,7 @@ func (d *Decoder) nodeToValue(node ast.Node) (any, error) {
 		case token.NullTag:
 			return nil, nil
 		case token.BinaryTag:
-			v, err := d.nodeToValue(n.Value)
+			v, err := d.nodeToValue(ctx, n.Value)
 			if err != nil {
 				return nil, err
 			}
@@ -408,7 +407,7 @@ func (d *Decoder) nodeToValue(node ast.Node) (any, error) {
 			b, _ := base64.StdEncoding.DecodeString(str)
 			return b, nil
 		case token.BooleanTag:
-			v, err := d.nodeToValue(n.Value)
+			v, err := d.nodeToValue(ctx, n.Value)
 			if err != nil {
 				return nil, err
 			}
@@ -425,7 +424,7 @@ func (d *Decoder) nodeToValue(node ast.Node) (any, error) {
 			}
 			return nil, errors.ErrSyntax(fmt.Sprintf("cannot convert %q to boolean", fmt.Sprint(v)), n.Value.GetToken())
 		case token.StringTag:
-			v, err := d.nodeToValue(n.Value)
+			v, err := d.nodeToValue(ctx, n.Value)
 			if err != nil {
 				return nil, err
 			}
@@ -434,45 +433,41 @@ func (d *Decoder) nodeToValue(node ast.Node) (any, error) {
 			}
 			return fmt.Sprint(v), nil
 		case token.MappingTag:
-			return d.nodeToValue(n.Value)
+			return d.nodeToValue(ctx, n.Value)
 		default:
-			return d.nodeToValue(n.Value)
+			return d.nodeToValue(ctx, n.Value)
 		}
 	case *ast.AnchorNode:
 		anchorName := n.Name.GetToken().Value
 
 		// To handle the case where alias is processed recursively, the result of alias can be set to nil in advance.
 		d.anchorNodeMap[anchorName] = nil
-		anchorValue, err := d.nodeToValue(n.Value)
+		anchorValue, err := d.nodeToValue(withAnchor(ctx, anchorName), n.Value)
 		if err != nil {
 			delete(d.anchorNodeMap, anchorName)
 			return nil, err
 		}
 		d.anchorNodeMap[anchorName] = n.Value
+		d.anchorValueMap[anchorName] = reflect.ValueOf(anchorValue)
 		return anchorValue, nil
 	case *ast.AliasNode:
-		if v, exists := d.aliasValueMap[n]; exists {
-			return v, nil
-		}
-		// To handle the case where alias is processed recursively, the result of alias can be set to nil in advance.
-		d.aliasValueMap[n] = nil
-
-		aliasName := n.Value.GetToken().Value
-		node, exists := d.anchorNodeMap[aliasName]
-		if !exists {
-			return nil, errors.ErrSyntax(fmt.Sprintf("could not find alias %q", aliasName), n.Value.GetToken())
+		text := n.Value.String()
+		if _, exists := getAnchorMap(ctx)[text]; exists {
+			// self recursion.
+			return nil, nil
 		}
-		aliasValue, err := d.nodeToValue(node)
-		if err != nil {
-			return nil, err
+		if v, exists := d.anchorValueMap[text]; exists {
+			if !v.IsValid() {
+				return nil, nil
+			}
+			return v.Interface(), nil
 		}
-		// once the correct alias value is obtained, overwrite with that value.
-		d.aliasValueMap[n] = aliasValue
-		return aliasValue, nil
+		aliasName := n.Value.GetToken().Value
+		return nil, errors.ErrSyntax(fmt.Sprintf("could not find alias %q", aliasName), n.Value.GetToken())
 	case *ast.LiteralNode:
 		return n.Value.GetValue(), nil
 	case *ast.MappingKeyNode:
-		return d.nodeToValue(n.Value)
+		return d.nodeToValue(ctx, n.Value)
 	case *ast.MappingValueNode:
 		if n.Key.IsMergeKey() {
 			value, err := d.getMapNode(n.Value, true)
@@ -483,7 +478,7 @@ func (d *Decoder) nodeToValue(node ast.Node) (any, error) {
 			if d.useOrderedMap {
 				m := MapSlice{}
 				for iter.Next() {
-					if err := d.setToOrderedMapValue(iter.KeyValue(), &m); err != nil {
+					if err := d.setToOrderedMapValue(ctx, iter.KeyValue(), &m); err != nil {
 						return nil, err
 					}
 				}
@@ -491,24 +486,24 @@ func (d *Decoder) nodeToValue(node ast.Node) (any, error) {
 			}
 			m := make(map[string]any)
 			for iter.Next() {
-				if err := d.setToMapValue(iter.KeyValue(), m); err != nil {
+				if err := d.setToMapValue(ctx, iter.KeyValue(), m); err != nil {
 					return nil, err
 				}
 			}
 			return m, nil
 		}
-		key, err := d.mapKeyNodeToString(n.Key)
+		key, err := d.mapKeyNodeToString(ctx, n.Key)
 		if err != nil {
 			return nil, err
 		}
 		if d.useOrderedMap {
-			v, err := d.nodeToValue(n.Value)
+			v, err := d.nodeToValue(ctx, n.Value)
 			if err != nil {
 				return nil, err
 			}
 			return MapSlice{{Key: key, Value: v}}, nil
 		}
-		v, err := d.nodeToValue(n.Value)
+		v, err := d.nodeToValue(ctx, n.Value)
 		if err != nil {
 			return nil, err
 		}
@@ -517,7 +512,7 @@ func (d *Decoder) nodeToValue(node ast.Node) (any, error) {
 		if d.useOrderedMap {
 			m := make(MapSlice, 0, len(n.Values))
 			for _, value := range n.Values {
-				if err := d.setToOrderedMapValue(value, &m); err != nil {
+				if err := d.setToOrderedMapValue(ctx, value, &m); err != nil {
 					return nil, err
 				}
 			}
@@ -525,7 +520,7 @@ func (d *Decoder) nodeToValue(node ast.Node) (any, error) {
 		}
 		m := make(map[string]interface{}, len(n.Values))
 		for _, value := range n.Values {
-			if err := d.setToMapValue(value, m); err != nil {
+			if err := d.setToMapValue(ctx, value, m); err != nil {
 				return nil, err
 			}
 		}
@@ -533,7 +528,7 @@ func (d *Decoder) nodeToValue(node ast.Node) (any, error) {
 	case *ast.SequenceNode:
 		v := make([]interface{}, 0, len(n.Values))
 		for _, value := range n.Values {
-			vv, err := d.nodeToValue(value)
+			vv, err := d.nodeToValue(ctx, value)
 			if err != nil {
 				return nil, err
 			}
@@ -544,75 +539,6 @@ func (d *Decoder) nodeToValue(node ast.Node) (any, error) {
 	return nil, nil
 }
 
-func (d *Decoder) resolveAlias(node ast.Node) (ast.Node, error) {
-	d.stepIn()
-	defer d.stepOut()
-	if d.isExceededMaxDepth() {
-		return nil, ErrExceededMaxDepth
-	}
-
-	switch n := node.(type) {
-	case *ast.MappingNode:
-		for idx, v := range n.Values {
-			value, err := d.resolveAlias(v)
-			if err != nil {
-				return nil, err
-			}
-			n.Values[idx], _ = value.(*ast.MappingValueNode)
-		}
-	case *ast.TagNode:
-		value, err := d.resolveAlias(n.Value)
-		if err != nil {
-			return nil, err
-		}
-		n.Value = value
-	case *ast.MappingKeyNode:
-		value, err := d.resolveAlias(n.Value)
-		if err != nil {
-			return nil, err
-		}
-		n.Value = value
-	case *ast.MappingValueNode:
-		if n.Key.IsMergeKey() && n.Value.Type() == ast.AliasType {
-			value, err := d.resolveAlias(n.Value)
-			if err != nil {
-				return nil, err
-			}
-			keyColumn := n.Key.GetToken().Position.Column
-			requiredColumn := keyColumn + 2
-			value.AddColumn(requiredColumn)
-			n.Value = value
-		} else {
-			key, err := d.resolveAlias(n.Key)
-			if err != nil {
-				return nil, err
-			}
-			n.Key, _ = key.(ast.MapKeyNode)
-			value, err := d.resolveAlias(n.Value)
-			if err != nil {
-				return nil, err
-			}
-			n.Value = value
-		}
-	case *ast.SequenceNode:
-		for idx, v := range n.Values {
-			value, err := d.resolveAlias(v)
-			if err != nil {
-				return nil, err
-			}
-			n.Values[idx] = value
-		}
-	case *ast.AliasNode:
-		aliasName := n.Value.GetToken().Value
-		node := d.anchorNodeMap[aliasName]
-		if node == nil {
-			return nil, fmt.Errorf("cannot find anchor by alias name %s", aliasName)
-		}
-		return d.resolveAlias(node)
-	}
-	return node, nil
-}
-
 func (d *Decoder) getMapNode(node ast.Node, isMerge bool) (ast.MapNode, error) {
 	d.stepIn()
 	defer d.stepOut()
@@ -756,57 +682,18 @@ func (d *Decoder) deleteStructKeys(structType reflect.Type, unknownFields map[st
 	return nil
 }
 
-func (d *Decoder) lastNode(node ast.Node) ast.Node {
-	switch n := node.(type) {
-	case *ast.MappingNode:
-		if len(n.Values) > 0 {
-			return d.lastNode(n.Values[len(n.Values)-1])
-		}
-	case *ast.MappingValueNode:
-		return d.lastNode(n.Value)
-	case *ast.SequenceNode:
-		if len(n.Values) > 0 {
-			return d.lastNode(n.Values[len(n.Values)-1])
-		}
-	}
-	return node
-}
-
 func (d *Decoder) unmarshalableDocument(node ast.Node) ([]byte, error) {
-	var err error
-	node, err = d.resolveAlias(node)
-	if err != nil {
-		return nil, err
-	}
-	doc := node.String()
-	last := d.lastNode(node)
-	if last != nil && last.Type() == ast.LiteralType {
-		doc += "\n"
-	}
+	doc := format.FormatNodeWithResolvedAlias(node, d.anchorNodeMap)
 	return []byte(doc), nil
 }
 
-func (d *Decoder) unmarshalableText(node ast.Node) ([]byte, bool, error) {
-	var err error
-	node, err = d.resolveAlias(node)
-	if err != nil {
-		return nil, false, err
-	}
-	if node.Type() == ast.AnchorType {
-		node = node.(*ast.AnchorNode).Value
-	}
-	switch n := node.(type) {
-	case *ast.StringNode:
-		return []byte(n.Value), true, nil
-	case *ast.LiteralNode:
-		return []byte(n.Value.GetToken().Value), true, nil
-	default:
-		scalar, ok := n.(ast.ScalarNode)
-		if ok {
-			return []byte(fmt.Sprint(scalar.GetValue())), true, nil
-		}
+func (d *Decoder) unmarshalableText(node ast.Node) ([]byte, bool) {
+	doc := format.FormatNodeWithResolvedAlias(node, d.anchorNodeMap)
+	var v string
+	if err := Unmarshal([]byte(doc), &v); err != nil {
+		return nil, false
 	}
-	return nil, false, nil
+	return []byte(v), true
 }
 
 type jsonUnmarshaler interface {
@@ -846,19 +733,15 @@ func (d *Decoder) canDecodeByUnmarshaler(dst reflect.Value) bool {
 	}
 	iface := ptrValue.Interface()
 	switch iface.(type) {
-	case BytesUnmarshalerContext:
-		return true
-	case BytesUnmarshaler:
-		return true
-	case InterfaceUnmarshalerContext:
-		return true
-	case InterfaceUnmarshaler:
-		return true
-	case *time.Time:
-		return true
-	case *time.Duration:
-		return true
-	case encoding.TextUnmarshaler:
+	case BytesUnmarshalerContext,
+		BytesUnmarshaler,
+		InterfaceUnmarshalerContext,
+		InterfaceUnmarshaler,
+		NodeUnmarshaler,
+		NodeUnmarshalerContext,
+		*time.Time,
+		*time.Duration,
+		encoding.TextUnmarshaler:
 		return true
 	case jsonUnmarshaler:
 		return d.useJSONUnmarshaler
@@ -934,6 +817,22 @@ func (d *Decoder) decodeByUnmarshaler(ctx context.Context, dst reflect.Value, sr
 		return nil
 	}
 
+	if unmarshaler, ok := iface.(NodeUnmarshaler); ok {
+		if err := unmarshaler.UnmarshalYAML(src); err != nil {
+			return err
+		}
+
+		return nil
+	}
+
+	if unmarshaler, ok := iface.(NodeUnmarshalerContext); ok {
+		if err := unmarshaler.UnmarshalYAML(ctx, src); err != nil {
+			return err
+		}
+
+		return nil
+	}
+
 	if _, ok := iface.(*time.Time); ok {
 		return d.decodeTime(ctx, dst, src)
 	}
@@ -943,10 +842,7 @@ func (d *Decoder) decodeByUnmarshaler(ctx context.Context, dst reflect.Value, sr
 	}
 
 	if unmarshaler, isText := iface.(encoding.TextUnmarshaler); isText {
-		b, ok, err := d.unmarshalableText(src)
-		if err != nil {
-			return err
-		}
+		b, ok := d.unmarshalableText(src)
 		if ok {
 			if err := unmarshaler.UnmarshalText(b); err != nil {
 				return err
@@ -988,10 +884,13 @@ func (d *Decoder) decodeValue(ctx context.Context, dst reflect.Value, src ast.No
 	}
 
 	if src.Type() == ast.AnchorType {
-		anchorName := src.(*ast.AnchorNode).Name.GetToken().Value
-		if _, exists := d.anchorValueMap[anchorName]; !exists {
-			d.anchorValueMap[anchorName] = dst
+		anchor, _ := src.(*ast.AnchorNode)
+		anchorName := anchor.Name.GetToken().Value
+		if err := d.decodeValue(withAnchor(ctx, anchorName), dst, anchor.Value); err != nil {
+			return err
 		}
+		d.anchorValueMap[anchorName] = dst
+		return nil
 	}
 	if d.canDecodeByUnmarshaler(dst) {
 		if err := d.decodeByUnmarshaler(ctx, dst, src); err != nil {
@@ -1024,7 +923,7 @@ func (d *Decoder) decodeValue(ctx context.Context, dst reflect.Value, src ast.No
 			dst.Set(reflect.ValueOf(src))
 			return nil
 		}
-		srcVal, err := d.nodeToValue(src)
+		srcVal, err := d.nodeToValue(ctx, src)
 		if err != nil {
 			return err
 		}
@@ -1047,7 +946,7 @@ func (d *Decoder) decodeValue(ctx context.Context, dst reflect.Value, src ast.No
 		}
 		return d.decodeStruct(ctx, dst, src)
 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
-		v, err := d.nodeToValue(src)
+		v, err := d.nodeToValue(ctx, src)
 		if err != nil {
 			return err
 		}
@@ -1081,7 +980,7 @@ func (d *Decoder) decodeValue(ctx context.Context, dst reflect.Value, src ast.No
 		}
 		return errors.ErrOverflow(valueType, fmt.Sprint(v), src.GetToken())
 	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
-		v, err := d.nodeToValue(src)
+		v, err := d.nodeToValue(ctx, src)
 		if err != nil {
 			return err
 		}
@@ -1116,7 +1015,7 @@ func (d *Decoder) decodeValue(ctx context.Context, dst reflect.Value, src ast.No
 		}
 		return errors.ErrOverflow(valueType, fmt.Sprint(v), src.GetToken())
 	}
-	srcVal, err := d.nodeToValue(src)
+	srcVal, err := d.nodeToValue(ctx, src)
 	if err != nil {
 		return err
 	}
@@ -1156,6 +1055,9 @@ func (d *Decoder) castToAssignableValue(value reflect.Value, target reflect.Type
 		if value.Type().AssignableTo(target) {
 			break
 		}
+		if !value.CanAddr() {
+			break
+		}
 		value = value.Addr()
 	}
 	if !value.Type().AssignableTo(target) {
@@ -1201,7 +1103,7 @@ func (d *Decoder) createDecodedNewValue(
 	return d.castToAssignableValue(newValue, typ, node)
 }
 
-func (d *Decoder) keyToNodeMap(node ast.Node, ignoreMergeKey bool, getKeyOrValueNode func(*ast.MapNodeIter) ast.Node) (map[string]ast.Node, error) {
+func (d *Decoder) keyToNodeMap(ctx context.Context, node ast.Node, ignoreMergeKey bool, getKeyOrValueNode func(*ast.MapNodeIter) ast.Node) (map[string]ast.Node, error) {
 	d.stepIn()
 	defer d.stepOut()
 	if d.isExceededMaxDepth() {
@@ -1221,7 +1123,7 @@ func (d *Decoder) keyToNodeMap(node ast.Node, ignoreMergeKey bool, getKeyOrValue
 			if ignoreMergeKey {
 				continue
 			}
-			mergeMap, err := d.keyToNodeMap(mapIter.Value(), ignoreMergeKey, getKeyOrValueNode)
+			mergeMap, err := d.keyToNodeMap(ctx, mapIter.Value(), ignoreMergeKey, getKeyOrValueNode)
 			if err != nil {
 				return nil, err
 			}
@@ -1232,7 +1134,7 @@ func (d *Decoder) keyToNodeMap(node ast.Node, ignoreMergeKey bool, getKeyOrValue
 				keyToNodeMap[k] = v
 			}
 		} else {
-			keyVal, err := d.nodeToValue(keyNode)
+			keyVal, err := d.nodeToValue(ctx, keyNode)
 			if err != nil {
 				return nil, err
 			}
@@ -1249,16 +1151,16 @@ func (d *Decoder) keyToNodeMap(node ast.Node, ignoreMergeKey bool, getKeyOrValue
 	return keyToNodeMap, nil
 }
 
-func (d *Decoder) keyToKeyNodeMap(node ast.Node, ignoreMergeKey bool) (map[string]ast.Node, error) {
-	m, err := d.keyToNodeMap(node, ignoreMergeKey, func(nodeMap *ast.MapNodeIter) ast.Node { return nodeMap.Key() })
+func (d *Decoder) keyToKeyNodeMap(ctx context.Context, node ast.Node, ignoreMergeKey bool) (map[string]ast.Node, error) {
+	m, err := d.keyToNodeMap(ctx, node, ignoreMergeKey, func(nodeMap *ast.MapNodeIter) ast.Node { return nodeMap.Key() })
 	if err != nil {
 		return nil, err
 	}
 	return m, nil
 }
 
-func (d *Decoder) keyToValueNodeMap(node ast.Node, ignoreMergeKey bool) (map[string]ast.Node, error) {
-	m, err := d.keyToNodeMap(node, ignoreMergeKey, func(nodeMap *ast.MapNodeIter) ast.Node { return nodeMap.Value() })
+func (d *Decoder) keyToValueNodeMap(ctx context.Context, node ast.Node, ignoreMergeKey bool) (map[string]ast.Node, error) {
+	m, err := d.keyToNodeMap(ctx, node, ignoreMergeKey, func(nodeMap *ast.MapNodeIter) ast.Node { return nodeMap.Value() })
 	if err != nil {
 		return nil, err
 	}
@@ -1304,11 +1206,11 @@ var allowedTimestampFormats = []string{
 	"2006-1-2",                        // date only
 }
 
-func (d *Decoder) castToTime(src ast.Node) (time.Time, error) {
+func (d *Decoder) castToTime(ctx context.Context, src ast.Node) (time.Time, error) {
 	if src == nil {
 		return time.Time{}, nil
 	}
-	v, err := d.nodeToValue(src)
+	v, err := d.nodeToValue(ctx, src)
 	if err != nil {
 		return time.Time{}, err
 	}
@@ -1331,7 +1233,7 @@ func (d *Decoder) castToTime(src ast.Node) (time.Time, error) {
 }
 
 func (d *Decoder) decodeTime(ctx context.Context, dst reflect.Value, src ast.Node) error {
-	t, err := d.castToTime(src)
+	t, err := d.castToTime(ctx, src)
 	if err != nil {
 		return err
 	}
@@ -1339,11 +1241,11 @@ func (d *Decoder) decodeTime(ctx context.Context, dst reflect.Value, src ast.Nod
 	return nil
 }
 
-func (d *Decoder) castToDuration(src ast.Node) (time.Duration, error) {
+func (d *Decoder) castToDuration(ctx context.Context, src ast.Node) (time.Duration, error) {
 	if src == nil {
 		return 0, nil
 	}
-	v, err := d.nodeToValue(src)
+	v, err := d.nodeToValue(ctx, src)
 	if err != nil {
 		return 0, err
 	}
@@ -1362,7 +1264,7 @@ func (d *Decoder) castToDuration(src ast.Node) (time.Duration, error) {
 }
 
 func (d *Decoder) decodeDuration(ctx context.Context, dst reflect.Value, src ast.Node) error {
-	t, err := d.castToDuration(src)
+	t, err := d.castToDuration(ctx, src)
 	if err != nil {
 		return err
 	}
@@ -1414,13 +1316,13 @@ func (d *Decoder) decodeStruct(ctx context.Context, dst reflect.Value, src ast.N
 		return err
 	}
 	ignoreMergeKey := structFieldMap.hasMergeProperty()
-	keyToNodeMap, err := d.keyToValueNodeMap(src, ignoreMergeKey)
+	keyToNodeMap, err := d.keyToValueNodeMap(ctx, src, ignoreMergeKey)
 	if err != nil {
 		return err
 	}
 	var unknownFields map[string]ast.Node
 	if d.disallowUnknownField {
-		unknownFields, err = d.keyToKeyNodeMap(src, ignoreMergeKey)
+		unknownFields, err = d.keyToKeyNodeMap(ctx, src, ignoreMergeKey)
 		if err != nil {
 			return err
 		}
@@ -1676,11 +1578,11 @@ func (d *Decoder) decodeMapItem(ctx context.Context, dst *MapItem, src ast.Node)
 		}
 		return nil
 	}
-	k, err := d.nodeToValue(key)
+	k, err := d.nodeToValue(ctx, key)
 	if err != nil {
 		return err
 	}
-	v, err := d.nodeToValue(value)
+	v, err := d.nodeToValue(ctx, value)
 	if err != nil {
 		return err
 	}
@@ -1732,14 +1634,14 @@ func (d *Decoder) decodeMapSlice(ctx context.Context, dst *MapSlice, src ast.Nod
 			}
 			continue
 		}
-		k, err := d.nodeToValue(key)
+		k, err := d.nodeToValue(ctx, key)
 		if err != nil {
 			return err
 		}
 		if err := d.validateDuplicateKey(keyMap, k, key); err != nil {
 			return err
 		}
-		v, err := d.nodeToValue(value)
+		v, err := d.nodeToValue(ctx, value)
 		if err != nil {
 			return err
 		}
@@ -1790,7 +1692,7 @@ func (d *Decoder) decodeMap(ctx context.Context, dst reflect.Value, src ast.Node
 				return err
 			}
 		} else {
-			keyVal, err := d.nodeToValue(key)
+			keyVal, err := d.nodeToValue(ctx, key)
 			if err != nil {
 				return err
 			}
@@ -1893,7 +1795,7 @@ func (d *Decoder) readersUnderDirRecursive(dir string) ([]io.Reader, error) {
 	return readers, nil
 }
 
-func (d *Decoder) resolveReference() error {
+func (d *Decoder) resolveReference(ctx context.Context) error {
 	for _, opt := range d.opts {
 		if err := opt(d); err != nil {
 			return err
@@ -1928,7 +1830,7 @@ func (d *Decoder) resolveReference() error {
 		}
 
 		// assign new anchor definition to anchorMap
-		if _, err := d.parse(bytes); err != nil {
+		if _, err := d.parse(ctx, bytes); err != nil {
 			return err
 		}
 	}
@@ -1936,7 +1838,7 @@ func (d *Decoder) resolveReference() error {
 	return nil
 }
 
-func (d *Decoder) parse(bytes []byte) (*ast.File, error) {
+func (d *Decoder) parse(ctx context.Context, bytes []byte) (*ast.File, error) {
 	var parseMode parser.Mode
 	if d.toCommentMap != nil {
 		parseMode = parser.ParseComments
@@ -1952,7 +1854,7 @@ func (d *Decoder) parse(bytes []byte) (*ast.File, error) {
 	normalizedFile := &ast.File{}
 	for _, doc := range f.Docs {
 		// try to decode ast.Node to value and map anchor value to anchorMap
-		v, err := d.nodeToValue(doc.Body)
+		v, err := d.nodeToValue(ctx, doc.Body)
 		if err != nil {
 			return nil, err
 		}
@@ -1973,9 +1875,9 @@ func (d *Decoder) isInitialized() bool {
 	return d.parsedFile != nil
 }
 
-func (d *Decoder) decodeInit() error {
+func (d *Decoder) decodeInit(ctx context.Context) error {
 	if !d.isResolvedReference {
-		if err := d.resolveReference(); err != nil {
+		if err := d.resolveReference(ctx); err != nil {
 			return err
 		}
 	}
@@ -1983,7 +1885,7 @@ func (d *Decoder) decodeInit() error {
 	if _, err := io.Copy(&buf, d.reader); err != nil {
 		return err
 	}
-	file, err := d.parse(buf.Bytes())
+	file, err := d.parse(ctx, buf.Bytes())
 	if err != nil {
 		return err
 	}
@@ -1993,6 +1895,7 @@ func (d *Decoder) decodeInit() error {
 
 func (d *Decoder) decode(ctx context.Context, v reflect.Value) error {
 	d.decodeDepth = 0
+	d.anchorValueMap = make(map[string]reflect.Value)
 	if len(d.parsedFile.Docs) <= d.streamIndex {
 		return io.EOF
 	}
@@ -2035,7 +1938,7 @@ func (d *Decoder) DecodeContext(ctx context.Context, v interface{}) error {
 		}
 		return nil
 	}
-	if err := d.decodeInit(); err != nil {
+	if err := d.decodeInit(ctx); err != nil {
 		return err
 	}
 	if err := d.decode(ctx, rv); err != nil {
@@ -2059,12 +1962,12 @@ func (d *Decoder) DecodeFromNodeContext(ctx context.Context, node ast.Node, v in
 		return ErrDecodeRequiredPointerType
 	}
 	if !d.isInitialized() {
-		if err := d.decodeInit(); err != nil {
+		if err := d.decodeInit(ctx); err != nil {
 			return err
 		}
 	}
 	// resolve references to the anchor on the same file
-	if _, err := d.nodeToValue(node); err != nil {
+	if _, err := d.nodeToValue(ctx, node); err != nil {
 		return err
 	}
 	if err := d.decodeValue(ctx, rv.Elem(), node); err != nil {
diff --git a/docs/playground/package-lock.json b/docs/playground/package-lock.json
index 672c479..012910d 100644
--- a/docs/playground/package-lock.json
+++ b/docs/playground/package-lock.json
@@ -197,24 +197,24 @@
       }
     },
     "node_modules/@babel/helpers": {
-      "version": "7.26.0",
-      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz",
-      "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==",
+      "version": "7.26.10",
+      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.10.tgz",
+      "integrity": "sha512-UPYc3SauzZ3JGgj87GgZ89JVdC5dj0AoetR5Bw6wj4niittNyFh6+eOGonYvJ1ao6B8lEa3Q3klS7ADZ53bc5g==",
       "dev": true,
       "dependencies": {
-        "@babel/template": "^7.25.9",
-        "@babel/types": "^7.26.0"
+        "@babel/template": "^7.26.9",
+        "@babel/types": "^7.26.10"
       },
       "engines": {
         "node": ">=6.9.0"
       }
     },
     "node_modules/@babel/parser": {
-      "version": "7.26.3",
-      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz",
-      "integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==",
+      "version": "7.26.10",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.10.tgz",
+      "integrity": "sha512-6aQR2zGE/QFi8JpDLjUZEPYOs7+mhKXm86VaKFiLP35JQwQb6bwUE+XbvkH0EptsYhbNBSUGaUBLKqxH1xSgsA==",
       "dependencies": {
-        "@babel/types": "^7.26.3"
+        "@babel/types": "^7.26.10"
       },
       "bin": {
         "parser": "bin/babel-parser.js"
@@ -254,9 +254,9 @@
       }
     },
     "node_modules/@babel/runtime": {
-      "version": "7.26.0",
-      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz",
-      "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==",
+      "version": "7.26.10",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.10.tgz",
+      "integrity": "sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==",
       "dependencies": {
         "regenerator-runtime": "^0.14.0"
       },
@@ -265,13 +265,13 @@
       }
     },
     "node_modules/@babel/template": {
-      "version": "7.25.9",
-      "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz",
-      "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==",
+      "version": "7.26.9",
+      "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz",
+      "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==",
       "dependencies": {
-        "@babel/code-frame": "^7.25.9",
-        "@babel/parser": "^7.25.9",
-        "@babel/types": "^7.25.9"
+        "@babel/code-frame": "^7.26.2",
+        "@babel/parser": "^7.26.9",
+        "@babel/types": "^7.26.9"
       },
       "engines": {
         "node": ">=6.9.0"
@@ -303,9 +303,9 @@
       }
     },
     "node_modules/@babel/types": {
-      "version": "7.26.3",
-      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz",
-      "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==",
+      "version": "7.26.10",
+      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.10.tgz",
+      "integrity": "sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==",
       "dependencies": {
         "@babel/helper-string-parser": "^7.25.9",
         "@babel/helper-validator-identifier": "^7.25.9"
@@ -453,9 +453,9 @@
       "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg=="
     },
     "node_modules/@esbuild/aix-ppc64": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz",
-      "integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.1.tgz",
+      "integrity": "sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ==",
       "cpu": [
         "ppc64"
       ],
@@ -469,9 +469,9 @@
       }
     },
     "node_modules/@esbuild/android-arm": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz",
-      "integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.1.tgz",
+      "integrity": "sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q==",
       "cpu": [
         "arm"
       ],
@@ -485,9 +485,9 @@
       }
     },
     "node_modules/@esbuild/android-arm64": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz",
-      "integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.1.tgz",
+      "integrity": "sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA==",
       "cpu": [
         "arm64"
       ],
@@ -501,9 +501,9 @@
       }
     },
     "node_modules/@esbuild/android-x64": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz",
-      "integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.1.tgz",
+      "integrity": "sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw==",
       "cpu": [
         "x64"
       ],
@@ -517,9 +517,9 @@
       }
     },
     "node_modules/@esbuild/darwin-arm64": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz",
-      "integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.1.tgz",
+      "integrity": "sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ==",
       "cpu": [
         "arm64"
       ],
@@ -533,9 +533,9 @@
       }
     },
     "node_modules/@esbuild/darwin-x64": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz",
-      "integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.1.tgz",
+      "integrity": "sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA==",
       "cpu": [
         "x64"
       ],
@@ -549,9 +549,9 @@
       }
     },
     "node_modules/@esbuild/freebsd-arm64": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz",
-      "integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.1.tgz",
+      "integrity": "sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A==",
       "cpu": [
         "arm64"
       ],
@@ -565,9 +565,9 @@
       }
     },
     "node_modules/@esbuild/freebsd-x64": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz",
-      "integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.1.tgz",
+      "integrity": "sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww==",
       "cpu": [
         "x64"
       ],
@@ -581,9 +581,9 @@
       }
     },
     "node_modules/@esbuild/linux-arm": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz",
-      "integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.1.tgz",
+      "integrity": "sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ==",
       "cpu": [
         "arm"
       ],
@@ -597,9 +597,9 @@
       }
     },
     "node_modules/@esbuild/linux-arm64": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz",
-      "integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.1.tgz",
+      "integrity": "sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ==",
       "cpu": [
         "arm64"
       ],
@@ -613,9 +613,9 @@
       }
     },
     "node_modules/@esbuild/linux-ia32": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz",
-      "integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.1.tgz",
+      "integrity": "sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ==",
       "cpu": [
         "ia32"
       ],
@@ -629,9 +629,9 @@
       }
     },
     "node_modules/@esbuild/linux-loong64": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz",
-      "integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.1.tgz",
+      "integrity": "sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg==",
       "cpu": [
         "loong64"
       ],
@@ -645,9 +645,9 @@
       }
     },
     "node_modules/@esbuild/linux-mips64el": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz",
-      "integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.1.tgz",
+      "integrity": "sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg==",
       "cpu": [
         "mips64el"
       ],
@@ -661,9 +661,9 @@
       }
     },
     "node_modules/@esbuild/linux-ppc64": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz",
-      "integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.1.tgz",
+      "integrity": "sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg==",
       "cpu": [
         "ppc64"
       ],
@@ -677,9 +677,9 @@
       }
     },
     "node_modules/@esbuild/linux-riscv64": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz",
-      "integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.1.tgz",
+      "integrity": "sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ==",
       "cpu": [
         "riscv64"
       ],
@@ -693,9 +693,9 @@
       }
     },
     "node_modules/@esbuild/linux-s390x": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz",
-      "integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.1.tgz",
+      "integrity": "sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ==",
       "cpu": [
         "s390x"
       ],
@@ -709,9 +709,9 @@
       }
     },
     "node_modules/@esbuild/linux-x64": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz",
-      "integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.1.tgz",
+      "integrity": "sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA==",
       "cpu": [
         "x64"
       ],
@@ -725,9 +725,9 @@
       }
     },
     "node_modules/@esbuild/netbsd-arm64": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz",
-      "integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.1.tgz",
+      "integrity": "sha512-O96poM2XGhLtpTh+s4+nP7YCCAfb4tJNRVZHfIE7dgmax+yMP2WgMd2OecBuaATHKTHsLWHQeuaxMRnCsH8+5g==",
       "cpu": [
         "arm64"
       ],
@@ -741,9 +741,9 @@
       }
     },
     "node_modules/@esbuild/netbsd-x64": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz",
-      "integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.1.tgz",
+      "integrity": "sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA==",
       "cpu": [
         "x64"
       ],
@@ -757,9 +757,9 @@
       }
     },
     "node_modules/@esbuild/openbsd-arm64": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz",
-      "integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.1.tgz",
+      "integrity": "sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg==",
       "cpu": [
         "arm64"
       ],
@@ -773,9 +773,9 @@
       }
     },
     "node_modules/@esbuild/openbsd-x64": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz",
-      "integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.1.tgz",
+      "integrity": "sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw==",
       "cpu": [
         "x64"
       ],
@@ -789,9 +789,9 @@
       }
     },
     "node_modules/@esbuild/sunos-x64": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz",
-      "integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.1.tgz",
+      "integrity": "sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg==",
       "cpu": [
         "x64"
       ],
@@ -805,9 +805,9 @@
       }
     },
     "node_modules/@esbuild/win32-arm64": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz",
-      "integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.1.tgz",
+      "integrity": "sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ==",
       "cpu": [
         "arm64"
       ],
@@ -821,9 +821,9 @@
       }
     },
     "node_modules/@esbuild/win32-ia32": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz",
-      "integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.1.tgz",
+      "integrity": "sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A==",
       "cpu": [
         "ia32"
       ],
@@ -837,9 +837,9 @@
       }
     },
     "node_modules/@esbuild/win32-x64": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz",
-      "integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.1.tgz",
+      "integrity": "sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg==",
       "cpu": [
         "x64"
       ],
@@ -1421,9 +1421,9 @@
       }
     },
     "node_modules/@rollup/rollup-android-arm-eabi": {
-      "version": "4.29.1",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.29.1.tgz",
-      "integrity": "sha512-ssKhA8RNltTZLpG6/QNkCSge+7mBQGUqJRisZ2MDQcEGaK93QESEgWK2iOpIDZ7k9zPVkG5AS3ksvD5ZWxmItw==",
+      "version": "4.35.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.35.0.tgz",
+      "integrity": "sha512-uYQ2WfPaqz5QtVgMxfN6NpLD+no0MYHDBywl7itPYd3K5TjjSghNKmX8ic9S8NU8w81NVhJv/XojcHptRly7qQ==",
       "cpu": [
         "arm"
       ],
@@ -1434,9 +1434,9 @@
       ]
     },
     "node_modules/@rollup/rollup-android-arm64": {
-      "version": "4.29.1",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.29.1.tgz",
-      "integrity": "sha512-CaRfrV0cd+NIIcVVN/jx+hVLN+VRqnuzLRmfmlzpOzB87ajixsN/+9L5xNmkaUUvEbI5BmIKS+XTwXsHEb65Ew==",
+      "version": "4.35.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.35.0.tgz",
+      "integrity": "sha512-FtKddj9XZudurLhdJnBl9fl6BwCJ3ky8riCXjEw3/UIbjmIY58ppWwPEvU3fNu+W7FUsAsB1CdH+7EQE6CXAPA==",
       "cpu": [
         "arm64"
       ],
@@ -1447,9 +1447,9 @@
       ]
     },
     "node_modules/@rollup/rollup-darwin-arm64": {
-      "version": "4.29.1",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.29.1.tgz",
-      "integrity": "sha512-2ORr7T31Y0Mnk6qNuwtyNmy14MunTAMx06VAPI6/Ju52W10zk1i7i5U3vlDRWjhOI5quBcrvhkCHyF76bI7kEw==",
+      "version": "4.35.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.35.0.tgz",
+      "integrity": "sha512-Uk+GjOJR6CY844/q6r5DR/6lkPFOw0hjfOIzVx22THJXMxktXG6CbejseJFznU8vHcEBLpiXKY3/6xc+cBm65Q==",
       "cpu": [
         "arm64"
       ],
@@ -1460,9 +1460,9 @@
       ]
     },
     "node_modules/@rollup/rollup-darwin-x64": {
-      "version": "4.29.1",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.29.1.tgz",
-      "integrity": "sha512-j/Ej1oanzPjmN0tirRd5K2/nncAhS9W6ICzgxV+9Y5ZsP0hiGhHJXZ2JQ53iSSjj8m6cRY6oB1GMzNn2EUt6Ng==",
+      "version": "4.35.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.35.0.tgz",
+      "integrity": "sha512-3IrHjfAS6Vkp+5bISNQnPogRAW5GAV1n+bNCrDwXmfMHbPl5EhTmWtfmwlJxFRUCBZ+tZ/OxDyU08aF6NI/N5Q==",
       "cpu": [
         "x64"
       ],
@@ -1473,9 +1473,9 @@
       ]
     },
     "node_modules/@rollup/rollup-freebsd-arm64": {
-      "version": "4.29.1",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.29.1.tgz",
-      "integrity": "sha512-91C//G6Dm/cv724tpt7nTyP+JdN12iqeXGFM1SqnljCmi5yTXriH7B1r8AD9dAZByHpKAumqP1Qy2vVNIdLZqw==",
+      "version": "4.35.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.35.0.tgz",
+      "integrity": "sha512-sxjoD/6F9cDLSELuLNnY0fOrM9WA0KrM0vWm57XhrIMf5FGiN8D0l7fn+bpUeBSU7dCgPV2oX4zHAsAXyHFGcQ==",
       "cpu": [
         "arm64"
       ],
@@ -1486,9 +1486,9 @@
       ]
     },
     "node_modules/@rollup/rollup-freebsd-x64": {
-      "version": "4.29.1",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.29.1.tgz",
-      "integrity": "sha512-hEioiEQ9Dec2nIRoeHUP6hr1PSkXzQaCUyqBDQ9I9ik4gCXQZjJMIVzoNLBRGet+hIUb3CISMh9KXuCcWVW/8w==",
+      "version": "4.35.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.35.0.tgz",
+      "integrity": "sha512-2mpHCeRuD1u/2kruUiHSsnjWtHjqVbzhBkNVQ1aVD63CcexKVcQGwJ2g5VphOd84GvxfSvnnlEyBtQCE5hxVVw==",
       "cpu": [
         "x64"
       ],
@@ -1499,9 +1499,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
-      "version": "4.29.1",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.29.1.tgz",
-      "integrity": "sha512-Py5vFd5HWYN9zxBv3WMrLAXY3yYJ6Q/aVERoeUFwiDGiMOWsMs7FokXihSOaT/PMWUty/Pj60XDQndK3eAfE6A==",
+      "version": "4.35.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.35.0.tgz",
+      "integrity": "sha512-mrA0v3QMy6ZSvEuLs0dMxcO2LnaCONs1Z73GUDBHWbY8tFFocM6yl7YyMu7rz4zS81NDSqhrUuolyZXGi8TEqg==",
       "cpu": [
         "arm"
       ],
@@ -1512,9 +1512,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-arm-musleabihf": {
-      "version": "4.29.1",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.29.1.tgz",
-      "integrity": "sha512-RiWpGgbayf7LUcuSNIbahr0ys2YnEERD4gYdISA06wa0i8RALrnzflh9Wxii7zQJEB2/Eh74dX4y/sHKLWp5uQ==",
+      "version": "4.35.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.35.0.tgz",
+      "integrity": "sha512-DnYhhzcvTAKNexIql8pFajr0PiDGrIsBYPRvCKlA5ixSS3uwo/CWNZxB09jhIapEIg945KOzcYEAGGSmTSpk7A==",
       "cpu": [
         "arm"
       ],
@@ -1525,9 +1525,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-arm64-gnu": {
-      "version": "4.29.1",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.29.1.tgz",
-      "integrity": "sha512-Z80O+taYxTQITWMjm/YqNoe9d10OX6kDh8X5/rFCMuPqsKsSyDilvfg+vd3iXIqtfmp+cnfL1UrYirkaF8SBZA==",
+      "version": "4.35.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.35.0.tgz",
+      "integrity": "sha512-uagpnH2M2g2b5iLsCTZ35CL1FgyuzzJQ8L9VtlJ+FckBXroTwNOaD0z0/UF+k5K3aNQjbm8LIVpxykUOQt1m/A==",
       "cpu": [
         "arm64"
       ],
@@ -1538,9 +1538,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-arm64-musl": {
-      "version": "4.29.1",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.29.1.tgz",
-      "integrity": "sha512-fOHRtF9gahwJk3QVp01a/GqS4hBEZCV1oKglVVq13kcK3NeVlS4BwIFzOHDbmKzt3i0OuHG4zfRP0YoG5OF/rA==",
+      "version": "4.35.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.35.0.tgz",
+      "integrity": "sha512-XQxVOCd6VJeHQA/7YcqyV0/88N6ysSVzRjJ9I9UA/xXpEsjvAgDTgH3wQYz5bmr7SPtVK2TsP2fQ2N9L4ukoUg==",
       "cpu": [
         "arm64"
       ],
@@ -1551,9 +1551,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-loongarch64-gnu": {
-      "version": "4.29.1",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.29.1.tgz",
-      "integrity": "sha512-5a7q3tnlbcg0OodyxcAdrrCxFi0DgXJSoOuidFUzHZ2GixZXQs6Tc3CHmlvqKAmOs5eRde+JJxeIf9DonkmYkw==",
+      "version": "4.35.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.35.0.tgz",
+      "integrity": "sha512-5pMT5PzfgwcXEwOaSrqVsz/LvjDZt+vQ8RT/70yhPU06PTuq8WaHhfT1LW+cdD7mW6i/J5/XIkX/1tCAkh1W6g==",
       "cpu": [
         "loong64"
       ],
@@ -1564,9 +1564,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
-      "version": "4.29.1",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.29.1.tgz",
-      "integrity": "sha512-9b4Mg5Yfz6mRnlSPIdROcfw1BU22FQxmfjlp/CShWwO3LilKQuMISMTtAu/bxmmrE6A902W2cZJuzx8+gJ8e9w==",
+      "version": "4.35.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.35.0.tgz",
+      "integrity": "sha512-c+zkcvbhbXF98f4CtEIP1EBA/lCic5xB0lToneZYvMeKu5Kamq3O8gqrxiYYLzlZH6E3Aq+TSW86E4ay8iD8EA==",
       "cpu": [
         "ppc64"
       ],
@@ -1577,9 +1577,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-riscv64-gnu": {
-      "version": "4.29.1",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.29.1.tgz",
-      "integrity": "sha512-G5pn0NChlbRM8OJWpJFMX4/i8OEU538uiSv0P6roZcbpe/WfhEO+AT8SHVKfp8qhDQzaz7Q+1/ixMy7hBRidnQ==",
+      "version": "4.35.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.35.0.tgz",
+      "integrity": "sha512-s91fuAHdOwH/Tad2tzTtPX7UZyytHIRR6V4+2IGlV0Cej5rkG0R61SX4l4y9sh0JBibMiploZx3oHKPnQBKe4g==",
       "cpu": [
         "riscv64"
       ],
@@ -1590,9 +1590,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-s390x-gnu": {
-      "version": "4.29.1",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.29.1.tgz",
-      "integrity": "sha512-WM9lIkNdkhVwiArmLxFXpWndFGuOka4oJOZh8EP3Vb8q5lzdSCBuhjavJsw68Q9AKDGeOOIHYzYm4ZFvmWez5g==",
+      "version": "4.35.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.35.0.tgz",
+      "integrity": "sha512-hQRkPQPLYJZYGP+Hj4fR9dDBMIM7zrzJDWFEMPdTnTy95Ljnv0/4w/ixFw3pTBMEuuEuoqtBINYND4M7ujcuQw==",
       "cpu": [
         "s390x"
       ],
@@ -1603,9 +1603,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-x64-gnu": {
-      "version": "4.29.1",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.29.1.tgz",
-      "integrity": "sha512-87xYCwb0cPGZFoGiErT1eDcssByaLX4fc0z2nRM6eMtV9njAfEE6OW3UniAoDhX4Iq5xQVpE6qO9aJbCFumKYQ==",
+      "version": "4.35.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.35.0.tgz",
+      "integrity": "sha512-Pim1T8rXOri+0HmV4CdKSGrqcBWX0d1HoPnQ0uw0bdp1aP5SdQVNBy8LjYncvnLgu3fnnCt17xjWGd4cqh8/hA==",
       "cpu": [
         "x64"
       ],
@@ -1616,9 +1616,9 @@
       ]
     },
     "node_modules/@rollup/rollup-linux-x64-musl": {
-      "version": "4.29.1",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.29.1.tgz",
-      "integrity": "sha512-xufkSNppNOdVRCEC4WKvlR1FBDyqCSCpQeMMgv9ZyXqqtKBfkw1yfGMTUTs9Qsl6WQbJnsGboWCp7pJGkeMhKA==",
+      "version": "4.35.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.35.0.tgz",
+      "integrity": "sha512-QysqXzYiDvQWfUiTm8XmJNO2zm9yC9P/2Gkrwg2dH9cxotQzunBHYr6jk4SujCTqnfGxduOmQcI7c2ryuW8XVg==",
       "cpu": [
         "x64"
       ],
@@ -1629,9 +1629,9 @@
       ]
     },
     "node_modules/@rollup/rollup-win32-arm64-msvc": {
-      "version": "4.29.1",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.29.1.tgz",
-      "integrity": "sha512-F2OiJ42m77lSkizZQLuC+jiZ2cgueWQL5YC9tjo3AgaEw+KJmVxHGSyQfDUoYR9cci0lAywv2Clmckzulcq6ig==",
+      "version": "4.35.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.35.0.tgz",
+      "integrity": "sha512-OUOlGqPkVJCdJETKOCEf1mw848ZyJ5w50/rZ/3IBQVdLfR5jk/6Sr5m3iO2tdPgwo0x7VcncYuOvMhBWZq8ayg==",
       "cpu": [
         "arm64"
       ],
@@ -1642,9 +1642,9 @@
       ]
     },
     "node_modules/@rollup/rollup-win32-ia32-msvc": {
-      "version": "4.29.1",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.29.1.tgz",
-      "integrity": "sha512-rYRe5S0FcjlOBZQHgbTKNrqxCBUmgDJem/VQTCcTnA2KCabYSWQDrytOzX7avb79cAAweNmMUb/Zw18RNd4mng==",
+      "version": "4.35.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.35.0.tgz",
+      "integrity": "sha512-2/lsgejMrtwQe44glq7AFFHLfJBPafpsTa6JvP2NGef/ifOa4KBoglVf7AKN7EV9o32evBPRqfg96fEHzWo5kw==",
       "cpu": [
         "ia32"
       ],
@@ -1655,9 +1655,9 @@
       ]
     },
     "node_modules/@rollup/rollup-win32-x64-msvc": {
-      "version": "4.29.1",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.29.1.tgz",
-      "integrity": "sha512-+10CMg9vt1MoHj6x1pxyjPSMjHTIlqs8/tBztXvPAx24SKs9jwVnKqHJumlH/IzhaPUaj3T6T6wfZr8okdXaIg==",
+      "version": "4.35.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.35.0.tgz",
+      "integrity": "sha512-PIQeY5XDkrOysbQblSW7v3l1MDZzkTEzAfTPkj5VAu3FW8fS4ynyLg2sINp0fp3SjZ8xkRYpLqoKcYqAkhU1dw==",
       "cpu": [
         "x64"
       ],
@@ -2586,9 +2586,9 @@
       }
     },
     "node_modules/esbuild": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz",
-      "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.1.tgz",
+      "integrity": "sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==",
       "dev": true,
       "hasInstallScript": true,
       "bin": {
@@ -2598,31 +2598,31 @@
         "node": ">=18"
       },
       "optionalDependencies": {
-        "@esbuild/aix-ppc64": "0.24.2",
-        "@esbuild/android-arm": "0.24.2",
-        "@esbuild/android-arm64": "0.24.2",
-        "@esbuild/android-x64": "0.24.2",
-        "@esbuild/darwin-arm64": "0.24.2",
-        "@esbuild/darwin-x64": "0.24.2",
-        "@esbuild/freebsd-arm64": "0.24.2",
-        "@esbuild/freebsd-x64": "0.24.2",
-        "@esbuild/linux-arm": "0.24.2",
-        "@esbuild/linux-arm64": "0.24.2",
-        "@esbuild/linux-ia32": "0.24.2",
-        "@esbuild/linux-loong64": "0.24.2",
-        "@esbuild/linux-mips64el": "0.24.2",
-        "@esbuild/linux-ppc64": "0.24.2",
-        "@esbuild/linux-riscv64": "0.24.2",
-        "@esbuild/linux-s390x": "0.24.2",
-        "@esbuild/linux-x64": "0.24.2",
-        "@esbuild/netbsd-arm64": "0.24.2",
-        "@esbuild/netbsd-x64": "0.24.2",
-        "@esbuild/openbsd-arm64": "0.24.2",
-        "@esbuild/openbsd-x64": "0.24.2",
-        "@esbuild/sunos-x64": "0.24.2",
-        "@esbuild/win32-arm64": "0.24.2",
-        "@esbuild/win32-ia32": "0.24.2",
-        "@esbuild/win32-x64": "0.24.2"
+        "@esbuild/aix-ppc64": "0.25.1",
+        "@esbuild/android-arm": "0.25.1",
+        "@esbuild/android-arm64": "0.25.1",
+        "@esbuild/android-x64": "0.25.1",
+        "@esbuild/darwin-arm64": "0.25.1",
+        "@esbuild/darwin-x64": "0.25.1",
+        "@esbuild/freebsd-arm64": "0.25.1",
+        "@esbuild/freebsd-x64": "0.25.1",
+        "@esbuild/linux-arm": "0.25.1",
+        "@esbuild/linux-arm64": "0.25.1",
+        "@esbuild/linux-ia32": "0.25.1",
+        "@esbuild/linux-loong64": "0.25.1",
+        "@esbuild/linux-mips64el": "0.25.1",
+        "@esbuild/linux-ppc64": "0.25.1",
+        "@esbuild/linux-riscv64": "0.25.1",
+        "@esbuild/linux-s390x": "0.25.1",
+        "@esbuild/linux-x64": "0.25.1",
+        "@esbuild/netbsd-arm64": "0.25.1",
+        "@esbuild/netbsd-x64": "0.25.1",
+        "@esbuild/openbsd-arm64": "0.25.1",
+        "@esbuild/openbsd-x64": "0.25.1",
+        "@esbuild/sunos-x64": "0.25.1",
+        "@esbuild/win32-arm64": "0.25.1",
+        "@esbuild/win32-ia32": "0.25.1",
+        "@esbuild/win32-x64": "0.25.1"
       }
     },
     "node_modules/escalade": {
@@ -3307,9 +3307,9 @@
       "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
     },
     "node_modules/nanoid": {
-      "version": "3.3.8",
-      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
-      "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
+      "version": "3.3.10",
+      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.10.tgz",
+      "integrity": "sha512-vSJJTG+t/dIKAUhUDw/dLdZ9s//5OxcHqLaDWWrW4Cdq7o6tdLIczUkMXt2MBNmk6sJRZBZRXVixs7URY1CmIg==",
       "dev": true,
       "funding": [
         {
@@ -3478,9 +3478,9 @@
       }
     },
     "node_modules/postcss": {
-      "version": "8.4.49",
-      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz",
-      "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==",
+      "version": "8.5.3",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz",
+      "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==",
       "dev": true,
       "funding": [
         {
@@ -3497,7 +3497,7 @@
         }
       ],
       "dependencies": {
-        "nanoid": "^3.3.7",
+        "nanoid": "^3.3.8",
         "picocolors": "^1.1.1",
         "source-map-js": "^1.2.1"
       },
@@ -3661,9 +3661,9 @@
       }
     },
     "node_modules/rollup": {
-      "version": "4.29.1",
-      "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.29.1.tgz",
-      "integrity": "sha512-RaJ45M/kmJUzSWDs1Nnd5DdV4eerC98idtUOVr6FfKcgxqvjwHmxc5upLF9qZU9EpsVzzhleFahrT3shLuJzIw==",
+      "version": "4.35.0",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.35.0.tgz",
+      "integrity": "sha512-kg6oI4g+vc41vePJyO6dHt/yl0Rz3Thv0kJeVQ3D1kS3E5XSuKbPc29G4IpT/Kv1KQwgHVcN+HtyS+HYLNSvQg==",
       "dev": true,
       "dependencies": {
         "@types/estree": "1.0.6"
@@ -3676,25 +3676,25 @@
         "npm": ">=8.0.0"
       },
       "optionalDependencies": {
-        "@rollup/rollup-android-arm-eabi": "4.29.1",
-        "@rollup/rollup-android-arm64": "4.29.1",
-        "@rollup/rollup-darwin-arm64": "4.29.1",
-        "@rollup/rollup-darwin-x64": "4.29.1",
-        "@rollup/rollup-freebsd-arm64": "4.29.1",
-        "@rollup/rollup-freebsd-x64": "4.29.1",
-        "@rollup/rollup-linux-arm-gnueabihf": "4.29.1",
-        "@rollup/rollup-linux-arm-musleabihf": "4.29.1",
-        "@rollup/rollup-linux-arm64-gnu": "4.29.1",
-        "@rollup/rollup-linux-arm64-musl": "4.29.1",
-        "@rollup/rollup-linux-loongarch64-gnu": "4.29.1",
-        "@rollup/rollup-linux-powerpc64le-gnu": "4.29.1",
-        "@rollup/rollup-linux-riscv64-gnu": "4.29.1",
-        "@rollup/rollup-linux-s390x-gnu": "4.29.1",
-        "@rollup/rollup-linux-x64-gnu": "4.29.1",
-        "@rollup/rollup-linux-x64-musl": "4.29.1",
-        "@rollup/rollup-win32-arm64-msvc": "4.29.1",
-        "@rollup/rollup-win32-ia32-msvc": "4.29.1",
-        "@rollup/rollup-win32-x64-msvc": "4.29.1",
+        "@rollup/rollup-android-arm-eabi": "4.35.0",
+        "@rollup/rollup-android-arm64": "4.35.0",
+        "@rollup/rollup-darwin-arm64": "4.35.0",
+        "@rollup/rollup-darwin-x64": "4.35.0",
+        "@rollup/rollup-freebsd-arm64": "4.35.0",
+        "@rollup/rollup-freebsd-x64": "4.35.0",
+        "@rollup/rollup-linux-arm-gnueabihf": "4.35.0",
+        "@rollup/rollup-linux-arm-musleabihf": "4.35.0",
+        "@rollup/rollup-linux-arm64-gnu": "4.35.0",
+        "@rollup/rollup-linux-arm64-musl": "4.35.0",
+        "@rollup/rollup-linux-loongarch64-gnu": "4.35.0",
+        "@rollup/rollup-linux-powerpc64le-gnu": "4.35.0",
+        "@rollup/rollup-linux-riscv64-gnu": "4.35.0",
+        "@rollup/rollup-linux-s390x-gnu": "4.35.0",
+        "@rollup/rollup-linux-x64-gnu": "4.35.0",
+        "@rollup/rollup-linux-x64-musl": "4.35.0",
+        "@rollup/rollup-win32-arm64-msvc": "4.35.0",
+        "@rollup/rollup-win32-ia32-msvc": "4.35.0",
+        "@rollup/rollup-win32-x64-msvc": "4.35.0",
         "fsevents": "~2.3.2"
       }
     },
@@ -3959,14 +3959,14 @@
       }
     },
     "node_modules/vite": {
-      "version": "6.0.6",
-      "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.6.tgz",
-      "integrity": "sha512-NSjmUuckPmDU18bHz7QZ+bTYhRR0iA72cs2QAxCqDpafJ0S6qetco0LB3WW2OxlMHS0JmAv+yZ/R3uPmMyGTjQ==",
+      "version": "6.2.2",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.2.tgz",
+      "integrity": "sha512-yW7PeMM+LkDzc7CgJuRLMW2Jz0FxMOsVJ8Lv3gpgW9WLcb9cTW+121UEr1hvmfR7w3SegR5ItvYyzVz1vxNJgQ==",
       "dev": true,
       "dependencies": {
-        "esbuild": "^0.24.2",
-        "postcss": "^8.4.49",
-        "rollup": "^4.23.0"
+        "esbuild": "^0.25.0",
+        "postcss": "^8.5.3",
+        "rollup": "^4.30.1"
       },
       "bin": {
         "vite": "bin/vite.js"
@@ -4219,21 +4219,21 @@
       "dev": true
     },
     "@babel/helpers": {
-      "version": "7.26.0",
-      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz",
-      "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==",
+      "version": "7.26.10",
+      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.10.tgz",
+      "integrity": "sha512-UPYc3SauzZ3JGgj87GgZ89JVdC5dj0AoetR5Bw6wj4niittNyFh6+eOGonYvJ1ao6B8lEa3Q3klS7ADZ53bc5g==",
       "dev": true,
       "requires": {
-        "@babel/template": "^7.25.9",
-        "@babel/types": "^7.26.0"
+        "@babel/template": "^7.26.9",
+        "@babel/types": "^7.26.10"
       }
     },
     "@babel/parser": {
-      "version": "7.26.3",
-      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz",
-      "integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==",
+      "version": "7.26.10",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.10.tgz",
+      "integrity": "sha512-6aQR2zGE/QFi8JpDLjUZEPYOs7+mhKXm86VaKFiLP35JQwQb6bwUE+XbvkH0EptsYhbNBSUGaUBLKqxH1xSgsA==",
       "requires": {
-        "@babel/types": "^7.26.3"
+        "@babel/types": "^7.26.10"
       }
     },
     "@babel/plugin-transform-react-jsx-self": {
@@ -4255,21 +4255,21 @@
       }
     },
     "@babel/runtime": {
-      "version": "7.26.0",
-      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz",
-      "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==",
+      "version": "7.26.10",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.10.tgz",
+      "integrity": "sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==",
       "requires": {
         "regenerator-runtime": "^0.14.0"
       }
     },
     "@babel/template": {
-      "version": "7.25.9",
-      "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz",
-      "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==",
+      "version": "7.26.9",
+      "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz",
+      "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==",
       "requires": {
-        "@babel/code-frame": "^7.25.9",
-        "@babel/parser": "^7.25.9",
-        "@babel/types": "^7.25.9"
+        "@babel/code-frame": "^7.26.2",
+        "@babel/parser": "^7.26.9",
+        "@babel/types": "^7.26.9"
       }
     },
     "@babel/traverse": {
@@ -4294,9 +4294,9 @@
       }
     },
     "@babel/types": {
-      "version": "7.26.3",
-      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz",
-      "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==",
+      "version": "7.26.10",
+      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.10.tgz",
+      "integrity": "sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==",
       "requires": {
         "@babel/helper-string-parser": "^7.25.9",
         "@babel/helper-validator-identifier": "^7.25.9"
@@ -4424,177 +4424,177 @@
       "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg=="
     },
     "@esbuild/aix-ppc64": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz",
-      "integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.1.tgz",
+      "integrity": "sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ==",
       "dev": true,
       "optional": true
     },
     "@esbuild/android-arm": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz",
-      "integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.1.tgz",
+      "integrity": "sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q==",
       "dev": true,
       "optional": true
     },
     "@esbuild/android-arm64": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz",
-      "integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.1.tgz",
+      "integrity": "sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA==",
       "dev": true,
       "optional": true
     },
     "@esbuild/android-x64": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz",
-      "integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.1.tgz",
+      "integrity": "sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw==",
       "dev": true,
       "optional": true
     },
     "@esbuild/darwin-arm64": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz",
-      "integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.1.tgz",
+      "integrity": "sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ==",
       "dev": true,
       "optional": true
     },
     "@esbuild/darwin-x64": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz",
-      "integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.1.tgz",
+      "integrity": "sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA==",
       "dev": true,
       "optional": true
     },
     "@esbuild/freebsd-arm64": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz",
-      "integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.1.tgz",
+      "integrity": "sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A==",
       "dev": true,
       "optional": true
     },
     "@esbuild/freebsd-x64": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz",
-      "integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.1.tgz",
+      "integrity": "sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww==",
       "dev": true,
       "optional": true
     },
     "@esbuild/linux-arm": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz",
-      "integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.1.tgz",
+      "integrity": "sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ==",
       "dev": true,
       "optional": true
     },
     "@esbuild/linux-arm64": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz",
-      "integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.1.tgz",
+      "integrity": "sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ==",
       "dev": true,
       "optional": true
     },
     "@esbuild/linux-ia32": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz",
-      "integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.1.tgz",
+      "integrity": "sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ==",
       "dev": true,
       "optional": true
     },
     "@esbuild/linux-loong64": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz",
-      "integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.1.tgz",
+      "integrity": "sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg==",
       "dev": true,
       "optional": true
     },
     "@esbuild/linux-mips64el": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz",
-      "integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.1.tgz",
+      "integrity": "sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg==",
       "dev": true,
       "optional": true
     },
     "@esbuild/linux-ppc64": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz",
-      "integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.1.tgz",
+      "integrity": "sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg==",
       "dev": true,
       "optional": true
     },
     "@esbuild/linux-riscv64": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz",
-      "integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.1.tgz",
+      "integrity": "sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ==",
       "dev": true,
       "optional": true
     },
     "@esbuild/linux-s390x": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz",
-      "integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.1.tgz",
+      "integrity": "sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ==",
       "dev": true,
       "optional": true
     },
     "@esbuild/linux-x64": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz",
-      "integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.1.tgz",
+      "integrity": "sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA==",
       "dev": true,
       "optional": true
     },
     "@esbuild/netbsd-arm64": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz",
-      "integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.1.tgz",
+      "integrity": "sha512-O96poM2XGhLtpTh+s4+nP7YCCAfb4tJNRVZHfIE7dgmax+yMP2WgMd2OecBuaATHKTHsLWHQeuaxMRnCsH8+5g==",
       "dev": true,
       "optional": true
     },
     "@esbuild/netbsd-x64": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz",
-      "integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.1.tgz",
+      "integrity": "sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA==",
       "dev": true,
       "optional": true
     },
     "@esbuild/openbsd-arm64": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz",
-      "integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.1.tgz",
+      "integrity": "sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg==",
       "dev": true,
       "optional": true
     },
     "@esbuild/openbsd-x64": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz",
-      "integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.1.tgz",
+      "integrity": "sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw==",
       "dev": true,
       "optional": true
     },
     "@esbuild/sunos-x64": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz",
-      "integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.1.tgz",
+      "integrity": "sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg==",
       "dev": true,
       "optional": true
     },
     "@esbuild/win32-arm64": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz",
-      "integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.1.tgz",
+      "integrity": "sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ==",
       "dev": true,
       "optional": true
     },
     "@esbuild/win32-ia32": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz",
-      "integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.1.tgz",
+      "integrity": "sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A==",
       "dev": true,
       "optional": true
     },
     "@esbuild/win32-x64": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz",
-      "integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.1.tgz",
+      "integrity": "sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg==",
       "dev": true,
       "optional": true
     },
@@ -4927,135 +4927,135 @@
       }
     },
     "@rollup/rollup-android-arm-eabi": {
-      "version": "4.29.1",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.29.1.tgz",
-      "integrity": "sha512-ssKhA8RNltTZLpG6/QNkCSge+7mBQGUqJRisZ2MDQcEGaK93QESEgWK2iOpIDZ7k9zPVkG5AS3ksvD5ZWxmItw==",
+      "version": "4.35.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.35.0.tgz",
+      "integrity": "sha512-uYQ2WfPaqz5QtVgMxfN6NpLD+no0MYHDBywl7itPYd3K5TjjSghNKmX8ic9S8NU8w81NVhJv/XojcHptRly7qQ==",
       "dev": true,
       "optional": true
     },
     "@rollup/rollup-android-arm64": {
-      "version": "4.29.1",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.29.1.tgz",
-      "integrity": "sha512-CaRfrV0cd+NIIcVVN/jx+hVLN+VRqnuzLRmfmlzpOzB87ajixsN/+9L5xNmkaUUvEbI5BmIKS+XTwXsHEb65Ew==",
+      "version": "4.35.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.35.0.tgz",
+      "integrity": "sha512-FtKddj9XZudurLhdJnBl9fl6BwCJ3ky8riCXjEw3/UIbjmIY58ppWwPEvU3fNu+W7FUsAsB1CdH+7EQE6CXAPA==",
       "dev": true,
       "optional": true
     },
     "@rollup/rollup-darwin-arm64": {
-      "version": "4.29.1",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.29.1.tgz",
-      "integrity": "sha512-2ORr7T31Y0Mnk6qNuwtyNmy14MunTAMx06VAPI6/Ju52W10zk1i7i5U3vlDRWjhOI5quBcrvhkCHyF76bI7kEw==",
+      "version": "4.35.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.35.0.tgz",
+      "integrity": "sha512-Uk+GjOJR6CY844/q6r5DR/6lkPFOw0hjfOIzVx22THJXMxktXG6CbejseJFznU8vHcEBLpiXKY3/6xc+cBm65Q==",
       "dev": true,
       "optional": true
     },
     "@rollup/rollup-darwin-x64": {
-      "version": "4.29.1",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.29.1.tgz",
-      "integrity": "sha512-j/Ej1oanzPjmN0tirRd5K2/nncAhS9W6ICzgxV+9Y5ZsP0hiGhHJXZ2JQ53iSSjj8m6cRY6oB1GMzNn2EUt6Ng==",
+      "version": "4.35.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.35.0.tgz",
+      "integrity": "sha512-3IrHjfAS6Vkp+5bISNQnPogRAW5GAV1n+bNCrDwXmfMHbPl5EhTmWtfmwlJxFRUCBZ+tZ/OxDyU08aF6NI/N5Q==",
       "dev": true,
       "optional": true
     },
     "@rollup/rollup-freebsd-arm64": {
-      "version": "4.29.1",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.29.1.tgz",
-      "integrity": "sha512-91C//G6Dm/cv724tpt7nTyP+JdN12iqeXGFM1SqnljCmi5yTXriH7B1r8AD9dAZByHpKAumqP1Qy2vVNIdLZqw==",
+      "version": "4.35.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.35.0.tgz",
+      "integrity": "sha512-sxjoD/6F9cDLSELuLNnY0fOrM9WA0KrM0vWm57XhrIMf5FGiN8D0l7fn+bpUeBSU7dCgPV2oX4zHAsAXyHFGcQ==",
       "dev": true,
       "optional": true
     },
     "@rollup/rollup-freebsd-x64": {
-      "version": "4.29.1",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.29.1.tgz",
-      "integrity": "sha512-hEioiEQ9Dec2nIRoeHUP6hr1PSkXzQaCUyqBDQ9I9ik4gCXQZjJMIVzoNLBRGet+hIUb3CISMh9KXuCcWVW/8w==",
+      "version": "4.35.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.35.0.tgz",
+      "integrity": "sha512-2mpHCeRuD1u/2kruUiHSsnjWtHjqVbzhBkNVQ1aVD63CcexKVcQGwJ2g5VphOd84GvxfSvnnlEyBtQCE5hxVVw==",
       "dev": true,
       "optional": true
     },
     "@rollup/rollup-linux-arm-gnueabihf": {
-      "version": "4.29.1",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.29.1.tgz",
-      "integrity": "sha512-Py5vFd5HWYN9zxBv3WMrLAXY3yYJ6Q/aVERoeUFwiDGiMOWsMs7FokXihSOaT/PMWUty/Pj60XDQndK3eAfE6A==",
+      "version": "4.35.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.35.0.tgz",
+      "integrity": "sha512-mrA0v3QMy6ZSvEuLs0dMxcO2LnaCONs1Z73GUDBHWbY8tFFocM6yl7YyMu7rz4zS81NDSqhrUuolyZXGi8TEqg==",
       "dev": true,
       "optional": true
     },
     "@rollup/rollup-linux-arm-musleabihf": {
-      "version": "4.29.1",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.29.1.tgz",
-      "integrity": "sha512-RiWpGgbayf7LUcuSNIbahr0ys2YnEERD4gYdISA06wa0i8RALrnzflh9Wxii7zQJEB2/Eh74dX4y/sHKLWp5uQ==",
+      "version": "4.35.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.35.0.tgz",
+      "integrity": "sha512-DnYhhzcvTAKNexIql8pFajr0PiDGrIsBYPRvCKlA5ixSS3uwo/CWNZxB09jhIapEIg945KOzcYEAGGSmTSpk7A==",
       "dev": true,
       "optional": true
     },
     "@rollup/rollup-linux-arm64-gnu": {
-      "version": "4.29.1",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.29.1.tgz",
-      "integrity": "sha512-Z80O+taYxTQITWMjm/YqNoe9d10OX6kDh8X5/rFCMuPqsKsSyDilvfg+vd3iXIqtfmp+cnfL1UrYirkaF8SBZA==",
+      "version": "4.35.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.35.0.tgz",
+      "integrity": "sha512-uagpnH2M2g2b5iLsCTZ35CL1FgyuzzJQ8L9VtlJ+FckBXroTwNOaD0z0/UF+k5K3aNQjbm8LIVpxykUOQt1m/A==",
       "dev": true,
       "optional": true
     },
     "@rollup/rollup-linux-arm64-musl": {
-      "version": "4.29.1",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.29.1.tgz",
-      "integrity": "sha512-fOHRtF9gahwJk3QVp01a/GqS4hBEZCV1oKglVVq13kcK3NeVlS4BwIFzOHDbmKzt3i0OuHG4zfRP0YoG5OF/rA==",
+      "version": "4.35.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.35.0.tgz",
+      "integrity": "sha512-XQxVOCd6VJeHQA/7YcqyV0/88N6ysSVzRjJ9I9UA/xXpEsjvAgDTgH3wQYz5bmr7SPtVK2TsP2fQ2N9L4ukoUg==",
       "dev": true,
       "optional": true
     },
     "@rollup/rollup-linux-loongarch64-gnu": {
-      "version": "4.29.1",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.29.1.tgz",
-      "integrity": "sha512-5a7q3tnlbcg0OodyxcAdrrCxFi0DgXJSoOuidFUzHZ2GixZXQs6Tc3CHmlvqKAmOs5eRde+JJxeIf9DonkmYkw==",
+      "version": "4.35.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.35.0.tgz",
+      "integrity": "sha512-5pMT5PzfgwcXEwOaSrqVsz/LvjDZt+vQ8RT/70yhPU06PTuq8WaHhfT1LW+cdD7mW6i/J5/XIkX/1tCAkh1W6g==",
       "dev": true,
       "optional": true
     },
     "@rollup/rollup-linux-powerpc64le-gnu": {
-      "version": "4.29.1",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.29.1.tgz",
-      "integrity": "sha512-9b4Mg5Yfz6mRnlSPIdROcfw1BU22FQxmfjlp/CShWwO3LilKQuMISMTtAu/bxmmrE6A902W2cZJuzx8+gJ8e9w==",
+      "version": "4.35.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.35.0.tgz",
+      "integrity": "sha512-c+zkcvbhbXF98f4CtEIP1EBA/lCic5xB0lToneZYvMeKu5Kamq3O8gqrxiYYLzlZH6E3Aq+TSW86E4ay8iD8EA==",
       "dev": true,
       "optional": true
     },
     "@rollup/rollup-linux-riscv64-gnu": {
-      "version": "4.29.1",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.29.1.tgz",
-      "integrity": "sha512-G5pn0NChlbRM8OJWpJFMX4/i8OEU538uiSv0P6roZcbpe/WfhEO+AT8SHVKfp8qhDQzaz7Q+1/ixMy7hBRidnQ==",
+      "version": "4.35.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.35.0.tgz",
+      "integrity": "sha512-s91fuAHdOwH/Tad2tzTtPX7UZyytHIRR6V4+2IGlV0Cej5rkG0R61SX4l4y9sh0JBibMiploZx3oHKPnQBKe4g==",
       "dev": true,
       "optional": true
     },
     "@rollup/rollup-linux-s390x-gnu": {
-      "version": "4.29.1",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.29.1.tgz",
-      "integrity": "sha512-WM9lIkNdkhVwiArmLxFXpWndFGuOka4oJOZh8EP3Vb8q5lzdSCBuhjavJsw68Q9AKDGeOOIHYzYm4ZFvmWez5g==",
+      "version": "4.35.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.35.0.tgz",
+      "integrity": "sha512-hQRkPQPLYJZYGP+Hj4fR9dDBMIM7zrzJDWFEMPdTnTy95Ljnv0/4w/ixFw3pTBMEuuEuoqtBINYND4M7ujcuQw==",
       "dev": true,
       "optional": true
     },
     "@rollup/rollup-linux-x64-gnu": {
-      "version": "4.29.1",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.29.1.tgz",
-      "integrity": "sha512-87xYCwb0cPGZFoGiErT1eDcssByaLX4fc0z2nRM6eMtV9njAfEE6OW3UniAoDhX4Iq5xQVpE6qO9aJbCFumKYQ==",
+      "version": "4.35.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.35.0.tgz",
+      "integrity": "sha512-Pim1T8rXOri+0HmV4CdKSGrqcBWX0d1HoPnQ0uw0bdp1aP5SdQVNBy8LjYncvnLgu3fnnCt17xjWGd4cqh8/hA==",
       "dev": true,
       "optional": true
     },
     "@rollup/rollup-linux-x64-musl": {
-      "version": "4.29.1",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.29.1.tgz",
-      "integrity": "sha512-xufkSNppNOdVRCEC4WKvlR1FBDyqCSCpQeMMgv9ZyXqqtKBfkw1yfGMTUTs9Qsl6WQbJnsGboWCp7pJGkeMhKA==",
+      "version": "4.35.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.35.0.tgz",
+      "integrity": "sha512-QysqXzYiDvQWfUiTm8XmJNO2zm9yC9P/2Gkrwg2dH9cxotQzunBHYr6jk4SujCTqnfGxduOmQcI7c2ryuW8XVg==",
       "dev": true,
       "optional": true
     },
     "@rollup/rollup-win32-arm64-msvc": {
-      "version": "4.29.1",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.29.1.tgz",
-      "integrity": "sha512-F2OiJ42m77lSkizZQLuC+jiZ2cgueWQL5YC9tjo3AgaEw+KJmVxHGSyQfDUoYR9cci0lAywv2Clmckzulcq6ig==",
+      "version": "4.35.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.35.0.tgz",
+      "integrity": "sha512-OUOlGqPkVJCdJETKOCEf1mw848ZyJ5w50/rZ/3IBQVdLfR5jk/6Sr5m3iO2tdPgwo0x7VcncYuOvMhBWZq8ayg==",
       "dev": true,
       "optional": true
     },
     "@rollup/rollup-win32-ia32-msvc": {
-      "version": "4.29.1",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.29.1.tgz",
-      "integrity": "sha512-rYRe5S0FcjlOBZQHgbTKNrqxCBUmgDJem/VQTCcTnA2KCabYSWQDrytOzX7avb79cAAweNmMUb/Zw18RNd4mng==",
+      "version": "4.35.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.35.0.tgz",
+      "integrity": "sha512-2/lsgejMrtwQe44glq7AFFHLfJBPafpsTa6JvP2NGef/ifOa4KBoglVf7AKN7EV9o32evBPRqfg96fEHzWo5kw==",
       "dev": true,
       "optional": true
     },
     "@rollup/rollup-win32-x64-msvc": {
-      "version": "4.29.1",
-      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.29.1.tgz",
-      "integrity": "sha512-+10CMg9vt1MoHj6x1pxyjPSMjHTIlqs8/tBztXvPAx24SKs9jwVnKqHJumlH/IzhaPUaj3T6T6wfZr8okdXaIg==",
+      "version": "4.35.0",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.35.0.tgz",
+      "integrity": "sha512-PIQeY5XDkrOysbQblSW7v3l1MDZzkTEzAfTPkj5VAu3FW8fS4ynyLg2sINp0fp3SjZ8xkRYpLqoKcYqAkhU1dw==",
       "dev": true,
       "optional": true
     },
@@ -5659,36 +5659,36 @@
       }
     },
     "esbuild": {
-      "version": "0.24.2",
-      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz",
-      "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==",
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.1.tgz",
+      "integrity": "sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==",
       "dev": true,
       "requires": {
-        "@esbuild/aix-ppc64": "0.24.2",
-        "@esbuild/android-arm": "0.24.2",
-        "@esbuild/android-arm64": "0.24.2",
-        "@esbuild/android-x64": "0.24.2",
-        "@esbuild/darwin-arm64": "0.24.2",
-        "@esbuild/darwin-x64": "0.24.2",
-        "@esbuild/freebsd-arm64": "0.24.2",
-        "@esbuild/freebsd-x64": "0.24.2",
-        "@esbuild/linux-arm": "0.24.2",
-        "@esbuild/linux-arm64": "0.24.2",
-        "@esbuild/linux-ia32": "0.24.2",
-        "@esbuild/linux-loong64": "0.24.2",
-        "@esbuild/linux-mips64el": "0.24.2",
-        "@esbuild/linux-ppc64": "0.24.2",
-        "@esbuild/linux-riscv64": "0.24.2",
-        "@esbuild/linux-s390x": "0.24.2",
-        "@esbuild/linux-x64": "0.24.2",
-        "@esbuild/netbsd-arm64": "0.24.2",
-        "@esbuild/netbsd-x64": "0.24.2",
-        "@esbuild/openbsd-arm64": "0.24.2",
-        "@esbuild/openbsd-x64": "0.24.2",
-        "@esbuild/sunos-x64": "0.24.2",
-        "@esbuild/win32-arm64": "0.24.2",
-        "@esbuild/win32-ia32": "0.24.2",
-        "@esbuild/win32-x64": "0.24.2"
+        "@esbuild/aix-ppc64": "0.25.1",
+        "@esbuild/android-arm": "0.25.1",
+        "@esbuild/android-arm64": "0.25.1",
+        "@esbuild/android-x64": "0.25.1",
+        "@esbuild/darwin-arm64": "0.25.1",
+        "@esbuild/darwin-x64": "0.25.1",
+        "@esbuild/freebsd-arm64": "0.25.1",
+        "@esbuild/freebsd-x64": "0.25.1",
+        "@esbuild/linux-arm": "0.25.1",
+        "@esbuild/linux-arm64": "0.25.1",
+        "@esbuild/linux-ia32": "0.25.1",
+        "@esbuild/linux-loong64": "0.25.1",
+        "@esbuild/linux-mips64el": "0.25.1",
+        "@esbuild/linux-ppc64": "0.25.1",
+        "@esbuild/linux-riscv64": "0.25.1",
+        "@esbuild/linux-s390x": "0.25.1",
+        "@esbuild/linux-x64": "0.25.1",
+        "@esbuild/netbsd-arm64": "0.25.1",
+        "@esbuild/netbsd-x64": "0.25.1",
+        "@esbuild/openbsd-arm64": "0.25.1",
+        "@esbuild/openbsd-x64": "0.25.1",
+        "@esbuild/sunos-x64": "0.25.1",
+        "@esbuild/win32-arm64": "0.25.1",
+        "@esbuild/win32-ia32": "0.25.1",
+        "@esbuild/win32-x64": "0.25.1"
       }
     },
     "escalade": {
@@ -6200,9 +6200,9 @@
       "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
     },
     "nanoid": {
-      "version": "3.3.8",
-      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
-      "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
+      "version": "3.3.10",
+      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.10.tgz",
+      "integrity": "sha512-vSJJTG+t/dIKAUhUDw/dLdZ9s//5OxcHqLaDWWrW4Cdq7o6tdLIczUkMXt2MBNmk6sJRZBZRXVixs7URY1CmIg==",
       "dev": true
     },
     "natural-compare": {
@@ -6317,12 +6317,12 @@
       "dev": true
     },
     "postcss": {
-      "version": "8.4.49",
-      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz",
-      "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==",
+      "version": "8.5.3",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz",
+      "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==",
       "dev": true,
       "requires": {
-        "nanoid": "^3.3.7",
+        "nanoid": "^3.3.8",
         "picocolors": "^1.1.1",
         "source-map-js": "^1.2.1"
       }
@@ -6429,30 +6429,30 @@
       "dev": true
     },
     "rollup": {
-      "version": "4.29.1",
-      "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.29.1.tgz",
-      "integrity": "sha512-RaJ45M/kmJUzSWDs1Nnd5DdV4eerC98idtUOVr6FfKcgxqvjwHmxc5upLF9qZU9EpsVzzhleFahrT3shLuJzIw==",
+      "version": "4.35.0",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.35.0.tgz",
+      "integrity": "sha512-kg6oI4g+vc41vePJyO6dHt/yl0Rz3Thv0kJeVQ3D1kS3E5XSuKbPc29G4IpT/Kv1KQwgHVcN+HtyS+HYLNSvQg==",
       "dev": true,
       "requires": {
-        "@rollup/rollup-android-arm-eabi": "4.29.1",
-        "@rollup/rollup-android-arm64": "4.29.1",
-        "@rollup/rollup-darwin-arm64": "4.29.1",
-        "@rollup/rollup-darwin-x64": "4.29.1",
-        "@rollup/rollup-freebsd-arm64": "4.29.1",
-        "@rollup/rollup-freebsd-x64": "4.29.1",
-        "@rollup/rollup-linux-arm-gnueabihf": "4.29.1",
-        "@rollup/rollup-linux-arm-musleabihf": "4.29.1",
-        "@rollup/rollup-linux-arm64-gnu": "4.29.1",
-        "@rollup/rollup-linux-arm64-musl": "4.29.1",
-        "@rollup/rollup-linux-loongarch64-gnu": "4.29.1",
-        "@rollup/rollup-linux-powerpc64le-gnu": "4.29.1",
-        "@rollup/rollup-linux-riscv64-gnu": "4.29.1",
-        "@rollup/rollup-linux-s390x-gnu": "4.29.1",
-        "@rollup/rollup-linux-x64-gnu": "4.29.1",
-        "@rollup/rollup-linux-x64-musl": "4.29.1",
-        "@rollup/rollup-win32-arm64-msvc": "4.29.1",
-        "@rollup/rollup-win32-ia32-msvc": "4.29.1",
-        "@rollup/rollup-win32-x64-msvc": "4.29.1",
+        "@rollup/rollup-android-arm-eabi": "4.35.0",
+        "@rollup/rollup-android-arm64": "4.35.0",
+        "@rollup/rollup-darwin-arm64": "4.35.0",
+        "@rollup/rollup-darwin-x64": "4.35.0",
+        "@rollup/rollup-freebsd-arm64": "4.35.0",
+        "@rollup/rollup-freebsd-x64": "4.35.0",
+        "@rollup/rollup-linux-arm-gnueabihf": "4.35.0",
+        "@rollup/rollup-linux-arm-musleabihf": "4.35.0",
+        "@rollup/rollup-linux-arm64-gnu": "4.35.0",
+        "@rollup/rollup-linux-arm64-musl": "4.35.0",
+        "@rollup/rollup-linux-loongarch64-gnu": "4.35.0",
+        "@rollup/rollup-linux-powerpc64le-gnu": "4.35.0",
+        "@rollup/rollup-linux-riscv64-gnu": "4.35.0",
+        "@rollup/rollup-linux-s390x-gnu": "4.35.0",
+        "@rollup/rollup-linux-x64-gnu": "4.35.0",
+        "@rollup/rollup-linux-x64-musl": "4.35.0",
+        "@rollup/rollup-win32-arm64-msvc": "4.35.0",
+        "@rollup/rollup-win32-ia32-msvc": "4.35.0",
+        "@rollup/rollup-win32-x64-msvc": "4.35.0",
         "@types/estree": "1.0.6",
         "fsevents": "~2.3.2"
       }
@@ -6625,15 +6625,15 @@
       }
     },
     "vite": {
-      "version": "6.0.6",
-      "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.6.tgz",
-      "integrity": "sha512-NSjmUuckPmDU18bHz7QZ+bTYhRR0iA72cs2QAxCqDpafJ0S6qetco0LB3WW2OxlMHS0JmAv+yZ/R3uPmMyGTjQ==",
+      "version": "6.2.2",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.2.tgz",
+      "integrity": "sha512-yW7PeMM+LkDzc7CgJuRLMW2Jz0FxMOsVJ8Lv3gpgW9WLcb9cTW+121UEr1hvmfR7w3SegR5ItvYyzVz1vxNJgQ==",
       "dev": true,
       "requires": {
-        "esbuild": "^0.24.2",
+        "esbuild": "^0.25.0",
         "fsevents": "~2.3.3",
-        "postcss": "^8.4.49",
-        "rollup": "^4.23.0"
+        "postcss": "^8.5.3",
+        "rollup": "^4.30.1"
       }
     },
     "vite-plugin-svgr": {
diff --git a/encode.go b/encode.go
index 5e0bfe3..e0c795e 100644
--- a/encode.go
+++ b/encode.go
@@ -32,9 +32,13 @@ type Encoder struct {
 	isFlowStyle                bool
 	isJSONStyle                bool
 	useJSONMarshaler           bool
+	enableSmartAnchor          bool
+	aliasRefToName             map[uintptr]string
+	anchorRefToName            map[uintptr]string
+	anchorNameMap              map[string]struct{}
 	anchorCallback             func(*ast.AnchorNode, interface{}) error
-	anchorPtrToNameMap         map[uintptr]string
 	customMarshalerMap         map[reflect.Type]func(interface{}) ([]byte, error)
+	autoInt                    bool
 	useLiteralStyleIfMultiline bool
 	commentMap                 map[*Path][]*Comment
 	written                    bool
@@ -53,12 +57,14 @@ func NewEncoder(w io.Writer, opts ...EncodeOption) *Encoder {
 	return &Encoder{
 		writer:             w,
 		opts:               opts,
-		anchorPtrToNameMap: map[uintptr]string{},
 		customMarshalerMap: map[reflect.Type]func(interface{}) ([]byte, error){},
 		line:               1,
 		column:             1,
 		offset:             0,
 		indentNum:          DefaultIndentSpaces,
+		anchorRefToName:    make(map[uintptr]string),
+		anchorNameMap:      make(map[string]struct{}),
+		aliasRefToName:     make(map[uintptr]string),
 	}
 }
 
@@ -110,6 +116,13 @@ func (e *Encoder) EncodeToNodeContext(ctx context.Context, v interface{}) (ast.N
 			return nil, err
 		}
 	}
+	if e.enableSmartAnchor {
+		// during the first encoding, store all mappings between alias addresses and their names.
+		if _, err := e.encodeValue(ctx, reflect.ValueOf(v), 1); err != nil {
+			return nil, err
+		}
+		e.clearSmartAnchorRef()
+	}
 	node, err := e.encodeValue(ctx, reflect.ValueOf(v), 1)
 	if err != nil {
 		return nil, err
@@ -445,12 +458,8 @@ func (e *Encoder) encodeValue(ctx context.Context, v reflect.Value, column int)
 	case reflect.Float64:
 		return e.encodeFloat(v.Float(), 64), nil
 	case reflect.Ptr:
-		anchorName := e.anchorPtrToNameMap[v.Pointer()]
-		if anchorName != "" {
-			aliasName := anchorName
-			alias := ast.Alias(token.New("*", "*", e.pos(column)))
-			alias.Value = ast.String(token.New(aliasName, aliasName, e.pos(column)))
-			return alias, nil
+		if value := e.encodePtrAnchor(v, column); value != nil {
+			return value, nil
 		}
 		return e.encodeValue(ctx, v.Elem(), column)
 	case reflect.Interface:
@@ -463,6 +472,9 @@ func (e *Encoder) encodeValue(ctx context.Context, v reflect.Value, column int)
 		if mapSlice, ok := v.Interface().(MapSlice); ok {
 			return e.encodeMapSlice(ctx, mapSlice, column)
 		}
+		if value := e.encodePtrAnchor(v, column); value != nil {
+			return value, nil
+		}
 		return e.encodeSlice(ctx, v)
 	case reflect.Array:
 		return e.encodeArray(ctx, v)
@@ -477,12 +489,27 @@ func (e *Encoder) encodeValue(ctx context.Context, v reflect.Value, column int)
 		}
 		return e.encodeStruct(ctx, v, column)
 	case reflect.Map:
-		return e.encodeMap(ctx, v, column), nil
+		if value := e.encodePtrAnchor(v, column); value != nil {
+			return value, nil
+		}
+		return e.encodeMap(ctx, v, column)
 	default:
 		return nil, fmt.Errorf("unknown value type %s", v.Type().String())
 	}
 }
 
+func (e *Encoder) encodePtrAnchor(v reflect.Value, column int) ast.Node {
+	anchorName, exists := e.getAnchor(v.Pointer())
+	if !exists {
+		return nil
+	}
+	aliasName := anchorName
+	alias := ast.Alias(token.New("*", "*", e.pos(column)))
+	alias.Value = ast.String(token.New(aliasName, aliasName, e.pos(column)))
+	e.setSmartAlias(aliasName, v.Pointer())
+	return alias
+}
+
 func (e *Encoder) pos(column int) *token.Position {
 	return &token.Position{
 		Line:        e.line,
@@ -521,6 +548,9 @@ func (e *Encoder) encodeFloat(v float64, bitSize int) ast.Node {
 	}
 	value := strconv.FormatFloat(v, 'g', -1, bitSize)
 	if !strings.Contains(value, ".") && !strings.Contains(value, "e") {
+		if e.autoInt {
+			return ast.Integer(token.New(value, value, e.pos(e.column)))
+		}
 		// append x.0 suffix to keep float value context
 		value = fmt.Sprintf("%s.0", value)
 	}
@@ -654,7 +684,7 @@ func (e *Encoder) isTagAndMapNode(node ast.Node) bool {
 	return ok && e.isMapNode(tn.Value)
 }
 
-func (e *Encoder) encodeMap(ctx context.Context, value reflect.Value, column int) ast.Node {
+func (e *Encoder) encodeMap(ctx context.Context, value reflect.Value, column int) (ast.Node, error) {
 	node := ast.Mapping(token.New("", "", e.pos(column)), e.isFlowStyle)
 	keys := make([]interface{}, len(value.MapKeys()))
 	for i, k := range value.MapKeys() {
@@ -668,7 +698,7 @@ func (e *Encoder) encodeMap(ctx context.Context, value reflect.Value, column int
 		v := value.MapIndex(k)
 		value, err := e.encodeValue(ctx, v, column)
 		if err != nil {
-			return nil
+			return nil, err
 		}
 		if e.isMapNode(value) {
 			value.AddColumn(e.indentNum)
@@ -676,13 +706,25 @@ func (e *Encoder) encodeMap(ctx context.Context, value reflect.Value, column int
 		if e.isTagAndMapNode(value) {
 			value.AddColumn(e.indentNum)
 		}
+		keyText := fmt.Sprint(key)
+		vRef := e.toPointer(v)
+
+		// during the second encoding, an anchor is assigned if it is found to be used by an alias.
+		if aliasName, exists := e.getSmartAlias(vRef); exists {
+			anchorName := aliasName
+			anchorNode := ast.Anchor(token.New("&", "&", e.pos(column)))
+			anchorNode.Name = ast.String(token.New(anchorName, anchorName, e.pos(column)))
+			anchorNode.Value = value
+			value = anchorNode
+		}
 		node.Values = append(node.Values, ast.MappingValue(
 			nil,
-			e.encodeString(fmt.Sprint(key), column),
+			e.encodeString(keyText, column),
 			value,
 		))
+		e.setSmartAnchor(vRef, keyText)
 	}
-	return node
+	return node, nil
 }
 
 // IsZeroer is used to check whether an object is zero to determine
@@ -761,7 +803,7 @@ func (e *Encoder) encodeAnchor(anchorName string, value ast.Node, fieldValue ref
 		}
 	}
 	if fieldValue.Kind() == reflect.Ptr {
-		e.anchorPtrToNameMap[fieldValue.Pointer()] = anchorName
+		e.setAnchor(fieldValue.Pointer(), anchorName)
 	}
 	return anchorNode, nil
 }
@@ -876,9 +918,87 @@ func (e *Encoder) encodeStruct(ctx context.Context, value reflect.Value, column
 			}
 		}
 		if inlineAnchorValue.Kind() == reflect.Ptr {
-			e.anchorPtrToNameMap[inlineAnchorValue.Pointer()] = anchorName
+			e.setAnchor(inlineAnchorValue.Pointer(), anchorName)
 		}
 		return anchorNode, nil
 	}
 	return node, nil
 }
+
+func (e *Encoder) toPointer(v reflect.Value) uintptr {
+	if e.isInvalidValue(v) {
+		return 0
+	}
+
+	switch v.Type().Kind() {
+	case reflect.Ptr:
+		return v.Pointer()
+	case reflect.Interface:
+		return e.toPointer(v.Elem())
+	case reflect.Slice:
+		return v.Pointer()
+	case reflect.Map:
+		return v.Pointer()
+	}
+	return 0
+}
+
+func (e *Encoder) clearSmartAnchorRef() {
+	if !e.enableSmartAnchor {
+		return
+	}
+	e.anchorRefToName = make(map[uintptr]string)
+	e.anchorNameMap = make(map[string]struct{})
+}
+
+func (e *Encoder) setSmartAnchor(ptr uintptr, name string) {
+	if !e.enableSmartAnchor {
+		return
+	}
+	e.setAnchor(ptr, e.generateAnchorName(name))
+}
+
+func (e *Encoder) setAnchor(ptr uintptr, name string) {
+	if ptr == 0 {
+		return
+	}
+	if name == "" {
+		return
+	}
+	e.anchorRefToName[ptr] = name
+	e.anchorNameMap[name] = struct{}{}
+}
+
+func (e *Encoder) generateAnchorName(base string) string {
+	if _, exists := e.anchorNameMap[base]; !exists {
+		return base
+	}
+	for i := 1; i < 100; i++ {
+		name := base + strconv.Itoa(i)
+		if _, exists := e.anchorNameMap[name]; exists {
+			continue
+		}
+		return name
+	}
+	return ""
+}
+
+func (e *Encoder) getAnchor(ref uintptr) (string, bool) {
+	anchorName, exists := e.anchorRefToName[ref]
+	return anchorName, exists
+}
+
+func (e *Encoder) setSmartAlias(name string, ref uintptr) {
+	if !e.enableSmartAnchor {
+		return
+	}
+	e.aliasRefToName[ref] = name
+}
+
+func (e *Encoder) getSmartAlias(ref uintptr) (string, bool) {
+	if !e.enableSmartAnchor {
+		return "", false
+	}
+	aliasName, exists := e.aliasRefToName[ref]
+	return aliasName, exists
+}
diff --git a/internal/format/format.go b/internal/format/format.go
new file mode 100644
index 0000000..a4539ed
--- /dev/null
+++ b/internal/format/format.go
@@ -0,0 +1,539 @@
+package format
+
+import (
+	"strings"
+
+	"github.com/goccy/go-yaml/ast"
+	"github.com/goccy/go-yaml/token"
+)
+
+func FormatNodeWithResolvedAlias(n ast.Node, anchorNodeMap map[string]ast.Node) string {
+	tk := getFirstToken(n)
+	if tk == nil {
+		return ""
+	}
+	formatter := newFormatter(tk, hasComment(n))
+	formatter.anchorNodeMap = anchorNodeMap
+	return formatter.format(n)
+}
+
+func FormatNode(n ast.Node) string {
+	tk := getFirstToken(n)
+	if tk == nil {
+		return ""
+	}
+	return newFormatter(tk, hasComment(n)).format(n)
+}
+
+func FormatFile(file *ast.File) string {
+	if len(file.Docs) == 0 {
+		return ""
+	}
+	tk := getFirstToken(file.Docs[0])
+	if tk == nil {
+		return ""
+	}
+	return newFormatter(tk, hasCommentFile(file)).formatFile(file)
+}
+
+func hasCommentFile(f *ast.File) bool {
+	for _, doc := range f.Docs {
+		if hasComment(doc.Body) {
+			return true
+		}
+	}
+	return false
+}
+
+func hasComment(n ast.Node) bool {
+	if n == nil {
+		return false
+	}
+	switch nn := n.(type) {
+	case *ast.DocumentNode:
+		return hasComment(nn.Body)
+	case *ast.NullNode:
+		return nn.Comment != nil
+	case *ast.BoolNode:
+		return nn.Comment != nil
+	case *ast.IntegerNode:
+		return nn.Comment != nil
+	case *ast.FloatNode:
+		return nn.Comment != nil
+	case *ast.StringNode:
+		return nn.Comment != nil
+	case *ast.InfinityNode:
+		return nn.Comment != nil
+	case *ast.NanNode:
+		return nn.Comment != nil
+	case *ast.LiteralNode:
+		return nn.Comment != nil
+	case *ast.DirectiveNode:
+		if nn.Comment != nil {
+			return true
+		}
+		for _, value := range nn.Values {
+			if hasComment(value) {
+				return true
+			}
+		}
+	case *ast.TagNode:
+		if nn.Comment != nil {
+			return true
+		}
+		return hasComment(nn.Value)
+	case *ast.MappingNode:
+		if nn.Comment != nil || nn.FootComment != nil {
+			return true
+		}
+		for _, value := range nn.Values {
+			if value.Comment != nil || value.FootComment != nil {
+				return true
+			}
+			if hasComment(value.Key) {
+				return true
+			}
+			if hasComment(value.Value) {
+				return true
+			}
+		}
+	case *ast.MappingKeyNode:
+		return nn.Comment != nil
+	case *ast.MergeKeyNode:
+		return nn.Comment != nil
+	case *ast.SequenceNode:
+		if nn.Comment != nil || nn.FootComment != nil {
+			return true
+		}
+		for _, entry := range nn.Entries {
+			if entry.Comment != nil || entry.HeadComment != nil || entry.LineComment != nil {
+				return true
+			}
+			if hasComment(entry.Value) {
+				return true
+			}
+		}
+	case *ast.AnchorNode:
+		if nn.Comment != nil {
+			return true
+		}
+		if hasComment(nn.Name) || hasComment(nn.Value) {
+			return true
+		}
+	case *ast.AliasNode:
+		if nn.Comment != nil {
+			return true
+		}
+		if hasComment(nn.Value) {
+			return true
+		}
+	}
+	return false
+}
+
+func getFirstToken(n ast.Node) *token.Token {
+	if n == nil {
+		return nil
+	}
+	switch nn := n.(type) {
+	case *ast.DocumentNode:
+		if nn.Start != nil {
+			return nn.Start
+		}
+		return getFirstToken(nn.Body)
+	case *ast.NullNode:
+		return nn.Token
+	case *ast.BoolNode:
+		return nn.Token
+	case *ast.IntegerNode:
+		return nn.Token
+	case *ast.FloatNode:
+		return nn.Token
+	case *ast.StringNode:
+		return nn.Token
+	case *ast.InfinityNode:
+		return nn.Token
+	case *ast.NanNode:
+		return nn.Token
+	case *ast.LiteralNode:
+		return nn.Start
+	case *ast.DirectiveNode:
+		return nn.Start
+	case *ast.TagNode:
+		return nn.Start
+	case *ast.MappingNode:
+		if nn.IsFlowStyle {
+			return nn.Start
+		}
+		if len(nn.Values) == 0 {
+			return nn.Start
+		}
+		return getFirstToken(nn.Values[0].Key)
+	case *ast.MappingKeyNode:
+		return nn.Start
+	case *ast.MergeKeyNode:
+		return nn.Token
+	case *ast.SequenceNode:
+		return nn.Start
+	case *ast.AnchorNode:
+		return nn.Start
+	case *ast.AliasNode:
+		return nn.Start
+	}
+	return nil
+}
+
+type Formatter struct {
+	existsComment    bool
+	tokenToOriginMap map[*token.Token]string
+	anchorNodeMap    map[string]ast.Node
+}
+
+func newFormatter(tk *token.Token, existsComment bool) *Formatter {
+	tokenToOriginMap := make(map[*token.Token]string)
+	for tk.Prev != nil {
+		tk = tk.Prev
+	}
+	tokenToOriginMap[tk] = tk.Origin
+
+	var origin string
+	for tk.Next != nil {
+		tk = tk.Next
+		if tk.Type == token.CommentType {
+			origin += strings.Repeat("\n", strings.Count(normalizeNewLineChars(tk.Origin), "\n"))
+			continue
+		}
+		origin += tk.Origin
+		tokenToOriginMap[tk] = origin
+		origin = ""
+	}
+	return &Formatter{
+		existsComment:    existsComment,
+		tokenToOriginMap: tokenToOriginMap,
+	}
+}
+
+func getIndentNumByFirstLineToken(tk *token.Token) int {
+	defaultIndent := tk.Position.Column - 1
+
+	// key: value
+	//    ^
+	//   next
+	if tk.Type == token.SequenceEntryType {
+		// If the current token is the sequence entry.
+		// the indent is calculated from the column value of the current token.
+		return defaultIndent
+	}
+
+	// key: value
+	//    ^
+	//   next
+	if tk.Next != nil && tk.Next.Type == token.MappingValueType {
+		// If the current token is the key in the mapping-value,
+		// the indent is calculated from the column value of the current token.
+		return defaultIndent
+	}
+
+	if tk.Prev == nil {
+		return defaultIndent
+	}
+	prev := tk.Prev
+
+	// key: value
+	//    ^
+	//   prev
+	if prev.Type == token.MappingValueType {
+		// If the current token is the value in the mapping-value,
+		// the indent is calculated from the column value of the key two steps back.
+		if prev.Prev == nil {
+			return defaultIndent
+		}
+		return prev.Prev.Position.Column - 1
+	}
+
+	// - value
+	// ^
+	// prev
+	if prev.Type == token.SequenceEntryType {
+		// If the value is not a mapping-value and the previous token was a sequence entry,
+		// the indent is calculated using the column value of the sequence entry token.
+		return prev.Position.Column - 1
+	}
+
+	return defaultIndent
+}
+
+func (f *Formatter) format(n ast.Node) string {
+	return f.trimSpacePrefix(
+		f.trimIndentSpace(
+			getIndentNumByFirstLineToken(getFirstToken(n)),
+			f.trimNewLineCharPrefix(f.formatNode(n)),
+		),
+	)
+}
+
+func (f *Formatter) formatFile(file *ast.File) string {
+	if len(file.Docs) == 0 {
+		return ""
+	}
+	var ret string
+	for _, doc := range file.Docs {
+		ret += f.formatDocument(doc)
+	}
+	return ret
+}
+
+func (f *Formatter) origin(tk *token.Token) string {
+	if tk == nil {
+		return ""
+	}
+	if f.existsComment {
+		return tk.Origin
+	}
+	return f.tokenToOriginMap[tk]
+}
+
+func (f *Formatter) formatDocument(n *ast.DocumentNode) string {
+	return f.origin(n.Start) + f.formatNode(n.Body) + f.origin(n.End)
+}
+
+func (f *Formatter) formatNull(n *ast.NullNode) string {
+	return f.origin(n.Token) + f.formatCommentGroup(n.Comment)
+}
+
+func (f *Formatter) formatString(n *ast.StringNode) string {
+	return f.origin(n.Token) + f.formatCommentGroup(n.Comment)
+}
+
+func (f *Formatter) formatInteger(n *ast.IntegerNode) string {
+	return f.origin(n.Token) + f.formatCommentGroup(n.Comment)
+}
+
+func (f *Formatter) formatFloat(n *ast.FloatNode) string {
+	return f.origin(n.Token) + f.formatCommentGroup(n.Comment)
+}
+
+func (f *Formatter) formatBool(n *ast.BoolNode) string {
+	return f.origin(n.Token) + f.formatCommentGroup(n.Comment)
+}
+
+func (f *Formatter) formatInfinity(n *ast.InfinityNode) string {
+	return f.origin(n.Token) + f.formatCommentGroup(n.Comment)
+}
+
+func (f *Formatter) formatNan(n *ast.NanNode) string {
+	return f.origin(n.Token) + f.formatCommentGroup(n.Comment)
+}
+
+func (f *Formatter) formatLiteral(n *ast.LiteralNode) string {
+	return f.origin(n.Start) + f.formatCommentGroup(n.Comment) + f.origin(n.Value.Token)
+}
+
+func (f *Formatter) formatMergeKey(n *ast.MergeKeyNode) string {
+	return f.origin(n.Token)
+}
+
+func (f *Formatter) formatMappingValue(n *ast.MappingValueNode) string {
+	return f.formatCommentGroup(n.Comment) +
+		f.formatMapKey(n.Key) + ":" + f.formatNode(n.Value) +
+		f.formatCommentGroup(n.FootComment)
+}
+
+func (f *Formatter) formatMapKey(n ast.MapKeyNode) string {
+	return f.formatCommentGroup(n.GetComment()) + f.formatNode(n)
+}
+
+func (f *Formatter) formatDirective(n *ast.DirectiveNode) string {
+	ret := f.origin(n.Start) + f.formatNode(n.Name)
+	for _, val := range n.Values {
+		ret += f.formatNode(val)
+	}
+	return ret
+}
+
+func (f *Formatter) formatMapping(n *ast.MappingNode) string {
+	var ret string
+	if n.IsFlowStyle {
+		ret = f.origin(n.Start)
+	}
+	ret += f.formatCommentGroup(n.Comment)
+	for _, value := range n.Values {
+		if value.CollectEntry != nil {
+			ret += f.origin(value.CollectEntry)
+		}
+		ret += f.formatMappingValue(value)
+	}
+	if n.IsFlowStyle {
+		ret += f.origin(n.End)
+	}
+	return ret
+}
+
+func (f *Formatter) formatTag(n *ast.TagNode) string {
+	return f.origin(n.Start) + f.formatNode(n.Value)
+}
+
+func (f *Formatter) formatMappingKey(n *ast.MappingKeyNode) string {
+	return f.origin(n.Start) + f.formatNode(n.Value)
+}
+
+func (f *Formatter) formatSequence(n *ast.SequenceNode) string {
+	var ret string
+	if n.IsFlowStyle {
+		ret = f.origin(n.Start)
+	}
+	for _, entry := range n.Entries {
+		ret += f.formatNode(entry)
+	}
+	if n.IsFlowStyle {
+		ret += f.origin(n.End)
+	}
+	ret += f.formatCommentGroup(n.FootComment)
+	return ret
+}
+
+func (f *Formatter) formatSequenceEntry(n *ast.SequenceEntryNode) string {
+	return f.formatCommentGroup(n.HeadComment) + f.origin(n.Start) + f.formatCommentGroup(n.LineComment) + f.formatNode(n.Value)
+}
+
+func (f *Formatter) formatAnchor(n *ast.AnchorNode) string {
+	return f.origin(n.Start) + f.formatNode(n.Name) + f.formatNode(n.Value)
+}
+
+func (f *Formatter) formatAlias(n *ast.AliasNode) string {
+	if f.anchorNodeMap != nil {
+		anchorName := n.Value.GetToken().Value
+		node := f.anchorNodeMap[anchorName]
+		if node != nil {
+			formatted := f.formatNode(node)
+			// If formatted text contains newline characters, indentation needs to be considered.
+			if strings.Contains(formatted, "\n") {
+				// If the first character is not a newline, the first line should be output without indentation.
+				isIgnoredFirstLine := !strings.HasPrefix(formatted, "\n")
+				formatted = f.addIndentSpace(n.GetToken().Position.IndentNum, formatted, isIgnoredFirstLine)
+			}
+			return formatted
+		}
+	}
+	return f.origin(n.Start) + f.formatNode(n.Value)
+}
+
+func (f *Formatter) formatNode(n ast.Node) string {
+	switch nn := n.(type) {
+	case *ast.DocumentNode:
+		return f.formatDocument(nn)
+	case *ast.NullNode:
+		return f.formatNull(nn)
+	case *ast.BoolNode:
+		return f.formatBool(nn)
+	case *ast.IntegerNode:
+		return f.formatInteger(nn)
+	case *ast.FloatNode:
+		return f.formatFloat(nn)
+	case *ast.StringNode:
+		return f.formatString(nn)
+	case *ast.InfinityNode:
+		return f.formatInfinity(nn)
+	case *ast.NanNode:
+		return f.formatNan(nn)
+	case *ast.LiteralNode:
+		return f.formatLiteral(nn)
+	case *ast.DirectiveNode:
+		return f.formatDirective(nn)
+	case *ast.TagNode:
+		return f.formatTag(nn)
+	case *ast.MappingNode:
+		return f.formatMapping(nn)
+	case *ast.MappingKeyNode:
+		return f.formatMappingKey(nn)
+	case *ast.MappingValueNode:
+		return f.formatMappingValue(nn)
+	case *ast.MergeKeyNode:
+		return f.formatMergeKey(nn)
+	case *ast.SequenceNode:
+		return f.formatSequence(nn)
+	case *ast.SequenceEntryNode:
+		return f.formatSequenceEntry(nn)
+	case *ast.AnchorNode:
+		return f.formatAnchor(nn)
+	case *ast.AliasNode:
+		return f.formatAlias(nn)
+	}
+	return ""
+}
+
+func (f *Formatter) formatCommentGroup(g *ast.CommentGroupNode) string {
+	if g == nil {
+		return ""
+	}
+	var ret string
+	for _, cm := range g.Comments {
+		ret += f.formatComment(cm)
+	}
+	return ret
+}
+
+func (f *Formatter) formatComment(n *ast.CommentNode) string {
+	if n == nil {
+		return ""
+	}
+	return n.Token.Origin
+}
+
+// nolint: unused
+func (f *Formatter) formatIndent(col int) string {
+	if col <= 1 {
+		return ""
+	}
+	return strings.Repeat(" ", col-1)
+}
+
+func (f *Formatter) trimNewLineCharPrefix(v string) string {
+	return strings.TrimLeftFunc(v, func(r rune) bool {
+		return r == '\n' || r == '\r'
+	})
+}
+
+func (f *Formatter) trimSpacePrefix(v string) string {
+	return strings.TrimLeftFunc(v, func(r rune) bool {
+		return r == ' '
+	})
+}
+
+func (f *Formatter) trimIndentSpace(trimIndentNum int, v string) string {
+	if trimIndentNum == 0 {
+		return v
+	}
+	lines := strings.Split(normalizeNewLineChars(v), "\n")
+	out := make([]string, 0, len(lines))
+	for _, line := range lines {
+		var cnt int
+		out = append(out, strings.TrimLeftFunc(line, func(r rune) bool {
+			cnt++
+			return r == ' ' && cnt <= trimIndentNum
+		}))
+	}
+	return strings.Join(out, "\n")
+}
+
+func (f *Formatter) addIndentSpace(indentNum int, v string, isIgnoredFirstLine bool) string {
+	if indentNum == 0 {
+		return v
+	}
+	indent := strings.Repeat(" ", indentNum)
+	lines := strings.Split(normalizeNewLineChars(v), "\n")
+	out := make([]string, 0, len(lines))
+	for idx, line := range lines {
+		if line == "" || (isIgnoredFirstLine && idx == 0) {
+			out = append(out, line)
+			continue
+		}
+		out = append(out, indent+line)
+	}
+	return strings.Join(out, "\n")
+}
+
+// normalizeNewLineChars normalize CRLF and CR to LF.
+func normalizeNewLineChars(v string) string {
+	return strings.ReplaceAll(strings.ReplaceAll(v, "\r\n", "\n"), "\r", "\n")
+}
diff --git a/option.go b/option.go
index bd685e2..7dcd2ba 100644
--- a/option.go
+++ b/option.go
@@ -143,6 +143,18 @@ func Flow(isFlowStyle bool) EncodeOption {
 	}
 }
 
+// WithSmartAnchor when multiple map values share the same pointer,
+// an anchor is automatically assigned to the first occurrence, and aliases are used for subsequent elements.
+// The map key name is used as the anchor name by default.
+// If key names conflict, a suffix is automatically added to avoid collisions.
+// This is an experimental feature and cannot be used simultaneously with anchor tags.
+func WithSmartAnchor() EncodeOption {
+	return func(e *Encoder) error {
+		e.enableSmartAnchor = true
+		return nil
+	}
+}
+
 // UseLiteralStyleIfMultiline causes encoding multiline strings with a literal syntax,
 // no matter what characters they include
 func UseLiteralStyleIfMultiline(useLiteralStyleIfMultiline bool) EncodeOption {
@@ -194,6 +206,15 @@ func CustomMarshaler[T any](marshaler func(T) ([]byte, error)) EncodeOption {
 	}
 }
 
+// AutoInt automatically converts floating-point numbers to integers when the fractional part is zero.
+// For example, a value of 1.0 will be encoded as 1.
+func AutoInt() EncodeOption {
+	return func(e *Encoder) error {
+		e.autoInt = true
+		return nil
+	}
+}
+
 // CommentPosition type of the position for comment.
 type CommentPosition int
 
diff --git a/parser/context.go b/parser/context.go
index dc11b51..76a3795 100644
--- a/parser/context.go
+++ b/parser/context.go
@@ -132,7 +132,6 @@ func (c *context) addNullValueToken(tk *Token) *Token {
 	rawTk := nullToken.RawToken()
 
 	// add space for map or sequence value.
-	rawTk.Origin = " null"
 	rawTk.Position.Column++
 
 	c.addToken(nullToken)
@@ -144,7 +143,7 @@ func (c *context) addNullValueToken(tk *Token) *Token {
 func (c *context) createNullToken(base *Token) *Token {
 	pos := *(base.RawToken().Position)
 	pos.Column++
-	return &Token{Token: token.New("null", "null", &pos)}
+	return &Token{Token: token.New("null", " null", &pos)}
 }
 
 func (c *context) insertToken(tk *Token) {
diff --git a/parser/node.go b/parser/node.go
index c9ebefc..8d35554 100644
--- a/parser/node.go
+++ b/parser/node.go
@@ -14,16 +14,25 @@ func newMappingNode(ctx *context, tk *Token, isFlow bool, values ...*ast.Mapping
 	return node, nil
 }
 
-func newMappingValueNode(ctx *context, tk *Token, key ast.MapKeyNode, value ast.Node) (*ast.MappingValueNode, error) {
-	node := ast.MappingValue(tk.RawToken(), key, value)
+func newMappingValueNode(ctx *context, colonTk, entryTk *Token, key ast.MapKeyNode, value ast.Node) (*ast.MappingValueNode, error) {
+	node := ast.MappingValue(colonTk.RawToken(), key, value)
 	node.SetPath(ctx.path)
+	node.CollectEntry = entryTk.RawToken()
 	if key.GetToken().Position.Line == value.GetToken().Position.Line {
 		// originally key was commented, but now that null value has been added, value must be commented.
-		if err := setLineComment(ctx, value, tk); err != nil {
+		if err := setLineComment(ctx, value, colonTk); err != nil {
+			return nil, err
+		}
+		// set line comment by colonTk or entryTk.
+		if err := setLineComment(ctx, value, entryTk); err != nil {
 			return nil, err
 		}
 	} else {
-		if err := setLineComment(ctx, key, tk); err != nil {
+		if err := setLineComment(ctx, key, colonTk); err != nil {
+			return nil, err
+		}
+		// set line comment by colonTk or entryTk.
+		if err := setLineComment(ctx, key, entryTk); err != nil {
 			return nil, err
 		}
 	}
@@ -218,7 +227,7 @@ func newTagDefaultScalarValueNode(ctx *context, tag *token.Token) (ast.ScalarNod
 }
 
 func setLineComment(ctx *context, node ast.Node, tk *Token) error {
-	if tk.LineComment == nil {
+	if tk == nil || tk.LineComment == nil {
 		return nil
 	}
 	comment := ast.CommentGroup([]*token.Token{tk.LineComment})
diff --git a/parser/parser.go b/parser/parser.go
index 49334a2..3da78fb 100644
--- a/parser/parser.go
+++ b/parser/parser.go
@@ -341,7 +341,9 @@ func (p *parser) parseFlowMap(ctx *context) (*ast.MappingNode, error) {
 			break
 		}
 
+		var entryTk *Token
 		if tk.Type() == token.CollectEntryType {
+			entryTk = tk
 			ctx.goNext()
 		} else if !isFirst {
 			return nil, errors.ErrSyntax("',' or '}' must be specified", tk.RawToken())
@@ -357,7 +359,7 @@ func (p *parser) parseFlowMap(ctx *context) (*ast.MappingNode, error) {
 		mapKeyTk := ctx.currentToken()
 		switch mapKeyTk.GroupType() {
 		case TokenGroupMapKeyValue:
-			value, err := p.parseMapKeyValue(ctx.withGroup(mapKeyTk.Group), mapKeyTk.Group)
+			value, err := p.parseMapKeyValue(ctx.withGroup(mapKeyTk.Group), mapKeyTk.Group, entryTk)
 			if err != nil {
 				return nil, err
 			}
@@ -375,7 +377,7 @@ func (p *parser) parseFlowMap(ctx *context) (*ast.MappingNode, error) {
 				if err != nil {
 					return nil, err
 				}
-				mapValue, err := newMappingValueNode(ctx, colonTk, key, value)
+				mapValue, err := newMappingValueNode(ctx, colonTk, entryTk, key, value)
 				if err != nil {
 					return nil, err
 				}
@@ -390,7 +392,7 @@ func (p *parser) parseFlowMap(ctx *context) (*ast.MappingNode, error) {
 				if err != nil {
 					return nil, err
 				}
-				mapValue, err := newMappingValueNode(ctx, colonTk, key, value)
+				mapValue, err := newMappingValueNode(ctx, colonTk, entryTk, key, value)
 				if err != nil {
 					return nil, err
 				}
@@ -408,7 +410,7 @@ func (p *parser) parseFlowMap(ctx *context) (*ast.MappingNode, error) {
 			if err != nil {
 				return nil, err
 			}
-			mapValue, err := newMappingValueNode(ctx, mapKeyTk, key, value)
+			mapValue, err := newMappingValueNode(ctx, mapKeyTk, entryTk, key, value)
 			if err != nil {
 				return nil, err
 			}
@@ -435,7 +437,7 @@ func (p *parser) parseMap(ctx *context) (*ast.MappingNode, error) {
 	}
 	var keyValueNode *ast.MappingValueNode
 	if keyTk.GroupType() == TokenGroupMapKeyValue {
-		node, err := p.parseMapKeyValue(ctx.withGroup(keyTk.Group), keyTk.Group)
+		node, err := p.parseMapKeyValue(ctx.withGroup(keyTk.Group), keyTk.Group, nil)
 		if err != nil {
 			return nil, err
 		}
@@ -460,7 +462,7 @@ func (p *parser) parseMap(ctx *context) (*ast.MappingNode, error) {
 		if err != nil {
 			return nil, err
 		}
-		node, err := newMappingValueNode(ctx, keyTk.Group.Last(), key, value)
+		node, err := newMappingValueNode(ctx, keyTk.Group.Last(), nil, key, value)
 		if err != nil {
 			return nil, err
 		}
@@ -549,7 +551,7 @@ func (p *parser) isMapToken(tk *Token) bool {
 	return g.Type == TokenGroupMapKey || g.Type == TokenGroupMapKeyValue
 }
 
-func (p *parser) parseMapKeyValue(ctx *context, g *TokenGroup) (*ast.MappingValueNode, error) {
+func (p *parser) parseMapKeyValue(ctx *context, g *TokenGroup, entryTk *Token) (*ast.MappingValueNode, error) {
 	if g.Type != TokenGroupMapKeyValue {
 		return nil, errors.ErrSyntax("unexpected map key-value pair", g.RawToken())
 	}
@@ -567,7 +569,7 @@ func (p *parser) parseMapKeyValue(ctx *context, g *TokenGroup) (*ast.MappingValu
 	if err != nil {
 		return nil, err
 	}
-	return newMappingValueNode(c, keyGroup.Last(), key, value)
+	return newMappingValueNode(c, keyGroup.Last(), entryTk, key, value)
 }
 
 func (p *parser) parseMapKey(ctx *context, g *TokenGroup) (ast.MapKeyNode, error) {
@@ -1017,10 +1019,12 @@ func (p *parser) parseFlowSequence(ctx *context) (*ast.SequenceNode, error) {
 			break
 		}
 
+		var entryTk *Token
 		if tk.Type() == token.CollectEntryType {
 			if isFirst {
 				return nil, errors.ErrSyntax("expected sequence element, but found ','", tk.RawToken())
 			}
+			entryTk = tk
 			ctx.goNext()
 		} else if !isFirst {
 			return nil, errors.ErrSyntax("',' or ']' must be specified", tk.RawToken())
@@ -1037,11 +1041,19 @@ func (p *parser) parseFlowSequence(ctx *context) (*ast.SequenceNode, error) {
 			break
 		}
 
-		value, err := p.parseToken(ctx.withIndex(uint(len(node.Values))), ctx.currentToken())
+		ctx := ctx.withIndex(uint(len(node.Values)))
+		value, err := p.parseToken(ctx, ctx.currentToken())
 		if err != nil {
 			return nil, err
 		}
 		node.Values = append(node.Values, value)
+		seqEntry := ast.SequenceEntry(entryTk.RawToken(), value, nil)
+		if err := setLineComment(ctx, seqEntry, entryTk); err != nil {
+			return nil, err
+		}
+		seqEntry.SetPath(ctx.path)
+		node.Entries = append(node.Entries, seqEntry)
+
 		isFirst = false
 	}
 	if node.End == nil {
@@ -1061,15 +1073,22 @@ func (p *parser) parseSequence(ctx *context) (*ast.SequenceNode, error) {
 	tk := seqTk
 	for tk.Type() == token.SequenceEntryType && tk.Column() == seqTk.Column() {
 		seqTk := tk
-		comment := p.parseHeadComment(ctx)
+		headComment := p.parseHeadComment(ctx)
 		ctx.goNext() // skip sequence entry token
 
-		value, err := p.parseSequenceValue(ctx.withIndex(uint(len(seqNode.Values))), seqTk)
+		ctx := ctx.withIndex(uint(len(seqNode.Values)))
+		value, err := p.parseSequenceValue(ctx, seqTk)
 		if err != nil {
 			return nil, err
 		}
-		seqNode.ValueHeadComments = append(seqNode.ValueHeadComments, comment)
+		seqEntry := ast.SequenceEntry(seqTk.RawToken(), value, headComment)
+		if err := setLineComment(ctx, seqEntry, seqTk); err != nil {
+			return nil, err
+		}
+		seqEntry.SetPath(ctx.path)
+		seqNode.ValueHeadComments = append(seqNode.ValueHeadComments, headComment)
 		seqNode.Values = append(seqNode.Values, value)
+		seqNode.Entries = append(seqNode.Entries, seqEntry)
 
 		if ctx.isComment() {
 			tk = ctx.nextNotCommentToken()
diff --git a/scanner/scanner.go b/scanner/scanner.go
index 6b8ed09..98e2781 100644
--- a/scanner/scanner.go
+++ b/scanner/scanner.go
@@ -98,6 +98,11 @@ func (s *Scanner) progressColumn(ctx *Context, num int) {
 	s.progress(ctx, num)
 }
 
+func (s *Scanner) progressOnly(ctx *Context, num int) {
+	s.offset += num
+	s.progress(ctx, num)
+}
+
 func (s *Scanner) progressLine(ctx *Context) {
 	s.prevLineIndentNum = s.indentNum
 	s.column = 1
@@ -1436,13 +1441,13 @@ func (s *Scanner) scan(ctx *Context) error {
 				// tab indent for plain text (yaml-test-suite's spec-example-7-12-plain-lines).
 				s.indentNum++
 				ctx.addOriginBuf(c)
-				s.progressColumn(ctx, 1)
+				s.progressOnly(ctx, 1)
 				continue
 			}
 			if s.lastDelimColumn < s.column {
 				s.indentNum++
 				ctx.addOriginBuf(c)
-				s.progressColumn(ctx, 1)
+				s.progressOnly(ctx, 1)
 				continue
 			}
 			if err := s.scanTab(ctx, c); err != nil {
diff --git a/stdlib_quote.go b/stdlib_quote.go
index be50ae6..120448d 100644
--- a/stdlib_quote.go
+++ b/stdlib_quote.go
@@ -53,8 +53,18 @@ func appendQuotedWith(buf []byte, s string, quote byte) []byte {
 
 func appendEscapedRune(buf []byte, r rune, quote byte) []byte {
 	var runeTmp [utf8.UTFMax]byte
-	if r == rune(quote) || r == '\\' { // always backslashed
-		buf = append(buf, '\\')
+	// goccy/go-yaml patch on top of the standard library's appendEscapedRune function.
+	//
+	// We use this to implement the YAML single-quoted string, where the only escape sequence is '', which represents a single quote.
+	// The below snippet from the standard library is for escaping e.g. \ with \\, which is not what we want for the single-quoted string.
+	//
+	// if r == rune(quote) || r == '\\' { // always backslashed
+	// 	buf = append(buf, '\\')
+	// 	buf = append(buf, byte(r))
+	// 	return buf
+	// }
+	if r == rune(quote) {
+		buf = append(buf, byte(r))
 		buf = append(buf, byte(r))
 		return buf
 	}
diff --git a/token/token.go b/token/token.go
index 54ab179..4fe300f 100644
--- a/token/token.go
+++ b/token/token.go
@@ -679,6 +679,9 @@ func IsNeedQuoted(value string) bool {
 	if isNumber(value) {
 		return true
 	}
+	if value == "-" {
+		return true
+	}
 	first := value[0]
 	switch first {
 	case '*', '&', '[', '{', '}', ']', ',', '!', '|', '>', '%', '\'', '"', '@', ' ', '`':
@@ -696,7 +699,7 @@ func IsNeedQuoted(value string) bool {
 		switch c {
 		case '#', '\\':
 			return true
-		case ':':
+		case ':', '-':
 			if i+1 < len(value) && value[i+1] == ' ' {
 				return true
 			}
diff --git a/yaml.go b/yaml.go
index ec2103b..16f1b31 100644
--- a/yaml.go
+++ b/yaml.go
@@ -57,6 +57,16 @@ type InterfaceUnmarshalerContext interface {
 	UnmarshalYAML(context.Context, func(interface{}) error) error
 }
 
+// NodeUnmarshaler interface is similar to BytesUnmarshaler but provide related AST node instead of raw YAML source.
+type NodeUnmarshaler interface {
+	UnmarshalYAML(ast.Node) error
+}
+
+// NodeUnmarshalerContext interface is similar to BytesUnmarshaler but provide related AST node instead of raw YAML source.
+type NodeUnmarshalerContext interface {
+	UnmarshalYAML(context.Context, ast.Node) error
+}
+
 // MapItem is an item in a MapSlice.
 type MapItem struct {
 	Key, Value interface{}


Test patch:
diff --git a/decode_test.go b/decode_test.go
index 8ed7652..9aa9ee1 100644
--- a/decode_test.go
+++ b/decode_test.go
@@ -2757,13 +2757,14 @@ type unmarshalList struct {
 
 func (u *unmarshalList) UnmarshalYAML(b []byte) error {
 	expected := `
- - b: c
-   d: |
-     hello
+- b: c # comment
+  # comment
+  d: | # comment
+    hello
 
-     hello
-   f: g
- - h: i`
+    hello
+  f: g
+- h: i`
 	actual := "\n" + string(b)
 	if expected != actual {
 		return fmt.Errorf("unexpected bytes: expected [%q] but got [%q]", expected, actual)
@@ -2807,8 +2808,9 @@ config:
 func TestDecoder_UnmarshalBytesWithSeparatedList(t *testing.T) {
 	yml := `
 a:
- - b: c
-   d: |
+ - b: c # comment
+   # comment
+   d: | # comment
      hello
 
      hello
@@ -2818,7 +2820,8 @@ a:
 	var v struct {
 		A unmarshalList
 	}
-	if err := yaml.Unmarshal([]byte(yml), &v); err != nil {
+	cm := yaml.CommentMap{}
+	if err := yaml.UnmarshalWithOptions([]byte(yml), &v, yaml.CommentToMap(cm)); err != nil {
 		t.Fatal(err)
 	}
 	if len(v.A.v) != 2 {
@@ -3055,6 +3058,28 @@ bar:
 	})
 }
 
+func TestDecodeWithSameAnchor(t *testing.T) {
+	yml := `
+a: &a 1
+b: &a 2
+c: &a 3
+d: *a
+`
+	type T struct {
+		A int
+		B int
+		C int
+		D int
+	}
+	var v T
+	if err := yaml.Unmarshal([]byte(yml), &v); err != nil {
+		t.Fatal(err)
+	}
+	if !reflect.DeepEqual(v, T{A: 1, B: 2, C: 3, D: 3}) {
+		t.Fatalf("failed to decode same anchor: %+v", v)
+	}
+}
+
 func TestUnmarshalMapSliceParallel(t *testing.T) {
 	content := `
 steps:
@@ -3131,6 +3156,182 @@ func (mk *unmarshableMapKey) UnmarshalYAML(b []byte) error {
 	return nil
 }
 
+type testNodeUnmarshalerCtx struct {
+	outErr   error
+	received ast.Node
+}
+
+func (u *testNodeUnmarshalerCtx) UnmarshalYAML(ctx context.Context, node ast.Node) error {
+	if u.outErr != nil {
+		return u.outErr
+	}
+
+	if ctx == nil {
+		return errors.New("nil context")
+	}
+
+	u.received = node
+	return nil
+}
+
+func TestNodeUnmarshalerContext(t *testing.T) {
+	type testNodeUnmarshalerBody struct {
+		Root testNodeUnmarshalerCtx `yaml:"root"`
+	}
+
+	cases := []struct {
+		name      string
+		expectErr string
+		src       []string
+		body      testNodeUnmarshalerBody
+	}{
+		{
+			name: "should pass node",
+			src: []string{
+				"root:",
+				"  foo: bar",
+				"  fizz: buzz",
+			},
+		},
+		{
+			name: "should pass returned error",
+			body: testNodeUnmarshalerBody{
+				Root: testNodeUnmarshalerCtx{
+					outErr: errors.New("test error"),
+				},
+			},
+			expectErr: "test error",
+			src: []string{
+				"root:",
+				"  foo: bar",
+				"  fizz: buzz",
+			},
+		},
+	}
+
+	for _, c := range cases {
+		t.Run(c.name, func(t *testing.T) {
+			src := []byte(strings.Join(c.src, "\n"))
+			out := c.body
+			err := yaml.Unmarshal(src, &out)
+			if c.expectErr != "" {
+				if err == nil {
+					t.Fatal("expected error but got nil")
+					return
+				}
+
+				if !strings.Contains(err.Error(), c.expectErr) {
+					t.Fatalf("error message %q should contain %q", err.Error(), c.expectErr)
+				}
+				return
+			}
+
+			expect := struct {
+				Root ast.Node `yaml:"root"`
+			}{}
+			if err := yaml.UnmarshalContext(context.TODO(), src, &expect); err != nil {
+				t.Fatal("invalid test yaml:", err)
+				return
+			}
+
+			if err != nil {
+				t.Fatalf("unexpected error: %s", err)
+			}
+
+			if !reflect.DeepEqual(out.Root.received, expect.Root) {
+				t.Fatalf("expected:\n%#v\n but got:\n%#v", expect.Root, out.Root.received)
+			}
+		})
+	}
+
+}
+
+type testNodeUnmarshaler struct {
+	outErr   error
+	received ast.Node
+}
+
+func (u *testNodeUnmarshaler) UnmarshalYAML(node ast.Node) error {
+	if u.outErr != nil {
+		return u.outErr
+	}
+
+	u.received = node
+	return nil
+}
+
+func TestNodeUnmarshaler(t *testing.T) {
+	type testNodeUnmarshalerBody struct {
+		Root testNodeUnmarshaler `yaml:"root"`
+	}
+
+	cases := []struct {
+		name      string
+		expectErr string
+		src       []string
+		body      testNodeUnmarshalerBody
+	}{
+		{
+			name: "should pass node",
+			src: []string{
+				"root:",
+				"  foo: bar",
+				"  fizz: buzz",
+			},
+		},
+		{
+			name: "should pass returned error",
+			body: testNodeUnmarshalerBody{
+				Root: testNodeUnmarshaler{
+					outErr: errors.New("test error"),
+				},
+			},
+			expectErr: "test error",
+			src: []string{
+				"root:",
+				"  foo: bar",
+				"  fizz: buzz",
+			},
+		},
+	}
+
+	for _, c := range cases {
+		t.Run(c.name, func(t *testing.T) {
+			src := []byte(strings.Join(c.src, "\n"))
+			out := c.body
+			err := yaml.Unmarshal(src, &out)
+			if c.expectErr != "" {
+				if err == nil {
+					t.Fatal("expected error but got nil")
+					return
+				}
+
+				if !strings.Contains(err.Error(), c.expectErr) {
+					t.Fatalf("error message %q should contain %q", err.Error(), c.expectErr)
+				}
+				return
+			}
+
+			expect := struct {
+				Root ast.Node `yaml:"root"`
+			}{}
+			if err := yaml.Unmarshal(src, &expect); err != nil {
+				t.Fatal("invalid test yaml:", err)
+				return
+			}
+
+			if err != nil {
+				t.Fatalf("unexpected error: %s", err)
+			}
+
+			if !reflect.DeepEqual(out.Root.received, expect.Root) {
+				t.Fatalf("expected:\n%#v\n but got:\n%#v", expect.Root, out.Root.received)
+			}
+		})
+	}
+
+}
+
 func TestMapKeyCustomUnmarshaler(t *testing.T) {
 	var m map[unmarshableMapKey]string
 	if err := yaml.Unmarshal([]byte(`key: value`), &m); err != nil {
@@ -3148,6 +3349,180 @@ func TestMapKeyCustomUnmarshaler(t *testing.T) {
 	}
 }
 
+type bytesUnmershalerWithMapAlias struct{}
+
+func (*bytesUnmershalerWithMapAlias) UnmarshalYAML(b []byte) error {
+	expected := strings.TrimPrefix(`
+aaaaa:
+  bbbbb:
+    bar:
+      - |
+        foo
+          bar
+      - name: |
+          foo
+            bar
+
+`, "\n")
+	if string(b) != expected {
+		return fmt.Errorf("failed to decode: expected:\n[%s]\nbut got:\n[%s]\n", expected, string(b))
+	}
+	return nil
+}
+
+func TestBytesUnmarshalerWithMapAlias(t *testing.T) {
+	yml := `
+x-foo: &data
+  bar:
+    - |
+      foo
+        bar
+    - name: |
+        foo
+          bar
+
+foo:
+  aaaaa:
+    bbbbb: *data
+`
+	type T struct {
+		Foo bytesUnmershalerWithMapAlias `yaml:"foo"`
+	}
+	var v T
+	if err := yaml.Unmarshal([]byte(yml), &v); err != nil {
+		t.Fatal(err)
+	}
+}
+
+func TestBytesUnmarshalerWithEmptyValue(t *testing.T) {
+	type T struct{}
+
+	unmarshaler := func(dst *T, b []byte) error {
+		var v any
+		return yaml.Unmarshal(b, &v)
+	}
+
+	yml := `
+map: &m {}
+seq: &seq []
+foo: # comment
+bar: *m
+baz: *seq
+`
+	m := yaml.CommentMap{}
+	var v T
+	if err := yaml.UnmarshalWithOptions(
+		[]byte(yml),
+		&v,
+		yaml.CommentToMap(m),
+		yaml.CustomUnmarshaler[T](unmarshaler),
+	); err != nil {
+		t.Fatal(err)
+	}
+	if err := yaml.UnmarshalWithOptions(
+		[]byte(yml),
+		&v,
+		yaml.CustomUnmarshaler[T](unmarshaler),
+	); err != nil {
+		t.Fatal(err)
+	}
+
+}
+
+func TestIssue650(t *testing.T) {
+	type Disk struct {
+		Name   string `yaml:"name"`
+		Format *bool  `yaml:"format"`
+	}
+
+	type Sample struct {
+		Disks []Disk `yaml:"disks"`
+	}
+
+	unmarshalDisk := func(dst *Disk, b []byte) error {
+		var s string
+		if err := yaml.Unmarshal(b, &s); err == nil {
+			*dst = Disk{Name: s}
+			return nil
+		}
+		return yaml.Unmarshal(b, dst)
+	}
+
+	data := []byte(`
+disks:
+    -      name: foo
+           format: true
+`)
+
+	var sample Sample
+	if err := yaml.UnmarshalWithOptions(data, &sample, yaml.CustomUnmarshaler[Disk](unmarshalDisk)); err != nil {
+		t.Fatal(err)
+	}
+}
+
+func TestBytesUnmarshalerWithLiteral(t *testing.T) {
+	t.Run("map value", func(t *testing.T) {
+		type Literal string
+
+		unmarshalLit := func(dst *Literal, b []byte) error {
+			var s string
+			if err := yaml.Unmarshal(b, &s); err != nil {
+				return err
+			}
+			*dst = Literal(s)
+			return nil
+		}
+
+		data := []byte(`
+-         name:  |
+           foo
+             bar
+-         name:
+           |
+           foo
+           bar
+`)
+
+		var v []map[string]Literal
+		if err := yaml.UnmarshalWithOptions(data, &v, yaml.CustomUnmarshaler[Literal](unmarshalLit)); err != nil {
+			t.Fatal(err)
+		}
+		if !reflect.DeepEqual(v, []map[string]Literal{{"name": "foo\n  bar\n"}, {"name": "foo\nbar\n"}}) {
+			t.Fatalf("failed to get decoded value. got: %q", v)
+		}
+	})
+	t.Run("sequence value", func(t *testing.T) {
+		type Literal string
+
+		unmarshalLit := func(dst *Literal, b []byte) error {
+			var s string
+			if err := yaml.Unmarshal(b, &s); err != nil {
+				return err
+			}
+			*dst = Literal(s)
+			return nil
+		}
+
+		data := []byte(`
+-            |
+  foo
+    bar
+-
+ |
+ foo
+ bar
+`)
+
+		var v []Literal
+		if err := yaml.UnmarshalWithOptions(data, &v, yaml.CustomUnmarshaler[Literal](unmarshalLit)); err != nil {
+			t.Fatal(err)
+		}
+		if !reflect.DeepEqual(v, []Literal{"foo\n  bar\n", "foo\nbar\n"}) {
+			t.Fatalf("failed to get decoded value. got: %q", v)
+		}
+	})
+}
+
 func TestDecoderPreservesDefaultValues(t *testing.T) {
 	type nested struct {
 		Val string `yaml:"val"`
@@ -3207,3 +3582,52 @@ a: !Not [!Equals [!Ref foo, 'bar']]
 		t.Fatalf("found unexpected value: %v", v)
 	}
 }
+
+type issue337Template struct{}
+
+func (i *issue337Template) UnmarshalYAML(b []byte) error {
+	expected := strings.TrimPrefix(`
+|
+  apiVersion: v1
+  kind: ConfigMap
+  metadata:
+    name: "abc"
+    namespace: "abc"
+  data:
+    foo: FOO
+`, "\n")
+	if !bytes.Equal(b, []byte(expected)) {
+		return fmt.Errorf("expected:\n%s\nbut got:\n%s\n", expected, string(b))
+	}
+	return nil
+}
+
+func TestIssue337(t *testing.T) {
+	yml := `
+releases:
+- name: foo
+  chart: ./raw
+  values:
+  - templates:
+    - |
+      apiVersion: v1
+      kind: ConfigMap
+      metadata:
+        name: "abc"
+        namespace: "abc"
+      data:
+        foo: FOO
+`
+	type Value struct {
+		Templates []*issue337Template `yaml:"templates"`
+	}
+	type Release struct {
+		Values []*Value `yaml:"values"`
+	}
+	var v struct {
+		Releases []*Release `yaml:"releases"`
+	}
+	if err := yaml.Unmarshal([]byte(yml), &v); err != nil {
+		t.Fatal(err)
+	}
+}
diff --git a/encode_test.go b/encode_test.go
index 3095b00..a82988f 100644
--- a/encode_test.go
+++ b/encode_test.go
@@ -11,6 +11,7 @@ import (
 	"strings"
 	"testing"
 	"time"
+	"unsafe"
 
 	"github.com/goccy/go-yaml"
 	"github.com/goccy/go-yaml/ast"
@@ -171,7 +172,7 @@ func TestEncoder(t *testing.T) {
 			},
 		},
 		{
-			"a: -\n",
+			"a: \"-\"\n",
 			map[string]string{"a": "-"},
 			nil,
 		},
@@ -704,7 +705,7 @@ func TestEncoder(t *testing.T) {
 		},
 		// Quote style
 		{
-			`v: '\'a\'b'` + "\n",
+			`v: '''a''b'` + "\n",
 			map[string]string{"v": `'a'b`},
 			[]yaml.EncodeOption{
 				yaml.UseSingleQuote(true),
@@ -717,6 +718,13 @@ func TestEncoder(t *testing.T) {
 				yaml.UseSingleQuote(false),
 			},
 		},
+		{
+			`a: '\.yaml'` + "\n",
+			map[string]string{"a": `\.yaml`},
+			[]yaml.EncodeOption{
+				yaml.UseSingleQuote(true),
+			},
+		},
 	}
 	for _, test := range tests {
 		t.Run(test.source, func(t *testing.T) {
@@ -1276,6 +1284,54 @@ func TestEncoder_CustomMarshaler(t *testing.T) {
 	})
 }
 
+func TestEncoder_AutoInt(t *testing.T) {
+	for _, test := range []struct {
+		desc     string
+		input    any
+		expected string
+	}{
+		{
+			desc: "int-convertible float64",
+			input: map[string]float64{
+				"key": 1.0,
+			},
+			expected: "key: 1\n",
+		},
+		{
+			desc: "non int-convertible float64",
+			input: map[string]float64{
+				"key": 1.1,
+			},
+			expected: "key: 1.1\n",
+		},
+		{
+			desc: "int-convertible float32",
+			input: map[string]float32{
+				"key": 1.0,
+			},
+			expected: "key: 1\n",
+		},
+		{
+			desc: "non int-convertible float32",
+			input: map[string]float32{
+				"key": 1.1,
+			},
+			expected: "key: 1.1\n",
+		},
+	} {
+		t.Run(test.desc, func(t *testing.T) {
+			var buf bytes.Buffer
+			enc := yaml.NewEncoder(&buf, yaml.AutoInt())
+			if err := enc.Encode(test.input); err != nil {
+				t.Fatalf("failed to encode: %s", err)
+			}
+			if actual := buf.String(); actual != test.expected {
+				t.Errorf("expect:\n%s\nactual\n%s\n", test.expected, actual)
+			}
+		})
+	}
+}
+
 func TestEncoder_MultipleDocuments(t *testing.T) {
 	var buf bytes.Buffer
 	enc := yaml.NewEncoder(&buf)
@@ -1290,6 +1346,81 @@ func TestEncoder_MultipleDocuments(t *testing.T) {
 	}
 }
 
+func TestEncoder_UnmarshallableTypes(t *testing.T) {
+	for _, test := range []struct {
+		desc        string
+		input       any
+		expectedErr string
+	}{
+		{
+			desc:        "channel",
+			input:       make(chan int),
+			expectedErr: "unknown value type chan int",
+		},
+		{
+			desc:        "function",
+			input:       func() {},
+			expectedErr: "unknown value type func()",
+		},
+		{
+			desc:        "complex number",
+			input:       complex(10, 11),
+			expectedErr: "unknown value type complex128",
+		},
+		{
+			desc:        "unsafe pointer",
+			input:       unsafe.Pointer(&struct{}{}),
+			expectedErr: "unknown value type unsafe.Pointer",
+		},
+		{
+			desc:        "uintptr",
+			input:       uintptr(0x1234),
+			expectedErr: "unknown value type uintptr",
+		},
+		{
+			desc:        "map with channel",
+			input:       map[string]any{"key": make(chan string)},
+			expectedErr: "unknown value type chan string",
+		},
+		{
+			desc: "nested map with func",
+			input: map[string]any{
+				"a": map[string]any{
+					"b": func(_ string) {},
+				},
+			},
+			expectedErr: "unknown value type func(string)",
+		},
+		{
+			desc:        "slice with channel",
+			input:       []any{make(chan bool)},
+			expectedErr: "unknown value type chan bool",
+		},
+		{
+			desc:        "nested slice with complex number",
+			input:       []any{[]any{complex(10, 11)}},
+			expectedErr: "unknown value type complex128",
+		},
+		{
+			desc: "struct with unsafe pointer",
+			input: struct {
+				Field unsafe.Pointer `yaml:"field"`
+			}{},
+			expectedErr: "unknown value type unsafe.Pointer",
+		},
+	} {
+		t.Run(test.desc, func(t *testing.T) {
+			var buf bytes.Buffer
+			err := yaml.NewEncoder(&buf).Encode(test.input)
+			if err == nil {
+				t.Errorf("expect error:\n%s\nbut got none\n", test.expectedErr)
+			} else if err.Error() != test.expectedErr {
+				t.Errorf("expect error:\n%s\nactual\n%s\n", test.expectedErr, err)
+			}
+		})
+	}
+}
+
 func ExampleMarshal_node() {
 	type T struct {
 		Text ast.Node `yaml:"text"`
diff --git a/parser/parser_test.go b/parser/parser_test.go
index 487bfe1..1b86a3a 100644
--- a/parser/parser_test.go
+++ b/parser/parser_test.go
@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"path/filepath"
 	"reflect"
+	"strconv"
 	"strings"
 	"testing"
 
@@ -142,14 +143,32 @@ foo: xxx
 foo: yyy
 ---
 foo: zzz
+`,
+		`
+v:
+  a	: 'a'
+  bb	: 'a'
+`,
+		`
+v:
+  a : 'x'
+  b	: 'y'
+`,
+		`
+v:
+  a	: 'x'
+  b	: 'y'
+  c		: 'z'
 `,
 	}
-	for _, src := range sources {
-		f, err := parser.Parse(lexer.Tokenize(src), 0)
-		if err != nil {
-			t.Fatalf("parse error: source [%s]: %+v", src, err)
-		}
-		_ = f.String() // ensure no panic
+	for idx, src := range sources {
+		t.Run(strconv.Itoa(idx), func(t *testing.T) {
+			f, err := parser.Parse(lexer.Tokenize(src), 0)
+			if err != nil {
+				t.Fatalf("parse error: source [%s]: %+v", src, err)
+			}
+			_ = f.String() // ensure no panic
+		})
 	}
 }
 
@@ -1715,6 +1734,70 @@ multiple:
 	}
 }
 
+func TestInFlowStyle(t *testing.T) {
+	type inFlowStyle interface {
+		SetIsFlowStyle(bool)
+	}
+
+	tests := []struct {
+		source string
+		expect string
+	}{
+		{
+			`
+  - foo
+  - bar
+  - baz
+`,
+			`[foo, bar, baz]
+`,
+		},
+		{
+			`
+foo: bar
+baz: fizz
+`,
+			`{foo: bar, baz: fizz}
+`,
+		},
+		{
+			`
+foo:
+  - bar
+  - baz
+  - fizz: buzz
+`,
+			`{foo: [bar, baz, {fizz: buzz}]}
+`,
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.source, func(t *testing.T) {
+			f, err := parser.ParseBytes([]byte(test.source), parser.ParseComments)
+			if err != nil {
+				t.Fatal(err)
+			}
+
+			if len(f.Docs) != 1 {
+				t.Fatal("failed to parse content")
+			}
+
+			ifs, ok := f.Docs[0].Body.(inFlowStyle)
+			if !ok {
+				t.Fatalf("failed to get inFlowStyle. got: %T", f.Docs[0].Body)
+			}
+			ifs.SetIsFlowStyle(true)
+
+			got := f.String()
+
+			if got != test.expect {
+				t.Fatalf("failed to parse comment:\nexpected:\n%s\ngot:\n%s", test.expect, got)
+			}
+		})
+	}
+}
+
 func TestNodePath(t *testing.T) {
 	yml := `
 a: # commentA
diff --git a/token/token_test.go b/token/token_test.go
index 3b339b4..489a531 100644
--- a/token/token_test.go
+++ b/token/token_test.go
@@ -133,6 +133,8 @@ func TestIsNeedQuoted(t *testing.T) {
 		"Null",
 		"NULL",
 		"~",
+		"-",
+		"- --foo",
 	}
 	for i, test := range needQuotedTests {
 		if !token.IsNeedQuoted(test) {
diff --git a/yaml_test.go b/yaml_test.go
index 560a93c..7c86ee3 100644
--- a/yaml_test.go
+++ b/yaml_test.go
@@ -1,6 +1,7 @@
 package yaml_test
 
 import (
+	"fmt"
 	"io"
 	"reflect"
 	"strings"
@@ -72,3 +73,159 @@ foo: bar # comment
 		t.Fatalf("failed to get comment: %v", commentPathsWithDocIndex)
 	}
 }
+
+func TestDecodeKeepAddress(t *testing.T) {
+	var data = `
+a: &a [_]
+b: &b [*a,*a]
+c: &c [*b,*b]
+d: &d [*c,*c]
+`
+	var v map[string]any
+	if err := yaml.Unmarshal([]byte(data), &v); err != nil {
+		t.Fatal(err)
+	}
+	a := v["a"]
+	b := v["b"]
+	for _, vv := range v["b"].([]any) {
+		if fmt.Sprintf("%p", a) != fmt.Sprintf("%p", vv) {
+			t.Fatalf("failed to decode b element as keep address")
+		}
+	}
+	for _, vv := range v["c"].([]any) {
+		if fmt.Sprintf("%p", b) != fmt.Sprintf("%p", vv) {
+			t.Fatalf("failed to decode c element as keep address")
+		}
+	}
+}
+
+func TestSmartAnchor(t *testing.T) {
+	var data = `
+a: &a [_,_,_,_,_,_,_,_,_,_,_,_,_,_,_]
+b: &b [*a,*a,*a,*a,*a,*a,*a,*a,*a,*a]
+c: &c [*b,*b,*b,*b,*b,*b,*b,*b,*b,*b]
+d: &d [*c,*c,*c,*c,*c,*c,*c,*c,*c,*c]
+e: &e [*d,*d,*d,*d,*d,*d,*d,*d,*d,*d]
+f: &f [*e,*e,*e,*e,*e,*e,*e,*e,*e,*e]
+g: &g [*f,*f,*f,*f,*f,*f,*f,*f,*f,*f]
+h: &h [*g,*g,*g,*g,*g,*g,*g,*g,*g,*g]
+i: &i [*h,*h,*h,*h,*h,*h,*h,*h,*h,*h]
+`
+	var v any
+	if err := yaml.Unmarshal([]byte(data), &v); err != nil {
+		t.Fatal(err)
+	}
+	got, err := yaml.MarshalWithOptions(v, yaml.WithSmartAnchor())
+	if err != nil {
+		t.Fatal(err)
+	}
+	expected := `
+a: &a
+- _
+- _
+- _
+- _
+- _
+- _
+- _
+- _
+- _
+- _
+- _
+- _
+- _
+- _
+- _
+b: &b
+- *a
+- *a
+- *a
+- *a
+- *a
+- *a
+- *a
+- *a
+- *a
+- *a
+c: &c
+- *b
+- *b
+- *b
+- *b
+- *b
+- *b
+- *b
+- *b
+- *b
+- *b
+d: &d
+- *c
+- *c
+- *c
+- *c
+- *c
+- *c
+- *c
+- *c
+- *c
+- *c
+e: &e
+- *d
+- *d
+- *d
+- *d
+- *d
+- *d
+- *d
+- *d
+- *d
+- *d
+f: &f
+- *e
+- *e
+- *e
+- *e
+- *e
+- *e
+- *e
+- *e
+- *e
+- *e
+g: &g
+- *f
+- *f
+- *f
+- *f
+- *f
+- *f
+- *f
+- *f
+- *f
+- *f
+h: &h
+- *g
+- *g
+- *g
+- *g
+- *g
+- *g
+- *g
+- *g
+- *g
+- *g
+i:
+- *h
+- *h
+- *h
+- *h
+- *h
+- *h
+- *h
+- *h
+- *h
+- *h
+`
+	if strings.TrimPrefix(expected, "\n") != string(got) {
+		t.Fatalf("failed to encode: %s", string(got))
+	}
+}