@hiscojs/yaml-updater 1.0.17 → 1.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +363 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -44,7 +44,8 @@ console.log(result);
|
|
|
44
44
|
- **Type-Safe**: Full TypeScript support with generic type parameters
|
|
45
45
|
- **Comment Preservation**: Automatically preserves existing YAML comments
|
|
46
46
|
- **Comment Manipulation**: Add, remove, or update comments programmatically
|
|
47
|
-
- **
|
|
47
|
+
- **Complete YAML Sub-Documents**: Extract and insert YAML blocks with all formatting preserved (new!)
|
|
48
|
+
- **Document Headers**: Add, extract, and manage headers at the top of YAML documents
|
|
48
49
|
- **YAML Anchors & Aliases**: Create reusable content with anchors, aliases, and merge keys
|
|
49
50
|
- **Anchor Renaming**: Rename anchors and update all references document-wide
|
|
50
51
|
- **Schema-Level Instructions**: Define structure metadata separately from data (OpenAPI-style)
|
|
@@ -73,6 +74,8 @@ interface UpdateYamlOptions<T> {
|
|
|
73
74
|
documentHeader?: DocumentHeader; // Optional document header
|
|
74
75
|
annotate?: (annotator: {
|
|
75
76
|
change: <L>(options: ChangeOptions<T, L>) => void;
|
|
77
|
+
setYamlNode: <L>(options: SetYamlNodeOptions<T, L>) => void;
|
|
78
|
+
getYamlNode: <L>(options: GetYamlNodeOptions<T, L>) => string;
|
|
76
79
|
}) => void;
|
|
77
80
|
defaultFlow?: boolean; // Default flow style for all nodes
|
|
78
81
|
}
|
|
@@ -134,6 +137,30 @@ interface ChangeOptions<T, L> {
|
|
|
134
137
|
}
|
|
135
138
|
```
|
|
136
139
|
|
|
140
|
+
### `setYamlNode<L>(options)`
|
|
141
|
+
|
|
142
|
+
Inserts a complete YAML sub-document with all formatting, comments, and anchors preserved exactly as-is.
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
interface SetYamlNodeOptions<T, L> {
|
|
146
|
+
findKey: (parsed: T) => L;
|
|
147
|
+
yamlString: string; // YAML string to insert
|
|
148
|
+
comment?: (previousComment?: string) => string | undefined;
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### `getYamlNode<L>(options)`
|
|
153
|
+
|
|
154
|
+
Extracts a complete YAML sub-document as a string with all formatting, comments, and anchors preserved.
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
interface GetYamlNodeOptions<T, L> {
|
|
158
|
+
findKey: (parsed: T) => L;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Returns: string (YAML with all metadata preserved)
|
|
162
|
+
```
|
|
163
|
+
|
|
137
164
|
## Basic Usage
|
|
138
165
|
|
|
139
166
|
### Simple Property Update
|
|
@@ -632,6 +659,341 @@ const { result } = updateYaml({
|
|
|
632
659
|
});
|
|
633
660
|
```
|
|
634
661
|
|
|
662
|
+
## Working with Complete YAML Sub-Documents
|
|
663
|
+
|
|
664
|
+
The `getYamlNode()` and `setYamlNode()` functions allow you to extract and insert complete YAML sub-documents with **all formatting, comments, and anchors preserved exactly as-is**.
|
|
665
|
+
|
|
666
|
+
### Why Use These Functions?
|
|
667
|
+
|
|
668
|
+
**vs. `change()` + `merge()`:**
|
|
669
|
+
- `change()` works with plain JavaScript objects - loses all YAML metadata (comments, anchors, formatting)
|
|
670
|
+
- `getYamlNode()` + `setYamlNode()` preserve **everything**: comments, anchors, aliases, flow style, indentation
|
|
671
|
+
|
|
672
|
+
**Use cases:**
|
|
673
|
+
- Extract configuration sections from large YAML files
|
|
674
|
+
- Copy/move YAML blocks between locations
|
|
675
|
+
- Move sub-documents to root level
|
|
676
|
+
- Migrate YAML sections to separate files
|
|
677
|
+
- Template generation with full formatting control
|
|
678
|
+
|
|
679
|
+
### Extracting YAML Sub-Documents
|
|
680
|
+
|
|
681
|
+
Use `getYamlNode()` to extract a complete YAML sub-document as a string:
|
|
682
|
+
|
|
683
|
+
```typescript
|
|
684
|
+
const yamlString = `
|
|
685
|
+
database:
|
|
686
|
+
# Production database configuration
|
|
687
|
+
host: db.production.com
|
|
688
|
+
port: 5432
|
|
689
|
+
credentials:
|
|
690
|
+
# Stored in secrets manager
|
|
691
|
+
username: admin
|
|
692
|
+
password: secret
|
|
693
|
+
`;
|
|
694
|
+
|
|
695
|
+
let extractedYaml = '';
|
|
696
|
+
updateYaml({
|
|
697
|
+
yamlString,
|
|
698
|
+
annotate: ({ getYamlNode }) => {
|
|
699
|
+
extractedYaml = getYamlNode({
|
|
700
|
+
findKey: (parsed) => parsed.database
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
});
|
|
704
|
+
|
|
705
|
+
console.log(extractedYaml);
|
|
706
|
+
// Output:
|
|
707
|
+
// # Production database configuration
|
|
708
|
+
// host: db.production.com
|
|
709
|
+
// port: 5432
|
|
710
|
+
// credentials:
|
|
711
|
+
// # Stored in secrets manager
|
|
712
|
+
// username: admin
|
|
713
|
+
// password: secret
|
|
714
|
+
```
|
|
715
|
+
|
|
716
|
+
**All metadata is preserved:**
|
|
717
|
+
- ✓ All comments (block and inline)
|
|
718
|
+
- ✓ Anchors and aliases
|
|
719
|
+
- ✓ Flow style formatting
|
|
720
|
+
- ✓ Exact indentation and spacing
|
|
721
|
+
|
|
722
|
+
### Inserting YAML Sub-Documents
|
|
723
|
+
|
|
724
|
+
Use `setYamlNode()` to insert a complete YAML string:
|
|
725
|
+
|
|
726
|
+
```typescript
|
|
727
|
+
const mainYaml = `
|
|
728
|
+
application:
|
|
729
|
+
name: myapp
|
|
730
|
+
database: {}
|
|
731
|
+
`;
|
|
732
|
+
|
|
733
|
+
const databaseConfig = `
|
|
734
|
+
# Database configuration
|
|
735
|
+
host: localhost
|
|
736
|
+
port: 5432
|
|
737
|
+
credentials:
|
|
738
|
+
# Sensitive data
|
|
739
|
+
username: admin
|
|
740
|
+
password: secret
|
|
741
|
+
`;
|
|
742
|
+
|
|
743
|
+
const { result } = updateYaml({
|
|
744
|
+
yamlString: mainYaml,
|
|
745
|
+
annotate: ({ setYamlNode }) => {
|
|
746
|
+
setYamlNode({
|
|
747
|
+
findKey: (parsed) => parsed.database,
|
|
748
|
+
yamlString: databaseConfig,
|
|
749
|
+
comment: () => 'Complete database configuration'
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
});
|
|
753
|
+
|
|
754
|
+
// Output:
|
|
755
|
+
// application:
|
|
756
|
+
// name: myapp
|
|
757
|
+
// # Complete database configuration
|
|
758
|
+
// database:
|
|
759
|
+
// # Database configuration
|
|
760
|
+
// host: localhost
|
|
761
|
+
// port: 5432
|
|
762
|
+
// credentials:
|
|
763
|
+
// # Sensitive data
|
|
764
|
+
// username: admin
|
|
765
|
+
// password: secret
|
|
766
|
+
```
|
|
767
|
+
|
|
768
|
+
### Copy/Move Operations
|
|
769
|
+
|
|
770
|
+
Combine `getYamlNode()` and `setYamlNode()` for powerful copy/move operations:
|
|
771
|
+
|
|
772
|
+
```typescript
|
|
773
|
+
const yamlString = `
|
|
774
|
+
environments:
|
|
775
|
+
staging:
|
|
776
|
+
# Staging environment config
|
|
777
|
+
replicas: 2
|
|
778
|
+
resources:
|
|
779
|
+
memory: 1Gi
|
|
780
|
+
cpu: 500m
|
|
781
|
+
features:
|
|
782
|
+
- feature-flags
|
|
783
|
+
- debug-mode # Enable debug logs
|
|
784
|
+
production:
|
|
785
|
+
replicas: 5
|
|
786
|
+
`;
|
|
787
|
+
|
|
788
|
+
const { result } = updateYaml({
|
|
789
|
+
yamlString,
|
|
790
|
+
annotate: ({ getYamlNode, setYamlNode }) => {
|
|
791
|
+
// Extract staging resources
|
|
792
|
+
const stagingResources = getYamlNode({
|
|
793
|
+
findKey: (parsed) => parsed.environments.staging.resources
|
|
794
|
+
});
|
|
795
|
+
|
|
796
|
+
// Copy to production (all formatting preserved)
|
|
797
|
+
setYamlNode({
|
|
798
|
+
findKey: (parsed) => parsed.environments.production.resources,
|
|
799
|
+
yamlString: stagingResources,
|
|
800
|
+
comment: () => 'Copied from staging'
|
|
801
|
+
});
|
|
802
|
+
}
|
|
803
|
+
});
|
|
804
|
+
|
|
805
|
+
// Output:
|
|
806
|
+
// environments:
|
|
807
|
+
// staging:
|
|
808
|
+
// # Staging environment config
|
|
809
|
+
// replicas: 2
|
|
810
|
+
// resources:
|
|
811
|
+
// memory: 1Gi
|
|
812
|
+
// cpu: 500m
|
|
813
|
+
// features:
|
|
814
|
+
// - feature-flags
|
|
815
|
+
// - debug-mode # Enable debug logs
|
|
816
|
+
// production:
|
|
817
|
+
// replicas: 5
|
|
818
|
+
// # Copied from staging
|
|
819
|
+
// resources:
|
|
820
|
+
// memory: 1Gi
|
|
821
|
+
// cpu: 500m
|
|
822
|
+
```
|
|
823
|
+
|
|
824
|
+
### Moving Sub-Document to Root Level
|
|
825
|
+
|
|
826
|
+
Extract a sub-document and make it the entire document:
|
|
827
|
+
|
|
828
|
+
```typescript
|
|
829
|
+
const yamlString = `
|
|
830
|
+
# Top level comment
|
|
831
|
+
application:
|
|
832
|
+
name: myapp
|
|
833
|
+
version: 1.0.0
|
|
834
|
+
|
|
835
|
+
database:
|
|
836
|
+
# Database configuration
|
|
837
|
+
host: localhost
|
|
838
|
+
port: 5432
|
|
839
|
+
credentials:
|
|
840
|
+
# Sensitive data
|
|
841
|
+
username: admin
|
|
842
|
+
password: secret
|
|
843
|
+
settings:
|
|
844
|
+
pool_size: 10 # Connection pool
|
|
845
|
+
timeout: 30
|
|
846
|
+
|
|
847
|
+
cache:
|
|
848
|
+
enabled: true
|
|
849
|
+
`;
|
|
850
|
+
|
|
851
|
+
const { result } = updateYaml({
|
|
852
|
+
yamlString,
|
|
853
|
+
annotate: ({ getYamlNode, setYamlNode }) => {
|
|
854
|
+
// Extract database configuration
|
|
855
|
+
const databaseConfig = getYamlNode({
|
|
856
|
+
findKey: (parsed) => parsed.database
|
|
857
|
+
});
|
|
858
|
+
|
|
859
|
+
// Replace entire document with database config
|
|
860
|
+
setYamlNode({
|
|
861
|
+
findKey: (parsed) => parsed, // Root level
|
|
862
|
+
yamlString: databaseConfig
|
|
863
|
+
});
|
|
864
|
+
}
|
|
865
|
+
});
|
|
866
|
+
|
|
867
|
+
// Output (database is now the entire document):
|
|
868
|
+
// # Database configuration
|
|
869
|
+
// host: localhost
|
|
870
|
+
// port: 5432
|
|
871
|
+
// credentials:
|
|
872
|
+
// # Sensitive data
|
|
873
|
+
// username: admin
|
|
874
|
+
// password: secret
|
|
875
|
+
// settings:
|
|
876
|
+
// pool_size: 10 # Connection pool
|
|
877
|
+
// timeout: 30
|
|
878
|
+
```
|
|
879
|
+
|
|
880
|
+
**Result:**
|
|
881
|
+
- Database configuration becomes the entire document
|
|
882
|
+
- All comments preserved at every level
|
|
883
|
+
- Original top-level fields (`application`, `cache`) removed
|
|
884
|
+
- The `database:` key is gone - its contents are now the root
|
|
885
|
+
|
|
886
|
+
### Extracting Different Types
|
|
887
|
+
|
|
888
|
+
**Extract scalar values:**
|
|
889
|
+
```typescript
|
|
890
|
+
const yaml = `version: "1.0.0"`;
|
|
891
|
+
const version = getYamlNode({ findKey: (p) => p.version });
|
|
892
|
+
// Returns: "1.0.0"
|
|
893
|
+
```
|
|
894
|
+
|
|
895
|
+
**Extract arrays with comments:**
|
|
896
|
+
```typescript
|
|
897
|
+
const yaml = `
|
|
898
|
+
items:
|
|
899
|
+
- item1
|
|
900
|
+
- item2 # important item
|
|
901
|
+
- item3
|
|
902
|
+
`;
|
|
903
|
+
const items = getYamlNode({ findKey: (p) => p.items });
|
|
904
|
+
// Returns:
|
|
905
|
+
// - item1
|
|
906
|
+
// - item2 # important item
|
|
907
|
+
// - item3
|
|
908
|
+
```
|
|
909
|
+
|
|
910
|
+
**Extract with anchors:**
|
|
911
|
+
```typescript
|
|
912
|
+
const yaml = `
|
|
913
|
+
defaults: &def
|
|
914
|
+
timeout: 30
|
|
915
|
+
retries: 3
|
|
916
|
+
production:
|
|
917
|
+
host: prod.com
|
|
918
|
+
`;
|
|
919
|
+
const defaults = getYamlNode({ findKey: (p) => p.defaults });
|
|
920
|
+
// Returns:
|
|
921
|
+
// &def
|
|
922
|
+
// timeout: 30
|
|
923
|
+
// retries: 3
|
|
924
|
+
```
|
|
925
|
+
|
|
926
|
+
### Error Handling
|
|
927
|
+
|
|
928
|
+
`getYamlNode()` throws an error if the path doesn't exist:
|
|
929
|
+
|
|
930
|
+
```typescript
|
|
931
|
+
try {
|
|
932
|
+
updateYaml({
|
|
933
|
+
yamlString: 'foo: bar',
|
|
934
|
+
annotate: ({ getYamlNode }) => {
|
|
935
|
+
getYamlNode({ findKey: (p) => p.nonexistent }); // Throws error
|
|
936
|
+
}
|
|
937
|
+
});
|
|
938
|
+
} catch (error) {
|
|
939
|
+
// Error: Cannot extract YAML node: path ["nonexistent"] not found in document
|
|
940
|
+
}
|
|
941
|
+
```
|
|
942
|
+
|
|
943
|
+
### Comparison: `change()` vs `setYamlNode()`
|
|
944
|
+
|
|
945
|
+
| Feature | `change()` + `merge()` | `setYamlNode()` |
|
|
946
|
+
|---------|------------------------|------------------|
|
|
947
|
+
| **Input** | JavaScript object | YAML string |
|
|
948
|
+
| **Comments** | Lost (must add manually) | Preserved exactly |
|
|
949
|
+
| **Anchors** | Lost (must recreate) | Preserved exactly |
|
|
950
|
+
| **Formatting** | New formatting applied | Original formatting kept |
|
|
951
|
+
| **Flow style** | Lost | Preserved |
|
|
952
|
+
| **Use case** | Modify values | Insert complete YAML blocks |
|
|
953
|
+
|
|
954
|
+
**When to use `change()`:**
|
|
955
|
+
- Updating specific property values
|
|
956
|
+
- Merging new data with existing
|
|
957
|
+
- Working with plain JavaScript objects
|
|
958
|
+
|
|
959
|
+
**When to use `setYamlNode()` + `getYamlNode()`:**
|
|
960
|
+
- Copying YAML sections with all formatting
|
|
961
|
+
- Moving configuration blocks
|
|
962
|
+
- Extracting sub-documents to separate files
|
|
963
|
+
- Preserving all metadata exactly
|
|
964
|
+
|
|
965
|
+
### Real-World Example: Configuration Migration
|
|
966
|
+
|
|
967
|
+
```typescript
|
|
968
|
+
// Extract staging config and promote to production
|
|
969
|
+
const { result } = updateYaml({
|
|
970
|
+
yamlString: multiEnvConfig,
|
|
971
|
+
annotate: ({ getYamlNode, setYamlNode }) => {
|
|
972
|
+
// Extract staging configuration
|
|
973
|
+
const stagingConfig = getYamlNode({
|
|
974
|
+
findKey: (parsed) => parsed.environments.staging
|
|
975
|
+
});
|
|
976
|
+
|
|
977
|
+
// Archive current production to rollback
|
|
978
|
+
const currentProd = getYamlNode({
|
|
979
|
+
findKey: (parsed) => parsed.environments.production
|
|
980
|
+
});
|
|
981
|
+
setYamlNode({
|
|
982
|
+
findKey: (parsed) => parsed.environments.rollback,
|
|
983
|
+
yamlString: currentProd,
|
|
984
|
+
comment: () => 'Previous production (for rollback)'
|
|
985
|
+
});
|
|
986
|
+
|
|
987
|
+
// Promote staging to production
|
|
988
|
+
setYamlNode({
|
|
989
|
+
findKey: (parsed) => parsed.environments.production,
|
|
990
|
+
yamlString: stagingConfig,
|
|
991
|
+
comment: () => 'Promoted from staging'
|
|
992
|
+
});
|
|
993
|
+
}
|
|
994
|
+
});
|
|
995
|
+
```
|
|
996
|
+
|
|
635
997
|
## Array Merging Strategies
|
|
636
998
|
|
|
637
999
|
### `mergeByContents` - Deduplicate by Deep Equality
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hiscojs/yaml-updater",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.18",
|
|
4
4
|
"description": "Type-safe, immutable YAML updates with comment preservation, YAML/JSON formatting control, per-item array formatting, multi-document support, and advanced array merging strategies",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|