@jagadeesh52423/atlassian-mcp-server 1.0.2 → 1.1.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.
Files changed (85) hide show
  1. package/dist/clients/base-client.d.ts +26 -0
  2. package/dist/clients/base-client.d.ts.map +1 -0
  3. package/dist/clients/base-client.js +58 -0
  4. package/dist/clients/base-client.js.map +1 -0
  5. package/dist/clients/bitbucket-client.d.ts +17 -0
  6. package/dist/clients/bitbucket-client.d.ts.map +1 -0
  7. package/dist/clients/bitbucket-client.js +36 -0
  8. package/dist/clients/bitbucket-client.js.map +1 -0
  9. package/dist/clients/confluence-client.d.ts +8 -0
  10. package/dist/clients/confluence-client.d.ts.map +1 -0
  11. package/dist/clients/confluence-client.js +16 -0
  12. package/dist/clients/confluence-client.js.map +1 -0
  13. package/dist/clients/index.d.ts +5 -0
  14. package/dist/clients/index.d.ts.map +1 -0
  15. package/dist/clients/index.js +5 -0
  16. package/dist/clients/index.js.map +1 -0
  17. package/dist/clients/jira-client.d.ts +8 -0
  18. package/dist/clients/jira-client.d.ts.map +1 -0
  19. package/dist/clients/jira-client.js +16 -0
  20. package/dist/clients/jira-client.js.map +1 -0
  21. package/dist/constants.d.ts +67 -0
  22. package/dist/constants.d.ts.map +1 -0
  23. package/dist/constants.js +78 -0
  24. package/dist/constants.js.map +1 -0
  25. package/dist/error-handler.d.ts +83 -0
  26. package/dist/error-handler.d.ts.map +1 -0
  27. package/dist/error-handler.js +141 -0
  28. package/dist/error-handler.js.map +1 -0
  29. package/dist/formatters/adf-formatter.d.ts +25 -0
  30. package/dist/formatters/adf-formatter.d.ts.map +1 -0
  31. package/dist/formatters/adf-formatter.js +609 -0
  32. package/dist/formatters/adf-formatter.js.map +1 -0
  33. package/dist/formatters/index.d.ts +2 -0
  34. package/dist/formatters/index.d.ts.map +1 -0
  35. package/dist/formatters/index.js +2 -0
  36. package/dist/formatters/index.js.map +1 -0
  37. package/dist/handlers/bitbucket-handlers.d.ts +94 -0
  38. package/dist/handlers/bitbucket-handlers.d.ts.map +1 -0
  39. package/dist/handlers/bitbucket-handlers.js +251 -0
  40. package/dist/handlers/bitbucket-handlers.js.map +1 -0
  41. package/dist/handlers/confluence-handlers.d.ts +106 -0
  42. package/dist/handlers/confluence-handlers.d.ts.map +1 -0
  43. package/dist/handlers/confluence-handlers.js +340 -0
  44. package/dist/handlers/confluence-handlers.js.map +1 -0
  45. package/dist/handlers/index.d.ts +4 -0
  46. package/dist/handlers/index.d.ts.map +1 -0
  47. package/dist/handlers/index.js +4 -0
  48. package/dist/handlers/index.js.map +1 -0
  49. package/dist/handlers/jira-handlers.d.ts +237 -0
  50. package/dist/handlers/jira-handlers.d.ts.map +1 -0
  51. package/dist/handlers/jira-handlers.js +816 -0
  52. package/dist/handlers/jira-handlers.js.map +1 -0
  53. package/dist/index.js +189 -2536
  54. package/dist/index.js.map +1 -1
  55. package/dist/services/bitbucket.d.ts +11 -1
  56. package/dist/services/bitbucket.d.ts.map +1 -1
  57. package/dist/services/bitbucket.js +78 -0
  58. package/dist/services/bitbucket.js.map +1 -1
  59. package/dist/services/confluence.d.ts +5 -0
  60. package/dist/services/confluence.d.ts.map +1 -1
  61. package/dist/services/confluence.js +59 -4
  62. package/dist/services/confluence.js.map +1 -1
  63. package/dist/services/jira.d.ts +57 -0
  64. package/dist/services/jira.d.ts.map +1 -1
  65. package/dist/services/jira.js +395 -24
  66. package/dist/services/jira.js.map +1 -1
  67. package/dist/tools/bitbucket-tools.d.ts +6 -0
  68. package/dist/tools/bitbucket-tools.d.ts.map +1 -0
  69. package/dist/tools/bitbucket-tools.js +386 -0
  70. package/dist/tools/bitbucket-tools.js.map +1 -0
  71. package/dist/tools/confluence-tools.d.ts +17 -0
  72. package/dist/tools/confluence-tools.d.ts.map +1 -0
  73. package/dist/tools/confluence-tools.js +440 -0
  74. package/dist/tools/confluence-tools.js.map +1 -0
  75. package/dist/tools/index.d.ts +8 -0
  76. package/dist/tools/index.d.ts.map +1 -0
  77. package/dist/tools/index.js +15 -0
  78. package/dist/tools/index.js.map +1 -0
  79. package/dist/tools/jira-tools.d.ts +6 -0
  80. package/dist/tools/jira-tools.d.ts.map +1 -0
  81. package/dist/tools/jira-tools.js +1032 -0
  82. package/dist/tools/jira-tools.js.map +1 -0
  83. package/dist/types.d.ts +23 -0
  84. package/dist/types.d.ts.map +1 -1
  85. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- import { JiraClient } from '../jira-client.js';
1
+ import { JiraClient } from '../clients/jira-client.js';
2
2
  export class JiraService {
3
3
  client;
4
4
  // Smart suggestion rules for common field patterns
@@ -30,33 +30,87 @@ export class JiraService {
30
30
  this.client = new JiraClient();
31
31
  }
32
32
  async searchIssues(options = {}) {
33
- const { query, limit = 50, startAt = 0 } = options;
34
- // Create bounded JQL query (Jira requires bounded queries now)
35
- let jql = 'created >= -90d ORDER BY created DESC';
36
- if (query) {
37
- jql = `text ~ "${query}" AND created >= -90d ORDER BY created DESC`;
33
+ const { query, assignee, status, project, labels, reporter, createdAfter, updatedAfter, jql: rawJql, fields: extraFields, limit = 50, startAt = 0 } = options;
34
+ let jql;
35
+ if (rawJql) {
36
+ // Use raw JQL directly when provided
37
+ jql = rawJql;
38
+ }
39
+ else {
40
+ // Build JQL from individual filters
41
+ const conditions = [];
42
+ if (query) {
43
+ conditions.push(`text ~ "${query}"`);
44
+ }
45
+ if (assignee) {
46
+ conditions.push(`assignee = ${assignee}`);
47
+ }
48
+ if (status) {
49
+ conditions.push(`status = "${status}"`);
50
+ }
51
+ if (project) {
52
+ conditions.push(`project = "${project}"`);
53
+ }
54
+ if (labels && labels.length > 0) {
55
+ conditions.push(labels.map(l => `labels = "${l}"`).join(' AND '));
56
+ }
57
+ if (reporter) {
58
+ conditions.push(`reporter = ${reporter}`);
59
+ }
60
+ if (createdAfter) {
61
+ // Support relative days (e.g., "10d") or absolute dates (e.g., "2026-01-15")
62
+ const dateValue = /^\d+d$/.test(createdAfter) ? `-${createdAfter}` : createdAfter;
63
+ conditions.push(`created >= "${dateValue}"`);
64
+ }
65
+ if (updatedAfter) {
66
+ const dateValue = /^\d+d$/.test(updatedAfter) ? `-${updatedAfter}` : updatedAfter;
67
+ conditions.push(`updated >= "${dateValue}"`);
68
+ }
69
+ // Only apply 90-day bound when no specific filters are provided
70
+ if (conditions.length === 0) {
71
+ conditions.push('created >= -90d');
72
+ }
73
+ jql = `${conditions.join(' AND ')} ORDER BY updated DESC`;
38
74
  }
75
+ const defaultFields = ['summary', 'description', 'status', 'assignee', 'labels', 'created', 'updated'];
76
+ const requestFields = extraFields && extraFields.length > 0
77
+ ? [...new Set(extraFields)]
78
+ : defaultFields;
39
79
  const params = {
40
80
  jql,
41
81
  maxResults: limit,
42
82
  startAt,
43
- fields: 'summary,description,status,assignee,created,updated'
83
+ fields: requestFields.join(',')
44
84
  };
45
85
  const response = await this.client.get('/rest/api/3/search/jql', params);
46
- return response.issues.map((issue) => ({
47
- id: issue.id,
48
- key: issue.key,
49
- summary: issue.fields.summary || '',
50
- description: this.extractTextFromADF(issue.fields.description),
51
- status: issue.fields.status?.name || '',
52
- assignee: issue.fields.assignee?.displayName || '',
53
- created: issue.fields.created || '',
54
- updated: issue.fields.updated || ''
55
- }));
86
+ return response.issues.map((issue) => {
87
+ const result = {
88
+ key: issue.key,
89
+ };
90
+ for (const field of requestFields) {
91
+ const value = issue.fields[field];
92
+ if (value === undefined || value === null) {
93
+ result[field] = null;
94
+ }
95
+ else if (field === 'description') {
96
+ result[field] = this.extractTextFromADF(value);
97
+ }
98
+ else if (typeof value === 'object' && !Array.isArray(value)) {
99
+ result[field] = value.name || value.displayName || value.value || value;
100
+ }
101
+ else if (Array.isArray(value)) {
102
+ result[field] = value.map((v) => typeof v === 'object' ? (v.name || v.displayName || v.value || v) : v);
103
+ }
104
+ else {
105
+ result[field] = value;
106
+ }
107
+ }
108
+ return result;
109
+ });
56
110
  }
57
111
  async getIssue(issueKey) {
58
112
  const params = {
59
- fields: ['summary', 'description', 'status', 'assignee', 'created', 'updated']
113
+ fields: ['summary', 'description', 'status', 'assignee', 'labels', 'created', 'updated']
60
114
  };
61
115
  const issue = await this.client.get(`/rest/api/3/issue/${issueKey}`, params);
62
116
  return {
@@ -66,6 +120,7 @@ export class JiraService {
66
120
  description: this.extractTextFromADF(issue.fields.description),
67
121
  status: issue.fields.status?.name || '',
68
122
  assignee: issue.fields.assignee?.displayName || '',
123
+ labels: issue.fields.labels || [],
69
124
  created: issue.fields.created || '',
70
125
  updated: issue.fields.updated || ''
71
126
  };
@@ -472,6 +527,48 @@ export class JiraService {
472
527
  async removeWatcher(issueKey, accountId) {
473
528
  await this.client.delete(`/rest/api/3/issue/${issueKey}/watchers?accountId=${accountId}`);
474
529
  }
530
+ // Field Discovery Methods
531
+ async getFields(type) {
532
+ const response = await this.client.get('/rest/api/3/field');
533
+ let fields = response.map((f) => ({
534
+ id: f.id,
535
+ name: f.name,
536
+ custom: f.custom || false,
537
+ schema: f.schema?.type || null,
538
+ }));
539
+ if (type === 'standard') {
540
+ fields = fields.filter((f) => !f.custom);
541
+ }
542
+ else if (type === 'custom') {
543
+ fields = fields.filter((f) => f.custom);
544
+ }
545
+ return fields.sort((a, b) => a.name.localeCompare(b.name));
546
+ }
547
+ // Label Methods
548
+ async getLabels(query, maxResults = 50) {
549
+ const params = { maxResults };
550
+ if (query) {
551
+ params.query = query;
552
+ }
553
+ const response = await this.client.get('/rest/api/3/label', params);
554
+ return response.values || [];
555
+ }
556
+ async addLabels(issueKey, labels) {
557
+ const data = {
558
+ update: {
559
+ labels: labels.map(label => ({ add: label }))
560
+ }
561
+ };
562
+ await this.client.put(`/rest/api/3/issue/${issueKey}`, data);
563
+ }
564
+ async removeLabels(issueKey, labels) {
565
+ const data = {
566
+ update: {
567
+ labels: labels.map(label => ({ remove: label }))
568
+ }
569
+ };
570
+ await this.client.put(`/rest/api/3/issue/${issueKey}`, data);
571
+ }
475
572
  // Sub-task Methods
476
573
  async getSubTasks(issueKey) {
477
574
  const issue = await this.client.get(`/rest/api/3/issue/${issueKey}`, {
@@ -514,6 +611,242 @@ export class JiraService {
514
611
  }))
515
612
  })) || [];
516
613
  }
614
+ // Agile Methods
615
+ async getAgileBoards(options) {
616
+ const params = { maxResults: 50 };
617
+ if (options?.name)
618
+ params.name = options.name;
619
+ if (options?.type)
620
+ params.type = options.type;
621
+ if (options?.projectKeyOrId)
622
+ params.projectKeyOrId = options.projectKeyOrId;
623
+ const response = await this.client.get('/rest/agile/1.0/board', params);
624
+ return response.values?.map((board) => ({
625
+ id: board.id,
626
+ name: board.name,
627
+ type: board.type,
628
+ location: board.location ? {
629
+ projectId: board.location.projectId,
630
+ projectKey: board.location.projectKey,
631
+ projectName: board.location.projectName,
632
+ } : null,
633
+ })) || [];
634
+ }
635
+ async getBoardIssues(boardId, options) {
636
+ const params = { maxResults: options?.maxResults || 50 };
637
+ if (options?.jql)
638
+ params.jql = options.jql;
639
+ if (options?.startAt)
640
+ params.startAt = options.startAt;
641
+ const response = await this.client.get(`/rest/agile/1.0/board/${boardId}/issue`, params);
642
+ return {
643
+ total: response.total,
644
+ issues: response.issues?.map((issue) => ({
645
+ key: issue.key,
646
+ summary: issue.fields?.summary || '',
647
+ status: issue.fields?.status?.name || '',
648
+ assignee: issue.fields?.assignee?.displayName || '',
649
+ priority: issue.fields?.priority?.name || '',
650
+ })) || [],
651
+ };
652
+ }
653
+ async getSprints(boardId, state) {
654
+ const params = { maxResults: 50 };
655
+ if (state)
656
+ params.state = state;
657
+ const response = await this.client.get(`/rest/agile/1.0/board/${boardId}/sprint`, params);
658
+ return response.values?.map((sprint) => ({
659
+ id: sprint.id,
660
+ name: sprint.name,
661
+ state: sprint.state,
662
+ startDate: sprint.startDate || null,
663
+ endDate: sprint.endDate || null,
664
+ completeDate: sprint.completeDate || null,
665
+ goal: sprint.goal || '',
666
+ })) || [];
667
+ }
668
+ async getSprintIssues(sprintId, options) {
669
+ const params = { maxResults: options?.maxResults || 50 };
670
+ if (options?.jql)
671
+ params.jql = options.jql;
672
+ if (options?.startAt)
673
+ params.startAt = options.startAt;
674
+ const response = await this.client.get(`/rest/agile/1.0/sprint/${sprintId}/issue`, params);
675
+ return {
676
+ total: response.total,
677
+ issues: response.issues?.map((issue) => ({
678
+ key: issue.key,
679
+ summary: issue.fields?.summary || '',
680
+ status: issue.fields?.status?.name || '',
681
+ assignee: issue.fields?.assignee?.displayName || '',
682
+ priority: issue.fields?.priority?.name || '',
683
+ })) || [],
684
+ };
685
+ }
686
+ // Batch Create
687
+ async batchCreateIssues(issues) {
688
+ if (issues.length > 50) {
689
+ throw new Error('Batch create supports a maximum of 50 issues at a time');
690
+ }
691
+ const issueUpdates = issues.map(issue => ({
692
+ fields: {
693
+ project: { key: issue.projectKey },
694
+ summary: issue.summary,
695
+ description: this.parseDescriptionToADF(issue.description),
696
+ issuetype: { name: issue.issueType || 'Task' },
697
+ ...issue.additionalFields,
698
+ },
699
+ }));
700
+ const response = await this.client.post('/rest/api/3/issue/bulk', { issueUpdates });
701
+ return {
702
+ issues: response.issues?.map((i) => ({ id: i.id, key: i.key, self: i.self })) || [],
703
+ errors: response.errors || [],
704
+ };
705
+ }
706
+ // Dev Status
707
+ async getDevStatus(issueId, applicationType, dataType) {
708
+ const params = { issueId };
709
+ if (applicationType)
710
+ params.applicationType = applicationType;
711
+ if (dataType)
712
+ params.dataType = dataType;
713
+ const response = await this.client.get('/rest/dev-status/latest/issue/detail', params);
714
+ return response;
715
+ }
716
+ // Delete Issue
717
+ async deleteIssue(issueKey, deleteSubtasks = false) {
718
+ const query = deleteSubtasks ? '?deleteSubtasks=true' : '';
719
+ await this.client.delete(`/rest/api/3/issue/${issueKey}${query}`);
720
+ }
721
+ // Sprint Management
722
+ async createSprint(boardId, name, options) {
723
+ const data = {
724
+ name,
725
+ originBoardId: boardId,
726
+ };
727
+ if (options?.startDate)
728
+ data.startDate = options.startDate;
729
+ if (options?.endDate)
730
+ data.endDate = options.endDate;
731
+ if (options?.goal)
732
+ data.goal = options.goal;
733
+ const response = await this.client.post('/rest/agile/1.0/sprint', data);
734
+ return {
735
+ id: response.id,
736
+ name: response.name,
737
+ state: response.state,
738
+ startDate: response.startDate || null,
739
+ endDate: response.endDate || null,
740
+ goal: response.goal || '',
741
+ };
742
+ }
743
+ async updateSprint(sprintId, updates) {
744
+ const data = {};
745
+ if (updates.name)
746
+ data.name = updates.name;
747
+ if (updates.state)
748
+ data.state = updates.state;
749
+ if (updates.startDate)
750
+ data.startDate = updates.startDate;
751
+ if (updates.endDate)
752
+ data.endDate = updates.endDate;
753
+ if (updates.goal !== undefined)
754
+ data.goal = updates.goal;
755
+ const response = await this.client.post(`/rest/agile/1.0/sprint/${sprintId}`, data);
756
+ return {
757
+ id: response.id,
758
+ name: response.name,
759
+ state: response.state,
760
+ startDate: response.startDate || null,
761
+ endDate: response.endDate || null,
762
+ goal: response.goal || '',
763
+ };
764
+ }
765
+ // Version/Release Management
766
+ async getProjectVersions(projectKey) {
767
+ const response = await this.client.get(`/rest/api/3/project/${projectKey}/versions`);
768
+ return response.map((v) => ({
769
+ id: v.id,
770
+ name: v.name,
771
+ description: v.description || '',
772
+ released: v.released || false,
773
+ archived: v.archived || false,
774
+ startDate: v.startDate || null,
775
+ releaseDate: v.releaseDate || null,
776
+ }));
777
+ }
778
+ async createVersion(projectKey, name, options) {
779
+ const data = {
780
+ project: projectKey,
781
+ name,
782
+ };
783
+ if (options?.description)
784
+ data.description = options.description;
785
+ if (options?.startDate)
786
+ data.startDate = options.startDate;
787
+ if (options?.releaseDate)
788
+ data.releaseDate = options.releaseDate;
789
+ if (options?.released !== undefined)
790
+ data.released = options.released;
791
+ const response = await this.client.post('/rest/api/3/version', data);
792
+ return {
793
+ id: response.id,
794
+ name: response.name,
795
+ description: response.description || '',
796
+ released: response.released || false,
797
+ archived: response.archived || false,
798
+ startDate: response.startDate || null,
799
+ releaseDate: response.releaseDate || null,
800
+ };
801
+ }
802
+ async updateVersion(versionId, updates) {
803
+ const data = {};
804
+ if (updates.name)
805
+ data.name = updates.name;
806
+ if (updates.description !== undefined)
807
+ data.description = updates.description;
808
+ if (updates.startDate)
809
+ data.startDate = updates.startDate;
810
+ if (updates.releaseDate)
811
+ data.releaseDate = updates.releaseDate;
812
+ if (updates.released !== undefined)
813
+ data.released = updates.released;
814
+ if (updates.archived !== undefined)
815
+ data.archived = updates.archived;
816
+ const response = await this.client.put(`/rest/api/3/version/${versionId}`, data);
817
+ return {
818
+ id: response.id,
819
+ name: response.name,
820
+ description: response.description || '',
821
+ released: response.released || false,
822
+ archived: response.archived || false,
823
+ startDate: response.startDate || null,
824
+ releaseDate: response.releaseDate || null,
825
+ };
826
+ }
827
+ // Epic Linking
828
+ async linkToEpic(issueKey, epicKey) {
829
+ // In next-gen projects, Epic is the parent. In classic, it's the Epic Link custom field.
830
+ // Try parent field first (works for next-gen/team-managed), fall back to Epic Link.
831
+ try {
832
+ await this.client.put(`/rest/api/3/issue/${issueKey}`, {
833
+ fields: { parent: { key: epicKey } }
834
+ });
835
+ }
836
+ catch {
837
+ // Fallback: find the Epic Link custom field ID and use that
838
+ const fields = await this.client.get('/rest/api/3/field');
839
+ const epicLinkField = fields.find((f) => f.name === 'Epic Link' || f.clauseNames?.includes('cf[10014]'));
840
+ if (epicLinkField) {
841
+ await this.client.put(`/rest/api/3/issue/${issueKey}`, {
842
+ fields: { [epicLinkField.id]: epicKey }
843
+ });
844
+ }
845
+ else {
846
+ throw new Error('Could not find Epic Link field. Ensure the issue type supports epic linking.');
847
+ }
848
+ }
849
+ }
517
850
  // Smart Field Handling Methods
518
851
  async handleTransitionError(issueKey, transitionId, error) {
519
852
  try {
@@ -734,7 +1067,16 @@ export class JiraService {
734
1067
  for (let i = 0; i < lines.length; i++) {
735
1068
  const line = lines[i].trim();
736
1069
  if (line === '') {
737
- // Skip empty lines - let Claude handle spacing naturally
1070
+ // Only add an empty paragraph for double blank lines (true paragraph breaks)
1071
+ // Single blank lines are natural separators between block elements and don't need extra spacing
1072
+ const nextLine = i + 1 < lines.length ? lines[i + 1].trim() : '';
1073
+ if (nextLine === '') {
1074
+ const lastNode = content[content.length - 1];
1075
+ const isLastEmpty = lastNode && lastNode.type === 'paragraph' && (!lastNode.content || lastNode.content.length === 0);
1076
+ if (!isLastEmpty) {
1077
+ content.push({ type: 'paragraph', content: [] });
1078
+ }
1079
+ }
738
1080
  continue;
739
1081
  }
740
1082
  // Handle Confluence wiki markup headers (h1., h2., h3., etc.)
@@ -1023,6 +1365,11 @@ export class JiraService {
1023
1365
  { regex: /`([^`]+?)`/g, type: 'code' }, // `code` (markdown style)
1024
1366
  { regex: /\{\{(.+?)\}\}/g, type: 'code' }, // {{code}} (Confluence/Jira wiki style)
1025
1367
  ];
1368
+ // Link patterns (markdown [text](url) and Jira wiki [text|url])
1369
+ const linkPatterns = [
1370
+ { regex: /\[([^\]]+?)\]\(([^)]+?)\)/g }, // [text](url) - markdown style
1371
+ { regex: /\[([^|\]]+?)\|([^\]]+?)\]/g }, // [text|url] - Jira wiki style
1372
+ ];
1026
1373
  let matches = [];
1027
1374
  // Find all formatting matches
1028
1375
  patterns.forEach(pattern => {
@@ -1038,6 +1385,21 @@ export class JiraService {
1038
1385
  });
1039
1386
  }
1040
1387
  });
1388
+ // Find all link matches
1389
+ linkPatterns.forEach(pattern => {
1390
+ let match;
1391
+ const regex = new RegExp(pattern.regex.source, 'g');
1392
+ while ((match = regex.exec(text)) !== null) {
1393
+ matches.push({
1394
+ start: match.index,
1395
+ end: match.index + match[0].length,
1396
+ text: match[1],
1397
+ type: 'link',
1398
+ href: match[2],
1399
+ fullMatch: match[0]
1400
+ });
1401
+ }
1402
+ });
1041
1403
  // Sort matches by position and remove overlapping matches
1042
1404
  matches.sort((a, b) => a.start - b.start);
1043
1405
  // After processing bold and code, handle italic patterns that don't overlap
@@ -1085,11 +1447,20 @@ export class JiraService {
1085
1447
  }
1086
1448
  }
1087
1449
  // Add formatted text
1088
- result.push({
1089
- type: 'text',
1090
- text: match.text,
1091
- marks: [{ type: match.type }]
1092
- });
1450
+ if (match.type === 'link') {
1451
+ result.push({
1452
+ type: 'text',
1453
+ text: match.text,
1454
+ marks: [{ type: 'link', attrs: { href: match.href } }]
1455
+ });
1456
+ }
1457
+ else {
1458
+ result.push({
1459
+ type: 'text',
1460
+ text: match.text,
1461
+ marks: [{ type: match.type }]
1462
+ });
1463
+ }
1093
1464
  currentPos = match.end;
1094
1465
  });
1095
1466
  // Add remaining text