@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 +21 -0
- package/README.md +905 -0
- package/dist/index.d.ts +94 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +279 -0
- package/dist/index.js.map +1 -0
- package/package.json +53 -0
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
|
package/dist/index.d.ts
ADDED
|
@@ -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
|
+
}
|