difit 3.1.17 → 4.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/README.ja.md +30 -3
- package/README.ko.md +30 -3
- package/README.md +30 -3
- package/README.zh.md +30 -3
- package/dist/cli/github.d.ts +65 -0
- package/dist/cli/github.js +296 -0
- package/dist/cli/github.test.d.ts +1 -0
- package/dist/cli/github.test.js +341 -0
- package/dist/cli/index.js +26 -1
- package/dist/cli/index.test.js +206 -4
- package/dist/cli/utils.d.ts +2 -8
- package/dist/cli/utils.js +43 -56
- package/dist/cli/utils.test.js +61 -67
- package/dist/client/assets/{_basePickBy-r8KiD0PT.js → _basePickBy-B9N-f0iT.js} +1 -1
- package/dist/client/assets/{_baseUniq-WYpg9s_f.js → _baseUniq-tbL7nVvN.js} +1 -1
- package/dist/client/assets/{arc-BZWd656X.js → arc-BOY-7mep.js} +1 -1
- package/dist/client/assets/{architectureDiagram-2XIMDMQ5-BiaoV1Oc.js → architectureDiagram-2XIMDMQ5-59AvHaSB.js} +1 -1
- package/dist/client/assets/{blockDiagram-WCTKOSBZ-T1RU4TI6.js → blockDiagram-WCTKOSBZ-DXIlumQk.js} +1 -1
- package/dist/client/assets/{c4Diagram-IC4MRINW-C1aQSMsj.js → c4Diagram-IC4MRINW-BbfZ0uRn.js} +1 -1
- package/dist/client/assets/channel-cZXsTJxA.js +1 -0
- package/dist/client/assets/{chunk-4BX2VUAB-DFcwtPlK.js → chunk-4BX2VUAB-l7rcB2IW.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-Bl3vvNDx.js → chunk-55IACEB6-CrZL3qv9.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-B_2obFwM.js → chunk-FMBD7UC4-CrKv7ndg.js} +1 -1
- package/dist/client/assets/{chunk-JSJVCQXG-BrSq4jyX.js → chunk-JSJVCQXG-DyBDhAEM.js} +1 -1
- package/dist/client/assets/{chunk-KX2RTZJC-18m3UONJ.js → chunk-KX2RTZJC-By5mkZmU.js} +1 -1
- package/dist/client/assets/{chunk-NQ4KR5QH-hFDbMzZU.js → chunk-NQ4KR5QH-C30p9xRx.js} +1 -1
- package/dist/client/assets/{chunk-QZHKN3VN-CyCFXX2j.js → chunk-QZHKN3VN-DVlhR2wU.js} +1 -1
- package/dist/client/assets/{chunk-WL4C6EOR-BDdHa7t1.js → chunk-WL4C6EOR-Cn7a6CO3.js} +1 -1
- package/dist/client/assets/classDiagram-VBA2DB6C-B_coIPEy.js +1 -0
- package/dist/client/assets/classDiagram-v2-RAHNMMFH-B_coIPEy.js +1 -0
- package/dist/client/assets/clone-BjaT2HOk.js +1 -0
- package/dist/client/assets/{cose-bilkent-S5V4N54A-D7t718Sq.js → cose-bilkent-S5V4N54A-LyauIk_9.js} +1 -1
- package/dist/client/assets/{dagre-KLK3FWXG-DJXcjsV8.js → dagre-KLK3FWXG-DRWb2KE3.js} +1 -1
- package/dist/client/assets/{diagram-E7M64L7V-DL8ck_Al.js → diagram-E7M64L7V-ChT6mNWK.js} +1 -1
- package/dist/client/assets/{diagram-IFDJBPK2-NTxUWyD3.js → diagram-IFDJBPK2-CqbTduoP.js} +1 -1
- package/dist/client/assets/{diagram-P4PSJMXO-CGkcnGxk.js → diagram-P4PSJMXO-Bzv5Z3ri.js} +1 -1
- package/dist/client/assets/{erDiagram-INFDFZHY-BqpbHQrZ.js → erDiagram-INFDFZHY-CvXfUZ4L.js} +1 -1
- package/dist/client/assets/{flowDiagram-PKNHOUZH-B-DK3_9I.js → flowDiagram-PKNHOUZH-CxmpNUKq.js} +1 -1
- package/dist/client/assets/{ganttDiagram-A5KZAMGK-BK1C57ll.js → ganttDiagram-A5KZAMGK-9LpZCsg6.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-K3NZZRJ6-Duxlcz8R.js → gitGraphDiagram-K3NZZRJ6-C6yZOrQJ.js} +1 -1
- package/dist/client/assets/{graph-C7r58m4O.js → graph-bUZ7uHLW.js} +1 -1
- package/dist/client/assets/index-BLNN1bfE.js +98 -0
- package/dist/client/assets/index-VxkpzDXr.css +1 -0
- package/dist/client/assets/{infoDiagram-LFFYTUFH-Bqt-4V9X.js → infoDiagram-LFFYTUFH-Djdy3W21.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-PHBUUO56-B1ZVSkls.js → ishikawaDiagram-PHBUUO56-oOdwCpeS.js} +1 -1
- package/dist/client/assets/{journeyDiagram-4ABVD52K-LSEcxqrO.js → journeyDiagram-4ABVD52K-DTb_nGAw.js} +1 -1
- package/dist/client/assets/{kanban-definition-K7BYSVSG-CldPadPs.js → kanban-definition-K7BYSVSG-CMtP7pHA.js} +1 -1
- package/dist/client/assets/{layout-NpxIVVkp.js → layout-CXr5MatK.js} +1 -1
- package/dist/client/assets/{linear-JpKpxaS-.js → linear-pOMS9pjV.js} +1 -1
- package/dist/client/assets/{mermaid.core-gANNEmg0.js → mermaid.core-DV5JJ1Ie.js} +4 -4
- package/dist/client/assets/{mindmap-definition-YRQLILUH-ewFI1yc5.js → mindmap-definition-YRQLILUH-DN-sbonc.js} +1 -1
- package/dist/client/assets/{pieDiagram-SKSYHLDU-CWlAr2t8.js → pieDiagram-SKSYHLDU-tAHCkgh1.js} +1 -1
- package/dist/client/assets/{prism-csharp-CxRfePTX.js → prism-csharp-5CQ0RcEE.js} +1 -1
- package/dist/client/assets/{prism-elixir-B0H1PC_E.js → prism-elixir-BSOTyVg2.js} +1 -1
- package/dist/client/assets/{prism-hcl-Csmcce-t.js → prism-hcl-BYvi1mtM.js} +1 -1
- package/dist/client/assets/{prism-java-BRzwomgj.js → prism-java-DMU2FM4X.js} +1 -1
- package/dist/client/assets/{prism-perl-DQMRA6u_.js → prism-perl-CpfvaEQk.js} +1 -1
- package/dist/client/assets/{prism-php-C6fR1C7-.js → prism-php-SC920LoD.js} +1 -1
- package/dist/client/assets/{prism-ruby-CWeh27h1.js → prism-ruby-DZph-YiO.js} +1 -1
- package/dist/client/assets/{prism-solidity-3wCU4ra_.js → prism-solidity-qTLbmiAT.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-337W2JSQ-D76E3PCD.js → quadrantDiagram-337W2JSQ-B0wODmgR.js} +1 -1
- package/dist/client/assets/{requirementDiagram-Z7DCOOCP-C49LvKzR.js → requirementDiagram-Z7DCOOCP-A3aeHC06.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-WA2Y5GQK-DOvEhLMf.js → sankeyDiagram-WA2Y5GQK-BWa6kZhG.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-2WXFIKYE-BR6dsfEq.js → sequenceDiagram-2WXFIKYE-Cx_COX9G.js} +1 -1
- package/dist/client/assets/{stateDiagram-RAJIS63D-CHII26YE.js → stateDiagram-RAJIS63D-BXGnN6rZ.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-FVOUBMTO-CMw3xNha.js +1 -0
- package/dist/client/assets/{timeline-definition-YZTLITO2-DhUTiAsW.js → timeline-definition-YZTLITO2-DbqaUm9k.js} +1 -1
- package/dist/client/assets/{treemap-KZPCXAKY-C0Rh3R0y.js → treemap-KZPCXAKY-CfEujPCR.js} +1 -1
- package/dist/client/assets/{vennDiagram-LZ73GAT5-CWt3wBDG.js → vennDiagram-LZ73GAT5-CqJE8CAD.js} +1 -1
- package/dist/client/assets/{xychartDiagram-JWTSCODW-DhwJwxGz.js → xychartDiagram-JWTSCODW-CfdDvzHC.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/server/generated-file-check.js +113 -58
- package/dist/server/generated-file-check.test.js +2 -0
- package/dist/server/git-diff.d.ts +1 -0
- package/dist/server/git-diff.js +33 -6
- package/dist/server/git-diff.test.js +45 -0
- package/dist/server/server.d.ts +2 -0
- package/dist/server/server.js +107 -34
- package/dist/server/server.test.js +120 -0
- package/dist/types/diff.d.ts +73 -14
- package/dist/utils/commentFormatting.d.ts +4 -2
- package/dist/utils/commentFormatting.js +57 -19
- package/dist/utils/commentImports.d.ts +9 -0
- package/dist/utils/commentImports.js +264 -0
- package/dist/utils/commentImports.test.d.ts +1 -0
- package/dist/utils/commentImports.test.js +197 -0
- package/package.json +3 -3
- package/dist/client/assets/channel-C081SflL.js +0 -1
- package/dist/client/assets/classDiagram-VBA2DB6C-CD8hB8X7.js +0 -1
- package/dist/client/assets/classDiagram-v2-RAHNMMFH-CD8hB8X7.js +0 -1
- package/dist/client/assets/clone-DL1yO1kL.js +0 -1
- package/dist/client/assets/index-DcsVWNsS.css +0 -1
- package/dist/client/assets/index-Igyd6olF.js +0 -92
- package/dist/client/assets/stateDiagram-v2-FVOUBMTO-DtCFGPiV.js +0 -1
package/dist/cli/index.test.js
CHANGED
|
@@ -14,12 +14,16 @@ vi.mock('./utils.js', async () => {
|
|
|
14
14
|
promptUser: vi.fn(),
|
|
15
15
|
findUntrackedFiles: vi.fn(),
|
|
16
16
|
markFilesIntentToAdd: vi.fn(),
|
|
17
|
-
getPrPatch: vi.fn(),
|
|
18
17
|
};
|
|
19
18
|
});
|
|
19
|
+
vi.mock('./github.js', () => ({
|
|
20
|
+
getPrPatch: vi.fn(),
|
|
21
|
+
getPrCommentImports: vi.fn(),
|
|
22
|
+
}));
|
|
20
23
|
const { simpleGit } = await import('simple-git');
|
|
21
24
|
const { startServer } = await import('../server/server.js');
|
|
22
|
-
const { promptUser, findUntrackedFiles, markFilesIntentToAdd,
|
|
25
|
+
const { promptUser, findUntrackedFiles, markFilesIntentToAdd, parseCommentOptions } = await import('./utils.js');
|
|
26
|
+
const { getPrPatch, getPrCommentImports } = await import('./github.js');
|
|
23
27
|
describe('CLI index.ts', () => {
|
|
24
28
|
let mockGit;
|
|
25
29
|
let mockStartServer;
|
|
@@ -27,9 +31,12 @@ describe('CLI index.ts', () => {
|
|
|
27
31
|
let mockFindUntrackedFiles;
|
|
28
32
|
let mockMarkFilesIntentToAdd;
|
|
29
33
|
let mockGetPrPatch;
|
|
34
|
+
let mockGetPrCommentImports;
|
|
35
|
+
let actualParseCommentOptions;
|
|
30
36
|
// Store original console methods
|
|
31
37
|
let originalConsoleLog;
|
|
32
38
|
let originalConsoleError;
|
|
39
|
+
let originalConsoleWarn;
|
|
33
40
|
let originalProcessExit;
|
|
34
41
|
beforeEach(() => {
|
|
35
42
|
// Setup mocks
|
|
@@ -48,12 +55,17 @@ describe('CLI index.ts', () => {
|
|
|
48
55
|
mockFindUntrackedFiles = vi.mocked(findUntrackedFiles);
|
|
49
56
|
mockMarkFilesIntentToAdd = vi.mocked(markFilesIntentToAdd);
|
|
50
57
|
mockGetPrPatch = vi.mocked(getPrPatch);
|
|
58
|
+
mockGetPrCommentImports = vi.mocked(getPrCommentImports);
|
|
59
|
+
mockGetPrCommentImports.mockResolvedValue([]);
|
|
60
|
+
actualParseCommentOptions = parseCommentOptions;
|
|
51
61
|
// Mock console and process.exit
|
|
52
62
|
originalConsoleLog = console.log;
|
|
53
63
|
originalConsoleError = console.error;
|
|
64
|
+
originalConsoleWarn = console.warn;
|
|
54
65
|
originalProcessExit = process.exit;
|
|
55
66
|
console.log = vi.fn();
|
|
56
67
|
console.error = vi.fn();
|
|
68
|
+
console.warn = vi.fn();
|
|
57
69
|
process.exit = vi.fn();
|
|
58
70
|
// Reset all mocks
|
|
59
71
|
vi.clearAllMocks();
|
|
@@ -62,6 +74,7 @@ describe('CLI index.ts', () => {
|
|
|
62
74
|
// Restore original methods
|
|
63
75
|
console.log = originalConsoleLog;
|
|
64
76
|
console.error = originalConsoleError;
|
|
77
|
+
console.warn = originalConsoleWarn;
|
|
65
78
|
process.exit = originalProcessExit;
|
|
66
79
|
});
|
|
67
80
|
describe('CLI argument processing', () => {
|
|
@@ -409,14 +422,38 @@ describe('CLI index.ts', () => {
|
|
|
409
422
|
});
|
|
410
423
|
});
|
|
411
424
|
describe('GitHub PR integration', () => {
|
|
412
|
-
it('loads PR patch
|
|
425
|
+
it('loads PR patch, appends manual comments after PR imports, and starts server with stdin diff', async () => {
|
|
413
426
|
const prUrl = 'https://github.com/owner/repo/pull/123';
|
|
414
427
|
const prPatch = 'diff --git a/file.ts b/file.ts\nindex 1111111..2222222 100644\n';
|
|
428
|
+
const prCommentImports = [
|
|
429
|
+
{
|
|
430
|
+
type: 'thread',
|
|
431
|
+
id: 'PR_COMMENT_1',
|
|
432
|
+
filePath: 'src/example.ts',
|
|
433
|
+
position: { side: 'new', line: 10 },
|
|
434
|
+
body: 'Imported PR thread',
|
|
435
|
+
author: 'octocat',
|
|
436
|
+
createdAt: '2026-03-25T09:00:00Z',
|
|
437
|
+
updatedAt: '2026-03-25T09:05:00Z',
|
|
438
|
+
},
|
|
439
|
+
{
|
|
440
|
+
type: 'reply',
|
|
441
|
+
id: 'PR_COMMENT_2',
|
|
442
|
+
filePath: 'src/example.ts',
|
|
443
|
+
position: { side: 'new', line: 10 },
|
|
444
|
+
body: 'Imported PR reply',
|
|
445
|
+
author: 'hubot',
|
|
446
|
+
createdAt: '2026-03-25T09:10:00Z',
|
|
447
|
+
updatedAt: '2026-03-25T09:12:00Z',
|
|
448
|
+
},
|
|
449
|
+
];
|
|
415
450
|
mockGetPrPatch.mockReturnValue(prPatch);
|
|
451
|
+
mockGetPrCommentImports.mockResolvedValue(prCommentImports);
|
|
416
452
|
const program = new Command();
|
|
417
453
|
program
|
|
418
454
|
.argument('[commit-ish]', 'commit-ish', 'HEAD')
|
|
419
455
|
.argument('[compare-with]', 'compare-with')
|
|
456
|
+
.option('--comment <json>', 'comment', (value, previous = []) => [...previous, value], [])
|
|
420
457
|
.option('--port <port>', 'port', parseInt)
|
|
421
458
|
.option('--host <host>', 'host', '')
|
|
422
459
|
.option('--no-open', 'no-open')
|
|
@@ -424,11 +461,15 @@ describe('CLI index.ts', () => {
|
|
|
424
461
|
.option('--tui', 'tui')
|
|
425
462
|
.option('--pr <url>', 'pr')
|
|
426
463
|
.action(async (commitish, _compareWith, options) => {
|
|
464
|
+
const manualCommentImports = actualParseCommentOptions(options.comment);
|
|
465
|
+
let commentImports = manualCommentImports;
|
|
427
466
|
if (options.pr) {
|
|
428
467
|
if (commitish !== 'HEAD' || _compareWith) {
|
|
429
468
|
console.error('Error: --pr option cannot be used with positional arguments');
|
|
430
469
|
process.exit(1);
|
|
431
470
|
}
|
|
471
|
+
const importedPrComments = await getPrCommentImports(options.pr);
|
|
472
|
+
commentImports = [...importedPrComments, ...manualCommentImports];
|
|
432
473
|
}
|
|
433
474
|
await startServer({
|
|
434
475
|
stdinDiff: getPrPatch(options.pr),
|
|
@@ -436,10 +477,82 @@ describe('CLI index.ts', () => {
|
|
|
436
477
|
host: options.host,
|
|
437
478
|
openBrowser: options.open,
|
|
438
479
|
mode: options.mode,
|
|
480
|
+
commentImports,
|
|
439
481
|
});
|
|
440
482
|
});
|
|
441
|
-
await program.parseAsync([
|
|
483
|
+
await program.parseAsync([
|
|
484
|
+
'--pr',
|
|
485
|
+
prUrl,
|
|
486
|
+
'--comment',
|
|
487
|
+
'{"type":"reply","filePath":"src/example.ts","position":{"side":"new","line":10},"body":"Manual reply"}',
|
|
488
|
+
], { from: 'user' });
|
|
442
489
|
expect(mockGetPrPatch).toHaveBeenCalledWith(prUrl);
|
|
490
|
+
expect(mockGetPrCommentImports).toHaveBeenCalledWith(prUrl);
|
|
491
|
+
expect(mockStartServer).toHaveBeenCalledWith({
|
|
492
|
+
stdinDiff: prPatch,
|
|
493
|
+
preferredPort: undefined,
|
|
494
|
+
host: '',
|
|
495
|
+
openBrowser: true,
|
|
496
|
+
mode: 'split',
|
|
497
|
+
commentImports: [
|
|
498
|
+
...prCommentImports,
|
|
499
|
+
{
|
|
500
|
+
type: 'reply',
|
|
501
|
+
id: undefined,
|
|
502
|
+
filePath: 'src/example.ts',
|
|
503
|
+
position: { side: 'new', line: 10 },
|
|
504
|
+
body: 'Manual reply',
|
|
505
|
+
author: undefined,
|
|
506
|
+
createdAt: undefined,
|
|
507
|
+
updatedAt: undefined,
|
|
508
|
+
codeSnapshot: undefined,
|
|
509
|
+
},
|
|
510
|
+
],
|
|
511
|
+
});
|
|
512
|
+
});
|
|
513
|
+
it('continues with patch only when PR comment import fetch fails', async () => {
|
|
514
|
+
const prUrl = 'https://github.com/owner/repo/pull/123';
|
|
515
|
+
const prPatch = 'diff --git a/file.ts b/file.ts\nindex 1111111..2222222 100644\n';
|
|
516
|
+
mockGetPrPatch.mockReturnValue(prPatch);
|
|
517
|
+
mockGetPrCommentImports.mockRejectedValue(new Error('gh api graphql failed'));
|
|
518
|
+
const program = new Command();
|
|
519
|
+
program
|
|
520
|
+
.argument('[commit-ish]', 'commit-ish', 'HEAD')
|
|
521
|
+
.argument('[compare-with]', 'compare-with')
|
|
522
|
+
.option('--comment <json>', 'comment', (value, previous = []) => [...previous, value], [])
|
|
523
|
+
.option('--port <port>', 'port', parseInt)
|
|
524
|
+
.option('--host <host>', 'host', '')
|
|
525
|
+
.option('--no-open', 'no-open')
|
|
526
|
+
.option('--mode <mode>', 'mode', normalizeDiffViewMode, DEFAULT_DIFF_VIEW_MODE)
|
|
527
|
+
.option('--tui', 'tui')
|
|
528
|
+
.option('--pr <url>', 'pr')
|
|
529
|
+
.action(async (commitish, _compareWith, options) => {
|
|
530
|
+
const manualCommentImports = actualParseCommentOptions(options.comment);
|
|
531
|
+
let commentImports = manualCommentImports;
|
|
532
|
+
if (options.pr) {
|
|
533
|
+
if (commitish !== 'HEAD' || _compareWith) {
|
|
534
|
+
console.error('Error: --pr option cannot be used with positional arguments');
|
|
535
|
+
process.exit(1);
|
|
536
|
+
}
|
|
537
|
+
try {
|
|
538
|
+
const importedPrComments = await getPrCommentImports(options.pr);
|
|
539
|
+
commentImports = [...importedPrComments, ...manualCommentImports];
|
|
540
|
+
}
|
|
541
|
+
catch (error) {
|
|
542
|
+
console.warn(`Warning: Failed to load PR review comments: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
await startServer({
|
|
546
|
+
stdinDiff: getPrPatch(options.pr),
|
|
547
|
+
preferredPort: options.port,
|
|
548
|
+
host: options.host,
|
|
549
|
+
openBrowser: options.open,
|
|
550
|
+
mode: options.mode,
|
|
551
|
+
...(commentImports.length > 0 ? { commentImports } : {}),
|
|
552
|
+
});
|
|
553
|
+
});
|
|
554
|
+
await program.parseAsync(['--pr', prUrl], { from: 'user' });
|
|
555
|
+
expect(console.warn).toHaveBeenCalledWith('Warning: Failed to load PR review comments: gh api graphql failed');
|
|
443
556
|
expect(mockStartServer).toHaveBeenCalledWith({
|
|
444
557
|
stdinDiff: prPatch,
|
|
445
558
|
preferredPort: undefined,
|
|
@@ -503,6 +616,95 @@ describe('CLI index.ts', () => {
|
|
|
503
616
|
expect(mockStartServer).not.toHaveBeenCalled();
|
|
504
617
|
});
|
|
505
618
|
});
|
|
619
|
+
describe('--comment option', () => {
|
|
620
|
+
it('passes parsed comment imports to startServer', async () => {
|
|
621
|
+
const program = new Command();
|
|
622
|
+
program
|
|
623
|
+
.argument('[commit-ish]', 'commit-ish', 'HEAD')
|
|
624
|
+
.option('--comment <json>', 'comment', (value, previous = []) => [...previous, value], [])
|
|
625
|
+
.option('--port <port>', 'port', parseInt)
|
|
626
|
+
.option('--host <host>', 'host', '')
|
|
627
|
+
.option('--no-open', 'no-open')
|
|
628
|
+
.option('--mode <mode>', 'mode', normalizeDiffViewMode, DEFAULT_DIFF_VIEW_MODE)
|
|
629
|
+
.action(async (commitish, options) => {
|
|
630
|
+
const commentImports = actualParseCommentOptions(options.comment);
|
|
631
|
+
await startServer({
|
|
632
|
+
targetCommitish: commitish,
|
|
633
|
+
baseCommitish: `${commitish}^`,
|
|
634
|
+
preferredPort: options.port,
|
|
635
|
+
host: options.host,
|
|
636
|
+
openBrowser: options.open,
|
|
637
|
+
mode: options.mode,
|
|
638
|
+
commentImports,
|
|
639
|
+
});
|
|
640
|
+
});
|
|
641
|
+
await program.parseAsync([
|
|
642
|
+
'--comment',
|
|
643
|
+
'{"type":"thread","filePath":"src/example.ts","position":{"side":"new","line":10},"body":"Imported comment"}',
|
|
644
|
+
], { from: 'user' });
|
|
645
|
+
expect(mockStartServer).toHaveBeenCalledWith({
|
|
646
|
+
targetCommitish: 'HEAD',
|
|
647
|
+
baseCommitish: 'HEAD^',
|
|
648
|
+
preferredPort: undefined,
|
|
649
|
+
host: '',
|
|
650
|
+
openBrowser: true,
|
|
651
|
+
mode: 'split',
|
|
652
|
+
commentImports: [
|
|
653
|
+
{
|
|
654
|
+
type: 'thread',
|
|
655
|
+
id: undefined,
|
|
656
|
+
filePath: 'src/example.ts',
|
|
657
|
+
position: { side: 'new', line: 10 },
|
|
658
|
+
body: 'Imported comment',
|
|
659
|
+
author: undefined,
|
|
660
|
+
createdAt: undefined,
|
|
661
|
+
updatedAt: undefined,
|
|
662
|
+
codeSnapshot: undefined,
|
|
663
|
+
},
|
|
664
|
+
],
|
|
665
|
+
});
|
|
666
|
+
});
|
|
667
|
+
it('rejects --comment with --tui', async () => {
|
|
668
|
+
const program = new Command();
|
|
669
|
+
program
|
|
670
|
+
.argument('[commit-ish]', 'commit-ish', 'HEAD')
|
|
671
|
+
.option('--comment <json>', 'comment', (value, previous = []) => [...previous, value], [])
|
|
672
|
+
.option('--tui', 'tui')
|
|
673
|
+
.action(async (_commitish, options) => {
|
|
674
|
+
const commentImports = actualParseCommentOptions(options.comment);
|
|
675
|
+
if (options.tui && commentImports.length > 0) {
|
|
676
|
+
console.error('Error: --comment option cannot be used with --tui');
|
|
677
|
+
process.exit(1);
|
|
678
|
+
}
|
|
679
|
+
});
|
|
680
|
+
await program.parseAsync([
|
|
681
|
+
'--tui',
|
|
682
|
+
'--comment',
|
|
683
|
+
'{"type":"thread","filePath":"src/example.ts","position":{"side":"new","line":10},"body":"Imported comment"}',
|
|
684
|
+
], { from: 'user' });
|
|
685
|
+
expect(console.error).toHaveBeenCalledWith('Error: --comment option cannot be used with --tui');
|
|
686
|
+
expect(process.exit).toHaveBeenCalledWith(1);
|
|
687
|
+
});
|
|
688
|
+
it('reports invalid comment json before starting the server', async () => {
|
|
689
|
+
const program = new Command();
|
|
690
|
+
program
|
|
691
|
+
.argument('[commit-ish]', 'commit-ish', 'HEAD')
|
|
692
|
+
.option('--comment <json>', 'comment', (value, previous = []) => [...previous, value], [])
|
|
693
|
+
.action(async (_commitish, options) => {
|
|
694
|
+
try {
|
|
695
|
+
actualParseCommentOptions(options.comment);
|
|
696
|
+
}
|
|
697
|
+
catch (error) {
|
|
698
|
+
console.error(`Error: ${error instanceof Error ? error.message : 'Invalid --comment value'}`);
|
|
699
|
+
process.exit(1);
|
|
700
|
+
}
|
|
701
|
+
});
|
|
702
|
+
await program.parseAsync(['--comment', '{'], { from: 'user' });
|
|
703
|
+
expect(console.error).toHaveBeenCalledWith('Error: Invalid --comment JSON');
|
|
704
|
+
expect(process.exit).toHaveBeenCalledWith(1);
|
|
705
|
+
expect(mockStartServer).not.toHaveBeenCalled();
|
|
706
|
+
});
|
|
707
|
+
});
|
|
506
708
|
describe('Clean flag functionality', () => {
|
|
507
709
|
it('displays clean message when flag is used', async () => {
|
|
508
710
|
mockFindUntrackedFiles.mockResolvedValue([]);
|
package/dist/cli/utils.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Stats } from 'node:fs';
|
|
2
2
|
import type { SimpleGit } from 'simple-git';
|
|
3
|
+
import type { CommentImport } from '../types/diff.js';
|
|
3
4
|
type StdinStat = Pick<Stats, 'isFIFO' | 'isFile' | 'isSocket'>;
|
|
4
5
|
type StdinSource = 'pipe' | 'file' | 'socket' | 'tty';
|
|
5
6
|
export declare function detectStdinSource(stdinStat?: StdinStat): StdinSource;
|
|
@@ -15,14 +16,7 @@ export declare function getGitRoot(): string;
|
|
|
15
16
|
export declare function validateCommitish(commitish: string): boolean;
|
|
16
17
|
export declare function shortHash(hash: string): string;
|
|
17
18
|
export declare function createCommitRangeString(baseHash: string, targetHash: string): string;
|
|
18
|
-
|
|
19
|
-
owner: string;
|
|
20
|
-
repo: string;
|
|
21
|
-
pullNumber: number;
|
|
22
|
-
hostname: string;
|
|
23
|
-
}
|
|
24
|
-
export declare function parseGitHubPrUrl(url: string): PullRequestInfo | null;
|
|
25
|
-
export declare function getPrPatch(prArg: string): string;
|
|
19
|
+
export declare function parseCommentOptions(commentValues: string[]): CommentImport[];
|
|
26
20
|
export declare function validateDiffArguments(targetCommitish: string, baseCommitish?: string): {
|
|
27
21
|
valid: boolean;
|
|
28
22
|
error?: string;
|
package/dist/cli/utils.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
2
|
import { fstatSync } from 'node:fs';
|
|
3
3
|
import { createInterface } from 'readline/promises';
|
|
4
|
+
import { parseCommentImportValue } from '../utils/commentImports.js';
|
|
4
5
|
export function detectStdinSource(stdinStat = fstatSync(0)) {
|
|
5
6
|
if (stdinStat.isFIFO()) {
|
|
6
7
|
return 'pipe';
|
|
@@ -43,26 +44,52 @@ export function validateCommitish(commitish) {
|
|
|
43
44
|
if (trimmed.length === 0) {
|
|
44
45
|
return false;
|
|
45
46
|
}
|
|
46
|
-
// Special cases
|
|
47
|
-
if (trimmed === 'HEAD~') {
|
|
48
|
-
return false;
|
|
49
|
-
}
|
|
50
47
|
if (trimmed === '.' || trimmed === 'working' || trimmed === 'staged') {
|
|
51
48
|
return true; // Allow special keywords for working directory and staging area diff
|
|
52
49
|
}
|
|
53
|
-
const
|
|
50
|
+
const baseCommitish = stripRevisionSuffix(trimmed);
|
|
51
|
+
if (baseCommitish.length === 0) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
return isValidCommitishBase(baseCommitish);
|
|
55
|
+
}
|
|
56
|
+
function isValidCommitishBase(baseCommitish) {
|
|
57
|
+
const validBasePatterns = [
|
|
54
58
|
/^[a-f0-9]{4,40}$/i, // SHA hashes
|
|
55
|
-
/^
|
|
56
|
-
|
|
57
|
-
/^HEAD(~\d+|\^\d*)*$/, // HEAD, HEAD~1, HEAD^, HEAD^2, etc.
|
|
58
|
-
/^@(~\d+|\^\d*)*$/, // @, @~1, @^, @^2, etc. (@ is Git alias for HEAD)
|
|
59
|
+
/^HEAD$/, // HEAD
|
|
60
|
+
/^@$/, // @ is Git alias for HEAD
|
|
59
61
|
];
|
|
60
|
-
|
|
61
|
-
if (validPatterns.some((pattern) => pattern.test(trimmed))) {
|
|
62
|
+
if (validBasePatterns.some((pattern) => pattern.test(baseCommitish))) {
|
|
62
63
|
return true;
|
|
63
64
|
}
|
|
64
|
-
// For branch
|
|
65
|
-
return isValidBranchName(
|
|
65
|
+
// For branch, tag, and remote refs, use git's ref naming rules.
|
|
66
|
+
return isValidBranchName(baseCommitish);
|
|
67
|
+
}
|
|
68
|
+
function stripRevisionSuffix(commitish) {
|
|
69
|
+
let suffixStart = commitish.length;
|
|
70
|
+
while (suffixStart > 0) {
|
|
71
|
+
const current = commitish[suffixStart - 1];
|
|
72
|
+
if (current === '^') {
|
|
73
|
+
suffixStart--;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (!isAsciiDigit(current)) {
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
let digitStart = suffixStart - 1;
|
|
80
|
+
while (digitStart > 0 && isAsciiDigit(commitish[digitStart - 1])) {
|
|
81
|
+
digitStart--;
|
|
82
|
+
}
|
|
83
|
+
const operator = commitish[digitStart - 1];
|
|
84
|
+
if (operator !== '^' && operator !== '~') {
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
suffixStart = digitStart - 1;
|
|
88
|
+
}
|
|
89
|
+
return commitish.slice(0, suffixStart);
|
|
90
|
+
}
|
|
91
|
+
function isAsciiDigit(char) {
|
|
92
|
+
return char >= '0' && char <= '9';
|
|
66
93
|
}
|
|
67
94
|
function isValidBranchName(name) {
|
|
68
95
|
// Git branch name rules
|
|
@@ -103,48 +130,8 @@ export function shortHash(hash) {
|
|
|
103
130
|
export function createCommitRangeString(baseHash, targetHash) {
|
|
104
131
|
return `${baseHash}...${targetHash}`;
|
|
105
132
|
}
|
|
106
|
-
export function
|
|
107
|
-
|
|
108
|
-
const urlObj = new URL(url);
|
|
109
|
-
// Allow any hostname for GitHub Enterprise support
|
|
110
|
-
// Just validate the path structure
|
|
111
|
-
const pathParts = urlObj.pathname.split('/').filter(Boolean);
|
|
112
|
-
if (pathParts.length < 4 || pathParts[2] !== 'pull') {
|
|
113
|
-
return null;
|
|
114
|
-
}
|
|
115
|
-
const owner = pathParts[0];
|
|
116
|
-
const repo = pathParts[1];
|
|
117
|
-
const pullNumber = parseInt(pathParts[3], 10);
|
|
118
|
-
if (isNaN(pullNumber)) {
|
|
119
|
-
return null;
|
|
120
|
-
}
|
|
121
|
-
return { owner, repo, pullNumber, hostname: urlObj.hostname };
|
|
122
|
-
}
|
|
123
|
-
catch {
|
|
124
|
-
return null;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
export function getPrPatch(prArg) {
|
|
128
|
-
try {
|
|
129
|
-
const patch = execFileSync('gh', ['pr', 'diff', prArg], {
|
|
130
|
-
encoding: 'utf8',
|
|
131
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
132
|
-
});
|
|
133
|
-
if (!patch.trim()) {
|
|
134
|
-
throw new Error('No diff content returned from gh pr diff');
|
|
135
|
-
}
|
|
136
|
-
return patch;
|
|
137
|
-
}
|
|
138
|
-
catch (error) {
|
|
139
|
-
const stderr = error.stderr;
|
|
140
|
-
const stderrText = typeof stderr === 'string'
|
|
141
|
-
? stderr.trim()
|
|
142
|
-
: Buffer.isBuffer(stderr)
|
|
143
|
-
? stderr.toString('utf8').trim()
|
|
144
|
-
: '';
|
|
145
|
-
const message = stderrText || (error instanceof Error ? error.message : 'Unknown error while running gh');
|
|
146
|
-
throw new Error(`${message}\nTry: gh auth login`);
|
|
147
|
-
}
|
|
133
|
+
export function parseCommentOptions(commentValues) {
|
|
134
|
+
return commentValues.flatMap((value) => parseCommentImportValue(value));
|
|
148
135
|
}
|
|
149
136
|
export function validateDiffArguments(targetCommitish, baseCommitish) {
|
|
150
137
|
// Validate target commitish format
|
package/dist/cli/utils.test.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { detectStdinSource,
|
|
2
|
+
import { detectStdinSource, parseCommentOptions, shortHash, shouldReadStdin, validateCommitish, validateDiffArguments, } from './utils';
|
|
3
3
|
describe('CLI Utils', () => {
|
|
4
4
|
describe('stdin detection', () => {
|
|
5
5
|
it('detects pipe from stdin stat', () => {
|
|
@@ -99,6 +99,55 @@ describe('CLI Utils', () => {
|
|
|
99
99
|
})).toBe(false);
|
|
100
100
|
});
|
|
101
101
|
});
|
|
102
|
+
describe('parseCommentOptions', () => {
|
|
103
|
+
it('parses a single comment import', () => {
|
|
104
|
+
const result = parseCommentOptions([
|
|
105
|
+
JSON.stringify({
|
|
106
|
+
type: 'thread',
|
|
107
|
+
filePath: 'src/example.ts',
|
|
108
|
+
position: { side: 'new', line: 10 },
|
|
109
|
+
body: 'Imported comment',
|
|
110
|
+
}),
|
|
111
|
+
]);
|
|
112
|
+
expect(result).toEqual([
|
|
113
|
+
{
|
|
114
|
+
type: 'thread',
|
|
115
|
+
id: undefined,
|
|
116
|
+
filePath: 'src/example.ts',
|
|
117
|
+
position: { side: 'new', line: 10 },
|
|
118
|
+
body: 'Imported comment',
|
|
119
|
+
author: undefined,
|
|
120
|
+
createdAt: undefined,
|
|
121
|
+
updatedAt: undefined,
|
|
122
|
+
codeSnapshot: undefined,
|
|
123
|
+
},
|
|
124
|
+
]);
|
|
125
|
+
});
|
|
126
|
+
it('flattens array values from repeated options', () => {
|
|
127
|
+
const result = parseCommentOptions([
|
|
128
|
+
JSON.stringify([
|
|
129
|
+
{
|
|
130
|
+
type: 'thread',
|
|
131
|
+
filePath: 'src/example.ts',
|
|
132
|
+
position: { side: 'new', line: 10 },
|
|
133
|
+
body: 'Imported comment',
|
|
134
|
+
},
|
|
135
|
+
]),
|
|
136
|
+
JSON.stringify({
|
|
137
|
+
type: 'reply',
|
|
138
|
+
filePath: 'src/example.ts',
|
|
139
|
+
position: { side: 'new', line: 10 },
|
|
140
|
+
body: 'Imported reply',
|
|
141
|
+
}),
|
|
142
|
+
]);
|
|
143
|
+
expect(result).toHaveLength(2);
|
|
144
|
+
expect(result[0]?.type).toBe('thread');
|
|
145
|
+
expect(result[1]?.type).toBe('reply');
|
|
146
|
+
});
|
|
147
|
+
it('throws for invalid json', () => {
|
|
148
|
+
expect(() => parseCommentOptions(['{'])).toThrow('Invalid --comment JSON');
|
|
149
|
+
});
|
|
150
|
+
});
|
|
102
151
|
describe('validateCommitish', () => {
|
|
103
152
|
it('should validate full SHA hashes', () => {
|
|
104
153
|
expect(validateCommitish('a1b2c3d4e5f6789012345678901234567890abcd')).toBe(true);
|
|
@@ -152,6 +201,13 @@ describe('CLI Utils', () => {
|
|
|
152
201
|
expect(validateCommitish('release/v2.3.1')).toBe(true); // version numbers
|
|
153
202
|
expect(validateCommitish('bugfix/login-timeout')).toBe(true); // path with dash
|
|
154
203
|
});
|
|
204
|
+
it('should validate branch and remote refs with revision suffixes', () => {
|
|
205
|
+
expect(validateCommitish('main^')).toBe(true);
|
|
206
|
+
expect(validateCommitish('origin/main~2')).toBe(true);
|
|
207
|
+
expect(validateCommitish('codex/comment-thread^')).toBe(true);
|
|
208
|
+
expect(validateCommitish('feature/new-feature^2')).toBe(true);
|
|
209
|
+
expect(validateCommitish('release/v2.3.1~3^1')).toBe(true);
|
|
210
|
+
});
|
|
155
211
|
it('should validate special cases', () => {
|
|
156
212
|
expect(validateCommitish('.')).toBe(true); // working directory diff
|
|
157
213
|
});
|
|
@@ -282,6 +338,10 @@ describe('CLI Utils', () => {
|
|
|
282
338
|
expect(validateDiffArguments('feature/branch-name', 'origin/main')).toEqual({
|
|
283
339
|
valid: true,
|
|
284
340
|
});
|
|
341
|
+
expect(validateDiffArguments('main', 'main^')).toEqual({ valid: true });
|
|
342
|
+
expect(validateDiffArguments('codex/comment-thread', 'codex/comment-thread^')).toEqual({
|
|
343
|
+
valid: true,
|
|
344
|
+
});
|
|
285
345
|
});
|
|
286
346
|
it('should handle SHA hashes with parent/ancestor references', () => {
|
|
287
347
|
expect(validateDiffArguments('bd4b7513e075b5b245284c38fd23427b9bd0f42e^', 'abc123')).toEqual({ valid: true });
|
|
@@ -304,70 +364,4 @@ describe('CLI Utils', () => {
|
|
|
304
364
|
expect(shortHash('')).toBe('');
|
|
305
365
|
});
|
|
306
366
|
});
|
|
307
|
-
describe('parseGitHubPrUrl', () => {
|
|
308
|
-
it('should parse valid GitHub PR URLs', () => {
|
|
309
|
-
const result = parseGitHubPrUrl('https://github.com/owner/repo/pull/123');
|
|
310
|
-
expect(result).toEqual({
|
|
311
|
-
owner: 'owner',
|
|
312
|
-
repo: 'repo',
|
|
313
|
-
pullNumber: 123,
|
|
314
|
-
hostname: 'github.com',
|
|
315
|
-
});
|
|
316
|
-
});
|
|
317
|
-
it('should parse GitHub PR URLs with additional path segments', () => {
|
|
318
|
-
const result = parseGitHubPrUrl('https://github.com/owner/repo/pull/456/files');
|
|
319
|
-
expect(result).toEqual({
|
|
320
|
-
owner: 'owner',
|
|
321
|
-
repo: 'repo',
|
|
322
|
-
pullNumber: 456,
|
|
323
|
-
hostname: 'github.com',
|
|
324
|
-
});
|
|
325
|
-
});
|
|
326
|
-
it('should parse GitHub PR URLs with query parameters', () => {
|
|
327
|
-
const result = parseGitHubPrUrl('https://github.com/owner/repo/pull/789?tab=files');
|
|
328
|
-
expect(result).toEqual({
|
|
329
|
-
owner: 'owner',
|
|
330
|
-
repo: 'repo',
|
|
331
|
-
pullNumber: 789,
|
|
332
|
-
hostname: 'github.com',
|
|
333
|
-
});
|
|
334
|
-
});
|
|
335
|
-
it('should handle URLs with hyphens and underscores in owner/repo names', () => {
|
|
336
|
-
const result = parseGitHubPrUrl('https://github.com/owner-name/repo_name/pull/123');
|
|
337
|
-
expect(result).toEqual({
|
|
338
|
-
owner: 'owner-name',
|
|
339
|
-
repo: 'repo_name',
|
|
340
|
-
pullNumber: 123,
|
|
341
|
-
hostname: 'github.com',
|
|
342
|
-
});
|
|
343
|
-
});
|
|
344
|
-
it('should parse GitHub Enterprise PR URLs', () => {
|
|
345
|
-
const result1 = parseGitHubPrUrl('https://github.enterprise.com/owner/repo/pull/123');
|
|
346
|
-
expect(result1).toEqual({
|
|
347
|
-
owner: 'owner',
|
|
348
|
-
repo: 'repo',
|
|
349
|
-
pullNumber: 123,
|
|
350
|
-
hostname: 'github.enterprise.com',
|
|
351
|
-
});
|
|
352
|
-
const result2 = parseGitHubPrUrl('https://git.company.io/team/project/pull/456');
|
|
353
|
-
expect(result2).toEqual({
|
|
354
|
-
owner: 'team',
|
|
355
|
-
repo: 'project',
|
|
356
|
-
pullNumber: 456,
|
|
357
|
-
hostname: 'git.company.io',
|
|
358
|
-
});
|
|
359
|
-
});
|
|
360
|
-
it('should return null for invalid URLs', () => {
|
|
361
|
-
expect(parseGitHubPrUrl('not-a-url')).toBe(null);
|
|
362
|
-
expect(parseGitHubPrUrl('https://github.com/owner/repo/issues/123')).toBe(null);
|
|
363
|
-
expect(parseGitHubPrUrl('https://github.com/owner/repo')).toBe(null);
|
|
364
|
-
expect(parseGitHubPrUrl('https://github.com/owner/repo/pull/abc')).toBe(null);
|
|
365
|
-
});
|
|
366
|
-
it('should handle malformed URLs gracefully', () => {
|
|
367
|
-
expect(parseGitHubPrUrl('')).toBe(null);
|
|
368
|
-
expect(parseGitHubPrUrl('https://github.com')).toBe(null);
|
|
369
|
-
expect(parseGitHubPrUrl('https://github.com/owner')).toBe(null);
|
|
370
|
-
expect(parseGitHubPrUrl('https://github.com/owner/repo/pull')).toBe(null);
|
|
371
|
-
});
|
|
372
|
-
});
|
|
373
367
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{e as x,c as O,g as m,k as P,h as p,j as w,l as c,m as A,n as I,t as N,o as _}from"./_baseUniq-
|
|
1
|
+
import{e as x,c as O,g as m,k as P,h as p,j as w,l as c,m as A,n as I,t as N,o as _}from"./_baseUniq-tbL7nVvN.js";import{aT as g,at as $,aU as E,aV as F,aW as M,aX as l,aY as T,aZ as B,a_ as y,a$ as S}from"./mermaid.core-DV5JJ1Ie.js";var G=/\s/;function H(n){for(var r=n.length;r--&&G.test(n.charAt(r)););return r}var L=/^\s+/;function R(n){return n&&n.slice(0,H(n)+1).replace(L,"")}var o=NaN,W=/^[-+]0x[0-9a-f]+$/i,X=/^0b[01]+$/i,Y=/^0o[0-7]+$/i,q=parseInt;function z(n){if(typeof n=="number")return n;if(x(n))return o;if(g(n)){var r=typeof n.valueOf=="function"?n.valueOf():n;n=g(r)?r+"":r}if(typeof n!="string")return n===0?n:+n;n=R(n);var t=X.test(n);return t||Y.test(n)?q(n.slice(2),t?2:8):W.test(n)?o:+n}var v=1/0,C=17976931348623157e292;function K(n){if(!n)return n===0?n:0;if(n=z(n),n===v||n===-v){var r=n<0?-1:1;return r*C}return n===n?n:0}function U(n){var r=K(n),t=r%1;return r===r?t?r-t:r:0}function fn(n){var r=n==null?0:n.length;return r?O(n):[]}var b=Object.prototype,Z=b.hasOwnProperty,dn=$(function(n,r){n=Object(n);var t=-1,i=r.length,a=i>2?r[2]:void 0;for(a&&E(r[0],r[1],a)&&(i=1);++t<i;)for(var f=r[t],e=F(f),s=-1,d=e.length;++s<d;){var u=e[s],h=n[u];(h===void 0||M(h,b[u])&&!Z.call(n,u))&&(n[u]=f[u])}return n});function un(n){var r=n==null?0:n.length;return r?n[r-1]:void 0}function D(n){return function(r,t,i){var a=Object(r);if(!l(r)){var f=m(t);r=P(r),t=function(s){return f(a[s],s,a)}}var e=n(r,t,i);return e>-1?a[f?r[e]:e]:void 0}}var J=Math.max;function Q(n,r,t){var i=n==null?0:n.length;if(!i)return-1;var a=t==null?0:U(t);return a<0&&(a=J(i+a,0)),p(n,m(r),a)}var hn=D(Q);function V(n,r){var t=-1,i=l(n)?Array(n.length):[];return w(n,function(a,f,e){i[++t]=r(a,f,e)}),i}function gn(n,r){var t=T(n)?c:V;return t(n,m(r))}var j=Object.prototype,k=j.hasOwnProperty;function nn(n,r){return n!=null&&k.call(n,r)}function mn(n,r){return n!=null&&A(n,r,nn)}function rn(n,r){return n<r}function tn(n,r,t){for(var i=-1,a=n.length;++i<a;){var f=n[i],e=r(f);if(e!=null&&(s===void 0?e===e&&!x(e):t(e,s)))var s=e,d=f}return d}function on(n){return n&&n.length?tn(n,B,rn):void 0}function an(n,r,t,i){if(!g(n))return n;r=I(r,n);for(var a=-1,f=r.length,e=f-1,s=n;s!=null&&++a<f;){var d=N(r[a]),u=t;if(d==="__proto__"||d==="constructor"||d==="prototype")return n;if(a!=e){var h=s[d];u=void 0,u===void 0&&(u=g(h)?h:y(r[a+1])?[]:{})}S(s,d,u),s=s[d]}return n}function vn(n,r,t){for(var i=-1,a=r.length,f={};++i<a;){var e=r[i],s=_(n,e);t(s,e)&&an(f,I(e,n),s)}return f}export{rn as a,tn as b,V as c,vn as d,on as e,fn as f,hn as g,mn as h,dn as i,U as j,un as l,gn as m,K as t};
|