@sudocode-ai/cli 0.1.16 → 0.1.18-dev.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.
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Git Merge-File Wrapper
3
+ *
4
+ * Wrapper around `git merge-file` command for three-way merging of YAML content.
5
+ * Enables git's native three-way merge algorithm to work on YAML representations.
6
+ *
7
+ * Security: Uses execFile (not exec) to prevent shell injection vulnerabilities.
8
+ */
9
+ /**
10
+ * Result of a merge operation
11
+ */
12
+ export interface MergeResult {
13
+ /** Whether the merge completed without conflicts */
14
+ success: boolean;
15
+ /** The merged content (may include conflict markers if success=false) */
16
+ content: string;
17
+ /** Whether conflicts were detected */
18
+ hasConflicts: boolean;
19
+ }
20
+ /**
21
+ * Three-way merge input versions
22
+ */
23
+ export interface MergeInput {
24
+ /** Base version (common ancestor) */
25
+ base: string;
26
+ /** Our version (current/local changes) */
27
+ ours: string;
28
+ /** Their version (incoming changes) */
29
+ theirs: string;
30
+ }
31
+ /**
32
+ * Perform three-way merge using git merge-file
33
+ *
34
+ * This function wraps the `git merge-file` command to provide three-way merging
35
+ * of YAML content. It creates temporary files for the merge operation and cleans
36
+ * them up afterwards.
37
+ *
38
+ * Security: Uses execFileSync with array arguments to prevent shell injection.
39
+ *
40
+ * Git merge-file exit codes:
41
+ * - 0: Clean merge, no conflicts
42
+ * - 1: Conflicts detected (most common, merge produces output with conflict markers)
43
+ * - 2+: Can indicate conflicts OR fatal errors
44
+ *
45
+ * Strategy: If git produced output (file exists and has content), treat as conflict
46
+ * scenario regardless of exit code. Only throw if no output was produced.
47
+ *
48
+ * @param input - The three versions to merge (base, ours, theirs)
49
+ * @returns MergeResult - The merge result with success status and content
50
+ * @throws Error if git command fails fatally (no output produced)
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * const result = mergeYamlContent({
55
+ * base: 'title: Original\nstatus: open',
56
+ * ours: 'title: Updated\nstatus: open',
57
+ * theirs: 'title: Original\nstatus: closed'
58
+ * });
59
+ *
60
+ * if (result.success) {
61
+ * console.log('Clean merge:', result.content);
62
+ * } else {
63
+ * console.log('Conflicts detected:', result.content);
64
+ * }
65
+ * ```
66
+ */
67
+ export declare function mergeYamlContent(input: MergeInput): MergeResult;
68
+ /**
69
+ * Read content from a specific git index stage
70
+ *
71
+ * During a merge conflict, git maintains three versions in the index:
72
+ * - Stage 1: Base version (common ancestor)
73
+ * - Stage 2: Ours version (current branch)
74
+ * - Stage 3: Theirs version (incoming branch)
75
+ *
76
+ * @param filePath - Path to the file (relative to git root or absolute)
77
+ * @param stage - Git stage number (1=base, 2=ours, 3=theirs)
78
+ * @returns File content as string, or null if stage doesn't exist
79
+ *
80
+ * @example
81
+ * ```typescript
82
+ * const base = readGitStage('.sudocode/issues/issues.jsonl', 1);
83
+ * const ours = readGitStage('.sudocode/issues/issues.jsonl', 2);
84
+ * const theirs = readGitStage('.sudocode/issues/issues.jsonl', 3);
85
+ * ```
86
+ */
87
+ export declare function readGitStage(filePath: string, stage: 1 | 2 | 3): string | null;
88
+ //# sourceMappingURL=git-merge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git-merge.d.ts","sourceRoot":"","sources":["../src/git-merge.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,oDAAoD;IACpD,OAAO,EAAE,OAAO,CAAC;IACjB,yEAAyE;IACzE,OAAO,EAAE,MAAM,CAAC;IAChB,sCAAsC;IACtC,YAAY,EAAE,OAAO,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,qCAAqC;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,uCAAuC;IACvC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,UAAU,GAAG,WAAW,CAkG/D;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM,GAAG,IAAI,CAiE9E"}
@@ -0,0 +1,214 @@
1
+ /**
2
+ * Git Merge-File Wrapper
3
+ *
4
+ * Wrapper around `git merge-file` command for three-way merging of YAML content.
5
+ * Enables git's native three-way merge algorithm to work on YAML representations.
6
+ *
7
+ * Security: Uses execFile (not exec) to prevent shell injection vulnerabilities.
8
+ */
9
+ import { execFileSync } from 'child_process';
10
+ import * as fs from 'fs';
11
+ import * as path from 'path';
12
+ import * as os from 'os';
13
+ /**
14
+ * Perform three-way merge using git merge-file
15
+ *
16
+ * This function wraps the `git merge-file` command to provide three-way merging
17
+ * of YAML content. It creates temporary files for the merge operation and cleans
18
+ * them up afterwards.
19
+ *
20
+ * Security: Uses execFileSync with array arguments to prevent shell injection.
21
+ *
22
+ * Git merge-file exit codes:
23
+ * - 0: Clean merge, no conflicts
24
+ * - 1: Conflicts detected (most common, merge produces output with conflict markers)
25
+ * - 2+: Can indicate conflicts OR fatal errors
26
+ *
27
+ * Strategy: If git produced output (file exists and has content), treat as conflict
28
+ * scenario regardless of exit code. Only throw if no output was produced.
29
+ *
30
+ * @param input - The three versions to merge (base, ours, theirs)
31
+ * @returns MergeResult - The merge result with success status and content
32
+ * @throws Error if git command fails fatally (no output produced)
33
+ *
34
+ * @example
35
+ * ```typescript
36
+ * const result = mergeYamlContent({
37
+ * base: 'title: Original\nstatus: open',
38
+ * ours: 'title: Updated\nstatus: open',
39
+ * theirs: 'title: Original\nstatus: closed'
40
+ * });
41
+ *
42
+ * if (result.success) {
43
+ * console.log('Clean merge:', result.content);
44
+ * } else {
45
+ * console.log('Conflicts detected:', result.content);
46
+ * }
47
+ * ```
48
+ */
49
+ export function mergeYamlContent(input) {
50
+ // Create temporary directory for merge files
51
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'git-merge-'));
52
+ // Temp file paths
53
+ const basePath = path.join(tmpDir, 'base.yaml');
54
+ const oursPath = path.join(tmpDir, 'ours.yaml');
55
+ const theirsPath = path.join(tmpDir, 'theirs.yaml');
56
+ // Track whether base is empty (simulated 3-way merge)
57
+ const baseIsEmpty = input.base.length === 0;
58
+ try {
59
+ // Write content to temp files
60
+ fs.writeFileSync(basePath, input.base, 'utf8');
61
+ fs.writeFileSync(oursPath, input.ours, 'utf8');
62
+ fs.writeFileSync(theirsPath, input.theirs, 'utf8');
63
+ // Execute git merge-file using execFileSync (safe from shell injection)
64
+ try {
65
+ execFileSync('git', ['merge-file', oursPath, basePath, theirsPath], {
66
+ encoding: 'utf8',
67
+ });
68
+ // Exit code 0 means clean merge
69
+ const mergedContent = fs.readFileSync(oursPath, 'utf8');
70
+ return {
71
+ success: true,
72
+ content: mergedContent,
73
+ hasConflicts: false,
74
+ };
75
+ }
76
+ catch (error) {
77
+ // Git merge-file exit codes:
78
+ // - 0: Clean merge (handled above, no error thrown)
79
+ // - 1: Conflicts detected (most common)
80
+ // - 2+: Can indicate conflicts OR fatal errors
81
+ //
82
+ // Strategy: If git produced output (file exists and has content),
83
+ // treat as conflict scenario regardless of exit code.
84
+ // Only throw if no output was produced (indicates real error).
85
+ const exitCode = error.status || error.code || 'unknown';
86
+ // Debug: log what we got
87
+ if (process.env.DEBUG_GIT_MERGE) {
88
+ console.log('Git merge-file error:', {
89
+ status: error.status,
90
+ code: error.code,
91
+ baseIsEmpty,
92
+ stderr: error.stderr?.toString(),
93
+ stdout: error.stdout?.toString(),
94
+ });
95
+ }
96
+ // Try to read the output file - if it has content, this is a conflict scenario
97
+ if (error.status > 0) {
98
+ try {
99
+ const mergedContent = fs.readFileSync(oursPath, 'utf8');
100
+ // Validate that git produced output (not a real error)
101
+ if (mergedContent.length > 0) {
102
+ return {
103
+ success: false,
104
+ content: mergedContent,
105
+ hasConflicts: true,
106
+ };
107
+ }
108
+ // If no output, this is likely a real error - fall through to throw
109
+ }
110
+ catch (readError) {
111
+ // File read failed - fall through to throw with details
112
+ const stderr = error.stderr?.toString().trim();
113
+ const stdout = error.stdout?.toString().trim();
114
+ const errorDetails = stderr || stdout || error.message || 'Unknown error';
115
+ throw new Error(`Git merge-file command failed (exit code ${exitCode}, baseIsEmpty=${baseIsEmpty}, fileExists=${fs.existsSync(oursPath)}, readError=${readError}): ${errorDetails}`);
116
+ }
117
+ }
118
+ // No exit code or negative exit code means a fatal error occurred
119
+ // Include stderr output which contains the actual git error message
120
+ const stderr = error.stderr?.toString().trim();
121
+ const stdout = error.stdout?.toString().trim();
122
+ const errorDetails = stderr || stdout || error.message || 'Unknown error';
123
+ throw new Error(`Git merge-file command failed (status=${error.status}, code=${error.code}, exitCode=${exitCode}, baseIsEmpty=${baseIsEmpty}, baseLength=${input.base.length}, oursLength=${input.ours.length}, theirsLength=${input.theirs.length}): ${errorDetails}`);
124
+ }
125
+ }
126
+ finally {
127
+ // Clean up temp files and directory
128
+ try {
129
+ fs.rmSync(tmpDir, { recursive: true, force: true });
130
+ }
131
+ catch (cleanupError) {
132
+ // Log cleanup errors but don't throw
133
+ console.error('Warning: Failed to clean up temp files:', cleanupError);
134
+ }
135
+ }
136
+ }
137
+ /**
138
+ * Read content from a specific git index stage
139
+ *
140
+ * During a merge conflict, git maintains three versions in the index:
141
+ * - Stage 1: Base version (common ancestor)
142
+ * - Stage 2: Ours version (current branch)
143
+ * - Stage 3: Theirs version (incoming branch)
144
+ *
145
+ * @param filePath - Path to the file (relative to git root or absolute)
146
+ * @param stage - Git stage number (1=base, 2=ours, 3=theirs)
147
+ * @returns File content as string, or null if stage doesn't exist
148
+ *
149
+ * @example
150
+ * ```typescript
151
+ * const base = readGitStage('.sudocode/issues/issues.jsonl', 1);
152
+ * const ours = readGitStage('.sudocode/issues/issues.jsonl', 2);
153
+ * const theirs = readGitStage('.sudocode/issues/issues.jsonl', 3);
154
+ * ```
155
+ */
156
+ export function readGitStage(filePath, stage) {
157
+ try {
158
+ // Convert absolute path to relative path from repo root
159
+ let relativePath = filePath;
160
+ if (path.isAbsolute(filePath)) {
161
+ const repoRoot = execFileSync('git', ['rev-parse', '--show-toplevel'], {
162
+ encoding: 'utf8',
163
+ stdio: ['pipe', 'pipe', 'pipe'],
164
+ }).trim();
165
+ relativePath = path.relative(repoRoot, filePath);
166
+ }
167
+ // Use git checkout-index to extract stage to temp file
168
+ // This avoids buffer size limits by having git write directly to disk
169
+ // Format: "tempfile\tpath" (tempfile is relative to repo root)
170
+ const output = execFileSync('git', ['checkout-index', `--stage=${stage}`, '--temp', relativePath], {
171
+ encoding: 'utf8',
172
+ cwd: path.isAbsolute(filePath)
173
+ ? execFileSync('git', ['rev-parse', '--show-toplevel'], { encoding: 'utf8' }).trim()
174
+ : undefined,
175
+ }).trim();
176
+ // Parse output: "tempfile\toriginalpath"
177
+ const [tempFile] = output.split('\t');
178
+ if (!tempFile) {
179
+ return null;
180
+ }
181
+ // Get repo root to resolve temp file path (git outputs relative to repo root)
182
+ const repoRoot = execFileSync('git', ['rev-parse', '--show-toplevel'], {
183
+ encoding: 'utf8',
184
+ }).trim();
185
+ const tempFilePath = path.join(repoRoot, tempFile);
186
+ try {
187
+ // Read content from git's temp file
188
+ const content = fs.readFileSync(tempFilePath, 'utf8');
189
+ return content;
190
+ }
191
+ finally {
192
+ // Always clean up the temp file git created
193
+ try {
194
+ fs.unlinkSync(tempFilePath);
195
+ }
196
+ catch (unlinkError) {
197
+ // Best effort cleanup - don't fail if file already gone
198
+ if (process.env.DEBUG_GIT_MERGE) {
199
+ console.warn('Warning: Failed to clean up temp file:', unlinkError);
200
+ }
201
+ }
202
+ }
203
+ }
204
+ catch (error) {
205
+ // Stage doesn't exist (e.g., file added in one branch, deleted in another)
206
+ // This is expected and not an error
207
+ if (error.status === 128 || error.status === 1) {
208
+ return null;
209
+ }
210
+ // Re-throw unexpected errors
211
+ throw new Error(`Failed to read git stage ${stage} for ${filePath}: ${error.message}`);
212
+ }
213
+ }
214
+ //# sourceMappingURL=git-merge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git-merge.js","sourceRoot":"","sources":["../src/git-merge.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -12,4 +12,6 @@ export * from "./markdown.js";
12
12
  export * from "./sync.js";
13
13
  export * from "./id-generator.js";
14
14
  export * from "./config.js";
15
+ export * from "./yaml-converter.js";
16
+ export * from "./yaml-conflict-resolver.js";
15
17
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,oBAAoB,CAAC;AACnC,cAAc,SAAS,CAAC;AACxB,cAAc,uBAAuB,CAAC;AACtC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,oBAAoB,CAAC;AACnC,cAAc,SAAS,CAAC;AACxB,cAAc,uBAAuB,CAAC;AACtC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC;AAC5B,cAAc,qBAAqB,CAAC;AACpC,cAAc,6BAA6B,CAAC"}
package/dist/index.js CHANGED
@@ -12,4 +12,6 @@ export * from "./markdown.js";
12
12
  export * from "./sync.js";
13
13
  export * from "./id-generator.js";
14
14
  export * from "./config.js";
15
+ export * from "./yaml-converter.js";
16
+ export * from "./yaml-conflict-resolver.js";
15
17
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,oBAAoB,CAAC;AACnC,cAAc,SAAS,CAAC;AACxB,cAAc,uBAAuB,CAAC;AACtC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,oBAAoB,CAAC;AACnC,cAAc,SAAS,CAAC;AACxB,cAAc,uBAAuB,CAAC;AACtC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC;AAC5B,cAAc,qBAAqB,CAAC;AACpC,cAAc,6BAA6B,CAAC"}
@@ -70,16 +70,88 @@ export declare function hasGitConflictMarkers(filePath: string): boolean;
70
70
  export declare function parseMergeConflictFile(content: string): ConflictSection[];
71
71
  /**
72
72
  * Merge metadata from multiple versions of same entity
73
+ *
74
+ * USE FOR: Two-way merge scenarios (manual conflict resolution)
75
+ * Merges both array fields (union) and scalar fields (latest-wins)
73
76
  */
74
77
  export declare function mergeMetadata<T extends JSONLEntity>(entities: T[]): T;
78
+ /**
79
+ * Merge ONLY array fields from multiple versions (union semantics)
80
+ *
81
+ * USE FOR: Three-way merge scenarios (git merge driver)
82
+ * Only merges array fields (relationships, tags, feedback)
83
+ * Scalar fields (status, priority, title, etc.) are NOT merged
84
+ *
85
+ * This allows git merge-file to see actual differences in scalar fields
86
+ * for proper three-way merge semantics.
87
+ */
88
+ export declare function mergeArrayFields<T extends JSONLEntity>(entities: T[]): Partial<T>;
89
+ /**
90
+ * Resolve git conflict markers in YAML content using latest-wins per block
91
+ *
92
+ * When git merge-file produces conflicts in YAML, this function resolves
93
+ * each conflict block individually while preserving cleanly merged sections.
94
+ *
95
+ * @param yamlWithConflicts - YAML content containing git conflict markers
96
+ * @param useOurs - If true, use "ours" side for conflicts; if false, use "theirs"
97
+ * @returns Resolved YAML content without conflict markers
98
+ *
99
+ * @example
100
+ * ```typescript
101
+ * const yaml = `title: test
102
+ * <<<<<<< ours
103
+ * content: ours text
104
+ * =======
105
+ * content: theirs text
106
+ * >>>>>>> theirs
107
+ * priority: 3`;
108
+ *
109
+ * const resolved = resolveYamlConflicts(yaml, true);
110
+ * // Returns: "title: test\ncontent: ours text\npriority: 3"
111
+ * ```
112
+ */
113
+ export declare function resolveYamlConflicts(yamlWithConflicts: string, useOurs: boolean): string;
75
114
  /**
76
115
  * Resolve all entities using UUID-based deduplication
77
116
  * Handles different UUIDs, same UUID conflicts, and metadata merging
117
+ *
118
+ * USE CASE: TWO-WAY MERGE
119
+ * - Manual conflict resolution (sudocode resolve-conflicts)
120
+ * - Conflicts already isolated by git conflict markers
121
+ * - No base version available (git index cleared after conflict)
122
+ * - Simple UUID deduplication is sufficient and faster
123
+ * - No benefit from YAML expansion overhead
124
+ *
125
+ * DO NOT USE FOR: Three-way merge with base/ours/theirs
126
+ * For that case, use mergeThreeWay() instead.
78
127
  */
79
128
  export declare function resolveEntities<T extends JSONLEntity>(entities: T[], options?: ResolveOptions): ResolvedResult<T>;
80
129
  /**
81
130
  * Three-way merge for git merge driver
82
- * Merges base, ours, and theirs versions
131
+ * Uses hybrid approach: field-level merge for scalars + line-level merge for multi-line text
132
+ *
133
+ * USE CASE: THREE-WAY MERGE
134
+ * - Git merge driver operations (automatic merge)
135
+ * - Worktree sync with true base/ours/theirs versions
136
+ * - Field-level merging preserves changes from either branch
137
+ *
138
+ * DO NOT USE FOR: Manual conflict resolution (use resolveEntities)
139
+ *
140
+ * This function implements true three-way merge semantics:
141
+ * 1. Group entities by UUID across base/ours/theirs
142
+ * 2. Handle deletion cases (modification wins over deletion)
143
+ * 3. Merge array fields FIRST (tags, relationships, feedback) with union semantics
144
+ * 4. Field-level three-way merge for SCALAR fields only (status, priority, etc.)
145
+ * 5. YAML + git merge-file for multi-line text fields (content, description)
146
+ * 6. Handle ID collisions (hash conflicts with .1, .2 suffixes)
147
+ * 7. Sort by created_at (git-friendly)
148
+ *
149
+ * Field-level three-way merge logic:
150
+ * - If base == ours == theirs: use any value (no changes)
151
+ * - If base == ours && base != theirs: use theirs (only theirs changed)
152
+ * - If base == theirs && base != ours: use ours (only ours changed)
153
+ * - If base != ours && base != theirs && ours == theirs: use either (both made same change)
154
+ * - If base != ours && base != theirs && ours != theirs: conflict -> latest wins
83
155
  */
84
156
  export declare function mergeThreeWay<T extends JSONLEntity>(base: T[], ours: T[], theirs: T[]): ResolvedResult<T>;
85
157
  //# sourceMappingURL=merge-resolver.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"merge-resolver.d.ts","sourceRoot":"","sources":["../src/merge-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAExD,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAEvE;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,OAAO,GAAG,UAAU,CAAC;IAC3B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,CAAC,EAAE,cAAc,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc,CAAC,CAAC;IAC/B,QAAQ,EAAE,CAAC,EAAE,CAAC;IACd,KAAK,EAAE,eAAe,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,kBAAkB,EAAE,CAAC;CACjC;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,iBAAiB,GAAG,wBAAwB,GAAG,mBAAmB,CAAC;IACzE,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAW/D;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,EAAE,CA0FzE;AAmCD;;GAEG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,WAAW,EAAE,QAAQ,EAAE,CAAC,EAAE,GAAG,CAAC,CAsDrE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,WAAW,EACnD,QAAQ,EAAE,CAAC,EAAE,EACb,OAAO,GAAE,cAAmB,GAC3B,cAAc,CAAC,CAAC,CAAC,CAuHnB;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,WAAW,EACjD,IAAI,EAAE,CAAC,EAAE,EACT,IAAI,EAAE,CAAC,EAAE,EACT,MAAM,EAAE,CAAC,EAAE,GACV,cAAc,CAAC,CAAC,CAAC,CAMnB"}
1
+ {"version":3,"file":"merge-resolver.d.ts","sourceRoot":"","sources":["../src/merge-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAIxD,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAEvE;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,OAAO,GAAG,UAAU,CAAC;IAC3B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,CAAC,EAAE,cAAc,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc,CAAC,CAAC;IAC/B,QAAQ,EAAE,CAAC,EAAE,CAAC;IACd,KAAK,EAAE,eAAe,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,kBAAkB,EAAE,CAAC;CACjC;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,iBAAiB,GAAG,wBAAwB,GAAG,mBAAmB,CAAC;IACzE,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAW/D;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,EAAE,CA0FzE;AAmCD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,WAAW,EAAE,QAAQ,EAAE,CAAC,EAAE,GAAG,CAAC,CAsDrE;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,WAAW,EAAE,QAAQ,EAAE,CAAC,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,CAkDjF;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,oBAAoB,CAAC,iBAAiB,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM,CAgBxF;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,WAAW,EACnD,QAAQ,EAAE,CAAC,EAAE,EACb,OAAO,GAAE,cAAmB,GAC3B,cAAc,CAAC,CAAC,CAAC,CAuHnB;AAsDD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,WAAW,EACjD,IAAI,EAAE,CAAC,EAAE,EACT,IAAI,EAAE,CAAC,EAAE,EACT,MAAM,EAAE,CAAC,EAAE,GACV,cAAc,CAAC,CAAC,CAAC,CAmSnB"}