@zincapp/znvault-cli 2.18.0 → 2.19.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/dist/commands/secret/index.d.ts.map +1 -1
- package/dist/commands/secret/index.js +2 -0
- package/dist/commands/secret/index.js.map +1 -1
- package/dist/commands/secret/patch/diff.d.ts +14 -0
- package/dist/commands/secret/patch/diff.d.ts.map +1 -0
- package/dist/commands/secret/patch/diff.js +181 -0
- package/dist/commands/secret/patch/diff.js.map +1 -0
- package/dist/commands/secret/patch/operations.d.ts +77 -0
- package/dist/commands/secret/patch/operations.d.ts.map +1 -0
- package/dist/commands/secret/patch/operations.js +351 -0
- package/dist/commands/secret/patch/operations.js.map +1 -0
- package/dist/commands/secret/patch/parsers.d.ts +22 -0
- package/dist/commands/secret/patch/parsers.d.ts.map +1 -0
- package/dist/commands/secret/patch/parsers.js +349 -0
- package/dist/commands/secret/patch/parsers.js.map +1 -0
- package/dist/commands/secret/patch/types.d.ts +109 -0
- package/dist/commands/secret/patch/types.d.ts.map +1 -0
- package/dist/commands/secret/patch/types.js +22 -0
- package/dist/commands/secret/patch/types.js.map +1 -0
- package/dist/commands/secret/patch.d.ts +9 -0
- package/dist/commands/secret/patch.d.ts.map +1 -0
- package/dist/commands/secret/patch.js +247 -0
- package/dist/commands/secret/patch.js.map +1 -0
- package/dist/lib/config/store.d.ts +4 -0
- package/dist/lib/config/store.d.ts.map +1 -1
- package/dist/lib/config/store.js +8 -0
- package/dist/lib/config/store.js.map +1 -1
- package/package.json +5 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/secret/index.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/secret/index.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA+BzC,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAiB7D;AAGD,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC"}
|
|
@@ -8,6 +8,7 @@ import { registerDeleteCommand } from './delete.js';
|
|
|
8
8
|
import { registerRotateCommand } from './rotate.js';
|
|
9
9
|
import { registerHistoryCommand } from './history.js';
|
|
10
10
|
import { registerCopyCommand } from './copy.js';
|
|
11
|
+
import { registerPatchCommand } from './patch.js';
|
|
11
12
|
// Help text for secret identifier format
|
|
12
13
|
const SECRET_ID_HELP = `
|
|
13
14
|
Secret Identifier Formats:
|
|
@@ -41,6 +42,7 @@ export function registerSecretCommands(program) {
|
|
|
41
42
|
registerRotateCommand(secretCmd);
|
|
42
43
|
registerHistoryCommand(secretCmd);
|
|
43
44
|
registerCopyCommand(secretCmd);
|
|
45
|
+
registerPatchCommand(secretCmd);
|
|
44
46
|
}
|
|
45
47
|
// Re-export types for external use
|
|
46
48
|
export * from './types.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/secret/index.ts"],"names":[],"mappings":"AAAA,qCAAqC;AAUrC,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/secret/index.ts"],"names":[],"mappings":"AAAA,qCAAqC;AAUrC,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAElD,yCAAyC;AACzC,MAAM,cAAc,GAAG;;;;;;;;;;;;;;;;CAgBtB,CAAC;AAEF,MAAM,UAAU,sBAAsB,CAAC,OAAgB;IACrD,MAAM,SAAS,GAAG,OAAO;SACtB,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,gBAAgB,CAAC;SAC7B,WAAW,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;IAExC,2BAA2B;IAC3B,mBAAmB,CAAC,SAAS,CAAC,CAAC;IAC/B,kBAAkB,CAAC,SAAS,CAAC,CAAC;IAC9B,sBAAsB,CAAC,SAAS,CAAC,CAAC;IAClC,qBAAqB,CAAC,SAAS,CAAC,CAAC;IACjC,qBAAqB,CAAC,SAAS,CAAC,CAAC;IACjC,qBAAqB,CAAC,SAAS,CAAC,CAAC;IACjC,qBAAqB,CAAC,SAAS,CAAC,CAAC;IACjC,sBAAsB,CAAC,SAAS,CAAC,CAAC;IAClC,mBAAmB,CAAC,SAAS,CAAC,CAAC;IAC/B,oBAAoB,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC;AAED,mCAAmC;AACnC,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { DiffResult, AppliedOperation } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Generate a diff between two serialized values
|
|
4
|
+
*/
|
|
5
|
+
export declare function generateDiff(before: string, after: string, operations: AppliedOperation[]): DiffResult;
|
|
6
|
+
/**
|
|
7
|
+
* Display a diff result
|
|
8
|
+
*/
|
|
9
|
+
export declare function displayDiff(diff: DiffResult): void;
|
|
10
|
+
/**
|
|
11
|
+
* Display a summary of operations to be applied
|
|
12
|
+
*/
|
|
13
|
+
export declare function displayOperationsSummary(operations: AppliedOperation[]): void;
|
|
14
|
+
//# sourceMappingURL=diff.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../../../src/commands/secret/patch/diff.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAc,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAO3E;;GAEG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,gBAAgB,EAAE,GAC7B,UAAU,CA0BZ;AA8CD;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,CAuClD;AAsDD;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,gBAAgB,EAAE,GAAG,IAAI,CAyB7E"}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
// Path: src/commands/secret/patch/diff.ts
|
|
2
|
+
/**
|
|
3
|
+
* Diff generation for dry-run mode
|
|
4
|
+
*/
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import { isPlainMode } from '../../../lib/output-mode.js';
|
|
7
|
+
// ============================================================================
|
|
8
|
+
// Diff Generation
|
|
9
|
+
// ============================================================================
|
|
10
|
+
/**
|
|
11
|
+
* Generate a diff between two serialized values
|
|
12
|
+
*/
|
|
13
|
+
export function generateDiff(before, after, operations) {
|
|
14
|
+
const changes = operations.map(op => {
|
|
15
|
+
if (op.type === 'set') {
|
|
16
|
+
if (op.previousValue === undefined) {
|
|
17
|
+
return {
|
|
18
|
+
type: 'add',
|
|
19
|
+
path: op.path,
|
|
20
|
+
newValue: op.newValue,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
type: 'modify',
|
|
25
|
+
path: op.path,
|
|
26
|
+
oldValue: op.previousValue,
|
|
27
|
+
newValue: op.newValue,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
return {
|
|
32
|
+
type: 'remove',
|
|
33
|
+
path: op.path,
|
|
34
|
+
oldValue: op.previousValue,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
return { before, after, changes };
|
|
39
|
+
}
|
|
40
|
+
// ============================================================================
|
|
41
|
+
// Diff Display
|
|
42
|
+
// ============================================================================
|
|
43
|
+
/**
|
|
44
|
+
* Format a diff change for display
|
|
45
|
+
*/
|
|
46
|
+
function formatChange(change, plain) {
|
|
47
|
+
const formatValue = (val) => {
|
|
48
|
+
if (val === undefined)
|
|
49
|
+
return 'undefined';
|
|
50
|
+
if (val === null)
|
|
51
|
+
return 'null';
|
|
52
|
+
if (typeof val === 'string')
|
|
53
|
+
return `"${val}"`;
|
|
54
|
+
if (typeof val === 'object')
|
|
55
|
+
return JSON.stringify(val);
|
|
56
|
+
if (typeof val === 'number' || typeof val === 'boolean')
|
|
57
|
+
return String(val);
|
|
58
|
+
// For bigint, symbol, or other types
|
|
59
|
+
return JSON.stringify(val);
|
|
60
|
+
};
|
|
61
|
+
switch (change.type) {
|
|
62
|
+
case 'add':
|
|
63
|
+
if (plain) {
|
|
64
|
+
return `+ ${change.path} = ${formatValue(change.newValue)}`;
|
|
65
|
+
}
|
|
66
|
+
return chalk.green(`+ ${change.path} = ${formatValue(change.newValue)}`);
|
|
67
|
+
case 'remove':
|
|
68
|
+
if (plain) {
|
|
69
|
+
return `- ${change.path} = ${formatValue(change.oldValue)}`;
|
|
70
|
+
}
|
|
71
|
+
return chalk.red(`- ${change.path} = ${formatValue(change.oldValue)}`);
|
|
72
|
+
case 'modify':
|
|
73
|
+
if (plain) {
|
|
74
|
+
return `~ ${change.path}: ${formatValue(change.oldValue)} -> ${formatValue(change.newValue)}`;
|
|
75
|
+
}
|
|
76
|
+
return chalk.yellow(`~ ${change.path}: ` +
|
|
77
|
+
chalk.red(formatValue(change.oldValue)) +
|
|
78
|
+
chalk.gray(' -> ') +
|
|
79
|
+
chalk.green(formatValue(change.newValue)));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Display a diff result
|
|
84
|
+
*/
|
|
85
|
+
export function displayDiff(diff) {
|
|
86
|
+
const plain = isPlainMode();
|
|
87
|
+
console.log();
|
|
88
|
+
if (plain) {
|
|
89
|
+
console.log('=== Changes to be applied ===');
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
console.log(chalk.bold.cyan('Changes to be applied:'));
|
|
93
|
+
}
|
|
94
|
+
console.log();
|
|
95
|
+
if (diff.changes.length === 0) {
|
|
96
|
+
if (plain) {
|
|
97
|
+
console.log('No changes detected');
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
console.log(chalk.dim('No changes detected'));
|
|
101
|
+
}
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
for (const change of diff.changes) {
|
|
105
|
+
console.log(' ' + formatChange(change, plain));
|
|
106
|
+
}
|
|
107
|
+
console.log();
|
|
108
|
+
if (plain) {
|
|
109
|
+
console.log('=== Before ===');
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
console.log(chalk.bold.dim('Before:'));
|
|
113
|
+
}
|
|
114
|
+
displayWithHighlight(diff.before, 'remove', plain);
|
|
115
|
+
console.log();
|
|
116
|
+
if (plain) {
|
|
117
|
+
console.log('=== After ===');
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
console.log(chalk.bold.dim('After:'));
|
|
121
|
+
}
|
|
122
|
+
displayWithHighlight(diff.after, 'add', plain);
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Display content with syntax highlighting
|
|
126
|
+
*/
|
|
127
|
+
function displayWithHighlight(content, _type, plain) {
|
|
128
|
+
const lines = content.split('\n');
|
|
129
|
+
for (const line of lines) {
|
|
130
|
+
if (plain) {
|
|
131
|
+
console.log(` ${line}`);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
// Simple syntax highlighting
|
|
135
|
+
let highlighted = line;
|
|
136
|
+
// Highlight keys
|
|
137
|
+
highlighted = highlighted.replace(/^(\s*)("?[a-zA-Z_][a-zA-Z0-9_-]*"?)\s*:/, `$1${chalk.cyan('$2')}:`);
|
|
138
|
+
// Highlight string values
|
|
139
|
+
highlighted = highlighted.replace(/:\s*("(?:[^"\\]|\\.)*")/g, `: ${chalk.green('$1')}`);
|
|
140
|
+
// Highlight numbers
|
|
141
|
+
highlighted = highlighted.replace(/:\s*(-?\d+\.?\d*)/g, `: ${chalk.yellow('$1')}`);
|
|
142
|
+
// Highlight booleans
|
|
143
|
+
highlighted = highlighted.replace(/:\s*(true|false)/g, `: ${chalk.magenta('$1')}`);
|
|
144
|
+
// Highlight null
|
|
145
|
+
highlighted = highlighted.replace(/:\s*(null)/g, `: ${chalk.dim('$1')}`);
|
|
146
|
+
console.log(` ${highlighted}`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// ============================================================================
|
|
151
|
+
// Summary Display
|
|
152
|
+
// ============================================================================
|
|
153
|
+
/**
|
|
154
|
+
* Display a summary of operations to be applied
|
|
155
|
+
*/
|
|
156
|
+
export function displayOperationsSummary(operations) {
|
|
157
|
+
const plain = isPlainMode();
|
|
158
|
+
const adds = operations.filter(o => o.type === 'set' && o.previousValue === undefined).length;
|
|
159
|
+
const modifies = operations.filter(o => o.type === 'set' && o.previousValue !== undefined).length;
|
|
160
|
+
const removes = operations.filter(o => o.type === 'unset').length;
|
|
161
|
+
const parts = [];
|
|
162
|
+
if (adds > 0) {
|
|
163
|
+
parts.push(plain ? `${adds} add` : chalk.green(`${adds} add`));
|
|
164
|
+
}
|
|
165
|
+
if (modifies > 0) {
|
|
166
|
+
parts.push(plain ? `${modifies} modify` : chalk.yellow(`${modifies} modify`));
|
|
167
|
+
}
|
|
168
|
+
if (removes > 0) {
|
|
169
|
+
parts.push(plain ? `${removes} remove` : chalk.red(`${removes} remove`));
|
|
170
|
+
}
|
|
171
|
+
if (parts.length > 0) {
|
|
172
|
+
console.log();
|
|
173
|
+
if (plain) {
|
|
174
|
+
console.log(`Summary: ${parts.join(', ')}`);
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
console.log(chalk.dim('Summary: ') + parts.join(chalk.dim(', ')));
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=diff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff.js","sourceRoot":"","sources":["../../../../src/commands/secret/patch/diff.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path parsing and set/unset operations for secret patching
|
|
3
|
+
*/
|
|
4
|
+
import { type ParsedPath, type PatchOperation, type AppliedOperation } from './types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Parse a dot-notation path into segments
|
|
7
|
+
* Supports:
|
|
8
|
+
* - key -> top-level key
|
|
9
|
+
* - a.b.c -> nested keys
|
|
10
|
+
* - arr[0] -> array index
|
|
11
|
+
* - arr[+] -> array append
|
|
12
|
+
* - a.b[0].c -> mixed paths
|
|
13
|
+
*
|
|
14
|
+
* @param path - The path string to parse
|
|
15
|
+
* @returns ParsedPath with segments
|
|
16
|
+
*/
|
|
17
|
+
export declare function parsePath(path: string): ParsedPath;
|
|
18
|
+
/**
|
|
19
|
+
* Infer the type of a string value and convert it
|
|
20
|
+
* - "true"/"false" -> boolean
|
|
21
|
+
* - numeric strings -> number
|
|
22
|
+
* - "null" -> null
|
|
23
|
+
* - otherwise -> string
|
|
24
|
+
*
|
|
25
|
+
* @param value - The string value to infer
|
|
26
|
+
* @returns The inferred value with correct type
|
|
27
|
+
*/
|
|
28
|
+
export declare function inferValueType(value: string): unknown;
|
|
29
|
+
/**
|
|
30
|
+
* Parse a value that might be JSON
|
|
31
|
+
* If it starts with { or [, try to parse as JSON
|
|
32
|
+
* Otherwise use type inference
|
|
33
|
+
*/
|
|
34
|
+
export declare function parseValue(value: string): unknown;
|
|
35
|
+
/**
|
|
36
|
+
* Get a value at a path from an object
|
|
37
|
+
*/
|
|
38
|
+
export declare function getAtPath(obj: unknown, parsedPath: ParsedPath): unknown;
|
|
39
|
+
/**
|
|
40
|
+
* Set a value at a path in an object (mutates the object)
|
|
41
|
+
*
|
|
42
|
+
* @param obj - The object to modify
|
|
43
|
+
* @param parsedPath - The parsed path
|
|
44
|
+
* @param value - The value to set
|
|
45
|
+
* @returns The previous value at the path (if any)
|
|
46
|
+
*/
|
|
47
|
+
export declare function setAtPath(obj: Record<string, unknown>, parsedPath: ParsedPath, value: unknown): unknown;
|
|
48
|
+
/**
|
|
49
|
+
* Remove a value at a path in an object (mutates the object)
|
|
50
|
+
*
|
|
51
|
+
* @param obj - The object to modify
|
|
52
|
+
* @param parsedPath - The parsed path
|
|
53
|
+
* @returns The removed value (if any)
|
|
54
|
+
*/
|
|
55
|
+
export declare function unsetAtPath(obj: Record<string, unknown>, parsedPath: ParsedPath): unknown;
|
|
56
|
+
/**
|
|
57
|
+
* Apply a list of patch operations to an object
|
|
58
|
+
*
|
|
59
|
+
* @param obj - The object to modify (will be deep cloned)
|
|
60
|
+
* @param operations - The operations to apply
|
|
61
|
+
* @returns Array of applied operations with details
|
|
62
|
+
*/
|
|
63
|
+
export declare function applyOperations(obj: Record<string, unknown>, operations: PatchOperation[]): AppliedOperation[];
|
|
64
|
+
/**
|
|
65
|
+
* Parse --set arguments into operations
|
|
66
|
+
* Format: path=value
|
|
67
|
+
*/
|
|
68
|
+
export declare function parseSetArgs(args: string[]): PatchOperation[];
|
|
69
|
+
/**
|
|
70
|
+
* Parse --unset arguments into operations
|
|
71
|
+
*/
|
|
72
|
+
export declare function parseUnsetArgs(args: string[]): PatchOperation[];
|
|
73
|
+
/**
|
|
74
|
+
* Deep clone an object
|
|
75
|
+
*/
|
|
76
|
+
export declare function deepClone<T>(obj: T): T;
|
|
77
|
+
//# sourceMappingURL=operations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"operations.d.ts","sourceRoot":"","sources":["../../../../src/commands/secret/patch/operations.ts"],"names":[],"mappings":"AAEA;;GAEG;AAEH,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,gBAAgB,EAEtB,MAAM,YAAY,CAAC;AAMpB;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CA0ElD;AAMD;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAkBrD;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAejD;AAMD;;GAEG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,GAAG,OAAO,CAyBvE;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CACvB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5B,UAAU,EAAE,UAAU,EACtB,KAAK,EAAE,OAAO,GACb,OAAO,CAyDT;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CACzB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5B,UAAU,EAAE,UAAU,GACrB,OAAO,CA+DT;AAMD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5B,UAAU,EAAE,cAAc,EAAE,GAC3B,gBAAgB,EAAE,CA0BpB;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,cAAc,EAAE,CAoB7D;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,cAAc,EAAE,CAK/D;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAEtC"}
|
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
// Path: src/commands/secret/patch/operations.ts
|
|
2
|
+
/**
|
|
3
|
+
* Path parsing and set/unset operations for secret patching
|
|
4
|
+
*/
|
|
5
|
+
import { PatchError, } from './types.js';
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// Path Parsing
|
|
8
|
+
// ============================================================================
|
|
9
|
+
/**
|
|
10
|
+
* Parse a dot-notation path into segments
|
|
11
|
+
* Supports:
|
|
12
|
+
* - key -> top-level key
|
|
13
|
+
* - a.b.c -> nested keys
|
|
14
|
+
* - arr[0] -> array index
|
|
15
|
+
* - arr[+] -> array append
|
|
16
|
+
* - a.b[0].c -> mixed paths
|
|
17
|
+
*
|
|
18
|
+
* @param path - The path string to parse
|
|
19
|
+
* @returns ParsedPath with segments
|
|
20
|
+
*/
|
|
21
|
+
export function parsePath(path) {
|
|
22
|
+
if (!path || path.trim() === '') {
|
|
23
|
+
throw new PatchError('Empty path', 'INVALID_PATH', { path });
|
|
24
|
+
}
|
|
25
|
+
const segments = [];
|
|
26
|
+
let current = '';
|
|
27
|
+
let i = 0;
|
|
28
|
+
while (i < path.length) {
|
|
29
|
+
const char = path[i];
|
|
30
|
+
if (char === '.') {
|
|
31
|
+
// End of a key segment
|
|
32
|
+
if (current) {
|
|
33
|
+
segments.push({ type: 'key', key: current });
|
|
34
|
+
current = '';
|
|
35
|
+
}
|
|
36
|
+
i++;
|
|
37
|
+
}
|
|
38
|
+
else if (char === '[') {
|
|
39
|
+
// Start of array index
|
|
40
|
+
if (current) {
|
|
41
|
+
segments.push({ type: 'key', key: current });
|
|
42
|
+
current = '';
|
|
43
|
+
}
|
|
44
|
+
// Find closing bracket
|
|
45
|
+
const closingBracket = path.indexOf(']', i);
|
|
46
|
+
if (closingBracket === -1) {
|
|
47
|
+
throw new PatchError(`Unclosed bracket in path: ${path}`, 'INVALID_PATH', { path, position: i });
|
|
48
|
+
}
|
|
49
|
+
const indexStr = path.slice(i + 1, closingBracket);
|
|
50
|
+
if (indexStr === '+') {
|
|
51
|
+
// Append operation
|
|
52
|
+
segments.push({ type: 'append' });
|
|
53
|
+
}
|
|
54
|
+
else if (/^\d+$/.test(indexStr)) {
|
|
55
|
+
// Numeric index
|
|
56
|
+
segments.push({ type: 'index', index: parseInt(indexStr, 10) });
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
throw new PatchError(`Invalid array index: ${indexStr}`, 'INVALID_PATH', { path, index: indexStr });
|
|
60
|
+
}
|
|
61
|
+
i = closingBracket + 1;
|
|
62
|
+
// Skip dot after bracket if present
|
|
63
|
+
if (i < path.length && path[i] === '.') {
|
|
64
|
+
i++;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
current += char;
|
|
69
|
+
i++;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// Add final segment
|
|
73
|
+
if (current) {
|
|
74
|
+
segments.push({ type: 'key', key: current });
|
|
75
|
+
}
|
|
76
|
+
if (segments.length === 0) {
|
|
77
|
+
throw new PatchError('Path has no valid segments', 'INVALID_PATH', { path });
|
|
78
|
+
}
|
|
79
|
+
return { segments, raw: path };
|
|
80
|
+
}
|
|
81
|
+
// ============================================================================
|
|
82
|
+
// Value Type Inference
|
|
83
|
+
// ============================================================================
|
|
84
|
+
/**
|
|
85
|
+
* Infer the type of a string value and convert it
|
|
86
|
+
* - "true"/"false" -> boolean
|
|
87
|
+
* - numeric strings -> number
|
|
88
|
+
* - "null" -> null
|
|
89
|
+
* - otherwise -> string
|
|
90
|
+
*
|
|
91
|
+
* @param value - The string value to infer
|
|
92
|
+
* @returns The inferred value with correct type
|
|
93
|
+
*/
|
|
94
|
+
export function inferValueType(value) {
|
|
95
|
+
// Boolean
|
|
96
|
+
if (value === 'true')
|
|
97
|
+
return true;
|
|
98
|
+
if (value === 'false')
|
|
99
|
+
return false;
|
|
100
|
+
// Null
|
|
101
|
+
if (value === 'null')
|
|
102
|
+
return null;
|
|
103
|
+
// Number (integer or float)
|
|
104
|
+
if (/^-?\d+$/.test(value)) {
|
|
105
|
+
return parseInt(value, 10);
|
|
106
|
+
}
|
|
107
|
+
if (/^-?\d+\.\d+$/.test(value)) {
|
|
108
|
+
return parseFloat(value);
|
|
109
|
+
}
|
|
110
|
+
// String (default)
|
|
111
|
+
return value;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Parse a value that might be JSON
|
|
115
|
+
* If it starts with { or [, try to parse as JSON
|
|
116
|
+
* Otherwise use type inference
|
|
117
|
+
*/
|
|
118
|
+
export function parseValue(value) {
|
|
119
|
+
const trimmed = value.trim();
|
|
120
|
+
// Try to parse as JSON for objects and arrays
|
|
121
|
+
if ((trimmed.startsWith('{') && trimmed.endsWith('}')) ||
|
|
122
|
+
(trimmed.startsWith('[') && trimmed.endsWith(']'))) {
|
|
123
|
+
try {
|
|
124
|
+
return JSON.parse(trimmed);
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
// Not valid JSON, return as string
|
|
128
|
+
return value;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return inferValueType(value);
|
|
132
|
+
}
|
|
133
|
+
// ============================================================================
|
|
134
|
+
// Set/Unset Operations
|
|
135
|
+
// ============================================================================
|
|
136
|
+
/**
|
|
137
|
+
* Get a value at a path from an object
|
|
138
|
+
*/
|
|
139
|
+
export function getAtPath(obj, parsedPath) {
|
|
140
|
+
let current = obj;
|
|
141
|
+
for (const segment of parsedPath.segments) {
|
|
142
|
+
if (current === null || current === undefined) {
|
|
143
|
+
return undefined;
|
|
144
|
+
}
|
|
145
|
+
if (segment.type === 'key') {
|
|
146
|
+
if (typeof current !== 'object' || Array.isArray(current)) {
|
|
147
|
+
return undefined;
|
|
148
|
+
}
|
|
149
|
+
current = current[segment.key];
|
|
150
|
+
}
|
|
151
|
+
else if (segment.type === 'index') {
|
|
152
|
+
if (!Array.isArray(current)) {
|
|
153
|
+
return undefined;
|
|
154
|
+
}
|
|
155
|
+
current = current[segment.index];
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
// segment.type === 'append': Can't get from append position
|
|
159
|
+
return undefined;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return current;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Set a value at a path in an object (mutates the object)
|
|
166
|
+
*
|
|
167
|
+
* @param obj - The object to modify
|
|
168
|
+
* @param parsedPath - The parsed path
|
|
169
|
+
* @param value - The value to set
|
|
170
|
+
* @returns The previous value at the path (if any)
|
|
171
|
+
*/
|
|
172
|
+
export function setAtPath(obj, parsedPath, value) {
|
|
173
|
+
const { segments } = parsedPath;
|
|
174
|
+
// Navigate to parent, creating intermediate objects/arrays as needed
|
|
175
|
+
let current = obj;
|
|
176
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
177
|
+
const segment = segments[i];
|
|
178
|
+
const nextSegment = segments[i + 1];
|
|
179
|
+
if (segment.type === 'key') {
|
|
180
|
+
const currentObj = current;
|
|
181
|
+
// Create intermediate container based on next segment type
|
|
182
|
+
currentObj[segment.key] ??= nextSegment.type === 'index' || nextSegment.type === 'append'
|
|
183
|
+
? []
|
|
184
|
+
: {};
|
|
185
|
+
current = currentObj[segment.key];
|
|
186
|
+
}
|
|
187
|
+
else if (segment.type === 'index') {
|
|
188
|
+
const currentArr = current;
|
|
189
|
+
currentArr[segment.index] ??= nextSegment.type === 'index' || nextSegment.type === 'append'
|
|
190
|
+
? []
|
|
191
|
+
: {};
|
|
192
|
+
current = currentArr[segment.index];
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
throw new PatchError('Append operator [+] can only be used at the end of a path', 'INVALID_PATH', { path: parsedPath.raw });
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
// Set the final value
|
|
199
|
+
const lastSegment = segments[segments.length - 1];
|
|
200
|
+
let previousValue;
|
|
201
|
+
if (lastSegment.type === 'key') {
|
|
202
|
+
const parent = current;
|
|
203
|
+
previousValue = parent[lastSegment.key];
|
|
204
|
+
parent[lastSegment.key] = value;
|
|
205
|
+
}
|
|
206
|
+
else if (lastSegment.type === 'index') {
|
|
207
|
+
const parent = current;
|
|
208
|
+
previousValue = parent[lastSegment.index];
|
|
209
|
+
parent[lastSegment.index] = value;
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
// lastSegment.type === 'append'
|
|
213
|
+
if (!Array.isArray(current)) {
|
|
214
|
+
throw new PatchError('Cannot append to non-array', 'TYPE_MISMATCH', { path: parsedPath.raw, actualType: typeof current });
|
|
215
|
+
}
|
|
216
|
+
current.push(value);
|
|
217
|
+
previousValue = undefined;
|
|
218
|
+
}
|
|
219
|
+
return previousValue;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Remove a value at a path in an object (mutates the object)
|
|
223
|
+
*
|
|
224
|
+
* @param obj - The object to modify
|
|
225
|
+
* @param parsedPath - The parsed path
|
|
226
|
+
* @returns The removed value (if any)
|
|
227
|
+
*/
|
|
228
|
+
export function unsetAtPath(obj, parsedPath) {
|
|
229
|
+
const { segments } = parsedPath;
|
|
230
|
+
// Navigate to parent
|
|
231
|
+
let current = obj;
|
|
232
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
233
|
+
const segment = segments[i];
|
|
234
|
+
if (segment.type === 'key') {
|
|
235
|
+
const currentObj = current;
|
|
236
|
+
if (currentObj[segment.key] === undefined) {
|
|
237
|
+
return undefined; // Path doesn't exist
|
|
238
|
+
}
|
|
239
|
+
current = currentObj[segment.key];
|
|
240
|
+
}
|
|
241
|
+
else if (segment.type === 'index') {
|
|
242
|
+
const currentArr = current;
|
|
243
|
+
if (currentArr[segment.index] === undefined) {
|
|
244
|
+
return undefined;
|
|
245
|
+
}
|
|
246
|
+
current = currentArr[segment.index];
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
throw new PatchError('Append operator [+] cannot be used with unset', 'UNSUPPORTED_OPERATION', { path: parsedPath.raw });
|
|
250
|
+
}
|
|
251
|
+
if (current === null || current === undefined) {
|
|
252
|
+
return undefined;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
// Remove the final value
|
|
256
|
+
const lastSegment = segments[segments.length - 1];
|
|
257
|
+
let removedValue;
|
|
258
|
+
if (lastSegment.type === 'key') {
|
|
259
|
+
if (typeof current !== 'object' || Array.isArray(current) || current === null) {
|
|
260
|
+
return undefined;
|
|
261
|
+
}
|
|
262
|
+
const parent = current;
|
|
263
|
+
const keyToDelete = lastSegment.key;
|
|
264
|
+
removedValue = parent[keyToDelete];
|
|
265
|
+
Reflect.deleteProperty(parent, keyToDelete);
|
|
266
|
+
}
|
|
267
|
+
else if (lastSegment.type === 'index') {
|
|
268
|
+
if (!Array.isArray(current)) {
|
|
269
|
+
return undefined;
|
|
270
|
+
}
|
|
271
|
+
const parent = current;
|
|
272
|
+
if (lastSegment.index >= 0 && lastSegment.index < parent.length) {
|
|
273
|
+
removedValue = parent[lastSegment.index];
|
|
274
|
+
parent.splice(lastSegment.index, 1);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
throw new PatchError('Append operator [+] cannot be used with unset', 'UNSUPPORTED_OPERATION', { path: parsedPath.raw });
|
|
279
|
+
}
|
|
280
|
+
return removedValue;
|
|
281
|
+
}
|
|
282
|
+
// ============================================================================
|
|
283
|
+
// Apply Operations
|
|
284
|
+
// ============================================================================
|
|
285
|
+
/**
|
|
286
|
+
* Apply a list of patch operations to an object
|
|
287
|
+
*
|
|
288
|
+
* @param obj - The object to modify (will be deep cloned)
|
|
289
|
+
* @param operations - The operations to apply
|
|
290
|
+
* @returns Array of applied operations with details
|
|
291
|
+
*/
|
|
292
|
+
export function applyOperations(obj, operations) {
|
|
293
|
+
const applied = [];
|
|
294
|
+
for (const op of operations) {
|
|
295
|
+
const parsedPath = parsePath(op.path);
|
|
296
|
+
if (op.type === 'set') {
|
|
297
|
+
const previousValue = setAtPath(obj, parsedPath, op.value);
|
|
298
|
+
applied.push({
|
|
299
|
+
type: 'set',
|
|
300
|
+
path: op.path,
|
|
301
|
+
previousValue,
|
|
302
|
+
newValue: op.value,
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
// op.type === 'unset'
|
|
307
|
+
const removedValue = unsetAtPath(obj, parsedPath);
|
|
308
|
+
applied.push({
|
|
309
|
+
type: 'unset',
|
|
310
|
+
path: op.path,
|
|
311
|
+
previousValue: removedValue,
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return applied;
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Parse --set arguments into operations
|
|
319
|
+
* Format: path=value
|
|
320
|
+
*/
|
|
321
|
+
export function parseSetArgs(args) {
|
|
322
|
+
return args.map(arg => {
|
|
323
|
+
const eqIndex = arg.indexOf('=');
|
|
324
|
+
if (eqIndex === -1) {
|
|
325
|
+
throw new PatchError(`Invalid --set argument: ${arg} (expected path=value)`, 'INVALID_VALUE', { arg });
|
|
326
|
+
}
|
|
327
|
+
const path = arg.slice(0, eqIndex);
|
|
328
|
+
const valueStr = arg.slice(eqIndex + 1);
|
|
329
|
+
return {
|
|
330
|
+
type: 'set',
|
|
331
|
+
path,
|
|
332
|
+
value: parseValue(valueStr),
|
|
333
|
+
};
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Parse --unset arguments into operations
|
|
338
|
+
*/
|
|
339
|
+
export function parseUnsetArgs(args) {
|
|
340
|
+
return args.map(path => ({
|
|
341
|
+
type: 'unset',
|
|
342
|
+
path,
|
|
343
|
+
}));
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Deep clone an object
|
|
347
|
+
*/
|
|
348
|
+
export function deepClone(obj) {
|
|
349
|
+
return JSON.parse(JSON.stringify(obj));
|
|
350
|
+
}
|
|
351
|
+
//# sourceMappingURL=operations.js.map
|