@mcp-consultant-tools/azure-devops 30.0.0-beta.9 → 30.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/build/azure-devops-client.d.ts.map +1 -1
- package/build/azure-devops-client.js +23 -3
- package/build/azure-devops-client.js.map +1 -1
- package/build/cli/commands/index.d.ts +1 -0
- package/build/cli/commands/index.d.ts.map +1 -1
- package/build/cli/commands/index.js +3 -0
- package/build/cli/commands/index.js.map +1 -1
- package/build/cli/commands/test-commands.d.ts +7 -0
- package/build/cli/commands/test-commands.d.ts.map +1 -0
- package/build/cli/commands/test-commands.js +131 -0
- package/build/cli/commands/test-commands.js.map +1 -0
- package/build/cli/commands/work-item-commands.d.ts.map +1 -1
- package/build/cli/commands/work-item-commands.js +34 -0
- package/build/cli/commands/work-item-commands.js.map +1 -1
- package/build/context-factory.d.ts.map +1 -1
- package/build/context-factory.js +3 -1
- package/build/context-factory.js.map +1 -1
- package/build/schemas.d.ts +13 -0
- package/build/schemas.d.ts.map +1 -0
- package/build/schemas.js +47 -0
- package/build/schemas.js.map +1 -0
- package/build/services/checklist-service.d.ts.map +1 -1
- package/build/services/checklist-service.js +3 -0
- package/build/services/checklist-service.js.map +1 -1
- package/build/services/index.d.ts +1 -0
- package/build/services/index.d.ts.map +1 -1
- package/build/services/index.js +1 -0
- package/build/services/index.js.map +1 -1
- package/build/services/sync-service.d.ts.map +1 -1
- package/build/services/sync-service.js +75 -14
- package/build/services/sync-service.js.map +1 -1
- package/build/services/test-service.d.ts +106 -0
- package/build/services/test-service.d.ts.map +1 -0
- package/build/services/test-service.js +245 -0
- package/build/services/test-service.js.map +1 -0
- package/build/services/work-item-service.d.ts +25 -0
- package/build/services/work-item-service.d.ts.map +1 -1
- package/build/services/work-item-service.js +51 -3
- package/build/services/work-item-service.js.map +1 -1
- package/build/sync/annotation-parser.d.ts +52 -0
- package/build/sync/annotation-parser.d.ts.map +1 -0
- package/build/sync/annotation-parser.js +83 -0
- package/build/sync/annotation-parser.js.map +1 -0
- package/build/sync/field-aliases.d.ts +35 -0
- package/build/sync/field-aliases.d.ts.map +1 -0
- package/build/sync/field-aliases.js +76 -0
- package/build/sync/field-aliases.js.map +1 -0
- package/build/sync/html-converter.d.ts.map +1 -1
- package/build/sync/html-converter.js +12 -2
- package/build/sync/html-converter.js.map +1 -1
- package/build/sync/html-detection.d.ts +18 -65
- package/build/sync/html-detection.d.ts.map +1 -1
- package/build/sync/html-detection.js +72 -113
- package/build/sync/html-detection.js.map +1 -1
- package/build/sync/image-handler.d.ts +66 -0
- package/build/sync/image-handler.d.ts.map +1 -0
- package/build/sync/image-handler.js +135 -0
- package/build/sync/image-handler.js.map +1 -0
- package/build/sync/image-manifest.d.ts +66 -0
- package/build/sync/image-manifest.d.ts.map +1 -0
- package/build/sync/image-manifest.js +96 -0
- package/build/sync/image-manifest.js.map +1 -0
- package/build/sync/image-sync.d.ts +88 -0
- package/build/sync/image-sync.d.ts.map +1 -0
- package/build/sync/image-sync.js +274 -0
- package/build/sync/image-sync.js.map +1 -0
- package/build/sync/index.d.ts +7 -0
- package/build/sync/index.d.ts.map +1 -1
- package/build/sync/index.js +7 -0
- package/build/sync/index.js.map +1 -1
- package/build/sync/legacy-mappings.d.ts +37 -0
- package/build/sync/legacy-mappings.d.ts.map +1 -0
- package/build/sync/legacy-mappings.js +75 -0
- package/build/sync/legacy-mappings.js.map +1 -0
- package/build/sync/markdown-serializer.d.ts +54 -60
- package/build/sync/markdown-serializer.d.ts.map +1 -1
- package/build/sync/markdown-serializer.js +607 -545
- package/build/sync/markdown-serializer.js.map +1 -1
- package/build/sync/task-serializer.d.ts.map +1 -1
- package/build/sync/task-serializer.js +46 -8
- package/build/sync/task-serializer.js.map +1 -1
- package/build/sync/template-loader.d.ts +56 -0
- package/build/sync/template-loader.d.ts.map +1 -0
- package/build/sync/template-loader.js +138 -0
- package/build/sync/template-loader.js.map +1 -0
- package/build/sync/templates/bug.md +25 -0
- package/build/sync/templates/epic.md +23 -0
- package/build/sync/templates/feature.md +23 -0
- package/build/sync/templates/task.md +14 -0
- package/build/sync/templates/user-story.md +26 -0
- package/build/tool-examples.d.ts +20 -0
- package/build/tool-examples.d.ts.map +1 -1
- package/build/tool-examples.js +41 -0
- package/build/tool-examples.js.map +1 -1
- package/build/tools/build-tools.d.ts.map +1 -1
- package/build/tools/build-tools.js +7 -6
- package/build/tools/build-tools.js.map +1 -1
- package/build/tools/checklist-tools.d.ts.map +1 -1
- package/build/tools/checklist-tools.js +6 -5
- package/build/tools/checklist-tools.js.map +1 -1
- package/build/tools/index.d.ts +1 -0
- package/build/tools/index.d.ts.map +1 -1
- package/build/tools/index.js +5 -2
- package/build/tools/index.js.map +1 -1
- package/build/tools/pull-request-tools.d.ts.map +1 -1
- package/build/tools/pull-request-tools.js +15 -14
- package/build/tools/pull-request-tools.js.map +1 -1
- package/build/tools/sync-tools.d.ts.map +1 -1
- package/build/tools/sync-tools.js +9 -8
- package/build/tools/sync-tools.js.map +1 -1
- package/build/tools/test-tools.d.ts +3 -0
- package/build/tools/test-tools.d.ts.map +1 -0
- package/build/tools/test-tools.js +184 -0
- package/build/tools/test-tools.js.map +1 -0
- package/build/tools/variable-group-tools.d.ts.map +1 -1
- package/build/tools/variable-group-tools.js +2 -1
- package/build/tools/variable-group-tools.js.map +1 -1
- package/build/tools/visualize-tools.d.ts.map +1 -1
- package/build/tools/visualize-tools.js +2 -1
- package/build/tools/visualize-tools.js.map +1 -1
- package/build/tools/wiki-tools.d.ts.map +1 -1
- package/build/tools/wiki-tools.js +4 -3
- package/build/tools/wiki-tools.js.map +1 -1
- package/build/tools/work-item-tools.d.ts.map +1 -1
- package/build/tools/work-item-tools.js +42 -11
- package/build/tools/work-item-tools.js.map +1 -1
- package/build/types.d.ts +2 -0
- package/build/types.d.ts.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frontmatter key → ADO reference name resolver.
|
|
3
|
+
*
|
|
4
|
+
* Frontmatter in synced markdown files uses raw ADO refnames as keys
|
|
5
|
+
* (e.g. `System.Title`, `Custom.ConsultancyProcess`). A small alias table
|
|
6
|
+
* provides friendly shortcuts for the most common fields so human-written
|
|
7
|
+
* files stay readable.
|
|
8
|
+
*
|
|
9
|
+
* Resolution rules:
|
|
10
|
+
* 1. Reserved keys (sync metadata) are returned as-is with a null refname.
|
|
11
|
+
* 2. Alias → refname lookup (case-sensitive).
|
|
12
|
+
* 3. Unknown keys are passed through unchanged (treated as refnames).
|
|
13
|
+
*/
|
|
14
|
+
/** Keys that are sync metadata, never sent to ADO as field updates. */
|
|
15
|
+
export const RESERVED_FRONTMATTER_KEYS = new Set([
|
|
16
|
+
'id',
|
|
17
|
+
'type',
|
|
18
|
+
'project',
|
|
19
|
+
'parent',
|
|
20
|
+
'parentTitle',
|
|
21
|
+
'url',
|
|
22
|
+
'lastSyncedRevision',
|
|
23
|
+
'lastSyncedAt',
|
|
24
|
+
]);
|
|
25
|
+
/**
|
|
26
|
+
* Friendly aliases → ADO reference names. Add entries here when a field is
|
|
27
|
+
* common enough across clients to deserve a short key.
|
|
28
|
+
*/
|
|
29
|
+
export const FIELD_ALIASES = {
|
|
30
|
+
title: 'System.Title',
|
|
31
|
+
state: 'System.State',
|
|
32
|
+
assignedTo: 'System.AssignedTo',
|
|
33
|
+
areaPath: 'System.AreaPath',
|
|
34
|
+
iterationPath: 'System.IterationPath',
|
|
35
|
+
tags: 'System.Tags',
|
|
36
|
+
priority: 'Microsoft.VSTS.Common.Priority',
|
|
37
|
+
severity: 'Microsoft.VSTS.Common.Severity',
|
|
38
|
+
storyPoints: 'Microsoft.VSTS.Scheduling.StoryPoints',
|
|
39
|
+
remainingWork: 'Microsoft.VSTS.Scheduling.RemainingWork',
|
|
40
|
+
effort: 'Microsoft.VSTS.Scheduling.Effort',
|
|
41
|
+
originalEstimate: 'Microsoft.VSTS.Scheduling.OriginalEstimate',
|
|
42
|
+
completedWork: 'Microsoft.VSTS.Scheduling.CompletedWork',
|
|
43
|
+
moscow: 'Custom.MoSCoW',
|
|
44
|
+
};
|
|
45
|
+
/** Reverse lookup: refname → canonical alias (first alias that resolves back). */
|
|
46
|
+
const REFNAME_TO_ALIAS = (() => {
|
|
47
|
+
const map = {};
|
|
48
|
+
for (const [alias, refname] of Object.entries(FIELD_ALIASES)) {
|
|
49
|
+
if (!(refname in map))
|
|
50
|
+
map[refname] = alias;
|
|
51
|
+
}
|
|
52
|
+
return map;
|
|
53
|
+
})();
|
|
54
|
+
/**
|
|
55
|
+
* Resolve a frontmatter key to its ADO refname, or null if the key is reserved
|
|
56
|
+
* sync metadata.
|
|
57
|
+
*/
|
|
58
|
+
export function resolveRefname(key) {
|
|
59
|
+
if (RESERVED_FRONTMATTER_KEYS.has(key))
|
|
60
|
+
return null;
|
|
61
|
+
return FIELD_ALIASES[key] ?? key;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* When writing frontmatter from ADO data, pick the friendliest key for a given
|
|
65
|
+
* refname (alias if one exists, else the refname itself).
|
|
66
|
+
*/
|
|
67
|
+
export function preferredKey(refname) {
|
|
68
|
+
return REFNAME_TO_ALIAS[refname] ?? refname;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* True when a frontmatter key is reserved sync metadata (not an ADO field).
|
|
72
|
+
*/
|
|
73
|
+
export function isReservedKey(key) {
|
|
74
|
+
return RESERVED_FRONTMATTER_KEYS.has(key);
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=field-aliases.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"field-aliases.js","sourceRoot":"","sources":["../../src/sync/field-aliases.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,uEAAuE;AACvE,MAAM,CAAC,MAAM,yBAAyB,GAAG,IAAI,GAAG,CAAS;IACvD,IAAI;IACJ,MAAM;IACN,SAAS;IACT,QAAQ;IACR,aAAa;IACb,KAAK;IACL,oBAAoB;IACpB,cAAc;CACf,CAAC,CAAC;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAA2B;IACnD,KAAK,EAAE,cAAc;IACrB,KAAK,EAAE,cAAc;IACrB,UAAU,EAAE,mBAAmB;IAC/B,QAAQ,EAAE,iBAAiB;IAC3B,aAAa,EAAE,sBAAsB;IACrC,IAAI,EAAE,aAAa;IACnB,QAAQ,EAAE,gCAAgC;IAC1C,QAAQ,EAAE,gCAAgC;IAC1C,WAAW,EAAE,uCAAuC;IACpD,aAAa,EAAE,yCAAyC;IACxD,MAAM,EAAE,kCAAkC;IAC1C,gBAAgB,EAAE,4CAA4C;IAC9D,aAAa,EAAE,yCAAyC;IACxD,MAAM,EAAE,eAAe;CACxB,CAAC;AAEF,kFAAkF;AAClF,MAAM,gBAAgB,GAA2B,CAAC,GAAG,EAAE;IACrD,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,KAAK,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC;QAC7D,IAAI,CAAC,CAAC,OAAO,IAAI,GAAG,CAAC;YAAE,GAAG,CAAC,OAAO,CAAC,GAAG,KAAK,CAAC;IAC9C,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC,CAAC,EAAE,CAAC;AAEL;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,IAAI,yBAAyB,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACpD,OAAO,aAAa,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC;AACnC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,OAAe;IAC1C,OAAO,gBAAgB,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC;AAC9C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,GAAW;IACvC,OAAO,yBAAyB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAC5C,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"html-converter.d.ts","sourceRoot":"","sources":["../../src/sync/html-converter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"html-converter.d.ts","sourceRoot":"","sources":["../../src/sync/html-converter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAmCH;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CA0BnD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,2BAA2B,CAC/C,OAAO,EAAE;IAAE,cAAc,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,CAAA;CAAE,EACrG,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAClC,eAAe,EAAE,MAAM,EAAE,GACxB,OAAO,CAAC,MAAM,EAAE,CAAC,CAiCnB"}
|
|
@@ -18,10 +18,20 @@ turndown.addRule('adoSpan', {
|
|
|
18
18
|
filter: ['span'],
|
|
19
19
|
replacement: (content) => content,
|
|
20
20
|
});
|
|
21
|
-
// Remove empty divs that ADO sometimes creates
|
|
21
|
+
// Remove empty divs that ADO sometimes creates.
|
|
22
|
+
// IMPORTANT: do NOT strip divs whose only "content" is a meaningful element
|
|
23
|
+
// like <img>, <iframe>, <video> etc. — those have no text content but are
|
|
24
|
+
// not visually empty and stripping them silently deletes embedded media.
|
|
22
25
|
turndown.addRule('emptyDiv', {
|
|
23
26
|
filter: (node) => {
|
|
24
|
-
|
|
27
|
+
if (node.nodeName !== 'DIV')
|
|
28
|
+
return false;
|
|
29
|
+
if (node.textContent?.trim())
|
|
30
|
+
return false;
|
|
31
|
+
if (node.querySelector?.('img, iframe, video, audio, picture, embed, object, svg, canvas')) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
return true;
|
|
25
35
|
},
|
|
26
36
|
replacement: () => '',
|
|
27
37
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"html-converter.js","sourceRoot":"","sources":["../../src/sync/html-converter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,eAAe,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,MAAM,QAAQ,GAAG,IAAI,eAAe,CAAC;IACnC,YAAY,EAAE,KAAK,EAAS,uBAAuB;IACnD,gBAAgB,EAAE,GAAG,EAAO,kBAAkB;IAC9C,cAAc,EAAE,QAAQ,EAAI,0BAA0B;IACtD,WAAW,EAAE,GAAG,EAAY,qBAAqB;IACjD,eAAe,EAAE,IAAI,EAAO,oBAAoB;CACjD,CAAC,CAAC;AAEH,0DAA0D;AAC1D,QAAQ,CAAC,OAAO,CAAC,SAAS,EAAE;IAC1B,MAAM,EAAE,CAAC,MAAM,CAAC;IAChB,WAAW,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO;CAClC,CAAC,CAAC;AAEH
|
|
1
|
+
{"version":3,"file":"html-converter.js","sourceRoot":"","sources":["../../src/sync/html-converter.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,93 +1,46 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* HTML
|
|
2
|
+
* HTML detection for ADO large-text fields.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Additional custom fields that can be synced.
|
|
9
|
-
* These are configurable via environment variables since field names may vary by organization.
|
|
4
|
+
* ADO supports both HTML and Markdown for long-text fields. Sync only
|
|
5
|
+
* works against markdown, so before pulling (or pushing) we auto-detect
|
|
6
|
+
* HTML and either convert it or report it as skipped.
|
|
10
7
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
|
|
14
|
-
export interface AdditionalFieldConfig {
|
|
15
|
-
howToTest: string;
|
|
16
|
-
deploymentInformation: string;
|
|
17
|
-
predeploymentSteps: string;
|
|
18
|
-
postdeploymentSteps: string;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Get additional field configuration from environment variables
|
|
22
|
-
* Falls back to common default field names
|
|
23
|
-
*/
|
|
24
|
-
export declare function getAdditionalFieldConfig(): AdditionalFieldConfig;
|
|
25
|
-
/**
|
|
26
|
-
* Human-readable names for additional fields (for display/logging)
|
|
27
|
-
*/
|
|
28
|
-
export declare const ADDITIONAL_FIELD_DISPLAY_NAMES: Record<keyof AdditionalFieldConfig, string>;
|
|
29
|
-
/**
|
|
30
|
-
* Large text fields that support markdown format in Azure DevOps.
|
|
31
|
-
* These are the standard ADO fields that can be set to markdown format.
|
|
8
|
+
* This module is intentionally generic — it no longer maintains a fixed
|
|
9
|
+
* list of "additional" custom fields. Callers pass in the refname list
|
|
10
|
+
* they care about (usually derived from the loaded template).
|
|
32
11
|
*/
|
|
33
|
-
export declare const LARGE_TEXT_FIELDS: readonly ["System.Description", "Microsoft.VSTS.Common.AcceptanceCriteria", "Microsoft.VSTS.TCM.ReproSteps"];
|
|
34
12
|
/**
|
|
35
|
-
*
|
|
36
|
-
* Used to determine which fields should have markdown format set.
|
|
13
|
+
* Standard ADO large-text fields with native markdown support.
|
|
37
14
|
*/
|
|
38
|
-
export declare
|
|
15
|
+
export declare const STANDARD_LARGE_TEXT_FIELDS: readonly ["System.Description", "Microsoft.VSTS.Common.AcceptanceCriteria", "Microsoft.VSTS.TCM.ReproSteps"];
|
|
39
16
|
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* @returns true if content appears to be HTML, false if markdown or empty
|
|
17
|
+
* Return the list of large-text fields to check for a work item. Combines
|
|
18
|
+
* the standard set with any caller-supplied custom refnames (from the
|
|
19
|
+
* loaded template).
|
|
44
20
|
*/
|
|
21
|
+
export declare function getAllLargeTextFields(customRefnames?: string[]): string[];
|
|
45
22
|
export declare function isHtmlContent(content: string | null | undefined): boolean;
|
|
46
|
-
/**
|
|
47
|
-
* Field format result type
|
|
48
|
-
*/
|
|
49
23
|
export type FieldFormat = 'markdown' | 'html' | 'not_present';
|
|
50
|
-
/**
|
|
51
|
-
* Result of checking additional field formats
|
|
52
|
-
*/
|
|
53
|
-
export interface AdditionalFieldFormats {
|
|
54
|
-
howToTest: FieldFormat;
|
|
55
|
-
deploymentInformation: FieldFormat;
|
|
56
|
-
predeploymentSteps: FieldFormat;
|
|
57
|
-
postdeploymentSteps: FieldFormat;
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Full field format check result
|
|
61
|
-
*/
|
|
62
24
|
export interface FieldFormatResult {
|
|
63
25
|
description: 'markdown' | 'html';
|
|
64
26
|
acceptanceCriteria: 'markdown' | 'html';
|
|
27
|
+
reproSteps: 'markdown' | 'html' | 'not_present';
|
|
65
28
|
ready: boolean;
|
|
66
29
|
warnings: string[];
|
|
67
|
-
|
|
30
|
+
/** Format for each custom body field, keyed by refname. */
|
|
31
|
+
additionalFields: Record<string, FieldFormat>;
|
|
68
32
|
details: {
|
|
69
33
|
descriptionLength: number;
|
|
70
34
|
acceptanceCriteriaLength: number;
|
|
35
|
+
reproStepsLength: number;
|
|
71
36
|
};
|
|
72
37
|
}
|
|
73
|
-
/**
|
|
74
|
-
* Check field formats for a work item
|
|
75
|
-
*
|
|
76
|
-
* Primary fields (Description, Acceptance Criteria): If HTML, work item is not ready for sync.
|
|
77
|
-
* Secondary fields (How to Test, Deployment fields): If HTML, warn but allow sync (skip those fields).
|
|
78
|
-
*
|
|
79
|
-
* @param workItem - The work item object from ADO API
|
|
80
|
-
* @returns Object with field format information
|
|
81
|
-
*/
|
|
82
38
|
export declare function checkFieldFormats(workItem: {
|
|
83
39
|
fields?: {
|
|
84
40
|
'System.Description'?: string;
|
|
85
41
|
'Microsoft.VSTS.Common.AcceptanceCriteria'?: string;
|
|
86
42
|
[key: string]: any;
|
|
87
43
|
};
|
|
88
|
-
}): FieldFormatResult;
|
|
89
|
-
/**
|
|
90
|
-
* Get human-readable conversion instructions
|
|
91
|
-
*/
|
|
44
|
+
}, additionalRefnames?: string[]): FieldFormatResult;
|
|
92
45
|
export declare function getConversionInstructions(): string;
|
|
93
46
|
//# sourceMappingURL=html-detection.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"html-detection.d.ts","sourceRoot":"","sources":["../../src/sync/html-detection.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"html-detection.d.ts","sourceRoot":"","sources":["../../src/sync/html-detection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH;;GAEG;AACH,eAAO,MAAM,0BAA0B,8GAI7B,CAAC;AAEX;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,cAAc,GAAE,MAAM,EAAO,GAAG,MAAM,EAAE,CAG7E;AAgCD,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CASzE;AAMD,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,MAAM,GAAG,aAAa,CAAC;AAE9D,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,UAAU,GAAG,MAAM,CAAC;IACjC,kBAAkB,EAAE,UAAU,GAAG,MAAM,CAAC;IACxC,UAAU,EAAE,UAAU,GAAG,MAAM,GAAG,aAAa,CAAC;IAChD,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,2DAA2D;IAC3D,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAC9C,OAAO,EAAE;QACP,iBAAiB,EAAE,MAAM,CAAC;QAC1B,wBAAwB,EAAE,MAAM,CAAC;QACjC,gBAAgB,EAAE,MAAM,CAAC;KAC1B,CAAC;CACH;AAED,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE;IACR,MAAM,CAAC,EAAE;QACP,oBAAoB,CAAC,EAAE,MAAM,CAAC;QAC9B,0CAA0C,CAAC,EAAE,MAAM,CAAC;QACpD,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;KACpB,CAAC;CACH,EACD,kBAAkB,GAAE,MAAM,EAAO,GAChC,iBAAiB,CAqDnB;AAED,wBAAgB,yBAAyB,IAAI,MAAM,CAUlD"}
|
|
@@ -1,160 +1,119 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* HTML
|
|
2
|
+
* HTML detection for ADO large-text fields.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
|
|
11
|
-
export function getAdditionalFieldConfig() {
|
|
12
|
-
return {
|
|
13
|
-
howToTest: process.env.AZUREDEVOPS_SYNC_FIELD_HOW_TO_TEST || 'Custom.Howtotest',
|
|
14
|
-
deploymentInformation: process.env.AZUREDEVOPS_SYNC_FIELD_DEPLOYMENT_INFO || 'Custom.Deploymentinformation',
|
|
15
|
-
predeploymentSteps: process.env.AZUREDEVOPS_SYNC_FIELD_PREDEPLOY || 'Custom.7519d1bc-5305-4905-822b-2b380e61b154',
|
|
16
|
-
postdeploymentSteps: process.env.AZUREDEVOPS_SYNC_FIELD_POSTDEPLOY || 'Custom.abd6763f-a242-4938-85ed-bda419e34e7e',
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Human-readable names for additional fields (for display/logging)
|
|
4
|
+
* ADO supports both HTML and Markdown for long-text fields. Sync only
|
|
5
|
+
* works against markdown, so before pulling (or pushing) we auto-detect
|
|
6
|
+
* HTML and either convert it or report it as skipped.
|
|
7
|
+
*
|
|
8
|
+
* This module is intentionally generic — it no longer maintains a fixed
|
|
9
|
+
* list of "additional" custom fields. Callers pass in the refname list
|
|
10
|
+
* they care about (usually derived from the loaded template).
|
|
21
11
|
*/
|
|
22
|
-
export const ADDITIONAL_FIELD_DISPLAY_NAMES = {
|
|
23
|
-
howToTest: 'How to Test',
|
|
24
|
-
deploymentInformation: 'Deployment Information',
|
|
25
|
-
predeploymentSteps: 'Predeployment Steps',
|
|
26
|
-
postdeploymentSteps: 'Postdeployment Steps',
|
|
27
|
-
};
|
|
28
12
|
/**
|
|
29
|
-
*
|
|
30
|
-
* These are the standard ADO fields that can be set to markdown format.
|
|
13
|
+
* Standard ADO large-text fields with native markdown support.
|
|
31
14
|
*/
|
|
32
|
-
export const
|
|
15
|
+
export const STANDARD_LARGE_TEXT_FIELDS = [
|
|
33
16
|
'System.Description',
|
|
34
17
|
'Microsoft.VSTS.Common.AcceptanceCriteria',
|
|
35
18
|
'Microsoft.VSTS.TCM.ReproSteps',
|
|
36
19
|
];
|
|
37
20
|
/**
|
|
38
|
-
*
|
|
39
|
-
*
|
|
21
|
+
* Return the list of large-text fields to check for a work item. Combines
|
|
22
|
+
* the standard set with any caller-supplied custom refnames (from the
|
|
23
|
+
* loaded template).
|
|
40
24
|
*/
|
|
41
|
-
export function getAllLargeTextFields() {
|
|
42
|
-
const
|
|
43
|
-
return [
|
|
44
|
-
...LARGE_TEXT_FIELDS,
|
|
45
|
-
config.howToTest,
|
|
46
|
-
config.deploymentInformation,
|
|
47
|
-
config.predeploymentSteps,
|
|
48
|
-
config.postdeploymentSteps,
|
|
49
|
-
];
|
|
25
|
+
export function getAllLargeTextFields(customRefnames = []) {
|
|
26
|
+
const set = new Set([...STANDARD_LARGE_TEXT_FIELDS, ...customRefnames]);
|
|
27
|
+
return [...set];
|
|
50
28
|
}
|
|
51
|
-
//
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// HTML detection (pattern-based heuristic)
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
52
32
|
const HTML_STRUCTURAL_PATTERNS = [
|
|
53
|
-
/<div[^>]*>/i,
|
|
54
|
-
/<p[^>]*>/i,
|
|
55
|
-
/<h[1-6][^>]*>/i,
|
|
56
|
-
/<strong[^>]*>/i,
|
|
57
|
-
/<em[^>]*>/i,
|
|
58
|
-
/<ul[^>]*>/i,
|
|
59
|
-
/<ol[^>]*>/i,
|
|
60
|
-
/<li[^>]*>/i,
|
|
61
|
-
/<table[^>]*>/i,
|
|
62
|
-
/<tr[^>]*>/i,
|
|
63
|
-
/<td[^>]*>/i,
|
|
64
|
-
/<span[^>]*>/i,
|
|
33
|
+
/<div[^>]*>/i,
|
|
34
|
+
/<p[^>]*>/i,
|
|
35
|
+
/<h[1-6][^>]*>/i,
|
|
36
|
+
/<strong[^>]*>/i,
|
|
37
|
+
/<em[^>]*>/i,
|
|
38
|
+
/<ul[^>]*>/i,
|
|
39
|
+
/<ol[^>]*>/i,
|
|
40
|
+
/<li[^>]*>/i,
|
|
41
|
+
/<table[^>]*>/i,
|
|
42
|
+
/<tr[^>]*>/i,
|
|
43
|
+
/<td[^>]*>/i,
|
|
44
|
+
/<span[^>]*>/i,
|
|
65
45
|
];
|
|
66
|
-
// Markdown patterns that indicate the content is already markdown
|
|
67
46
|
const MARKDOWN_PATTERNS = [
|
|
68
|
-
/^#{1,6}\s+/m,
|
|
69
|
-
/\*\*[^*]+\*\*/,
|
|
70
|
-
/^\s*[-*+]\s+/m,
|
|
71
|
-
/^\s*\d+\.\s+/m,
|
|
72
|
-
/\[.+?\]\(.+?\)/,
|
|
73
|
-
/```[\s\S]*?```/,
|
|
74
|
-
/`[^`]+`/,
|
|
75
|
-
/^\s*>\s+/m,
|
|
47
|
+
/^#{1,6}\s+/m,
|
|
48
|
+
/\*\*[^*]+\*\*/,
|
|
49
|
+
/^\s*[-*+]\s+/m,
|
|
50
|
+
/^\s*\d+\.\s+/m,
|
|
51
|
+
/\[.+?\]\(.+?\)/,
|
|
52
|
+
/```[\s\S]*?```/,
|
|
53
|
+
/`[^`]+`/,
|
|
54
|
+
/^\s*>\s+/m,
|
|
76
55
|
];
|
|
77
|
-
/**
|
|
78
|
-
* Check if content is HTML format (as opposed to markdown)
|
|
79
|
-
*
|
|
80
|
-
* @param content - The content string to check
|
|
81
|
-
* @returns true if content appears to be HTML, false if markdown or empty
|
|
82
|
-
*/
|
|
83
56
|
export function isHtmlContent(content) {
|
|
84
|
-
if (!content?.trim())
|
|
57
|
+
if (!content?.trim())
|
|
85
58
|
return false;
|
|
86
|
-
}
|
|
87
|
-
// If clear markdown syntax is present, treat as markdown (even if some inline HTML exists)
|
|
88
|
-
// This handles cases where users mix markdown with allowed inline HTML
|
|
89
59
|
for (const pattern of MARKDOWN_PATTERNS) {
|
|
90
|
-
if (pattern.test(content))
|
|
60
|
+
if (pattern.test(content))
|
|
91
61
|
return false;
|
|
92
|
-
}
|
|
93
62
|
}
|
|
94
|
-
// Check for structural HTML patterns that indicate this is HTML content
|
|
95
63
|
for (const pattern of HTML_STRUCTURAL_PATTERNS) {
|
|
96
|
-
if (pattern.test(content))
|
|
64
|
+
if (pattern.test(content))
|
|
97
65
|
return true;
|
|
98
|
-
}
|
|
99
66
|
}
|
|
100
|
-
// Default to markdown (plain text is valid markdown)
|
|
101
67
|
return false;
|
|
102
68
|
}
|
|
103
|
-
|
|
104
|
-
* Check field formats for a work item
|
|
105
|
-
*
|
|
106
|
-
* Primary fields (Description, Acceptance Criteria): If HTML, work item is not ready for sync.
|
|
107
|
-
* Secondary fields (How to Test, Deployment fields): If HTML, warn but allow sync (skip those fields).
|
|
108
|
-
*
|
|
109
|
-
* @param workItem - The work item object from ADO API
|
|
110
|
-
* @returns Object with field format information
|
|
111
|
-
*/
|
|
112
|
-
export function checkFieldFormats(workItem) {
|
|
69
|
+
export function checkFieldFormats(workItem, additionalRefnames = []) {
|
|
113
70
|
const fields = workItem.fields || {};
|
|
114
|
-
const fieldConfig = getAdditionalFieldConfig();
|
|
115
71
|
const description = fields['System.Description'] || '';
|
|
116
72
|
const acceptanceCriteria = fields['Microsoft.VSTS.Common.AcceptanceCriteria'] || '';
|
|
73
|
+
const reproSteps = fields['Microsoft.VSTS.TCM.ReproSteps'] || '';
|
|
117
74
|
const descriptionIsHtml = isHtmlContent(description);
|
|
118
75
|
const acceptanceCriteriaIsHtml = isHtmlContent(acceptanceCriteria);
|
|
119
|
-
|
|
76
|
+
const reproStepsIsHtml = isHtmlContent(reproSteps);
|
|
120
77
|
const warnings = [];
|
|
121
|
-
const additionalFields = {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
const checkAdditionalField = (fieldName, configKey) => {
|
|
129
|
-
const content = fields[fieldName];
|
|
130
|
-
if (!content || !content.trim()) {
|
|
131
|
-
return 'not_present';
|
|
78
|
+
const additionalFields = {};
|
|
79
|
+
for (const refname of additionalRefnames) {
|
|
80
|
+
if (refname === 'System.Description' ||
|
|
81
|
+
refname === 'Microsoft.VSTS.Common.AcceptanceCriteria' ||
|
|
82
|
+
refname === 'Microsoft.VSTS.TCM.ReproSteps') {
|
|
83
|
+
// Covered by the dedicated slots above.
|
|
84
|
+
continue;
|
|
132
85
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
86
|
+
const content = fields[refname];
|
|
87
|
+
if (!content || (typeof content === 'string' && !content.trim())) {
|
|
88
|
+
additionalFields[refname] = 'not_present';
|
|
89
|
+
continue;
|
|
136
90
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
91
|
+
if (typeof content === 'string' && isHtmlContent(content)) {
|
|
92
|
+
warnings.push(`${refname} is HTML - will be skipped`);
|
|
93
|
+
additionalFields[refname] = 'html';
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
additionalFields[refname] = 'markdown';
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
let reproStepsFormat = 'not_present';
|
|
100
|
+
if (reproSteps && reproSteps.trim()) {
|
|
101
|
+
reproStepsFormat = reproStepsIsHtml ? 'html' : 'markdown';
|
|
102
|
+
}
|
|
143
103
|
return {
|
|
144
104
|
description: descriptionIsHtml ? 'html' : 'markdown',
|
|
145
105
|
acceptanceCriteria: acceptanceCriteriaIsHtml ? 'html' : 'markdown',
|
|
146
|
-
|
|
106
|
+
reproSteps: reproStepsFormat,
|
|
107
|
+
ready: !descriptionIsHtml && !acceptanceCriteriaIsHtml && reproStepsFormat !== 'html',
|
|
147
108
|
warnings,
|
|
148
109
|
additionalFields,
|
|
149
110
|
details: {
|
|
150
111
|
descriptionLength: description.length,
|
|
151
112
|
acceptanceCriteriaLength: acceptanceCriteria.length,
|
|
152
|
-
|
|
113
|
+
reproStepsLength: reproSteps.length,
|
|
114
|
+
},
|
|
153
115
|
};
|
|
154
116
|
}
|
|
155
|
-
/**
|
|
156
|
-
* Get human-readable conversion instructions
|
|
157
|
-
*/
|
|
158
117
|
export function getConversionInstructions() {
|
|
159
118
|
return `To convert HTML fields to markdown in Azure DevOps:
|
|
160
119
|
1. Open the work item in Azure DevOps
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"html-detection.js","sourceRoot":"","sources":["../../src/sync/html-detection.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"html-detection.js","sourceRoot":"","sources":["../../src/sync/html-detection.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image Reference Handler
|
|
3
|
+
*
|
|
4
|
+
* Detects and rewrites image references in ADO work item content.
|
|
5
|
+
*
|
|
6
|
+
* Two source forms are recognised:
|
|
7
|
+
* - HTML <img src="..."> (raw HTML, typically pre-conversion)
|
|
8
|
+
* - Markdown  (post Turndown conversion or hand-written)
|
|
9
|
+
*
|
|
10
|
+
* Each <img>/![] reference is parsed; if the URL points at an ADO work item
|
|
11
|
+
* attachment (`_apis/wit/attachments/{guid}`), we extract the GUID, fileName,
|
|
12
|
+
* and project GUID. Callers can then download the file and rewrite the source
|
|
13
|
+
* to a local relative path — or, on push, walk the manifest and rewrite local
|
|
14
|
+
* paths back to the original ADO URL.
|
|
15
|
+
*/
|
|
16
|
+
export interface AdoAttachmentRef {
|
|
17
|
+
/** Attachment GUID extracted from the URL path */
|
|
18
|
+
guid: string;
|
|
19
|
+
/** Filename inferred from the `fileName` query param or the alt text */
|
|
20
|
+
fileName: string;
|
|
21
|
+
/** Project GUID from the URL (e.g. e4476c68-75d1-4d4b-...) */
|
|
22
|
+
projectGuid?: string;
|
|
23
|
+
/** The full original URL */
|
|
24
|
+
originalUrl: string;
|
|
25
|
+
}
|
|
26
|
+
export interface ImageRef {
|
|
27
|
+
/** Original src/url string from the markdown or HTML */
|
|
28
|
+
originalSrc: string;
|
|
29
|
+
/** True when the src is an HTML <img src="..."> tag (vs markdown ![]()) */
|
|
30
|
+
isHtmlTag: boolean;
|
|
31
|
+
/** Index of the match in the source string (for ordered processing) */
|
|
32
|
+
index: number;
|
|
33
|
+
/** Length of the matched substring */
|
|
34
|
+
length: number;
|
|
35
|
+
/** Parsed ADO attachment metadata, when the URL is an ADO attachment */
|
|
36
|
+
adoAttachment?: AdoAttachmentRef;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Parse an ADO work item attachment URL.
|
|
40
|
+
*
|
|
41
|
+
* Recognised patterns:
|
|
42
|
+
* https://dev.azure.com/{org}/{projectGuid}/_apis/wit/attachments/{guid}?fileName=image.png&...
|
|
43
|
+
* https://dev.azure.com/{org}/_apis/wit/attachments/{guid}?fileName=image.png&...
|
|
44
|
+
*
|
|
45
|
+
* Returns null when the URL is not an ADO attachment URL.
|
|
46
|
+
*/
|
|
47
|
+
export declare function parseAdoAttachmentUrl(url: string): AdoAttachmentRef | null;
|
|
48
|
+
/**
|
|
49
|
+
* Extract every image reference from a string of HTML or markdown content.
|
|
50
|
+
*
|
|
51
|
+
* Returns refs in source order. Both HTML <img> tags and markdown ![]() are
|
|
52
|
+
* detected. Refs whose URL parses as an ADO attachment have `adoAttachment`
|
|
53
|
+
* populated.
|
|
54
|
+
*/
|
|
55
|
+
export declare function extractImageRefs(content: string): ImageRef[];
|
|
56
|
+
/**
|
|
57
|
+
* Rewrite image src/url values in content using a mapper function.
|
|
58
|
+
*
|
|
59
|
+
* The mapper receives the original src and the parsed ADO attachment (if any)
|
|
60
|
+
* and returns the new src to substitute. Returning null/undefined leaves the
|
|
61
|
+
* original src in place.
|
|
62
|
+
*
|
|
63
|
+
* Both HTML <img src="..."> and markdown  forms are rewritten.
|
|
64
|
+
*/
|
|
65
|
+
export declare function rewriteImageSrcs(content: string, mapper: (src: string, ado: AdoAttachmentRef | undefined) => string | null | undefined): string;
|
|
66
|
+
//# sourceMappingURL=image-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-handler.d.ts","sourceRoot":"","sources":["../../src/sync/image-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,wEAAwE;IACxE,QAAQ,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4BAA4B;IAC5B,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,QAAQ;IACvB,wDAAwD;IACxD,WAAW,EAAE,MAAM,CAAC;IACpB,2EAA2E;IAC3E,SAAS,EAAE,OAAO,CAAC;IACnB,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,sCAAsC;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,aAAa,CAAC,EAAE,gBAAgB,CAAC;CAClC;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAgC1E;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,EAAE,CAmC5D;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,gBAAgB,GAAG,SAAS,KAAK,MAAM,GAAG,IAAI,GAAG,SAAS,GACpF,MAAM,CA6BR"}
|