@hiscojs/yaml-updater 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,905 @@
1
+ # @hiscojs/yaml-updater
2
+
3
+ Type-safe, immutable YAML updates with comment preservation, multi-document support, and advanced array merging strategies.
4
+
5
+ Built on top of [@hiscojs/object-updater](https://www.npmjs.com/package/@hiscojs/object-updater) for powerful object manipulation capabilities.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install @hiscojs/yaml-updater
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ```typescript
16
+ import { updateYaml } from '@hiscojs/yaml-updater';
17
+
18
+ const yamlString = `
19
+ apiVersion: v1
20
+ kind: ConfigMap
21
+ data:
22
+ database: localhost
23
+ `;
24
+
25
+ const { result } = updateYaml({
26
+ yamlString,
27
+ annotate: ({ change }) => {
28
+ change({
29
+ findKey: (parsed) => parsed.data,
30
+ merge: () => ({ database: 'db.production.com' })
31
+ });
32
+ }
33
+ });
34
+
35
+ console.log(result);
36
+ // apiVersion: v1
37
+ // kind: ConfigMap
38
+ // data:
39
+ // database: db.production.com
40
+ ```
41
+
42
+ ## Features
43
+
44
+ - **Type-Safe**: Full TypeScript support with generic type parameters
45
+ - **Comment Preservation**: Automatically preserves existing YAML comments
46
+ - **Comment Manipulation**: Add, remove, or update comments programmatically
47
+ - **Multi-Document Support**: Handle YAML files with multiple documents
48
+ - **Immutable**: Original YAML strings are never modified
49
+ - **Advanced Array Merging**: Multiple strategies for merging arrays
50
+ - **Proxy-Based Path Tracking**: Automatic path detection
51
+ - **Formatting Preservation**: Maintains indentation and spacing
52
+
53
+ ## API Reference
54
+
55
+ ### `updateYaml<T>(options)`
56
+
57
+ Updates a YAML string immutably with type safety and comment preservation.
58
+
59
+ #### Parameters
60
+
61
+ ```typescript
62
+ interface UpdateYamlOptions<T> {
63
+ yamlString: string;
64
+ selectDocument?: (yamlDocuments: Document[]) => number;
65
+ annotate?: (annotator: {
66
+ change: <L>(options: ChangeOptions<T, L>) => void;
67
+ }) => void;
68
+ }
69
+ ```
70
+
71
+ #### Returns
72
+
73
+ ```typescript
74
+ interface YamlEdit<T> {
75
+ result: string; // Updated YAML string
76
+ resultParsed: T; // Parsed updated object
77
+ originalParsed: T; // Original parsed object
78
+ comments: Array<{ // Comments that were added
79
+ path: (string | number)[];
80
+ comment: string;
81
+ }>;
82
+ }
83
+ ```
84
+
85
+ ### `change<L>(options)`
86
+
87
+ Defines a single change operation with optional commenting.
88
+
89
+ ```typescript
90
+ interface ChangeOptions<T, L> {
91
+ findKey: (parsed: T) => L;
92
+ merge: (originalValue: L) => Partial<L>;
93
+ comment?: (previousComment?: string) => string | undefined;
94
+ }
95
+ ```
96
+
97
+ ## Basic Usage
98
+
99
+ ### Simple Property Update
100
+
101
+ ```typescript
102
+ const yamlString = `
103
+ server:
104
+ host: localhost
105
+ port: 3000
106
+ `;
107
+
108
+ const { result } = updateYaml({
109
+ yamlString,
110
+ annotate: ({ change }) => {
111
+ change({
112
+ findKey: (parsed) => parsed.server,
113
+ merge: () => ({ port: 8080 })
114
+ });
115
+ }
116
+ });
117
+
118
+ // server:
119
+ // host: localhost
120
+ // port: 8080
121
+ ```
122
+
123
+ ### Type-Safe Updates
124
+
125
+ ```typescript
126
+ interface Config {
127
+ server: {
128
+ host: string;
129
+ port: number;
130
+ };
131
+ database: {
132
+ host: string;
133
+ port: number;
134
+ };
135
+ }
136
+
137
+ const { result, resultParsed } = updateYaml<Config>({
138
+ yamlString,
139
+ annotate: ({ change }) => {
140
+ change({
141
+ findKey: (parsed) => parsed.server, // Fully typed!
142
+ merge: () => ({ port: 8080 })
143
+ });
144
+ }
145
+ });
146
+
147
+ console.log(resultParsed.server.port); // Type-safe access
148
+ ```
149
+
150
+ ## Comment Management
151
+
152
+ ### Adding Comments
153
+
154
+ ```typescript
155
+ const { result } = updateYaml({
156
+ yamlString,
157
+ annotate: ({ change }) => {
158
+ change({
159
+ findKey: (parsed) => parsed.spec,
160
+ merge: () => ({ replicas: 3 }),
161
+ comment: () => 'Scaled to 3 replicas for high availability'
162
+ });
163
+ }
164
+ });
165
+
166
+ // spec:
167
+ // # Scaled to 3 replicas for high availability
168
+ // replicas: 3
169
+ ```
170
+
171
+ ### Dynamic Comments
172
+
173
+ ```typescript
174
+ const { result } = updateYaml({
175
+ yamlString,
176
+ annotate: ({ change }) => {
177
+ change({
178
+ findKey: (parsed) => parsed.spec,
179
+ merge: (originalValue) => ({
180
+ replicas: originalValue.replicas * 2
181
+ }),
182
+ comment: (prevComment) =>
183
+ `Scaled from ${originalValue.replicas} to ${originalValue.replicas * 2}`
184
+ });
185
+ }
186
+ });
187
+ ```
188
+
189
+ ### Using `addInstructions` for Comments
190
+
191
+ ```typescript
192
+ import { updateYaml, addInstructions } from '@hiscojs/yaml-updater';
193
+
194
+ const { result } = updateYaml({
195
+ yamlString,
196
+ annotate: ({ change }) => {
197
+ change({
198
+ findKey: (parsed) => parsed,
199
+ merge: () => ({
200
+ ...addInstructions({
201
+ prop: 'data',
202
+ comment: 'Configuration data section'
203
+ }),
204
+ data: {
205
+ key: 'value'
206
+ }
207
+ })
208
+ });
209
+ }
210
+ });
211
+
212
+ // # Configuration data section
213
+ // data:
214
+ // key: value
215
+ ```
216
+
217
+ ### Removing Comments
218
+
219
+ ```typescript
220
+ const { result } = updateYaml({
221
+ yamlString,
222
+ annotate: ({ change }) => {
223
+ change({
224
+ findKey: (parsed) => parsed,
225
+ merge: () => ({
226
+ ...addInstructions({
227
+ prop: 'data',
228
+ removeComment: true
229
+ }),
230
+ data: { key: 'value' }
231
+ })
232
+ });
233
+ }
234
+ });
235
+ ```
236
+
237
+ ## Array Merging Strategies
238
+
239
+ ### `mergeByContents` - Deduplicate by Deep Equality
240
+
241
+ ```typescript
242
+ const yamlString = `
243
+ items:
244
+ - item1
245
+ - item2
246
+ `;
247
+
248
+ const { result } = updateYaml({
249
+ yamlString,
250
+ annotate: ({ change }) => {
251
+ change({
252
+ findKey: (parsed) => parsed,
253
+ merge: () => ({
254
+ ...addInstructions({
255
+ prop: 'items',
256
+ mergeByContents: true
257
+ }),
258
+ items: ['item2', 'item3'] // item2 deduplicated
259
+ })
260
+ });
261
+ }
262
+ });
263
+
264
+ // items:
265
+ // - item1
266
+ // - item2
267
+ // - item3
268
+ ```
269
+
270
+ ### `mergeByName` - Merge by Name Property
271
+
272
+ ```typescript
273
+ const yamlString = `
274
+ spec:
275
+ containers:
276
+ - name: app
277
+ image: myapp:1.0
278
+ - name: sidecar
279
+ image: sidecar:1.0
280
+ `;
281
+
282
+ interface Container {
283
+ name: string;
284
+ image: string;
285
+ }
286
+
287
+ interface PodSpec {
288
+ spec: {
289
+ containers: Container[];
290
+ };
291
+ }
292
+
293
+ const { result } = updateYaml<PodSpec>({
294
+ yamlString,
295
+ annotate: ({ change }) => {
296
+ change({
297
+ findKey: (parsed) => parsed.spec,
298
+ merge: () => ({
299
+ ...addInstructions({
300
+ prop: 'containers',
301
+ mergeByName: true
302
+ }),
303
+ containers: [
304
+ { name: 'app', image: 'myapp:2.0' } // Updates 'app' container
305
+ ]
306
+ })
307
+ });
308
+ }
309
+ });
310
+
311
+ // spec:
312
+ // containers:
313
+ // - name: app
314
+ // image: myapp:2.0 # Updated
315
+ // - name: sidecar
316
+ // image: sidecar:1.0 # Preserved
317
+ ```
318
+
319
+ ### `mergeByProp` - Merge by Custom Property
320
+
321
+ ```typescript
322
+ const yamlString = `
323
+ services:
324
+ - serviceId: api
325
+ url: http://api.local
326
+ - serviceId: db
327
+ url: postgres://db.local
328
+ `;
329
+
330
+ const { result } = updateYaml({
331
+ yamlString,
332
+ annotate: ({ change }) => {
333
+ change({
334
+ findKey: (parsed) => parsed,
335
+ merge: () => ({
336
+ ...addInstructions({
337
+ prop: 'services',
338
+ mergeByProp: 'serviceId'
339
+ }),
340
+ services: [
341
+ { serviceId: 'api', url: 'https://api.prod' }
342
+ ]
343
+ })
344
+ });
345
+ }
346
+ });
347
+ ```
348
+
349
+ ### `deepMerge` - Deep Merge Nested Objects
350
+
351
+ ```typescript
352
+ const yamlString = `
353
+ configs:
354
+ - name: database
355
+ settings:
356
+ timeout: 30
357
+ pool: 10
358
+ ssl: true
359
+ `;
360
+
361
+ const { result } = updateYaml({
362
+ yamlString,
363
+ annotate: ({ change }) => {
364
+ change({
365
+ findKey: (parsed) => parsed,
366
+ merge: () => ({
367
+ ...addInstructions({
368
+ prop: 'configs',
369
+ mergeByName: true,
370
+ deepMerge: true
371
+ }),
372
+ configs: [
373
+ {
374
+ name: 'database',
375
+ settings: { timeout: 60 } // Only update timeout
376
+ }
377
+ ]
378
+ })
379
+ });
380
+ }
381
+ });
382
+
383
+ // configs:
384
+ // - name: database
385
+ // settings:
386
+ // timeout: 60 # Updated
387
+ // pool: 10 # Preserved
388
+ // ssl: true # Preserved
389
+ ```
390
+
391
+ ## Using `originalValue`
392
+
393
+ Access original values to make conditional updates:
394
+
395
+ ```typescript
396
+ const yamlString = `
397
+ apiVersion: apps/v1
398
+ kind: Deployment
399
+ spec:
400
+ replicas: 2
401
+ `;
402
+
403
+ const { result } = updateYaml({
404
+ yamlString,
405
+ annotate: ({ change }) => {
406
+ change({
407
+ findKey: (parsed) => parsed.spec,
408
+ merge: (originalValue) => ({
409
+ replicas: originalValue.replicas + 1 // Increment by 1
410
+ }),
411
+ comment: () => `Scaled from ${originalValue.replicas} to ${originalValue.replicas + 1} replicas`
412
+ });
413
+ }
414
+ });
415
+
416
+ // spec:
417
+ // # Scaled from 2 to 3 replicas
418
+ // replicas: 3
419
+ ```
420
+
421
+ ### Version Bumping
422
+
423
+ ```typescript
424
+ const yamlString = `
425
+ version: "1.2.3"
426
+ `;
427
+
428
+ const { result } = updateYaml({
429
+ yamlString,
430
+ annotate: ({ change }) => {
431
+ change({
432
+ findKey: (parsed) => parsed,
433
+ merge: (originalValue) => {
434
+ const [major, minor, patch] = originalValue.version.split('.').map(Number);
435
+ return {
436
+ version: `${major}.${minor}.${patch + 1}`
437
+ };
438
+ }
439
+ });
440
+ }
441
+ });
442
+
443
+ // version: "1.2.4"
444
+ ```
445
+
446
+ ## Multi-Document YAML
447
+
448
+ ### Select Document by Index
449
+
450
+ ```typescript
451
+ const yamlString = `---
452
+ apiVersion: v1
453
+ kind: ConfigMap
454
+ data:
455
+ key1: value1
456
+ ---
457
+ apiVersion: v1
458
+ kind: Secret
459
+ data:
460
+ key2: value2`;
461
+
462
+ const { result } = updateYaml({
463
+ yamlString,
464
+ selectDocument: () => 1, // Select second document (Secret)
465
+ annotate: ({ change }) => {
466
+ change({
467
+ findKey: (parsed) => parsed.data,
468
+ merge: () => ({ key2: 'updated' })
469
+ });
470
+ }
471
+ });
472
+ ```
473
+
474
+ ### Select Document by Content
475
+
476
+ ```typescript
477
+ const { result } = updateYaml({
478
+ yamlString,
479
+ selectDocument: (docs) => {
480
+ return docs.findIndex(doc => {
481
+ const parsed = doc.toJSON();
482
+ return parsed.kind === 'Deployment';
483
+ });
484
+ },
485
+ annotate: ({ change }) => {
486
+ change({
487
+ findKey: (parsed) => parsed.spec,
488
+ merge: () => ({ replicas: 5 })
489
+ });
490
+ }
491
+ });
492
+ ```
493
+
494
+ ## Real-World Examples
495
+
496
+ ### Kubernetes Deployment Update
497
+
498
+ ```typescript
499
+ const deploymentYaml = `
500
+ apiVersion: apps/v1
501
+ kind: Deployment
502
+ metadata:
503
+ name: myapp
504
+ spec:
505
+ replicas: 1
506
+ template:
507
+ spec:
508
+ containers:
509
+ - name: app
510
+ image: myapp:1.0
511
+ env:
512
+ - name: ENV
513
+ value: dev
514
+ `;
515
+
516
+ interface K8sDeployment {
517
+ apiVersion: string;
518
+ kind: string;
519
+ metadata: { name: string };
520
+ spec: {
521
+ replicas: number;
522
+ template: {
523
+ spec: {
524
+ containers: Array<{
525
+ name: string;
526
+ image: string;
527
+ env?: Array<{ name: string; value: string }>;
528
+ }>;
529
+ };
530
+ };
531
+ };
532
+ }
533
+
534
+ const { result } = updateYaml<K8sDeployment>({
535
+ yamlString: deploymentYaml,
536
+ annotate: ({ change }) => {
537
+ // Scale replicas
538
+ change({
539
+ findKey: (parsed) => parsed.spec,
540
+ merge: () => ({ replicas: 3 }),
541
+ comment: () => 'Scaled to 3 replicas for high availability'
542
+ });
543
+
544
+ // Update container image
545
+ change({
546
+ findKey: (parsed) => parsed.spec.template.spec,
547
+ merge: () => ({
548
+ ...addInstructions({
549
+ prop: 'containers',
550
+ mergeByName: true
551
+ }),
552
+ containers: [
553
+ {
554
+ name: 'app',
555
+ image: 'myapp:2.0',
556
+ env: [
557
+ { name: 'ENV', value: 'production' }
558
+ ]
559
+ }
560
+ ]
561
+ }),
562
+ comment: () => 'Updated to production environment'
563
+ });
564
+ }
565
+ });
566
+ ```
567
+
568
+ ### ConfigMap Updates
569
+
570
+ ```typescript
571
+ const configMapYaml = `
572
+ apiVersion: v1
573
+ kind: ConfigMap
574
+ metadata:
575
+ name: app-config
576
+ data:
577
+ database_host: localhost
578
+ cache_enabled: "false"
579
+ `;
580
+
581
+ const { result } = updateYaml({
582
+ yamlString: configMapYaml,
583
+ annotate: ({ change }) => {
584
+ change({
585
+ findKey: (parsed) => parsed.data,
586
+ merge: () => ({
587
+ database_host: 'db.production.com',
588
+ cache_enabled: 'true',
589
+ redis_host: 'redis.production.com'
590
+ }),
591
+ comment: () => 'Updated for production environment'
592
+ });
593
+ }
594
+ });
595
+
596
+ // apiVersion: v1
597
+ // kind: ConfigMap
598
+ // metadata:
599
+ // name: app-config
600
+ // data:
601
+ // # Updated for production environment
602
+ // database_host: db.production.com
603
+ // cache_enabled: "true"
604
+ // redis_host: redis.production.com
605
+ ```
606
+
607
+ ### Helm Values Update
608
+
609
+ ```typescript
610
+ const valuesYaml = `
611
+ replicaCount: 1
612
+
613
+ image:
614
+ repository: myapp
615
+ tag: "1.0.0"
616
+ pullPolicy: IfNotPresent
617
+
618
+ resources:
619
+ limits:
620
+ cpu: 100m
621
+ memory: 128Mi
622
+ requests:
623
+ cpu: 100m
624
+ memory: 128Mi
625
+ `;
626
+
627
+ const { result } = updateYaml({
628
+ yamlString: valuesYaml,
629
+ annotate: ({ change }) => {
630
+ change({
631
+ findKey: (parsed) => parsed,
632
+ merge: (original) => ({
633
+ replicaCount: 3,
634
+ image: {
635
+ ...original.image,
636
+ tag: '2.0.0'
637
+ }
638
+ })
639
+ });
640
+
641
+ change({
642
+ findKey: (parsed) => parsed.resources.limits,
643
+ merge: () => ({
644
+ cpu: '500m',
645
+ memory: '512Mi'
646
+ }),
647
+ comment: () => 'Increased for production workload'
648
+ });
649
+ }
650
+ });
651
+ ```
652
+
653
+ ## Advanced Features
654
+
655
+ ### Multiple Changes
656
+
657
+ Apply multiple changes in a single update:
658
+
659
+ ```typescript
660
+ const { result } = updateYaml({
661
+ yamlString,
662
+ annotate: ({ change }) => {
663
+ change({
664
+ findKey: (parsed) => parsed.database,
665
+ merge: () => ({ host: 'db.prod.com' })
666
+ });
667
+
668
+ change({
669
+ findKey: (parsed) => parsed.cache,
670
+ merge: () => ({ host: 'cache.prod.com' })
671
+ });
672
+
673
+ change({
674
+ findKey: (parsed) => parsed.api,
675
+ merge: () => ({ host: 'api.prod.com' })
676
+ });
677
+ }
678
+ });
679
+ ```
680
+
681
+ ### Conditional Updates
682
+
683
+ ```typescript
684
+ const { result } = updateYaml({
685
+ yamlString,
686
+ annotate: ({ change }) => {
687
+ change({
688
+ findKey: (parsed) => parsed.spec,
689
+ merge: (originalValue) => {
690
+ const newReplicas = originalValue.replicas < 3
691
+ ? originalValue.replicas * 2
692
+ : originalValue.replicas;
693
+
694
+ return { replicas: newReplicas };
695
+ },
696
+ comment: (prevComment) =>
697
+ originalValue.replicas < 3
698
+ ? `Scaled from ${originalValue.replicas} to ${newReplicas}`
699
+ : prevComment // Keep existing comment
700
+ });
701
+ }
702
+ });
703
+ ```
704
+
705
+ ### Preserve and Extend
706
+
707
+ ```typescript
708
+ const { result } = updateYaml({
709
+ yamlString,
710
+ annotate: ({ change }) => {
711
+ change({
712
+ findKey: (parsed) => parsed.spec,
713
+ merge: (originalValue) => ({
714
+ ...originalValue, // Preserve all existing
715
+ replicas: originalValue.replicas + 1, // Update one field
716
+ newField: 'added' // Add new field
717
+ })
718
+ });
719
+ }
720
+ });
721
+ ```
722
+
723
+ ## Comment Preservation
724
+
725
+ Comments are automatically preserved:
726
+
727
+ ```typescript
728
+ const yamlString = `
729
+ # Application configuration
730
+ apiVersion: v1
731
+ kind: ConfigMap
732
+ metadata:
733
+ # Metadata section
734
+ name: my-config
735
+ data:
736
+ # Database configuration
737
+ database: localhost
738
+ # Cache configuration
739
+ cache: redis
740
+ `;
741
+
742
+ const { result } = updateYaml({
743
+ yamlString,
744
+ annotate: ({ change }) => {
745
+ change({
746
+ findKey: (parsed) => parsed.data,
747
+ merge: () => ({ database: 'db.prod.com' })
748
+ });
749
+ }
750
+ });
751
+
752
+ // All comments are preserved!
753
+ // # Application configuration
754
+ // apiVersion: v1
755
+ // kind: ConfigMap
756
+ // metadata:
757
+ // # Metadata section
758
+ // name: my-config
759
+ // data:
760
+ // # Database configuration
761
+ // database: db.prod.com
762
+ // # Cache configuration
763
+ // cache: redis
764
+ ```
765
+
766
+ ## Formatting Preservation
767
+
768
+ Indentation and spacing are maintained:
769
+
770
+ ```typescript
771
+ const yamlString = `
772
+ server:
773
+ host: localhost
774
+ port: 3000
775
+
776
+
777
+ database:
778
+ host: localhost
779
+ port: 5432
780
+ `;
781
+
782
+ const { result } = updateYaml({
783
+ yamlString,
784
+ annotate: ({ change }) => {
785
+ change({
786
+ findKey: (parsed) => parsed.server,
787
+ merge: () => ({ port: 8080 })
788
+ });
789
+ }
790
+ });
791
+
792
+ // Blank lines and indentation preserved
793
+ // server:
794
+ // host: localhost
795
+ // port: 8080
796
+ //
797
+ //
798
+ // database:
799
+ // host: localhost
800
+ // port: 5432
801
+ ```
802
+
803
+ ## Best Practices
804
+
805
+ ### 1. Use Type Parameters
806
+
807
+ ```typescript
808
+ // ✅ Good - Type safe
809
+ const { result } = updateYaml<K8sDeployment>({ ... });
810
+
811
+ // ❌ Avoid - No type safety
812
+ const { result } = updateYaml({ ... });
813
+ ```
814
+
815
+ ### 2. Leverage `originalValue`
816
+
817
+ ```typescript
818
+ // ✅ Good - Conditional based on original
819
+ merge: (originalValue) => ({
820
+ replicas: originalValue.replicas + 1
821
+ })
822
+
823
+ // ❌ Avoid - Hardcoded without context
824
+ merge: () => ({ replicas: 3 })
825
+ ```
826
+
827
+ ### 3. Use Merge Strategies for Arrays
828
+
829
+ ```typescript
830
+ // ✅ Good - Explicit merge strategy
831
+ ...addInstructions({
832
+ prop: 'containers',
833
+ mergeByName: true
834
+ })
835
+
836
+ // ❌ Avoid - Array replacement
837
+ containers: newContainers // Loses existing items
838
+ ```
839
+
840
+ ### 4. Add Meaningful Comments
841
+
842
+ ```typescript
843
+ // ✅ Good - Descriptive comment
844
+ comment: () => 'Scaled to 3 replicas for high availability'
845
+
846
+ // ❌ Avoid - Obvious or missing comments
847
+ comment: () => 'Updated replicas'
848
+ ```
849
+
850
+ ### 5. Handle Multi-Document YAMLs
851
+
852
+ ```typescript
853
+ // ✅ Good - Explicit document selection
854
+ selectDocument: (docs) => {
855
+ return docs.findIndex(doc =>
856
+ doc.toJSON().kind === 'Deployment'
857
+ );
858
+ }
859
+
860
+ // ❌ Avoid - Assuming single document
861
+ // (works but fails silently with multi-doc)
862
+ ```
863
+
864
+ ## Error Handling
865
+
866
+ The library will throw descriptive errors for invalid YAML:
867
+
868
+ ```typescript
869
+ try {
870
+ const { result } = updateYaml({
871
+ yamlString: invalidYaml,
872
+ annotate: ({ change }) => { ... }
873
+ });
874
+ } catch (error) {
875
+ console.error('YAML parsing failed:', error.message);
876
+ }
877
+ ```
878
+
879
+ ## Performance Considerations
880
+
881
+ - **Large Files**: YAML parsing is memory-intensive. Consider streaming for very large files (>10MB).
882
+ - **Many Changes**: Each `change()` call creates proxies and performs deep cloning. Batch related changes when possible.
883
+ - **Comment Operations**: Adding/removing comments requires AST manipulation. Minimal performance impact for normal use.
884
+
885
+ ## Dependencies
886
+
887
+ - `yaml`: YAML 1.2 parser and stringifier
888
+ - `deep-diff`: Deep object diffing
889
+ - `@hiscojs/object-updater`: Core object manipulation with type-safe updates
890
+
891
+ ## Related Packages
892
+
893
+ - [@hiscojs/object-updater](https://www.npmjs.com/package/@hiscojs/object-updater) - The underlying object manipulation library
894
+
895
+ ## License
896
+
897
+ MIT
898
+
899
+ ## Contributing
900
+
901
+ Issues and pull requests welcome!
902
+
903
+ ## Repository
904
+
905
+ https://github.com/hisco/yaml-updater
@@ -0,0 +1,94 @@
1
+ import { Document } from 'yaml';
2
+ import { addInstructions as addObjectInstructions } from '@hiscojs/object-updater';
3
+ /**
4
+ * Re-export addInstructions for convenience
5
+ */
6
+ export declare const addInstructions: typeof addObjectInstructions;
7
+ /**
8
+ * Result of updateYaml operation including the YAML string, parsed objects, and comments
9
+ */
10
+ export interface YamlEdit<T> {
11
+ result: string;
12
+ resultParsed: T;
13
+ originalParsed: T;
14
+ comments: {
15
+ path: (string | number)[];
16
+ comment: string;
17
+ }[];
18
+ }
19
+ /**
20
+ * Focused YAML updater - mimics focused-object-updater interface but works with YAML strings.
21
+ * Internally uses @hiscojs/object-updater for object manipulation and applies changes to YAML AST.
22
+ *
23
+ * Type Safety:
24
+ * - Generic T preserves the type of the parsed YAML object
25
+ * - Generic L (inferred from findKey) represents the type at the selected path
26
+ * - The findKey function uses TypeScript's type inference to track nested paths
27
+ * - The merge function receives originalValue with the correct inferred type L
28
+ * - Comments are tracked with their paths
29
+ *
30
+ * @example Basic YAML update
31
+ * ```typescript
32
+ * const yamlString = `
33
+ * apiVersion: v1
34
+ * kind: ConfigMap
35
+ * data:
36
+ * database: localhost
37
+ * `;
38
+ *
39
+ * const { result, comments } = updateYaml({
40
+ * yamlString,
41
+ * annotate: ({ change }) => {
42
+ * change({
43
+ * findKey: (parsed) => parsed.data,
44
+ * merge: () => ({
45
+ * database: 'db.production.com',
46
+ * cache: 'redis.production.com'
47
+ * }),
48
+ * comment: () => 'Updated to production endpoints'
49
+ * });
50
+ * }
51
+ * });
52
+ * ```
53
+ *
54
+ * @example With addInstructions for array merging
55
+ * ```typescript
56
+ * const { result } = updateYaml({
57
+ * yamlString: k8sDeployment,
58
+ * annotate: ({ change }) => {
59
+ * change({
60
+ * findKey: (parsed) => parsed.spec.template.spec.containers,
61
+ * merge: () => ({
62
+ * ...addInstructions({
63
+ * prop: 'containers',
64
+ * mergeByName: true
65
+ * }),
66
+ * containers: [
67
+ * {
68
+ * name: 'app',
69
+ * image: 'myapp:2.0'
70
+ * }
71
+ * ]
72
+ * }),
73
+ * comment: () => 'Updated container image'
74
+ * });
75
+ * }
76
+ * });
77
+ * ```
78
+ */
79
+ export declare function updateYaml<T extends object = object>({ yamlString, selectDocument, annotate }: {
80
+ yamlString: string;
81
+ selectDocument?: (yamlDocuments: Document[]) => number;
82
+ annotate?: (annotator: {
83
+ change: <L>(options: {
84
+ findKey: (parsed: T) => L;
85
+ merge: (originalValue: L) => L extends any[] ? unknown[] : L extends object ? {
86
+ [K in keyof L]?: L[K];
87
+ } & {
88
+ [key: string]: unknown;
89
+ } : L;
90
+ comment?: (prev?: string) => string | undefined;
91
+ }) => void;
92
+ }) => void;
93
+ }): YamlEdit<T>;
94
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAqB,MAAM,MAAM,CAAC;AACnD,OAAO,EAAgB,eAAe,IAAI,qBAAqB,EAAuC,MAAM,yBAAyB,CAAC;AAEtI;;GAEG;AACH,eAAO,MAAM,eAAe,8BAAwB,CAAC;AAErD;;GAEG;AACH,MAAM,WAAW,QAAQ,CAAC,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,CAAC,CAAC;IAChB,cAAc,EAAE,CAAC,CAAC;IAClB,QAAQ,EAAE;QACR,IAAI,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;QAC1B,OAAO,EAAE,MAAM,CAAC;KACjB,EAAE,CAAC;CACL;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAAE,EACpD,UAAU,EACV,cAAwB,EACxB,QAAQ,EACT,EAAE;IACD,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,CAAC,aAAa,EAAE,QAAQ,EAAE,KAAK,MAAM,CAAC;IACvD,QAAQ,CAAC,EAAE,CAAC,SAAS,EAAE;QACrB,MAAM,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE;YACnB,OAAO,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,CAAC;YAC1B,KAAK,EAAE,CAAC,aAAa,EAAE,CAAC,KAAK,CAAC,SAAS,GAAG,EAAE,GACxC,OAAO,EAAE,GACT,CAAC,SAAS,MAAM,GACd;iBAAG,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;aAAE,GAAG;gBAAE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;aAAE,GACtD,CAAC,CAAC;YACR,OAAO,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;SACjD,KAAK,IAAI,CAAC;KACZ,KAAK,IAAI,CAAC;CACZ,GAAG,QAAQ,CAAC,CAAC,CAAC,CAqMd"}
package/dist/index.js ADDED
@@ -0,0 +1,279 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.addInstructions = void 0;
4
+ exports.updateYaml = updateYaml;
5
+ const deep_diff_1 = require("deep-diff");
6
+ const yaml_1 = require("yaml");
7
+ const object_updater_1 = require("@hiscojs/object-updater");
8
+ /**
9
+ * Re-export addInstructions for convenience
10
+ */
11
+ exports.addInstructions = object_updater_1.addInstructions;
12
+ /**
13
+ * Focused YAML updater - mimics focused-object-updater interface but works with YAML strings.
14
+ * Internally uses @hiscojs/object-updater for object manipulation and applies changes to YAML AST.
15
+ *
16
+ * Type Safety:
17
+ * - Generic T preserves the type of the parsed YAML object
18
+ * - Generic L (inferred from findKey) represents the type at the selected path
19
+ * - The findKey function uses TypeScript's type inference to track nested paths
20
+ * - The merge function receives originalValue with the correct inferred type L
21
+ * - Comments are tracked with their paths
22
+ *
23
+ * @example Basic YAML update
24
+ * ```typescript
25
+ * const yamlString = `
26
+ * apiVersion: v1
27
+ * kind: ConfigMap
28
+ * data:
29
+ * database: localhost
30
+ * `;
31
+ *
32
+ * const { result, comments } = updateYaml({
33
+ * yamlString,
34
+ * annotate: ({ change }) => {
35
+ * change({
36
+ * findKey: (parsed) => parsed.data,
37
+ * merge: () => ({
38
+ * database: 'db.production.com',
39
+ * cache: 'redis.production.com'
40
+ * }),
41
+ * comment: () => 'Updated to production endpoints'
42
+ * });
43
+ * }
44
+ * });
45
+ * ```
46
+ *
47
+ * @example With addInstructions for array merging
48
+ * ```typescript
49
+ * const { result } = updateYaml({
50
+ * yamlString: k8sDeployment,
51
+ * annotate: ({ change }) => {
52
+ * change({
53
+ * findKey: (parsed) => parsed.spec.template.spec.containers,
54
+ * merge: () => ({
55
+ * ...addInstructions({
56
+ * prop: 'containers',
57
+ * mergeByName: true
58
+ * }),
59
+ * containers: [
60
+ * {
61
+ * name: 'app',
62
+ * image: 'myapp:2.0'
63
+ * }
64
+ * ]
65
+ * }),
66
+ * comment: () => 'Updated container image'
67
+ * });
68
+ * }
69
+ * });
70
+ * ```
71
+ */
72
+ function updateYaml({ yamlString, selectDocument = () => 0, annotate }) {
73
+ // Step 1: Parse YAML
74
+ const yamlDocuments = (0, yaml_1.parseAllDocuments)(yamlString);
75
+ const docIndex = selectDocument(yamlDocuments);
76
+ const originalYamlDocument = yamlDocuments[docIndex];
77
+ const originalParsed = originalYamlDocument.toJSON();
78
+ // Step 2: Use focused-object-updater to perform the object transformation
79
+ // Also track comment instructions from addInstructions
80
+ const commentInstructionsMap = new Map();
81
+ // Helper to recursively extract comment instructions from merge result
82
+ const extractCommentInstructions = (obj, basePath, visited = new WeakSet()) => {
83
+ if (!obj || typeof obj !== 'object' || visited.has(obj)) {
84
+ return;
85
+ }
86
+ visited.add(obj);
87
+ // Extract symbols first
88
+ const symbols = Object.getOwnPropertySymbols(obj);
89
+ for (const sym of symbols) {
90
+ const symKey = sym.toString();
91
+ const propMatch = symKey.match(/Symbol\(merge_(.+)\)/);
92
+ if (propMatch) {
93
+ const prop = propMatch[1];
94
+ const instructions = obj[sym];
95
+ const fullPath = [...basePath, prop];
96
+ if (instructions && (instructions.comment || instructions.removeComment || instructions.commentBefore || instructions.commentAfter)) {
97
+ commentInstructionsMap.set(JSON.stringify(fullPath), instructions);
98
+ }
99
+ }
100
+ }
101
+ // Recursively process nested objects (not arrays)
102
+ const keys = Object.keys(obj);
103
+ for (const key of keys) {
104
+ const value = obj[key];
105
+ if (value && typeof value === 'object' && !Array.isArray(value)) {
106
+ extractCommentInstructions(value, [...basePath, key], visited);
107
+ }
108
+ }
109
+ };
110
+ const { result: updatedObject, comments: objectComments } = (0, object_updater_1.updateObject)({
111
+ sourceObject: originalParsed,
112
+ annotate: annotate ? (objectAnnotator) => {
113
+ annotate({
114
+ change: (options) => {
115
+ // Calculate base path ONCE before any merge operations
116
+ const basePath = (0, object_updater_1.findKeyByProxy)(originalParsed, options.findKey);
117
+ // Cache the merge result to avoid calling options.merge() twice
118
+ let mergeResultCache = null;
119
+ let mergeCalled = false;
120
+ const cachedMerge = (originalValue) => {
121
+ if (!mergeCalled) {
122
+ mergeResultCache = options.merge(originalValue);
123
+ mergeCalled = true;
124
+ // Extract comment instructions from the cached result
125
+ extractCommentInstructions(mergeResultCache, basePath);
126
+ }
127
+ return mergeResultCache;
128
+ };
129
+ objectAnnotator.change({
130
+ findKey: options.findKey,
131
+ merge: cachedMerge,
132
+ comment: options.comment ? (prev) => {
133
+ const commentText = options.comment(prev);
134
+ if (commentText) {
135
+ return {
136
+ text: commentText,
137
+ direction: 'right' // YAML comments don't have direction, but we need this for the interface
138
+ };
139
+ }
140
+ return undefined;
141
+ } : undefined
142
+ });
143
+ }
144
+ });
145
+ } : undefined
146
+ });
147
+ // Step 3: Calculate diff between original and updated objects
148
+ const diff = (0, deep_diff_1.diff)(originalParsed, updatedObject);
149
+ // Reverse array diff sequences (same logic as yaml-editor)
150
+ const locatedArrayIndex = [];
151
+ if (diff != undefined) {
152
+ diff.forEach((d, index) => {
153
+ if (d.kind === 'A') {
154
+ locatedArrayIndex.push(index);
155
+ }
156
+ });
157
+ }
158
+ const reversedArrayIndex = reverseArrayDiffSequences(locatedArrayIndex);
159
+ const clonedDiff = [...(diff == undefined ? [] : diff)];
160
+ locatedArrayIndex.forEach((index, i) => {
161
+ if (diff) {
162
+ clonedDiff[index] = diff[reversedArrayIndex[i]];
163
+ }
164
+ });
165
+ // Step 4: Apply diff to YAML AST
166
+ clonedDiff.forEach((df) => {
167
+ if (df.kind === 'N' && df.path) {
168
+ // New property
169
+ changeSpecificNodesToNoneFlow(originalYamlDocument, df.path.slice(0, -1));
170
+ originalYamlDocument.setIn(df.path, originalYamlDocument.createNode(df.rhs, { flow: false }));
171
+ }
172
+ else if (df.kind === 'D' && df.path) {
173
+ // Deleted property
174
+ originalYamlDocument.deleteIn(df.path);
175
+ }
176
+ else if (df.kind === 'E' && df.path) {
177
+ // Edited property
178
+ changeSpecificNodesToNoneFlow(originalYamlDocument, df.path.slice(0, -1));
179
+ originalYamlDocument.setIn(df.path, df.rhs);
180
+ }
181
+ else if (df.kind === 'A' && df.path) {
182
+ // Array change
183
+ changeSpecificNodesToNoneFlow(originalYamlDocument, df.path.slice(0, -1));
184
+ originalYamlDocument.addIn(df.path, originalYamlDocument.createNode(df.item.rhs, { flow: false }));
185
+ }
186
+ });
187
+ // Step 5: Apply comments to YAML nodes
188
+ const comments = [];
189
+ // First apply comments from the change() comment callback
190
+ objectComments.forEach(({ path, comment: commentText }) => {
191
+ const node = originalYamlDocument.getIn(path, true);
192
+ if (node) {
193
+ const prevComment = node['commentBefore'];
194
+ node['commentBefore'] = ' ' + commentText;
195
+ // If we're adding a comment to an empty object/array, ensure it's not in flow style
196
+ if (commentText && node.type === 'MAP' && node.items && node.items.length === 0) {
197
+ node.flow = false;
198
+ }
199
+ comments.push({
200
+ path,
201
+ comment: commentText
202
+ });
203
+ }
204
+ });
205
+ // Then apply comment instructions from addInstructions
206
+ commentInstructionsMap.forEach((instructions, pathKey) => {
207
+ const path = JSON.parse(pathKey);
208
+ const node = originalYamlDocument.getIn(path, true);
209
+ if (node) {
210
+ if (instructions.removeComment) {
211
+ // Remove existing comment
212
+ node['commentBefore'] = undefined;
213
+ }
214
+ else if (instructions.comment) {
215
+ // Add or replace comment
216
+ node['commentBefore'] = ' ' + instructions.comment;
217
+ comments.push({
218
+ path,
219
+ comment: instructions.comment
220
+ });
221
+ }
222
+ else if (instructions.commentBefore) {
223
+ // Add comment before (same as comment in YAML)
224
+ node['commentBefore'] = ' ' + instructions.commentBefore;
225
+ comments.push({
226
+ path,
227
+ comment: instructions.commentBefore
228
+ });
229
+ }
230
+ // If we're adding a comment to an empty object/array, ensure it's not in flow style
231
+ if ((instructions.comment || instructions.commentBefore) && node.type === 'MAP' && node.items && node.items.length === 0) {
232
+ node.flow = false;
233
+ }
234
+ }
235
+ });
236
+ // Step 6: Stringify YAML
237
+ const result = originalYamlDocument.toString({ lineWidth: 0 });
238
+ return {
239
+ result,
240
+ resultParsed: updatedObject,
241
+ originalParsed,
242
+ comments
243
+ };
244
+ }
245
+ /**
246
+ * Helper function to reverse array diff sequences
247
+ * (copied from yaml-editor.ts)
248
+ */
249
+ function reverseArrayDiffSequences(array) {
250
+ const result = [];
251
+ let start = 0;
252
+ for (let i = 1; i < array.length; i++) {
253
+ if (array[i] !== array[i - 1] + 1) {
254
+ result.push(...array.slice(start, i).reverse());
255
+ start = i;
256
+ }
257
+ }
258
+ result.push(...array.slice(start).reverse());
259
+ return result;
260
+ }
261
+ /**
262
+ * Helper function to change specific nodes to non-flow style
263
+ * (copied from yaml-editor.ts)
264
+ */
265
+ function changeSpecificNodesToNoneFlow(originalYamlDocument, path) {
266
+ const node = originalYamlDocument.getIn(path, true);
267
+ if (!node) {
268
+ return;
269
+ }
270
+ const isEmptyObject = node.items && Array.isArray(node.items) && node.items.length === 0;
271
+ const isEmptyArray = node.items && node.items.size === 0;
272
+ if (node.flow == true && (node.content == '{}' || node.content == '[]' || node.content == null || node.content == undefined || isEmptyObject || isEmptyArray)) {
273
+ node.flow = false;
274
+ }
275
+ if (node.type === 'MAP' && node.items && node.items.length === 0 && node.commentBefore) {
276
+ node.flow = false;
277
+ }
278
+ }
279
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@hiscojs/yaml-updater",
3
+ "version": "1.0.0",
4
+ "description": "Type-safe, immutable YAML updates with comment preservation, multi-document support, and advanced array merging strategies",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "publishConfig": {
8
+ "access": "public"
9
+ },
10
+ "scripts": {
11
+ "test": "jest",
12
+ "build": "rm -rf dist && tsc",
13
+ "prepublishOnly": "npm run build && npm test"
14
+ },
15
+ "keywords": [
16
+ "yaml",
17
+ "update",
18
+ "merge",
19
+ "immutable",
20
+ "typescript",
21
+ "type-safe",
22
+ "proxy",
23
+ "comment-preservation",
24
+ "multi-document",
25
+ "array-merge"
26
+ ],
27
+ "author": "",
28
+ "license": "MIT",
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "https://github.com/hisco/yaml-updater.git"
32
+ },
33
+ "dependencies": {
34
+ "@hiscojs/object-updater": "^1.0.0",
35
+ "deep-diff": "^1.0.2",
36
+ "yaml": "^2.7.0"
37
+ },
38
+ "devDependencies": {
39
+ "@jest/globals": "^29.7.0",
40
+ "@types/deep-diff": "^1.0.5",
41
+ "@types/jest": "^29.5.14",
42
+ "@types/node": "^22.10.2",
43
+ "jest": "^29.7.0",
44
+ "ts-jest": "^29.2.5",
45
+ "ts-node": "^10.9.2",
46
+ "typescript": "^5.7.2"
47
+ },
48
+ "files": [
49
+ "dist",
50
+ "README.md",
51
+ "LICENSE"
52
+ ]
53
+ }