@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.
Files changed (2) hide show
  1. package/README.md +363 -1
  2. 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
- - **Document Headers**: Add, extract, and manage headers at the top of YAML documents (new!)
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.17",
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",