doc-detective 4.0.1 → 4.0.2-dev.10

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 (208) hide show
  1. package/dist/agents/adapters/claude-code.d.ts +77 -0
  2. package/dist/agents/adapters/claude-code.d.ts.map +1 -0
  3. package/dist/agents/adapters/claude-code.js +461 -0
  4. package/dist/agents/adapters/claude-code.js.map +1 -0
  5. package/dist/agents/adapters/codex.d.ts +64 -0
  6. package/dist/agents/adapters/codex.d.ts.map +1 -0
  7. package/dist/agents/adapters/codex.js +299 -0
  8. package/dist/agents/adapters/codex.js.map +1 -0
  9. package/dist/agents/adapters/copilot-cli.d.ts +29 -0
  10. package/dist/agents/adapters/copilot-cli.d.ts.map +1 -0
  11. package/dist/agents/adapters/copilot-cli.js +195 -0
  12. package/dist/agents/adapters/copilot-cli.js.map +1 -0
  13. package/dist/agents/adapters/gemini-cli.d.ts +29 -0
  14. package/dist/agents/adapters/gemini-cli.d.ts.map +1 -0
  15. package/dist/agents/adapters/gemini-cli.js +207 -0
  16. package/dist/agents/adapters/gemini-cli.js.map +1 -0
  17. package/dist/agents/adapters/opencode.d.ts +67 -0
  18. package/dist/agents/adapters/opencode.d.ts.map +1 -0
  19. package/dist/agents/adapters/opencode.js +341 -0
  20. package/dist/agents/adapters/opencode.js.map +1 -0
  21. package/dist/agents/adapters/qwen-code.d.ts +30 -0
  22. package/dist/agents/adapters/qwen-code.d.ts.map +1 -0
  23. package/dist/agents/adapters/qwen-code.js +212 -0
  24. package/dist/agents/adapters/qwen-code.js.map +1 -0
  25. package/dist/agents/command.d.ts +11 -0
  26. package/dist/agents/command.d.ts.map +1 -0
  27. package/dist/agents/command.js +41 -0
  28. package/dist/agents/command.js.map +1 -0
  29. package/dist/agents/fetcher.d.ts +30 -0
  30. package/dist/agents/fetcher.d.ts.map +1 -0
  31. package/dist/agents/fetcher.js +112 -0
  32. package/dist/agents/fetcher.js.map +1 -0
  33. package/dist/agents/prompts.d.ts +24 -0
  34. package/dist/agents/prompts.d.ts.map +1 -0
  35. package/dist/agents/prompts.js +74 -0
  36. package/dist/agents/prompts.js.map +1 -0
  37. package/dist/agents/registry.d.ts +4 -0
  38. package/dist/agents/registry.d.ts.map +1 -0
  39. package/dist/agents/registry.js +25 -0
  40. package/dist/agents/registry.js.map +1 -0
  41. package/dist/agents/runner.d.ts +13 -0
  42. package/dist/agents/runner.d.ts.map +1 -0
  43. package/dist/agents/runner.js +155 -0
  44. package/dist/agents/runner.js.map +1 -0
  45. package/dist/agents/spawn-helper.d.ts +33 -0
  46. package/dist/agents/spawn-helper.d.ts.map +1 -0
  47. package/dist/agents/spawn-helper.js +98 -0
  48. package/dist/agents/spawn-helper.js.map +1 -0
  49. package/dist/agents/types.d.ts +41 -0
  50. package/dist/agents/types.d.ts.map +1 -0
  51. package/dist/agents/types.js +2 -0
  52. package/dist/agents/types.js.map +1 -0
  53. package/dist/cli.js +42 -10
  54. package/dist/cli.js.map +1 -1
  55. package/dist/common/src/detectTests.d.ts +101 -0
  56. package/dist/common/src/detectTests.d.ts.map +1 -0
  57. package/dist/common/src/detectTests.js +693 -0
  58. package/dist/common/src/detectTests.js.map +1 -0
  59. package/dist/common/src/fileTypes.d.ts +35 -0
  60. package/dist/common/src/fileTypes.d.ts.map +1 -0
  61. package/dist/common/src/fileTypes.js +303 -0
  62. package/dist/common/src/fileTypes.js.map +1 -0
  63. package/dist/common/src/index.d.ts +10 -0
  64. package/dist/common/src/index.d.ts.map +1 -0
  65. package/dist/common/src/index.js +5 -0
  66. package/dist/common/src/index.js.map +1 -0
  67. package/dist/common/src/schemas/index.d.ts +5 -0
  68. package/dist/common/src/schemas/index.d.ts.map +1 -0
  69. package/dist/common/src/schemas/index.js +3 -0
  70. package/dist/common/src/schemas/index.js.map +1 -0
  71. package/dist/common/src/schemas/schemas.json +128711 -0
  72. package/dist/common/src/types/generated/checkLink_v3.d.ts +42 -0
  73. package/dist/common/src/types/generated/checkLink_v3.d.ts.map +1 -0
  74. package/dist/common/src/types/generated/checkLink_v3.js +7 -0
  75. package/dist/common/src/types/generated/checkLink_v3.js.map +1 -0
  76. package/dist/common/src/types/generated/click_v3.d.ts +16 -0
  77. package/dist/common/src/types/generated/click_v3.d.ts.map +1 -0
  78. package/dist/common/src/types/generated/click_v3.js +7 -0
  79. package/dist/common/src/types/generated/click_v3.js.map +1 -0
  80. package/dist/common/src/types/generated/config_v3.d.ts +402 -0
  81. package/dist/common/src/types/generated/config_v3.d.ts.map +1 -0
  82. package/dist/common/src/types/generated/config_v3.js +7 -0
  83. package/dist/common/src/types/generated/config_v3.js.map +1 -0
  84. package/dist/common/src/types/generated/context_v3.d.ts +108 -0
  85. package/dist/common/src/types/generated/context_v3.d.ts.map +1 -0
  86. package/dist/common/src/types/generated/context_v3.js +7 -0
  87. package/dist/common/src/types/generated/context_v3.js.map +1 -0
  88. package/dist/common/src/types/generated/dragAndDrop_v3.d.ts +37 -0
  89. package/dist/common/src/types/generated/dragAndDrop_v3.d.ts.map +1 -0
  90. package/dist/common/src/types/generated/dragAndDrop_v3.js +7 -0
  91. package/dist/common/src/types/generated/dragAndDrop_v3.js.map +1 -0
  92. package/dist/common/src/types/generated/endRecord_v3.d.ts +9 -0
  93. package/dist/common/src/types/generated/endRecord_v3.d.ts.map +1 -0
  94. package/dist/common/src/types/generated/endRecord_v3.js +7 -0
  95. package/dist/common/src/types/generated/endRecord_v3.js.map +1 -0
  96. package/dist/common/src/types/generated/find_v3.d.ts +16 -0
  97. package/dist/common/src/types/generated/find_v3.d.ts.map +1 -0
  98. package/dist/common/src/types/generated/find_v3.js +7 -0
  99. package/dist/common/src/types/generated/find_v3.js.map +1 -0
  100. package/dist/common/src/types/generated/goTo_v3.d.ts +46 -0
  101. package/dist/common/src/types/generated/goTo_v3.d.ts.map +1 -0
  102. package/dist/common/src/types/generated/goTo_v3.js +7 -0
  103. package/dist/common/src/types/generated/goTo_v3.js.map +1 -0
  104. package/dist/common/src/types/generated/httpRequest_v3.d.ts +16 -0
  105. package/dist/common/src/types/generated/httpRequest_v3.d.ts.map +1 -0
  106. package/dist/common/src/types/generated/httpRequest_v3.js +7 -0
  107. package/dist/common/src/types/generated/httpRequest_v3.js.map +1 -0
  108. package/dist/common/src/types/generated/loadCookie_v3.d.ts +16 -0
  109. package/dist/common/src/types/generated/loadCookie_v3.d.ts.map +1 -0
  110. package/dist/common/src/types/generated/loadCookie_v3.js +7 -0
  111. package/dist/common/src/types/generated/loadCookie_v3.js.map +1 -0
  112. package/dist/common/src/types/generated/loadVariables_v3.d.ts +9 -0
  113. package/dist/common/src/types/generated/loadVariables_v3.d.ts.map +1 -0
  114. package/dist/common/src/types/generated/loadVariables_v3.js +7 -0
  115. package/dist/common/src/types/generated/loadVariables_v3.js.map +1 -0
  116. package/dist/common/src/types/generated/openApi_v3.d.ts +62 -0
  117. package/dist/common/src/types/generated/openApi_v3.d.ts.map +1 -0
  118. package/dist/common/src/types/generated/openApi_v3.js +7 -0
  119. package/dist/common/src/types/generated/openApi_v3.js.map +1 -0
  120. package/dist/common/src/types/generated/record_v3.d.ts +32 -0
  121. package/dist/common/src/types/generated/record_v3.d.ts.map +1 -0
  122. package/dist/common/src/types/generated/record_v3.js +7 -0
  123. package/dist/common/src/types/generated/record_v3.js.map +1 -0
  124. package/dist/common/src/types/generated/report_v3.d.ts +174 -0
  125. package/dist/common/src/types/generated/report_v3.d.ts.map +1 -0
  126. package/dist/common/src/types/generated/report_v3.js +7 -0
  127. package/dist/common/src/types/generated/report_v3.js.map +1 -0
  128. package/dist/common/src/types/generated/resolvedTests_v3.d.ts +575 -0
  129. package/dist/common/src/types/generated/resolvedTests_v3.d.ts.map +1 -0
  130. package/dist/common/src/types/generated/resolvedTests_v3.js +7 -0
  131. package/dist/common/src/types/generated/resolvedTests_v3.js.map +1 -0
  132. package/dist/common/src/types/generated/runCode_v3.d.ts +57 -0
  133. package/dist/common/src/types/generated/runCode_v3.d.ts.map +1 -0
  134. package/dist/common/src/types/generated/runCode_v3.js +7 -0
  135. package/dist/common/src/types/generated/runCode_v3.js.map +1 -0
  136. package/dist/common/src/types/generated/runShell_v3.d.ts +56 -0
  137. package/dist/common/src/types/generated/runShell_v3.d.ts.map +1 -0
  138. package/dist/common/src/types/generated/runShell_v3.js +7 -0
  139. package/dist/common/src/types/generated/runShell_v3.js.map +1 -0
  140. package/dist/common/src/types/generated/saveCookie_v3.d.ts +16 -0
  141. package/dist/common/src/types/generated/saveCookie_v3.d.ts.map +1 -0
  142. package/dist/common/src/types/generated/saveCookie_v3.js +7 -0
  143. package/dist/common/src/types/generated/saveCookie_v3.js.map +1 -0
  144. package/dist/common/src/types/generated/screenshot_v3.d.ts +74 -0
  145. package/dist/common/src/types/generated/screenshot_v3.d.ts.map +1 -0
  146. package/dist/common/src/types/generated/screenshot_v3.js +7 -0
  147. package/dist/common/src/types/generated/screenshot_v3.js.map +1 -0
  148. package/dist/common/src/types/generated/sourceIntegration_v3.d.ts +30 -0
  149. package/dist/common/src/types/generated/sourceIntegration_v3.d.ts.map +1 -0
  150. package/dist/common/src/types/generated/sourceIntegration_v3.js +7 -0
  151. package/dist/common/src/types/generated/sourceIntegration_v3.js.map +1 -0
  152. package/dist/common/src/types/generated/spec_v3.d.ts +159 -0
  153. package/dist/common/src/types/generated/spec_v3.d.ts.map +1 -0
  154. package/dist/common/src/types/generated/spec_v3.js +7 -0
  155. package/dist/common/src/types/generated/spec_v3.js.map +1 -0
  156. package/dist/common/src/types/generated/step_v3.d.ts +1573 -0
  157. package/dist/common/src/types/generated/step_v3.d.ts.map +1 -0
  158. package/dist/common/src/types/generated/step_v3.js +7 -0
  159. package/dist/common/src/types/generated/step_v3.js.map +1 -0
  160. package/dist/common/src/types/generated/stopRecord_v3.d.ts +9 -0
  161. package/dist/common/src/types/generated/stopRecord_v3.d.ts.map +1 -0
  162. package/dist/common/src/types/generated/stopRecord_v3.js +7 -0
  163. package/dist/common/src/types/generated/stopRecord_v3.js.map +1 -0
  164. package/dist/common/src/types/generated/test_v3.d.ts +3521 -0
  165. package/dist/common/src/types/generated/test_v3.d.ts.map +1 -0
  166. package/dist/common/src/types/generated/test_v3.js +7 -0
  167. package/dist/common/src/types/generated/test_v3.js.map +1 -0
  168. package/dist/common/src/types/generated/type_v3.d.ts +54 -0
  169. package/dist/common/src/types/generated/type_v3.d.ts.map +1 -0
  170. package/dist/common/src/types/generated/type_v3.js +7 -0
  171. package/dist/common/src/types/generated/type_v3.js.map +1 -0
  172. package/dist/common/src/types/generated/wait_v3.d.ts +12 -0
  173. package/dist/common/src/types/generated/wait_v3.d.ts.map +1 -0
  174. package/dist/common/src/types/generated/wait_v3.js +7 -0
  175. package/dist/common/src/types/generated/wait_v3.js.map +1 -0
  176. package/dist/common/src/validate.d.ts +41 -0
  177. package/dist/common/src/validate.d.ts.map +1 -0
  178. package/dist/common/src/validate.js +557 -0
  179. package/dist/common/src/validate.js.map +1 -0
  180. package/dist/core/config.d.ts.map +1 -1
  181. package/dist/core/config.js +10 -0
  182. package/dist/core/config.js.map +1 -1
  183. package/dist/core/detectTests.d.ts.map +1 -1
  184. package/dist/core/detectTests.js +50 -2
  185. package/dist/core/detectTests.js.map +1 -1
  186. package/dist/core/integrations/heretto.d.ts +32 -0
  187. package/dist/core/integrations/heretto.d.ts.map +1 -1
  188. package/dist/core/integrations/heretto.js +368 -0
  189. package/dist/core/integrations/heretto.js.map +1 -1
  190. package/dist/core/tests/checkLink.d.ts.map +1 -1
  191. package/dist/core/tests/checkLink.js +136 -29
  192. package/dist/core/tests/checkLink.js.map +1 -1
  193. package/dist/core/tests/loadCookie.d.ts.map +1 -1
  194. package/dist/core/tests/loadCookie.js +12 -2
  195. package/dist/core/tests/loadCookie.js.map +1 -1
  196. package/dist/index.cjs +2083 -965
  197. package/dist/reporters/htmlReporter.d.ts +2 -0
  198. package/dist/reporters/htmlReporter.d.ts.map +1 -0
  199. package/dist/reporters/htmlReporter.js +1589 -0
  200. package/dist/reporters/htmlReporter.js.map +1 -0
  201. package/dist/utils.d.ts +2 -1
  202. package/dist/utils.d.ts.map +1 -1
  203. package/dist/utils.js +43 -10
  204. package/dist/utils.js.map +1 -1
  205. package/package.json +146 -135
  206. package/.doc-detective.json +0 -1
  207. package/CONTRIBUTIONS.md +0 -27
  208. package/scripts/createCjsWrapper.js +0 -31
package/dist/index.cjs CHANGED
@@ -107,6 +107,26 @@ var schemas_default = {
107
107
  307,
108
108
  308
109
109
  ]
110
+ },
111
+ headers: {
112
+ description: "Additional HTTP headers to include in the request. Merged on top of Doc Detective's default browser-mimicking headers. Useful for sites behind bot protection or WAFs that allowlist specific headers (for example, a Cloudflare Access service token or a `Cookie` with a `cf_clearance` value).",
113
+ default: {},
114
+ anyOf: [
115
+ {
116
+ title: "Request headers (object)",
117
+ description: "Headers to include in the HTTP request, in key/value format. Values must be strings.",
118
+ type: "object",
119
+ additionalProperties: {
120
+ type: "string"
121
+ },
122
+ properties: {}
123
+ },
124
+ {
125
+ title: "Request headers (string)",
126
+ description: "Headers to include in the HTTP request, as newline-separated values. For example, `X-Api-Key: abc123\nAuthorization: Bearer token`.",
127
+ type: "string"
128
+ }
129
+ ]
110
130
  }
111
131
  }
112
132
  }
@@ -170,6 +190,26 @@ var schemas_default = {
170
190
  307,
171
191
  308
172
192
  ]
193
+ },
194
+ headers: {
195
+ description: "Additional HTTP headers to include in the request. Merged on top of Doc Detective's default browser-mimicking headers. Useful for sites behind bot protection or WAFs that allowlist specific headers (for example, a Cloudflare Access service token or a `Cookie` with a `cf_clearance` value).",
196
+ default: {},
197
+ anyOf: [
198
+ {
199
+ title: "Request headers (object)",
200
+ description: "Headers to include in the HTTP request, in key/value format. Values must be strings.",
201
+ type: "object",
202
+ additionalProperties: {
203
+ type: "string"
204
+ },
205
+ properties: {}
206
+ },
207
+ {
208
+ title: "Request headers (string)",
209
+ description: "Headers to include in the HTTP request, as newline-separated values. For example, `X-Api-Key: abc123\nAuthorization: Bearer token`.",
210
+ type: "string"
211
+ }
212
+ ]
173
213
  }
174
214
  }
175
215
  }
@@ -494,6 +534,18 @@ var schemas_default = {
494
534
  type: "string",
495
535
  default: "."
496
536
  },
537
+ reporters: {
538
+ description: "Reporters to use when emitting test results. Built-in reporters: `terminal`, `json`, `html`. Custom reporters registered via `registerReporter()` can also be referenced by name.",
539
+ type: "array",
540
+ items: {
541
+ type: "string",
542
+ minLength: 1
543
+ },
544
+ default: [
545
+ "terminal",
546
+ "json"
547
+ ]
548
+ },
497
549
  recursive: {
498
550
  description: "If `true` searches `input`, `setup`, and `cleanup` paths recursively for test specifications and source files.",
499
551
  type: "boolean",
@@ -1559,6 +1611,26 @@ var schemas_default = {
1559
1611
  307,
1560
1612
  308
1561
1613
  ]
1614
+ },
1615
+ headers: {
1616
+ description: "Additional HTTP headers to include in the request. Merged on top of Doc Detective's default browser-mimicking headers. Useful for sites behind bot protection or WAFs that allowlist specific headers (for example, a Cloudflare Access service token or a `Cookie` with a `cf_clearance` value).",
1617
+ default: {},
1618
+ anyOf: [
1619
+ {
1620
+ title: "Request headers (object)",
1621
+ description: "Headers to include in the HTTP request, in key/value format. Values must be strings.",
1622
+ type: "object",
1623
+ additionalProperties: {
1624
+ type: "string"
1625
+ },
1626
+ properties: {}
1627
+ },
1628
+ {
1629
+ title: "Request headers (string)",
1630
+ description: "Headers to include in the HTTP request, as newline-separated values. For example, `X-Api-Key: abc123\nAuthorization: Bearer token`.",
1631
+ type: "string"
1632
+ }
1633
+ ]
1562
1634
  }
1563
1635
  }
1564
1636
  }
@@ -1622,6 +1694,26 @@ var schemas_default = {
1622
1694
  307,
1623
1695
  308
1624
1696
  ]
1697
+ },
1698
+ headers: {
1699
+ description: "Additional HTTP headers to include in the request. Merged on top of Doc Detective's default browser-mimicking headers. Useful for sites behind bot protection or WAFs that allowlist specific headers (for example, a Cloudflare Access service token or a `Cookie` with a `cf_clearance` value).",
1700
+ default: {},
1701
+ anyOf: [
1702
+ {
1703
+ title: "Request headers (object)",
1704
+ description: "Headers to include in the HTTP request, in key/value format. Values must be strings.",
1705
+ type: "object",
1706
+ additionalProperties: {
1707
+ type: "string"
1708
+ },
1709
+ properties: {}
1710
+ },
1711
+ {
1712
+ title: "Request headers (string)",
1713
+ description: "Headers to include in the HTTP request, as newline-separated values. For example, `X-Api-Key: abc123\nAuthorization: Bearer token`.",
1714
+ type: "string"
1715
+ }
1716
+ ]
1625
1717
  }
1626
1718
  }
1627
1719
  }
@@ -10182,6 +10274,26 @@ var schemas_default = {
10182
10274
  307,
10183
10275
  308
10184
10276
  ]
10277
+ },
10278
+ headers: {
10279
+ description: "Additional HTTP headers to include in the request. Merged on top of Doc Detective's default browser-mimicking headers. Useful for sites behind bot protection or WAFs that allowlist specific headers (for example, a Cloudflare Access service token or a `Cookie` with a `cf_clearance` value).",
10280
+ default: {},
10281
+ anyOf: [
10282
+ {
10283
+ title: "Request headers (object)",
10284
+ description: "Headers to include in the HTTP request, in key/value format. Values must be strings.",
10285
+ type: "object",
10286
+ additionalProperties: {
10287
+ type: "string"
10288
+ },
10289
+ properties: {}
10290
+ },
10291
+ {
10292
+ title: "Request headers (string)",
10293
+ description: "Headers to include in the HTTP request, as newline-separated values. For example, `X-Api-Key: abc123\nAuthorization: Bearer token`.",
10294
+ type: "string"
10295
+ }
10296
+ ]
10185
10297
  }
10186
10298
  }
10187
10299
  }
@@ -10245,6 +10357,26 @@ var schemas_default = {
10245
10357
  307,
10246
10358
  308
10247
10359
  ]
10360
+ },
10361
+ headers: {
10362
+ description: "Additional HTTP headers to include in the request. Merged on top of Doc Detective's default browser-mimicking headers. Useful for sites behind bot protection or WAFs that allowlist specific headers (for example, a Cloudflare Access service token or a `Cookie` with a `cf_clearance` value).",
10363
+ default: {},
10364
+ anyOf: [
10365
+ {
10366
+ title: "Request headers (object)",
10367
+ description: "Headers to include in the HTTP request, in key/value format. Values must be strings.",
10368
+ type: "object",
10369
+ additionalProperties: {
10370
+ type: "string"
10371
+ },
10372
+ properties: {}
10373
+ },
10374
+ {
10375
+ title: "Request headers (string)",
10376
+ description: "Headers to include in the HTTP request, as newline-separated values. For example, `X-Api-Key: abc123\nAuthorization: Bearer token`.",
10377
+ type: "string"
10378
+ }
10379
+ ]
10248
10380
  }
10249
10381
  }
10250
10382
  }
@@ -17845,6 +17977,11 @@ var schemas_default = {
17845
17977
  loadVariables: ".env",
17846
17978
  fileTypes: [
17847
17979
  "markdown"
17980
+ ],
17981
+ reporters: [
17982
+ "terminal",
17983
+ "json",
17984
+ "html"
17848
17985
  ]
17849
17986
  },
17850
17987
  {
@@ -22153,6 +22290,18 @@ var schemas_default = {
22153
22290
  type: "string",
22154
22291
  default: "."
22155
22292
  },
22293
+ reporters: {
22294
+ description: "Reporters to use when emitting test results. Built-in reporters: `terminal`, `json`, `html`. Custom reporters registered via `registerReporter()` can also be referenced by name.",
22295
+ type: "array",
22296
+ items: {
22297
+ type: "string",
22298
+ minLength: 1
22299
+ },
22300
+ default: [
22301
+ "terminal",
22302
+ "json"
22303
+ ]
22304
+ },
22156
22305
  recursive: {
22157
22306
  description: "If `true` searches `input`, `setup`, and `cleanup` paths recursively for test specifications and source files.",
22158
22307
  type: "boolean",
@@ -23218,6 +23367,26 @@ var schemas_default = {
23218
23367
  307,
23219
23368
  308
23220
23369
  ]
23370
+ },
23371
+ headers: {
23372
+ description: "Additional HTTP headers to include in the request. Merged on top of Doc Detective's default browser-mimicking headers. Useful for sites behind bot protection or WAFs that allowlist specific headers (for example, a Cloudflare Access service token or a `Cookie` with a `cf_clearance` value).",
23373
+ default: {},
23374
+ anyOf: [
23375
+ {
23376
+ title: "Request headers (object)",
23377
+ description: "Headers to include in the HTTP request, in key/value format. Values must be strings.",
23378
+ type: "object",
23379
+ additionalProperties: {
23380
+ type: "string"
23381
+ },
23382
+ properties: {}
23383
+ },
23384
+ {
23385
+ title: "Request headers (string)",
23386
+ description: "Headers to include in the HTTP request, as newline-separated values. For example, `X-Api-Key: abc123\nAuthorization: Bearer token`.",
23387
+ type: "string"
23388
+ }
23389
+ ]
23221
23390
  }
23222
23391
  }
23223
23392
  }
@@ -23281,6 +23450,26 @@ var schemas_default = {
23281
23450
  307,
23282
23451
  308
23283
23452
  ]
23453
+ },
23454
+ headers: {
23455
+ description: "Additional HTTP headers to include in the request. Merged on top of Doc Detective's default browser-mimicking headers. Useful for sites behind bot protection or WAFs that allowlist specific headers (for example, a Cloudflare Access service token or a `Cookie` with a `cf_clearance` value).",
23456
+ default: {},
23457
+ anyOf: [
23458
+ {
23459
+ title: "Request headers (object)",
23460
+ description: "Headers to include in the HTTP request, in key/value format. Values must be strings.",
23461
+ type: "object",
23462
+ additionalProperties: {
23463
+ type: "string"
23464
+ },
23465
+ properties: {}
23466
+ },
23467
+ {
23468
+ title: "Request headers (string)",
23469
+ description: "Headers to include in the HTTP request, as newline-separated values. For example, `X-Api-Key: abc123\nAuthorization: Bearer token`.",
23470
+ type: "string"
23471
+ }
23472
+ ]
23284
23473
  }
23285
23474
  }
23286
23475
  }
@@ -31841,6 +32030,26 @@ var schemas_default = {
31841
32030
  307,
31842
32031
  308
31843
32032
  ]
32033
+ },
32034
+ headers: {
32035
+ description: "Additional HTTP headers to include in the request. Merged on top of Doc Detective's default browser-mimicking headers. Useful for sites behind bot protection or WAFs that allowlist specific headers (for example, a Cloudflare Access service token or a `Cookie` with a `cf_clearance` value).",
32036
+ default: {},
32037
+ anyOf: [
32038
+ {
32039
+ title: "Request headers (object)",
32040
+ description: "Headers to include in the HTTP request, in key/value format. Values must be strings.",
32041
+ type: "object",
32042
+ additionalProperties: {
32043
+ type: "string"
32044
+ },
32045
+ properties: {}
32046
+ },
32047
+ {
32048
+ title: "Request headers (string)",
32049
+ description: "Headers to include in the HTTP request, as newline-separated values. For example, `X-Api-Key: abc123\nAuthorization: Bearer token`.",
32050
+ type: "string"
32051
+ }
32052
+ ]
31844
32053
  }
31845
32054
  }
31846
32055
  }
@@ -31904,6 +32113,26 @@ var schemas_default = {
31904
32113
  307,
31905
32114
  308
31906
32115
  ]
32116
+ },
32117
+ headers: {
32118
+ description: "Additional HTTP headers to include in the request. Merged on top of Doc Detective's default browser-mimicking headers. Useful for sites behind bot protection or WAFs that allowlist specific headers (for example, a Cloudflare Access service token or a `Cookie` with a `cf_clearance` value).",
32119
+ default: {},
32120
+ anyOf: [
32121
+ {
32122
+ title: "Request headers (object)",
32123
+ description: "Headers to include in the HTTP request, in key/value format. Values must be strings.",
32124
+ type: "object",
32125
+ additionalProperties: {
32126
+ type: "string"
32127
+ },
32128
+ properties: {}
32129
+ },
32130
+ {
32131
+ title: "Request headers (string)",
32132
+ description: "Headers to include in the HTTP request, as newline-separated values. For example, `X-Api-Key: abc123\nAuthorization: Bearer token`.",
32133
+ type: "string"
32134
+ }
32135
+ ]
31907
32136
  }
31908
32137
  }
31909
32138
  }
@@ -39504,6 +39733,11 @@ var schemas_default = {
39504
39733
  loadVariables: ".env",
39505
39734
  fileTypes: [
39506
39735
  "markdown"
39736
+ ],
39737
+ reporters: [
39738
+ "terminal",
39739
+ "json",
39740
+ "html"
39507
39741
  ]
39508
39742
  },
39509
39743
  {
@@ -41153,6 +41387,26 @@ var schemas_default = {
41153
41387
  307,
41154
41388
  308
41155
41389
  ]
41390
+ },
41391
+ headers: {
41392
+ description: "Additional HTTP headers to include in the request. Merged on top of Doc Detective's default browser-mimicking headers. Useful for sites behind bot protection or WAFs that allowlist specific headers (for example, a Cloudflare Access service token or a `Cookie` with a `cf_clearance` value).",
41393
+ default: {},
41394
+ anyOf: [
41395
+ {
41396
+ title: "Request headers (object)",
41397
+ description: "Headers to include in the HTTP request, in key/value format. Values must be strings.",
41398
+ type: "object",
41399
+ additionalProperties: {
41400
+ type: "string"
41401
+ },
41402
+ properties: {}
41403
+ },
41404
+ {
41405
+ title: "Request headers (string)",
41406
+ description: "Headers to include in the HTTP request, as newline-separated values. For example, `X-Api-Key: abc123\nAuthorization: Bearer token`.",
41407
+ type: "string"
41408
+ }
41409
+ ]
41156
41410
  }
41157
41411
  }
41158
41412
  }
@@ -41216,6 +41470,26 @@ var schemas_default = {
41216
41470
  307,
41217
41471
  308
41218
41472
  ]
41473
+ },
41474
+ headers: {
41475
+ description: "Additional HTTP headers to include in the request. Merged on top of Doc Detective's default browser-mimicking headers. Useful for sites behind bot protection or WAFs that allowlist specific headers (for example, a Cloudflare Access service token or a `Cookie` with a `cf_clearance` value).",
41476
+ default: {},
41477
+ anyOf: [
41478
+ {
41479
+ title: "Request headers (object)",
41480
+ description: "Headers to include in the HTTP request, in key/value format. Values must be strings.",
41481
+ type: "object",
41482
+ additionalProperties: {
41483
+ type: "string"
41484
+ },
41485
+ properties: {}
41486
+ },
41487
+ {
41488
+ title: "Request headers (string)",
41489
+ description: "Headers to include in the HTTP request, as newline-separated values. For example, `X-Api-Key: abc123\nAuthorization: Bearer token`.",
41490
+ type: "string"
41491
+ }
41492
+ ]
41219
41493
  }
41220
41494
  }
41221
41495
  }
@@ -49250,6 +49524,26 @@ var schemas_default = {
49250
49524
  307,
49251
49525
  308
49252
49526
  ]
49527
+ },
49528
+ headers: {
49529
+ description: "Additional HTTP headers to include in the request. Merged on top of Doc Detective's default browser-mimicking headers. Useful for sites behind bot protection or WAFs that allowlist specific headers (for example, a Cloudflare Access service token or a `Cookie` with a `cf_clearance` value).",
49530
+ default: {},
49531
+ anyOf: [
49532
+ {
49533
+ title: "Request headers (object)",
49534
+ description: "Headers to include in the HTTP request, in key/value format. Values must be strings.",
49535
+ type: "object",
49536
+ additionalProperties: {
49537
+ type: "string"
49538
+ },
49539
+ properties: {}
49540
+ },
49541
+ {
49542
+ title: "Request headers (string)",
49543
+ description: "Headers to include in the HTTP request, as newline-separated values. For example, `X-Api-Key: abc123\nAuthorization: Bearer token`.",
49544
+ type: "string"
49545
+ }
49546
+ ]
49253
49547
  }
49254
49548
  }
49255
49549
  }
@@ -49313,6 +49607,26 @@ var schemas_default = {
49313
49607
  307,
49314
49608
  308
49315
49609
  ]
49610
+ },
49611
+ headers: {
49612
+ description: "Additional HTTP headers to include in the request. Merged on top of Doc Detective's default browser-mimicking headers. Useful for sites behind bot protection or WAFs that allowlist specific headers (for example, a Cloudflare Access service token or a `Cookie` with a `cf_clearance` value).",
49613
+ default: {},
49614
+ anyOf: [
49615
+ {
49616
+ title: "Request headers (object)",
49617
+ description: "Headers to include in the HTTP request, in key/value format. Values must be strings.",
49618
+ type: "object",
49619
+ additionalProperties: {
49620
+ type: "string"
49621
+ },
49622
+ properties: {}
49623
+ },
49624
+ {
49625
+ title: "Request headers (string)",
49626
+ description: "Headers to include in the HTTP request, as newline-separated values. For example, `X-Api-Key: abc123\nAuthorization: Bearer token`.",
49627
+ type: "string"
49628
+ }
49629
+ ]
49316
49630
  }
49317
49631
  }
49318
49632
  }
@@ -59057,6 +59371,26 @@ var schemas_default = {
59057
59371
  307,
59058
59372
  308
59059
59373
  ]
59374
+ },
59375
+ headers: {
59376
+ description: "Additional HTTP headers to include in the request. Merged on top of Doc Detective's default browser-mimicking headers. Useful for sites behind bot protection or WAFs that allowlist specific headers (for example, a Cloudflare Access service token or a `Cookie` with a `cf_clearance` value).",
59377
+ default: {},
59378
+ anyOf: [
59379
+ {
59380
+ title: "Request headers (object)",
59381
+ description: "Headers to include in the HTTP request, in key/value format. Values must be strings.",
59382
+ type: "object",
59383
+ additionalProperties: {
59384
+ type: "string"
59385
+ },
59386
+ properties: {}
59387
+ },
59388
+ {
59389
+ title: "Request headers (string)",
59390
+ description: "Headers to include in the HTTP request, as newline-separated values. For example, `X-Api-Key: abc123\nAuthorization: Bearer token`.",
59391
+ type: "string"
59392
+ }
59393
+ ]
59060
59394
  }
59061
59395
  }
59062
59396
  }
@@ -59120,6 +59454,26 @@ var schemas_default = {
59120
59454
  307,
59121
59455
  308
59122
59456
  ]
59457
+ },
59458
+ headers: {
59459
+ description: "Additional HTTP headers to include in the request. Merged on top of Doc Detective's default browser-mimicking headers. Useful for sites behind bot protection or WAFs that allowlist specific headers (for example, a Cloudflare Access service token or a `Cookie` with a `cf_clearance` value).",
59460
+ default: {},
59461
+ anyOf: [
59462
+ {
59463
+ title: "Request headers (object)",
59464
+ description: "Headers to include in the HTTP request, in key/value format. Values must be strings.",
59465
+ type: "object",
59466
+ additionalProperties: {
59467
+ type: "string"
59468
+ },
59469
+ properties: {}
59470
+ },
59471
+ {
59472
+ title: "Request headers (string)",
59473
+ description: "Headers to include in the HTTP request, as newline-separated values. For example, `X-Api-Key: abc123\nAuthorization: Bearer token`.",
59474
+ type: "string"
59475
+ }
59476
+ ]
59123
59477
  }
59124
59478
  }
59125
59479
  }
@@ -67154,6 +67508,26 @@ var schemas_default = {
67154
67508
  307,
67155
67509
  308
67156
67510
  ]
67511
+ },
67512
+ headers: {
67513
+ description: "Additional HTTP headers to include in the request. Merged on top of Doc Detective's default browser-mimicking headers. Useful for sites behind bot protection or WAFs that allowlist specific headers (for example, a Cloudflare Access service token or a `Cookie` with a `cf_clearance` value).",
67514
+ default: {},
67515
+ anyOf: [
67516
+ {
67517
+ title: "Request headers (object)",
67518
+ description: "Headers to include in the HTTP request, in key/value format. Values must be strings.",
67519
+ type: "object",
67520
+ additionalProperties: {
67521
+ type: "string"
67522
+ },
67523
+ properties: {}
67524
+ },
67525
+ {
67526
+ title: "Request headers (string)",
67527
+ description: "Headers to include in the HTTP request, as newline-separated values. For example, `X-Api-Key: abc123\nAuthorization: Bearer token`.",
67528
+ type: "string"
67529
+ }
67530
+ ]
67157
67531
  }
67158
67532
  }
67159
67533
  }
@@ -67217,6 +67591,26 @@ var schemas_default = {
67217
67591
  307,
67218
67592
  308
67219
67593
  ]
67594
+ },
67595
+ headers: {
67596
+ description: "Additional HTTP headers to include in the request. Merged on top of Doc Detective's default browser-mimicking headers. Useful for sites behind bot protection or WAFs that allowlist specific headers (for example, a Cloudflare Access service token or a `Cookie` with a `cf_clearance` value).",
67597
+ default: {},
67598
+ anyOf: [
67599
+ {
67600
+ title: "Request headers (object)",
67601
+ description: "Headers to include in the HTTP request, in key/value format. Values must be strings.",
67602
+ type: "object",
67603
+ additionalProperties: {
67604
+ type: "string"
67605
+ },
67606
+ properties: {}
67607
+ },
67608
+ {
67609
+ title: "Request headers (string)",
67610
+ description: "Headers to include in the HTTP request, as newline-separated values. For example, `X-Api-Key: abc123\nAuthorization: Bearer token`.",
67611
+ type: "string"
67612
+ }
67613
+ ]
67220
67614
  }
67221
67615
  }
67222
67616
  }
@@ -78343,6 +78737,26 @@ var schemas_default = {
78343
78737
  307,
78344
78738
  308
78345
78739
  ]
78740
+ },
78741
+ headers: {
78742
+ description: "Additional HTTP headers to include in the request. Merged on top of Doc Detective's default browser-mimicking headers. Useful for sites behind bot protection or WAFs that allowlist specific headers (for example, a Cloudflare Access service token or a `Cookie` with a `cf_clearance` value).",
78743
+ default: {},
78744
+ anyOf: [
78745
+ {
78746
+ title: "Request headers (object)",
78747
+ description: "Headers to include in the HTTP request, in key/value format. Values must be strings.",
78748
+ type: "object",
78749
+ additionalProperties: {
78750
+ type: "string"
78751
+ },
78752
+ properties: {}
78753
+ },
78754
+ {
78755
+ title: "Request headers (string)",
78756
+ description: "Headers to include in the HTTP request, as newline-separated values. For example, `X-Api-Key: abc123\nAuthorization: Bearer token`.",
78757
+ type: "string"
78758
+ }
78759
+ ]
78346
78760
  }
78347
78761
  }
78348
78762
  }
@@ -78406,6 +78820,26 @@ var schemas_default = {
78406
78820
  307,
78407
78821
  308
78408
78822
  ]
78823
+ },
78824
+ headers: {
78825
+ description: "Additional HTTP headers to include in the request. Merged on top of Doc Detective's default browser-mimicking headers. Useful for sites behind bot protection or WAFs that allowlist specific headers (for example, a Cloudflare Access service token or a `Cookie` with a `cf_clearance` value).",
78826
+ default: {},
78827
+ anyOf: [
78828
+ {
78829
+ title: "Request headers (object)",
78830
+ description: "Headers to include in the HTTP request, in key/value format. Values must be strings.",
78831
+ type: "object",
78832
+ additionalProperties: {
78833
+ type: "string"
78834
+ },
78835
+ properties: {}
78836
+ },
78837
+ {
78838
+ title: "Request headers (string)",
78839
+ description: "Headers to include in the HTTP request, as newline-separated values. For example, `X-Api-Key: abc123\nAuthorization: Bearer token`.",
78840
+ type: "string"
78841
+ }
78842
+ ]
78409
78843
  }
78410
78844
  }
78411
78845
  }
@@ -86440,6 +86874,26 @@ var schemas_default = {
86440
86874
  307,
86441
86875
  308
86442
86876
  ]
86877
+ },
86878
+ headers: {
86879
+ description: "Additional HTTP headers to include in the request. Merged on top of Doc Detective's default browser-mimicking headers. Useful for sites behind bot protection or WAFs that allowlist specific headers (for example, a Cloudflare Access service token or a `Cookie` with a `cf_clearance` value).",
86880
+ default: {},
86881
+ anyOf: [
86882
+ {
86883
+ title: "Request headers (object)",
86884
+ description: "Headers to include in the HTTP request, in key/value format. Values must be strings.",
86885
+ type: "object",
86886
+ additionalProperties: {
86887
+ type: "string"
86888
+ },
86889
+ properties: {}
86890
+ },
86891
+ {
86892
+ title: "Request headers (string)",
86893
+ description: "Headers to include in the HTTP request, as newline-separated values. For example, `X-Api-Key: abc123\nAuthorization: Bearer token`.",
86894
+ type: "string"
86895
+ }
86896
+ ]
86443
86897
  }
86444
86898
  }
86445
86899
  }
@@ -86503,6 +86957,26 @@ var schemas_default = {
86503
86957
  307,
86504
86958
  308
86505
86959
  ]
86960
+ },
86961
+ headers: {
86962
+ description: "Additional HTTP headers to include in the request. Merged on top of Doc Detective's default browser-mimicking headers. Useful for sites behind bot protection or WAFs that allowlist specific headers (for example, a Cloudflare Access service token or a `Cookie` with a `cf_clearance` value).",
86963
+ default: {},
86964
+ anyOf: [
86965
+ {
86966
+ title: "Request headers (object)",
86967
+ description: "Headers to include in the HTTP request, in key/value format. Values must be strings.",
86968
+ type: "object",
86969
+ additionalProperties: {
86970
+ type: "string"
86971
+ },
86972
+ properties: {}
86973
+ },
86974
+ {
86975
+ title: "Request headers (string)",
86976
+ description: "Headers to include in the HTTP request, as newline-separated values. For example, `X-Api-Key: abc123\nAuthorization: Bearer token`.",
86977
+ type: "string"
86978
+ }
86979
+ ]
86506
86980
  }
86507
86981
  }
86508
86982
  }
@@ -94817,6 +95291,26 @@ var schemas_default = {
94817
95291
  307,
94818
95292
  308
94819
95293
  ]
95294
+ },
95295
+ headers: {
95296
+ description: "Additional HTTP headers to include in the request. Merged on top of Doc Detective's default browser-mimicking headers. Useful for sites behind bot protection or WAFs that allowlist specific headers (for example, a Cloudflare Access service token or a `Cookie` with a `cf_clearance` value).",
95297
+ default: {},
95298
+ anyOf: [
95299
+ {
95300
+ title: "Request headers (object)",
95301
+ description: "Headers to include in the HTTP request, in key/value format. Values must be strings.",
95302
+ type: "object",
95303
+ additionalProperties: {
95304
+ type: "string"
95305
+ },
95306
+ properties: {}
95307
+ },
95308
+ {
95309
+ title: "Request headers (string)",
95310
+ description: "Headers to include in the HTTP request, as newline-separated values. For example, `X-Api-Key: abc123\nAuthorization: Bearer token`.",
95311
+ type: "string"
95312
+ }
95313
+ ]
94820
95314
  }
94821
95315
  }
94822
95316
  }
@@ -94880,6 +95374,26 @@ var schemas_default = {
94880
95374
  307,
94881
95375
  308
94882
95376
  ]
95377
+ },
95378
+ headers: {
95379
+ description: "Additional HTTP headers to include in the request. Merged on top of Doc Detective's default browser-mimicking headers. Useful for sites behind bot protection or WAFs that allowlist specific headers (for example, a Cloudflare Access service token or a `Cookie` with a `cf_clearance` value).",
95380
+ default: {},
95381
+ anyOf: [
95382
+ {
95383
+ title: "Request headers (object)",
95384
+ description: "Headers to include in the HTTP request, in key/value format. Values must be strings.",
95385
+ type: "object",
95386
+ additionalProperties: {
95387
+ type: "string"
95388
+ },
95389
+ properties: {}
95390
+ },
95391
+ {
95392
+ title: "Request headers (string)",
95393
+ description: "Headers to include in the HTTP request, as newline-separated values. For example, `X-Api-Key: abc123\nAuthorization: Bearer token`.",
95394
+ type: "string"
95395
+ }
95396
+ ]
94883
95397
  }
94884
95398
  }
94885
95399
  }
@@ -103267,6 +103781,26 @@ var schemas_default = {
103267
103781
  307,
103268
103782
  308
103269
103783
  ]
103784
+ },
103785
+ headers: {
103786
+ description: "Additional HTTP headers to include in the request. Merged on top of Doc Detective's default browser-mimicking headers. Useful for sites behind bot protection or WAFs that allowlist specific headers (for example, a Cloudflare Access service token or a `Cookie` with a `cf_clearance` value).",
103787
+ default: {},
103788
+ anyOf: [
103789
+ {
103790
+ title: "Request headers (object)",
103791
+ description: "Headers to include in the HTTP request, in key/value format. Values must be strings.",
103792
+ type: "object",
103793
+ additionalProperties: {
103794
+ type: "string"
103795
+ },
103796
+ properties: {}
103797
+ },
103798
+ {
103799
+ title: "Request headers (string)",
103800
+ description: "Headers to include in the HTTP request, as newline-separated values. For example, `X-Api-Key: abc123\nAuthorization: Bearer token`.",
103801
+ type: "string"
103802
+ }
103803
+ ]
103270
103804
  }
103271
103805
  }
103272
103806
  }
@@ -103330,6 +103864,26 @@ var schemas_default = {
103330
103864
  307,
103331
103865
  308
103332
103866
  ]
103867
+ },
103868
+ headers: {
103869
+ description: "Additional HTTP headers to include in the request. Merged on top of Doc Detective's default browser-mimicking headers. Useful for sites behind bot protection or WAFs that allowlist specific headers (for example, a Cloudflare Access service token or a `Cookie` with a `cf_clearance` value).",
103870
+ default: {},
103871
+ anyOf: [
103872
+ {
103873
+ title: "Request headers (object)",
103874
+ description: "Headers to include in the HTTP request, in key/value format. Values must be strings.",
103875
+ type: "object",
103876
+ additionalProperties: {
103877
+ type: "string"
103878
+ },
103879
+ properties: {}
103880
+ },
103881
+ {
103882
+ title: "Request headers (string)",
103883
+ description: "Headers to include in the HTTP request, as newline-separated values. For example, `X-Api-Key: abc123\nAuthorization: Bearer token`.",
103884
+ type: "string"
103885
+ }
103886
+ ]
103333
103887
  }
103334
103888
  }
103335
103889
  }
@@ -111364,6 +111918,26 @@ var schemas_default = {
111364
111918
  307,
111365
111919
  308
111366
111920
  ]
111921
+ },
111922
+ headers: {
111923
+ description: "Additional HTTP headers to include in the request. Merged on top of Doc Detective's default browser-mimicking headers. Useful for sites behind bot protection or WAFs that allowlist specific headers (for example, a Cloudflare Access service token or a `Cookie` with a `cf_clearance` value).",
111924
+ default: {},
111925
+ anyOf: [
111926
+ {
111927
+ title: "Request headers (object)",
111928
+ description: "Headers to include in the HTTP request, in key/value format. Values must be strings.",
111929
+ type: "object",
111930
+ additionalProperties: {
111931
+ type: "string"
111932
+ },
111933
+ properties: {}
111934
+ },
111935
+ {
111936
+ title: "Request headers (string)",
111937
+ description: "Headers to include in the HTTP request, as newline-separated values. For example, `X-Api-Key: abc123\nAuthorization: Bearer token`.",
111938
+ type: "string"
111939
+ }
111940
+ ]
111367
111941
  }
111368
111942
  }
111369
111943
  }
@@ -111427,6 +112001,26 @@ var schemas_default = {
111427
112001
  307,
111428
112002
  308
111429
112003
  ]
112004
+ },
112005
+ headers: {
112006
+ description: "Additional HTTP headers to include in the request. Merged on top of Doc Detective's default browser-mimicking headers. Useful for sites behind bot protection or WAFs that allowlist specific headers (for example, a Cloudflare Access service token or a `Cookie` with a `cf_clearance` value).",
112007
+ default: {},
112008
+ anyOf: [
112009
+ {
112010
+ title: "Request headers (object)",
112011
+ description: "Headers to include in the HTTP request, in key/value format. Values must be strings.",
112012
+ type: "object",
112013
+ additionalProperties: {
112014
+ type: "string"
112015
+ },
112016
+ properties: {}
112017
+ },
112018
+ {
112019
+ title: "Request headers (string)",
112020
+ description: "Headers to include in the HTTP request, as newline-separated values. For example, `X-Api-Key: abc123\nAuthorization: Bearer token`.",
112021
+ type: "string"
112022
+ }
112023
+ ]
111430
112024
  }
111431
112025
  }
111432
112026
  }
@@ -128419,14 +129013,15 @@ function transformToSchemaKey({ currentSchema = "", targetSchema = "", object =
128419
129013
  } else if (currentSchema === "stopRecording_v2") {
128420
129014
  transformedObject.stopRecord = true;
128421
129015
  } else if (currentSchema === "wait_v2") {
128422
- transformedObject.wait = object;
129016
+ transformedObject.wait = object.duration ?? 5e3;
128423
129017
  }
128424
129018
  const result = validate({
128425
129019
  schemaKey: "step_v3",
128426
129020
  object: transformedObject
128427
129021
  });
128428
129022
  if (!result.valid) {
128429
- throw new Error(`Invalid object: ${result.errors}`);
129023
+ throw new Error(`Failed to transform object to step.
129024
+ Errors: ${result.errors}`);
128430
129025
  }
128431
129026
  return result.object;
128432
129027
  } else if (targetSchema === "config_v3") {
@@ -129278,11 +129873,17 @@ var defaultFileTypes = {
129278
129873
  inlineStatements: {
129279
129874
  testStart: [
129280
129875
  "<\\?doc-detective\\s+test([\\s\\S]*?)\\?>",
129281
- "<!--\\s*test([\\s\\S]+?)-->"
129876
+ "<!--\\s*test([\\s\\S]+?)-->",
129877
+ `<data\\s+[^>]*?name=["']doc-detective["'][^>]*?value='test\\s+([^']+?)'[^>]*?(?:\\/\\s*>|>\\s*<\\/data>)`,
129878
+ `<data\\s+[^>]*?name=["']doc-detective["'][^>]*?value="test\\s+([^"]+?)"[^>]*?(?:\\/\\s*>|>\\s*<\\/data>)`,
129879
+ `<data\\s+[^>]*?value='test\\s+([^']+?)'[^>]*?name=["']doc-detective["'][^>]*?(?:\\/\\s*>|>\\s*<\\/data>)`,
129880
+ `<data\\s+[^>]*?value="test\\s+([^"]+?)"[^>]*?name=["']doc-detective["'][^>]*?(?:\\/\\s*>|>\\s*<\\/data>)`
129282
129881
  ],
129283
129882
  testEnd: [
129284
129883
  "<\\?doc-detective\\s+test\\s+end\\s*\\?>",
129285
- "<!--\\s*test end([\\s\\S]+?)-->"
129884
+ "<!--\\s*test end([\\s\\S]+?)-->",
129885
+ `<data\\s+[^>]*?name=["']doc-detective["'][^>]*?value=["']test end["'][^>]*?(?:\\/\\s*>|>\\s*<\\/data>)`,
129886
+ `<data\\s+[^>]*?value=["']test end["'][^>]*?name=["']doc-detective["'][^>]*?(?:\\/\\s*>|>\\s*<\\/data>)`
129286
129887
  ],
129287
129888
  ignoreStart: [
129288
129889
  "<\\?doc-detective\\s+test\\s+ignore\\s+start\\s*\\?>",
@@ -129295,7 +129896,11 @@ var defaultFileTypes = {
129295
129896
  step: [
129296
129897
  "<\\?doc-detective\\s+step\\s+([\\s\\S]*?)\\s*\\?>",
129297
129898
  "<!--\\s*step([\\s\\S]+?)-->",
129298
- '<data\\s+name="step"\\s*>([\\s\\S]*?)<\\/data>'
129899
+ '<data\\s+name="step"\\s*>([\\s\\S]*?)<\\/data>',
129900
+ `<data\\s+[^>]*?name=["']doc-detective["'][^>]*?value='step\\s+([^']+?)'[^>]*?(?:\\/\\s*>|>\\s*<\\/data>)`,
129901
+ `<data\\s+[^>]*?name=["']doc-detective["'][^>]*?value="step\\s+([^"]+?)"[^>]*?(?:\\/\\s*>|>\\s*<\\/data>)`,
129902
+ `<data\\s+[^>]*?value='step\\s+([^']+?)'[^>]*?name=["']doc-detective["'][^>]*?(?:\\/\\s*>|>\\s*<\\/data>)`,
129903
+ `<data\\s+[^>]*?value="step\\s+([^"]+?)"[^>]*?name=["']doc-detective["'][^>]*?(?:\\/\\s*>|>\\s*<\\/data>)`
129299
129904
  ]
129300
129905
  },
129301
129906
  markup: [
@@ -129729,10 +130334,10 @@ async function getAvailableApps({ config }) {
129729
130334
  }
129730
130335
 
129731
130336
  // dist/core/detectTests.js
129732
- var import_node_fs4 = __toESM(require("node:fs"), 1);
129733
- var import_node_path5 = __toESM(require("node:path"), 1);
129734
- var import_node_os3 = __toESM(require("node:os"), 1);
129735
- var import_node_crypto2 = __toESM(require("node:crypto"), 1);
130337
+ var import_node_fs5 = __toESM(require("node:fs"), 1);
130338
+ var import_node_path6 = __toESM(require("node:path"), 1);
130339
+ var import_node_os4 = __toESM(require("node:os"), 1);
130340
+ var import_node_crypto3 = __toESM(require("node:crypto"), 1);
129736
130341
  var import_yaml3 = __toESM(require("yaml"), 1);
129737
130342
 
129738
130343
  // dist/common/src/detectTests.js
@@ -129758,11 +130363,17 @@ var defaultFileTypesBase = {
129758
130363
  inlineStatements: {
129759
130364
  testStart: [
129760
130365
  "<\\?doc-detective\\s+test([\\s\\S]*?)\\?>",
129761
- "<!--\\s*test([\\s\\S]+?)-->"
130366
+ "<!--\\s*test([\\s\\S]+?)-->",
130367
+ `<data\\s+[^>]*?name=["']doc-detective["'][^>]*?value='test\\s+([^']+?)'[^>]*?(?:\\/\\s*>|>\\s*<\\/data>)`,
130368
+ `<data\\s+[^>]*?name=["']doc-detective["'][^>]*?value="test\\s+([^"]+?)"[^>]*?(?:\\/\\s*>|>\\s*<\\/data>)`,
130369
+ `<data\\s+[^>]*?value='test\\s+([^']+?)'[^>]*?name=["']doc-detective["'][^>]*?(?:\\/\\s*>|>\\s*<\\/data>)`,
130370
+ `<data\\s+[^>]*?value="test\\s+([^"]+?)"[^>]*?name=["']doc-detective["'][^>]*?(?:\\/\\s*>|>\\s*<\\/data>)`
129762
130371
  ],
129763
130372
  testEnd: [
129764
130373
  "<\\?doc-detective\\s+test\\s+end\\s*\\?>",
129765
- "<!--\\s*test end([\\s\\S]+?)-->"
130374
+ "<!--\\s*test end([\\s\\S]+?)-->",
130375
+ `<data\\s+[^>]*?name=["']doc-detective["'][^>]*?value=["']test end["'][^>]*?(?:\\/\\s*>|>\\s*<\\/data>)`,
130376
+ `<data\\s+[^>]*?value=["']test end["'][^>]*?name=["']doc-detective["'][^>]*?(?:\\/\\s*>|>\\s*<\\/data>)`
129766
130377
  ],
129767
130378
  ignoreStart: [
129768
130379
  "<\\?doc-detective\\s+test\\s+ignore\\s+start\\s*\\?>",
@@ -129775,7 +130386,11 @@ var defaultFileTypesBase = {
129775
130386
  step: [
129776
130387
  "<\\?doc-detective\\s+step\\s+([\\s\\S]*?)\\s*\\?>",
129777
130388
  "<!--\\s*step([\\s\\S]+?)-->",
129778
- '<data\\s+name="step"\\s*>([\\s\\S]*?)<\\/data>'
130389
+ '<data\\s+name="step"\\s*>([\\s\\S]*?)<\\/data>',
130390
+ `<data\\s+[^>]*?name=["']doc-detective["'][^>]*?value='step\\s+([^']+?)'[^>]*?(?:\\/\\s*>|>\\s*<\\/data>)`,
130391
+ `<data\\s+[^>]*?name=["']doc-detective["'][^>]*?value="step\\s+([^"]+?)"[^>]*?(?:\\/\\s*>|>\\s*<\\/data>)`,
130392
+ `<data\\s+[^>]*?value='step\\s+([^']+?)'[^>]*?name=["']doc-detective["'][^>]*?(?:\\/\\s*>|>\\s*<\\/data>)`,
130393
+ `<data\\s+[^>]*?value="step\\s+([^"]+?)"[^>]*?name=["']doc-detective["'][^>]*?(?:\\/\\s*>|>\\s*<\\/data>)`
129779
130394
  ]
129780
130395
  },
129781
130396
  markup: [
@@ -130128,6 +130743,9 @@ function parseXmlAttributes({ stringifiedObject }) {
130128
130743
  }
130129
130744
  function parseObject({ stringifiedObject }) {
130130
130745
  if (typeof stringifiedObject === "string") {
130746
+ if (/&(?:#\d+|#x[0-9a-fA-F]+|amp|lt|gt|quot|apos);/.test(stringifiedObject)) {
130747
+ stringifiedObject = stringifiedObject.replace(/&#39;|&apos;/g, "'").replace(/&#34;|&quot;/g, '"').replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&");
130748
+ }
130131
130749
  const xmlAttrs = parseXmlAttributes({ stringifiedObject });
130132
130750
  if (xmlAttrs !== null) {
130133
130751
  return xmlAttrs;
@@ -130387,17 +131005,7 @@ async function parseContent({ config = {}, content, filePath = "", fileType }) {
130387
131005
  if (step.screenshot && config._herettoPathMapping) {
130388
131006
  const herettoIntegration = findHerettoIntegration(config, filePath);
130389
131007
  if (herettoIntegration) {
130390
- if (typeof step.screenshot === "string") {
130391
- step.screenshot = { path: step.screenshot };
130392
- } else if (typeof step.screenshot === "boolean") {
130393
- step.screenshot = {};
130394
- }
130395
- step.screenshot.sourceIntegration = {
130396
- type: "heretto",
130397
- integrationName: herettoIntegration,
130398
- filePath: step.screenshot.path || "",
130399
- contentPath: filePath
130400
- };
131008
+ attachHerettoScreenshotSourceIntegration(step, herettoIntegration, filePath);
130401
131009
  }
130402
131010
  }
130403
131011
  }
@@ -130463,6 +131071,12 @@ async function parseContent({ config = {}, content, filePath = "", fileType }) {
130463
131071
  endIndex: statement._endIndex
130464
131072
  };
130465
131073
  }
131074
+ if (step.screenshot && config._herettoPathMapping) {
131075
+ const herettoIntegration = findHerettoIntegration(config, filePath);
131076
+ if (herettoIntegration) {
131077
+ attachHerettoScreenshotSourceIntegration(step, herettoIntegration, filePath);
131078
+ }
131079
+ }
130466
131080
  const validation = validate({
130467
131081
  schemaKey: "step_v3",
130468
131082
  object: step,
@@ -130501,6 +131115,19 @@ async function parseContent({ config = {}, content, filePath = "", fileType }) {
130501
131115
  });
130502
131116
  return validatedTests;
130503
131117
  }
131118
+ function attachHerettoScreenshotSourceIntegration(step, integrationName, contentPath) {
131119
+ if (typeof step.screenshot === "string") {
131120
+ step.screenshot = { path: step.screenshot };
131121
+ } else if (step.screenshot === null || typeof step.screenshot !== "object" || Array.isArray(step.screenshot)) {
131122
+ step.screenshot = {};
131123
+ }
131124
+ step.screenshot.sourceIntegration = {
131125
+ type: "heretto",
131126
+ integrationName,
131127
+ filePath: step.screenshot.path || "",
131128
+ contentPath
131129
+ };
131130
+ }
130504
131131
  function findHerettoIntegration(config, filePath) {
130505
131132
  if (!config._herettoPathMapping)
130506
131133
  return null;
@@ -130532,6 +131159,1130 @@ function log2(config, level, message) {
130532
131159
  }
130533
131160
  }
130534
131161
 
131162
+ // dist/core/integrations/heretto.js
131163
+ var import_node_fs4 = __toESM(require("node:fs"), 1);
131164
+ var import_node_path5 = __toESM(require("node:path"), 1);
131165
+ var import_node_https = __toESM(require("node:https"), 1);
131166
+ var import_node_http = __toESM(require("node:http"), 1);
131167
+ var import_axios3 = __toESM(require("axios"), 1);
131168
+ var import_node_os3 = __toESM(require("node:os"), 1);
131169
+ var import_node_crypto2 = __toESM(require("node:crypto"), 1);
131170
+ var import_adm_zip = __toESM(require("adm-zip"), 1);
131171
+ var HerettoUploader = class {
131172
+ /**
131173
+ * Checks if this uploader can handle the given source integration.
131174
+ * @param {Object} sourceIntegration - Source integration metadata
131175
+ * @returns {boolean} True if this uploader handles Heretto integrations
131176
+ */
131177
+ canHandle(sourceIntegration) {
131178
+ return sourceIntegration?.type === "heretto";
131179
+ }
131180
+ /**
131181
+ * Uploads a file to Heretto CMS.
131182
+ * @param {Object} options - Upload options
131183
+ * @param {Object} options.config - Doc Detective config
131184
+ * @param {Object} options.integrationConfig - Heretto integration config
131185
+ * @param {string} options.localFilePath - Local file path to upload
131186
+ * @param {Object} options.sourceIntegration - Source integration metadata
131187
+ * @param {Function} options.log - Logging function
131188
+ * @returns {Promise<Object>} Upload result with status and description
131189
+ */
131190
+ async upload({ config, integrationConfig, localFilePath, sourceIntegration, log: log3 }) {
131191
+ const result = {
131192
+ status: "FAIL",
131193
+ description: ""
131194
+ };
131195
+ if (!integrationConfig) {
131196
+ result.description = "No Heretto integration configuration found";
131197
+ return result;
131198
+ }
131199
+ if (!integrationConfig.organizationId || !integrationConfig.apiToken) {
131200
+ result.description = "Heretto integration missing organizationId or apiToken";
131201
+ return result;
131202
+ }
131203
+ const apiBaseUrl = `https://${integrationConfig.organizationId}.heretto.com`;
131204
+ let fileId = sourceIntegration.fileId;
131205
+ let parentFolderId = sourceIntegration.parentFolderId;
131206
+ const filename = import_node_path5.default.basename(sourceIntegration.filePath);
131207
+ const relativeFilePath = sourceIntegration.filePath;
131208
+ if (!fileId && integrationConfig.resourceDependencies) {
131209
+ const resolvedFile = this.resolveFromDependencies({
131210
+ resourceDependencies: integrationConfig.resourceDependencies,
131211
+ filePath: relativeFilePath,
131212
+ filename,
131213
+ log: (level, msg) => log3(config, level, msg)
131214
+ });
131215
+ if (resolvedFile) {
131216
+ fileId = resolvedFile.uuid;
131217
+ if (!parentFolderId && resolvedFile.parentFolderId) {
131218
+ parentFolderId = resolvedFile.parentFolderId;
131219
+ }
131220
+ log3(config, "debug", `Resolved from dependencies: ${relativeFilePath} -> ${fileId}`);
131221
+ }
131222
+ }
131223
+ if (!fileId) {
131224
+ log3(config, "debug", `No fileId found, resolving correct folder for: ${sourceIntegration.filePath}`);
131225
+ try {
131226
+ if (!parentFolderId && integrationConfig.resourceDependencies) {
131227
+ const folderResolution = this.findParentFolderFromDependencies({
131228
+ resourceDependencies: integrationConfig.resourceDependencies,
131229
+ filePath: relativeFilePath,
131230
+ log: (level, msg) => log3(config, level, msg)
131231
+ });
131232
+ parentFolderId = folderResolution.folderId;
131233
+ if (!parentFolderId && folderResolution.ditamapParentFolderId && folderResolution.targetFolderName) {
131234
+ log3(config, "debug", `Searching for folder '${folderResolution.targetFolderName}' in ditamap's parent folder`);
131235
+ parentFolderId = await this.getChildFolderByName({
131236
+ apiBaseUrl,
131237
+ apiToken: integrationConfig.apiToken,
131238
+ username: integrationConfig.username || "",
131239
+ parentFolderId: folderResolution.ditamapParentFolderId,
131240
+ folderName: folderResolution.targetFolderName,
131241
+ log: (level, msg) => log3(config, level, msg)
131242
+ });
131243
+ }
131244
+ }
131245
+ if (!parentFolderId && relativeFilePath) {
131246
+ const parentDirPath = import_node_path5.default.dirname(relativeFilePath);
131247
+ if (parentDirPath && parentDirPath !== ".") {
131248
+ const folderName = import_node_path5.default.basename(parentDirPath);
131249
+ log3(config, "debug", `Searching for parent folder by name: ${folderName}`);
131250
+ parentFolderId = await this.searchFolderByName({
131251
+ apiBaseUrl,
131252
+ apiToken: integrationConfig.apiToken,
131253
+ username: integrationConfig.username || "",
131254
+ folderName,
131255
+ log: (level, msg) => log3(config, level, msg)
131256
+ });
131257
+ }
131258
+ }
131259
+ if (parentFolderId) {
131260
+ log3(config, "debug", `Looking for file '${filename}' in target folder ${parentFolderId}`);
131261
+ fileId = await this.getFileInFolder({
131262
+ apiBaseUrl,
131263
+ apiToken: integrationConfig.apiToken,
131264
+ username: integrationConfig.username || "",
131265
+ folderId: parentFolderId,
131266
+ filename,
131267
+ log: (level, msg) => log3(config, level, msg)
131268
+ });
131269
+ if (fileId) {
131270
+ log3(config, "debug", `Found existing file in target folder with ID: ${fileId}`);
131271
+ } else {
131272
+ log3(config, "debug", `File not in target folder, creating new document`);
131273
+ const mimeType = this.getContentType(localFilePath);
131274
+ const createResult = await this.createDocument({
131275
+ apiBaseUrl,
131276
+ apiToken: integrationConfig.apiToken,
131277
+ username: integrationConfig.username || "",
131278
+ parentFolderId,
131279
+ filename,
131280
+ mimeType,
131281
+ log: (level, msg) => log3(config, level, msg)
131282
+ });
131283
+ if (createResult.created) {
131284
+ fileId = createResult.documentId;
131285
+ log3(config, "info", `Created new document in Heretto with ID: ${fileId}`);
131286
+ } else if (createResult.existsInFolder) {
131287
+ log3(config, "debug", `File exists in folder (race condition), searching for its ID`);
131288
+ fileId = await this.getFileInFolder({
131289
+ apiBaseUrl,
131290
+ apiToken: integrationConfig.apiToken,
131291
+ username: integrationConfig.username || "",
131292
+ folderId: parentFolderId,
131293
+ filename,
131294
+ log: (level, msg) => log3(config, level, msg)
131295
+ });
131296
+ if (!fileId) {
131297
+ result.description = `File exists in folder but could not get its ID: ${filename}`;
131298
+ return result;
131299
+ }
131300
+ } else {
131301
+ result.description = `Failed to create document in Heretto: ${filename}`;
131302
+ return result;
131303
+ }
131304
+ }
131305
+ } else {
131306
+ log3(config, "debug", `No target folder found, searching globally for file: ${filename}`);
131307
+ fileId = await this.searchFileByName({
131308
+ apiBaseUrl,
131309
+ apiToken: integrationConfig.apiToken,
131310
+ username: integrationConfig.username || "",
131311
+ filename,
131312
+ log: (level, msg) => log3(config, level, msg)
131313
+ });
131314
+ if (!fileId) {
131315
+ result.description = `Could not find file or parent folder in Heretto: ${sourceIntegration.filePath}`;
131316
+ return result;
131317
+ }
131318
+ }
131319
+ } catch (error) {
131320
+ result.description = `Error searching/creating file: ${error.message}`;
131321
+ return result;
131322
+ }
131323
+ }
131324
+ if (!import_node_fs4.default.existsSync(localFilePath)) {
131325
+ result.description = `Local file not found: ${localFilePath}`;
131326
+ return result;
131327
+ }
131328
+ const fileContent = import_node_fs4.default.readFileSync(localFilePath);
131329
+ const contentType = this.getContentType(localFilePath);
131330
+ try {
131331
+ await this.uploadFile({
131332
+ apiBaseUrl,
131333
+ apiToken: integrationConfig.apiToken,
131334
+ username: integrationConfig.username || "",
131335
+ documentId: fileId,
131336
+ content: fileContent,
131337
+ contentType,
131338
+ log: (level, msg) => log3(config, level, msg)
131339
+ });
131340
+ result.status = "PASS";
131341
+ result.description = `Successfully uploaded to Heretto (document ID: ${fileId})`;
131342
+ } catch (error) {
131343
+ result.description = `Upload failed: ${error.message}`;
131344
+ }
131345
+ return result;
131346
+ }
131347
+ /**
131348
+ * Resolves a file path to its UUID using the resource dependencies map.
131349
+ * @param {Object} options - Resolution options
131350
+ * @param {Object.<string, {uuid: string, parentFolderId: string}>} options.resourceDependencies - Map of resource paths to resource metadata
131351
+ * @param {string} options.filePath - Original file path to resolve
131352
+ * @param {string} options.filename - Filename extracted from the file path
131353
+ * @param {Function} options.log - Logging function
131354
+ * @returns {{uuid: string, parentFolderId: string}|null} File info with uuid and parentFolderId, or null if not found
131355
+ */
131356
+ resolveFromDependencies({ resourceDependencies, filePath, filename, log: log3 }) {
131357
+ if (!resourceDependencies)
131358
+ return null;
131359
+ const normalizedPath = import_node_path5.default.posix.normalize(filePath.replace(/\\/g, "/")).replace(/^\.\.\/+/g, "").replace(/^\.\//, "");
131360
+ for (const [depPath, info] of Object.entries(resourceDependencies)) {
131361
+ if (depPath.startsWith("_"))
131362
+ continue;
131363
+ const normalizedDepPath = depPath.replace(/\\/g, "/");
131364
+ if (normalizedDepPath === normalizedPath || normalizedDepPath.endsWith("/" + normalizedPath) || normalizedDepPath.endsWith(normalizedPath)) {
131365
+ log3("debug", `Found exact path match in dependencies: ${depPath}`);
131366
+ return info;
131367
+ }
131368
+ }
131369
+ const parentDir = import_node_path5.default.dirname(normalizedPath);
131370
+ const parentFolderName = import_node_path5.default.basename(parentDir);
131371
+ for (const [depPath, info] of Object.entries(resourceDependencies)) {
131372
+ if (depPath.startsWith("_"))
131373
+ continue;
131374
+ const depFilename = import_node_path5.default.basename(depPath);
131375
+ const depParentDir = import_node_path5.default.dirname(depPath);
131376
+ const depParentFolderName = import_node_path5.default.basename(depParentDir);
131377
+ if (depFilename === filename && depParentFolderName === parentFolderName) {
131378
+ log3("debug", `Found filename+folder match in dependencies: ${depPath}`);
131379
+ return info;
131380
+ }
131381
+ }
131382
+ for (const [depPath, info] of Object.entries(resourceDependencies)) {
131383
+ if (depPath.startsWith("_"))
131384
+ continue;
131385
+ const depFilename = import_node_path5.default.basename(depPath);
131386
+ if (depFilename === filename) {
131387
+ log3("debug", `Found filename match in dependencies: ${depPath}`);
131388
+ return info;
131389
+ }
131390
+ }
131391
+ log3("debug", `No match found in dependencies for: ${filePath}`);
131392
+ return null;
131393
+ }
131394
+ /**
131395
+ * Finds the parent folder ID for a file path using resource dependencies.
131396
+ * Returns the target folder name for API lookup if not found in dependencies.
131397
+ * @param {Object} options - Resolution options
131398
+ * @param {Object.<string, {uuid: string, parentFolderId: string}>} options.resourceDependencies - Map of resource paths to resource metadata.
131399
+ * Keys are relative file paths, values are objects with uuid and parentFolderId.
131400
+ * Special keys starting with '_' (e.g., '_ditamapParentFolderId') store internal metadata.
131401
+ * @param {string} options.filePath - File path to find parent folder for (can be relative with ../ or ./ prefixes)
131402
+ * @param {Function} options.log - Logging function with signature (level, message)
131403
+ * @returns {{folderId: string|null, targetFolderName: string|null, ditamapParentFolderId: string|null}} Resolution result containing:
131404
+ * - folderId: The parent folder UUID if found in dependencies, null otherwise
131405
+ * - targetFolderName: The name of the target folder extracted from the file path
131406
+ * - ditamapParentFolderId: The ditamap's parent folder ID from dependencies (for API fallback lookup)
131407
+ */
131408
+ findParentFolderFromDependencies({ resourceDependencies, filePath, log: log3 }) {
131409
+ const result = {
131410
+ folderId: null,
131411
+ targetFolderName: null,
131412
+ ditamapParentFolderId: null
131413
+ };
131414
+ if (!resourceDependencies)
131415
+ return result;
131416
+ const normalizedPath = import_node_path5.default.posix.normalize(filePath.replace(/\\/g, "/")).replace(/^(\.\.\/)+/g, "").replace(/^\.\//, "");
131417
+ const parentDir = import_node_path5.default.dirname(normalizedPath);
131418
+ const targetFolderName = import_node_path5.default.basename(parentDir);
131419
+ result.targetFolderName = targetFolderName;
131420
+ result.ditamapParentFolderId = resourceDependencies._ditamapParentFolderId || null;
131421
+ log3("debug", `Looking for parent folder '${targetFolderName}' in dependencies`);
131422
+ for (const [depPath, info] of Object.entries(resourceDependencies)) {
131423
+ if (depPath.startsWith("_"))
131424
+ continue;
131425
+ const depParentDir = import_node_path5.default.dirname(depPath);
131426
+ const depFolderName = import_node_path5.default.basename(depParentDir);
131427
+ if (depFolderName === targetFolderName && info.parentFolderId) {
131428
+ log3("debug", `Found sibling file ${depPath} with parent folder ID: ${info.parentFolderId}`);
131429
+ result.folderId = info.parentFolderId;
131430
+ return result;
131431
+ }
131432
+ }
131433
+ for (const [depPath, info] of Object.entries(resourceDependencies)) {
131434
+ if (depPath.startsWith("_"))
131435
+ continue;
131436
+ if (depPath.endsWith("/" + targetFolderName) || depPath === targetFolderName) {
131437
+ log3("debug", `Found folder ${depPath} with ID: ${info.uuid}`);
131438
+ result.folderId = info.uuid;
131439
+ return result;
131440
+ }
131441
+ }
131442
+ log3("debug", `Could not find parent folder '${targetFolderName}' in dependencies, will search via API`);
131443
+ return result;
131444
+ }
131445
+ /**
131446
+ * Gets a child folder within a parent folder by name.
131447
+ * @param {Object} options - Search options
131448
+ * @returns {Promise<string|null>} Child folder ID if found, null otherwise
131449
+ */
131450
+ async getChildFolderByName({ apiBaseUrl, apiToken, username, parentFolderId, folderName, log: log3 }) {
131451
+ const folderUrl = new URL(`/rest/all-files/${parentFolderId}`, apiBaseUrl);
131452
+ return new Promise((resolve, reject) => {
131453
+ const protocol = folderUrl.protocol === "https:" ? import_node_https.default : import_node_http.default;
131454
+ const authString = Buffer.from(`${username}:${apiToken}`).toString("base64");
131455
+ const options = {
131456
+ hostname: folderUrl.hostname,
131457
+ port: folderUrl.port || (folderUrl.protocol === "https:" ? 443 : 80),
131458
+ path: folderUrl.pathname,
131459
+ method: "GET",
131460
+ headers: {
131461
+ "Authorization": `Basic ${authString}`,
131462
+ "Accept": "application/xml"
131463
+ }
131464
+ };
131465
+ log3("debug", `Looking for child folder '${folderName}' in parent ${parentFolderId}`);
131466
+ const req = protocol.request(options, (res) => {
131467
+ let data = "";
131468
+ res.on("data", (chunk) => {
131469
+ data += chunk;
131470
+ });
131471
+ res.on("end", () => {
131472
+ if (res.statusCode === 200) {
131473
+ try {
131474
+ const escapedFolderName = folderName.replace(/[.*+?^${}()|[\]\\/]/g, "\\$&");
131475
+ const folderMatch = data.match(new RegExp(`<folder\\s+name="${escapedFolderName}"\\s+id="([^"]+)"`, "i"));
131476
+ if (folderMatch && folderMatch[1]) {
131477
+ log3("debug", `Found child folder '${folderName}' with ID: ${folderMatch[1]}`);
131478
+ resolve(folderMatch[1]);
131479
+ } else {
131480
+ log3("debug", `Child folder '${folderName}' not found in parent ${parentFolderId}`);
131481
+ resolve(null);
131482
+ }
131483
+ } catch (parseError) {
131484
+ log3("debug", `Error parsing folder contents: ${parseError.message}`);
131485
+ resolve(null);
131486
+ }
131487
+ } else {
131488
+ log3("debug", `Failed to get parent folder contents: ${res.statusCode}`);
131489
+ resolve(null);
131490
+ }
131491
+ });
131492
+ });
131493
+ req.setTimeout(3e4, () => {
131494
+ req.destroy(new Error("Request timeout"));
131495
+ });
131496
+ req.on("error", (error) => {
131497
+ log3("debug", `Error getting folder contents: ${error.message}`);
131498
+ resolve(null);
131499
+ });
131500
+ req.end();
131501
+ });
131502
+ }
131503
+ /**
131504
+ * Creates a new document in Heretto.
131505
+ * @param {Object} options - Creation options
131506
+ * @returns {Promise<Object>} Result with created: boolean, documentId: string (if successful or already exists)
131507
+ */
131508
+ async createDocument({ apiBaseUrl, apiToken, username, parentFolderId, filename, mimeType, log: log3 }) {
131509
+ const createUrl = new URL(`/rest/all-files/${parentFolderId}`, apiBaseUrl);
131510
+ const createBody = `<resource><name>${this.escapeXml(filename)}</name><mime-type>${mimeType}</mime-type></resource>`;
131511
+ return new Promise((resolve, reject) => {
131512
+ const protocol = createUrl.protocol === "https:" ? import_node_https.default : import_node_http.default;
131513
+ const authString = Buffer.from(`${username}:${apiToken}`).toString("base64");
131514
+ const options = {
131515
+ hostname: createUrl.hostname,
131516
+ port: createUrl.port || (createUrl.protocol === "https:" ? 443 : 80),
131517
+ path: createUrl.pathname,
131518
+ method: "POST",
131519
+ headers: {
131520
+ "Content-Type": "application/xml",
131521
+ "Authorization": `Basic ${authString}`,
131522
+ "Content-Length": Buffer.byteLength(createBody)
131523
+ }
131524
+ };
131525
+ log3("debug", `Creating document at ${createUrl.toString()}`);
131526
+ const req = protocol.request(options, (res) => {
131527
+ let data = "";
131528
+ res.on("data", (chunk) => {
131529
+ data += chunk;
131530
+ });
131531
+ res.on("end", () => {
131532
+ if (res.statusCode === 200 || res.statusCode === 201) {
131533
+ try {
131534
+ const idMatch = data.match(/id="([^"]+)"/);
131535
+ if (idMatch && idMatch[1]) {
131536
+ log3("debug", `Document created successfully with ID: ${idMatch[1]}`);
131537
+ resolve({ created: true, documentId: idMatch[1] });
131538
+ } else {
131539
+ log3("warning", `Document created but could not parse ID from response: ${data}`);
131540
+ reject(new Error("Could not parse document ID from create response"));
131541
+ }
131542
+ } catch (parseError) {
131543
+ reject(new Error(`Failed to parse create response: ${parseError.message}`));
131544
+ }
131545
+ } else if (res.statusCode === 400 && data.includes("already exists")) {
131546
+ log3("debug", `Document already exists in folder, will search for existing file`);
131547
+ resolve({ created: false, existsInFolder: true, parentFolderId });
131548
+ } else {
131549
+ reject(new Error(`Create document failed with status ${res.statusCode}: ${data}`));
131550
+ }
131551
+ });
131552
+ });
131553
+ req.on("error", (error) => {
131554
+ reject(new Error(`Create document request error: ${error.message}`));
131555
+ });
131556
+ req.setTimeout(3e4, () => {
131557
+ req.destroy(new Error("Request timeout"));
131558
+ });
131559
+ req.write(createBody);
131560
+ req.end();
131561
+ });
131562
+ }
131563
+ /**
131564
+ * Gets file information from a specific folder.
131565
+ * @param {Object} options - Options
131566
+ * @returns {Promise<string|null>} File ID if found, null otherwise
131567
+ */
131568
+ async getFileInFolder({ apiBaseUrl, apiToken, username, folderId, filename, log: log3 }) {
131569
+ const folderUrl = new URL(`/rest/all-files/${folderId}`, apiBaseUrl);
131570
+ return new Promise((resolve, reject) => {
131571
+ const protocol = folderUrl.protocol === "https:" ? import_node_https.default : import_node_http.default;
131572
+ const authString = Buffer.from(`${username}:${apiToken}`).toString("base64");
131573
+ const options = {
131574
+ hostname: folderUrl.hostname,
131575
+ port: folderUrl.port || (folderUrl.protocol === "https:" ? 443 : 80),
131576
+ path: folderUrl.pathname,
131577
+ method: "GET",
131578
+ headers: {
131579
+ "Authorization": `Basic ${authString}`,
131580
+ "Accept": "application/xml"
131581
+ }
131582
+ };
131583
+ log3("debug", `Getting folder contents: ${folderUrl.toString()}`);
131584
+ const req = protocol.request(options, (res) => {
131585
+ let data = "";
131586
+ res.on("data", (chunk) => {
131587
+ data += chunk;
131588
+ });
131589
+ res.on("end", () => {
131590
+ if (res.statusCode === 200) {
131591
+ try {
131592
+ const escapedFilename = filename.replace(/[.*+?^${}()|[\]\\/]/g, "\\$&");
131593
+ const nameIdMatch = data.match(new RegExp(`id="([^"]+)"[^>]*name="${escapedFilename}"`, "i"));
131594
+ const idNameMatch = data.match(new RegExp(`name="${escapedFilename}"[^>]*id="([^"]+)"`, "i"));
131595
+ const match = nameIdMatch || idNameMatch;
131596
+ if (match && match[1]) {
131597
+ log3("debug", `Found file ${filename} with ID: ${match[1]}`);
131598
+ resolve(match[1]);
131599
+ } else {
131600
+ log3("debug", `File ${filename} not found in folder ${folderId}`);
131601
+ resolve(null);
131602
+ }
131603
+ } catch (parseError) {
131604
+ log3("debug", `Error parsing folder contents: ${parseError.message}`);
131605
+ resolve(null);
131606
+ }
131607
+ } else {
131608
+ log3("debug", `Failed to get folder contents: ${res.statusCode}`);
131609
+ resolve(null);
131610
+ }
131611
+ });
131612
+ });
131613
+ req.on("error", (error) => {
131614
+ log3("debug", `Error getting folder contents: ${error.message}`);
131615
+ resolve(null);
131616
+ });
131617
+ req.setTimeout(3e4, () => {
131618
+ req.destroy(new Error("Request timeout"));
131619
+ });
131620
+ req.end();
131621
+ });
131622
+ }
131623
+ /**
131624
+ * Escapes special characters for XML.
131625
+ * @param {string} str - String to escape
131626
+ * @returns {string} Escaped string
131627
+ */
131628
+ escapeXml(str) {
131629
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
131630
+ }
131631
+ /**
131632
+ * Searches for a folder in Heretto by name.
131633
+ * @param {Object} options - Search options
131634
+ * @returns {Promise<string|null>} Folder ID if found, null otherwise
131635
+ */
131636
+ async searchFolderByName({ apiBaseUrl, apiToken, username, folderName, log: log3 }) {
131637
+ const searchUrl = new URL("/ezdnxtgen/api/search", apiBaseUrl);
131638
+ const searchBody = JSON.stringify({
131639
+ queryString: folderName,
131640
+ searchResultType: "FOLDERS_ONLY"
131641
+ });
131642
+ return new Promise((resolve, reject) => {
131643
+ const protocol = searchUrl.protocol === "https:" ? import_node_https.default : import_node_http.default;
131644
+ const authString = Buffer.from(`${username}:${apiToken}`).toString("base64");
131645
+ const options = {
131646
+ hostname: searchUrl.hostname,
131647
+ port: searchUrl.port || (searchUrl.protocol === "https:" ? 443 : 80),
131648
+ path: searchUrl.pathname,
131649
+ method: "POST",
131650
+ headers: {
131651
+ "Content-Type": "application/json",
131652
+ "Authorization": `Basic ${authString}`,
131653
+ "Content-Length": Buffer.byteLength(searchBody)
131654
+ }
131655
+ };
131656
+ log3("debug", `Searching for folder: ${folderName}`);
131657
+ const req = protocol.request(options, (res) => {
131658
+ let data = "";
131659
+ res.on("data", (chunk) => {
131660
+ data += chunk;
131661
+ });
131662
+ res.on("end", () => {
131663
+ if (res.statusCode >= 200 && res.statusCode < 300) {
131664
+ try {
131665
+ if (!data || data.trim() === "") {
131666
+ log3("debug", "Folder search returned empty response - no results found");
131667
+ resolve(null);
131668
+ return;
131669
+ }
131670
+ const result = JSON.parse(data);
131671
+ if (result.searchResults && result.searchResults.length > 0) {
131672
+ const match = result.searchResults.find((r) => r.name === folderName || r.title === folderName);
131673
+ if (match) {
131674
+ log3("debug", `Found folder: ${folderName} with ID: ${match.uuid || match.id}`);
131675
+ resolve(match.uuid || match.id);
131676
+ } else {
131677
+ log3("debug", `Exact folder match not found, using first result: ${result.searchResults[0].uuid || result.searchResults[0].id}`);
131678
+ resolve(result.searchResults[0].uuid || result.searchResults[0].id);
131679
+ }
131680
+ } else {
131681
+ log3("debug", `No folders found matching: ${folderName}`);
131682
+ resolve(null);
131683
+ }
131684
+ } catch (parseError) {
131685
+ reject(new Error(`Failed to parse folder search response: ${parseError.message}`));
131686
+ }
131687
+ } else {
131688
+ reject(new Error(`Folder search request failed with status ${res.statusCode}: ${data}`));
131689
+ }
131690
+ });
131691
+ });
131692
+ req.on("error", (error) => {
131693
+ reject(new Error(`Folder search request error: ${error.message}`));
131694
+ });
131695
+ req.setTimeout(3e4, () => {
131696
+ req.destroy(new Error("Request timeout"));
131697
+ });
131698
+ req.write(searchBody);
131699
+ req.end();
131700
+ });
131701
+ }
131702
+ /**
131703
+ * Searches for a file in Heretto by filename.
131704
+ * @param {Object} options - Search options
131705
+ * @returns {Promise<string|null>} Document ID if found, null otherwise
131706
+ */
131707
+ async searchFileByName({ apiBaseUrl, apiToken, username, filename, log: log3 }) {
131708
+ const searchUrl = new URL("/ezdnxtgen/api/search", apiBaseUrl);
131709
+ const searchBody = JSON.stringify({
131710
+ queryString: filename,
131711
+ searchResultType: "FILES_ONLY"
131712
+ });
131713
+ return new Promise((resolve, reject) => {
131714
+ const protocol = searchUrl.protocol === "https:" ? import_node_https.default : import_node_http.default;
131715
+ const authString = Buffer.from(`${username}:${apiToken}`).toString("base64");
131716
+ const options = {
131717
+ hostname: searchUrl.hostname,
131718
+ port: searchUrl.port || (searchUrl.protocol === "https:" ? 443 : 80),
131719
+ path: searchUrl.pathname,
131720
+ method: "POST",
131721
+ headers: {
131722
+ "Content-Type": "application/json",
131723
+ "Authorization": `Basic ${authString}`,
131724
+ "Content-Length": Buffer.byteLength(searchBody)
131725
+ }
131726
+ };
131727
+ const req = protocol.request(options, (res) => {
131728
+ let data = "";
131729
+ res.on("data", (chunk) => {
131730
+ data += chunk;
131731
+ });
131732
+ res.on("end", () => {
131733
+ if (res.statusCode >= 200 && res.statusCode < 300) {
131734
+ try {
131735
+ if (!data || data.trim() === "") {
131736
+ log3("debug", "Search returned empty response - no results found");
131737
+ resolve(null);
131738
+ return;
131739
+ }
131740
+ const result = JSON.parse(data);
131741
+ if (result.searchResults && result.searchResults.length > 0) {
131742
+ const match = result.searchResults.find((r) => r.name === filename || r.title === filename);
131743
+ if (match) {
131744
+ resolve(match.uuid || match.id);
131745
+ } else {
131746
+ resolve(result.searchResults[0].uuid || result.searchResults[0].id);
131747
+ }
131748
+ } else {
131749
+ resolve(null);
131750
+ }
131751
+ } catch (parseError) {
131752
+ reject(new Error(`Failed to parse search response: ${parseError.message}`));
131753
+ }
131754
+ } else {
131755
+ reject(new Error(`Search request failed with status ${res.statusCode}: ${data}`));
131756
+ }
131757
+ });
131758
+ });
131759
+ req.on("error", (error) => {
131760
+ reject(new Error(`Search request error: ${error.message}`));
131761
+ });
131762
+ req.setTimeout(3e4, () => {
131763
+ req.destroy(new Error("Request timeout"));
131764
+ });
131765
+ req.write(searchBody);
131766
+ req.end();
131767
+ });
131768
+ }
131769
+ /**
131770
+ * Uploads file content to Heretto.
131771
+ * @param {Object} options - Upload options
131772
+ * @returns {Promise<void>}
131773
+ */
131774
+ async uploadFile({ apiBaseUrl, apiToken, username, documentId, content, contentType, log: log3 }) {
131775
+ const uploadUrl = new URL(`/rest/all-files/${documentId}/content`, apiBaseUrl);
131776
+ return new Promise((resolve, reject) => {
131777
+ const protocol = uploadUrl.protocol === "https:" ? import_node_https.default : import_node_http.default;
131778
+ const authString = Buffer.from(`${username}:${apiToken}`).toString("base64");
131779
+ const options = {
131780
+ hostname: uploadUrl.hostname,
131781
+ port: uploadUrl.port || (uploadUrl.protocol === "https:" ? 443 : 80),
131782
+ path: uploadUrl.pathname,
131783
+ method: "PUT",
131784
+ headers: {
131785
+ "Content-Type": contentType,
131786
+ "Authorization": `Basic ${authString}`,
131787
+ "Content-Length": Buffer.byteLength(content)
131788
+ }
131789
+ };
131790
+ log3("debug", `Uploading to ${uploadUrl.toString()}`);
131791
+ const req = protocol.request(options, (res) => {
131792
+ let data = "";
131793
+ res.on("data", (chunk) => {
131794
+ data += chunk;
131795
+ });
131796
+ res.on("end", () => {
131797
+ if (res.statusCode >= 200 && res.statusCode < 300) {
131798
+ log3("debug", `Upload successful: ${res.statusCode}`);
131799
+ resolve();
131800
+ } else {
131801
+ reject(new Error(`Upload failed with status ${res.statusCode}: ${data}`));
131802
+ }
131803
+ });
131804
+ });
131805
+ req.on("error", (error) => {
131806
+ reject(new Error(`Upload request error: ${error.message}`));
131807
+ });
131808
+ req.setTimeout(3e4, () => {
131809
+ req.destroy(new Error("Request timeout"));
131810
+ });
131811
+ req.write(content);
131812
+ req.end();
131813
+ });
131814
+ }
131815
+ /**
131816
+ * Gets document information from Heretto.
131817
+ * @param {Object} options - Options
131818
+ * @returns {Promise<Object>} Document info including id, name, mimeType, folderUuid, uri
131819
+ */
131820
+ async getDocumentInfo({ apiBaseUrl, apiToken, username, documentId, log: log3 }) {
131821
+ const docUrl = new URL(`/rest/all-files/${documentId}`, apiBaseUrl);
131822
+ return new Promise((resolve, reject) => {
131823
+ const protocol = docUrl.protocol === "https:" ? import_node_https.default : import_node_http.default;
131824
+ const authString = Buffer.from(`${username}:${apiToken}`).toString("base64");
131825
+ const options = {
131826
+ hostname: docUrl.hostname,
131827
+ port: docUrl.port || (docUrl.protocol === "https:" ? 443 : 80),
131828
+ path: docUrl.pathname,
131829
+ method: "GET",
131830
+ headers: {
131831
+ "Authorization": `Basic ${authString}`,
131832
+ "Accept": "application/xml"
131833
+ }
131834
+ };
131835
+ log3("debug", `Getting document info: ${docUrl.toString()}`);
131836
+ const req = protocol.request(options, (res) => {
131837
+ let data = "";
131838
+ res.on("data", (chunk) => {
131839
+ data += chunk;
131840
+ });
131841
+ res.on("end", () => {
131842
+ if (res.statusCode === 200) {
131843
+ try {
131844
+ const resourceMatch = data.match(/<resource\s+([^>]+)>/);
131845
+ let id = null;
131846
+ let folderUuid = null;
131847
+ if (resourceMatch) {
131848
+ const attrs = resourceMatch[1];
131849
+ const idMatch = attrs.match(/\bid="([^"]+)"/);
131850
+ const folderMatch = attrs.match(/\bfolder-uuid="([^"]+)"/);
131851
+ id = idMatch ? idMatch[1] : null;
131852
+ folderUuid = folderMatch ? folderMatch[1] : null;
131853
+ }
131854
+ const nameMatch = data.match(/<name>([^<]+)<\/name>/);
131855
+ const mimeMatch = data.match(/<mime-type>([^<]+)<\/mime-type>/);
131856
+ const uriMatch = data.match(/<xmldb-uri>([^<]+)<\/xmldb-uri>/);
131857
+ resolve({
131858
+ id,
131859
+ name: nameMatch ? nameMatch[1] : null,
131860
+ mimeType: mimeMatch ? mimeMatch[1] : null,
131861
+ folderUuid,
131862
+ uri: uriMatch ? uriMatch[1] : null,
131863
+ rawXml: data
131864
+ });
131865
+ } catch (parseError) {
131866
+ reject(new Error(`Failed to parse document info: ${parseError.message}`));
131867
+ }
131868
+ } else {
131869
+ reject(new Error(`Get document info failed with status ${res.statusCode}: ${data}`));
131870
+ }
131871
+ });
131872
+ });
131873
+ req.on("error", (error) => {
131874
+ reject(new Error(`Get document info request error: ${error.message}`));
131875
+ });
131876
+ req.setTimeout(3e4, () => {
131877
+ req.destroy(new Error("Request timeout"));
131878
+ });
131879
+ req.end();
131880
+ });
131881
+ }
131882
+ /**
131883
+ * Gets document content from Heretto.
131884
+ * @param {Object} options - Options
131885
+ * @returns {Promise<Buffer>} Document content as buffer
131886
+ */
131887
+ async getDocumentContent({ apiBaseUrl, apiToken, username, documentId, log: log3 }) {
131888
+ const contentUrl = new URL(`/rest/all-files/${documentId}/content`, apiBaseUrl);
131889
+ return new Promise((resolve, reject) => {
131890
+ const protocol = contentUrl.protocol === "https:" ? import_node_https.default : import_node_http.default;
131891
+ const authString = Buffer.from(`${username}:${apiToken}`).toString("base64");
131892
+ const options = {
131893
+ hostname: contentUrl.hostname,
131894
+ port: contentUrl.port || (contentUrl.protocol === "https:" ? 443 : 80),
131895
+ path: contentUrl.pathname,
131896
+ method: "GET",
131897
+ headers: {
131898
+ "Authorization": `Basic ${authString}`
131899
+ }
131900
+ };
131901
+ log3("debug", `Getting document content: ${contentUrl.toString()}`);
131902
+ const req = protocol.request(options, (res) => {
131903
+ const chunks = [];
131904
+ res.on("data", (chunk) => {
131905
+ chunks.push(chunk);
131906
+ });
131907
+ res.on("end", () => {
131908
+ if (res.statusCode === 200) {
131909
+ resolve(Buffer.concat(chunks));
131910
+ } else {
131911
+ reject(new Error(`Get document content failed with status ${res.statusCode}`));
131912
+ }
131913
+ });
131914
+ });
131915
+ req.on("error", (error) => {
131916
+ reject(new Error(`Get document content request error: ${error.message}`));
131917
+ });
131918
+ req.setTimeout(3e4, () => {
131919
+ req.destroy(new Error("Request timeout"));
131920
+ });
131921
+ req.end();
131922
+ });
131923
+ }
131924
+ /**
131925
+ * Determines the content type based on file extension.
131926
+ * @param {string} filePath - File path
131927
+ * @returns {string} MIME content type
131928
+ */
131929
+ getContentType(filePath) {
131930
+ const ext = import_node_path5.default.extname(filePath).toLowerCase();
131931
+ const contentTypes = {
131932
+ ".png": "image/png",
131933
+ ".jpg": "image/jpeg",
131934
+ ".jpeg": "image/jpeg",
131935
+ ".gif": "image/gif",
131936
+ ".svg": "image/svg+xml",
131937
+ ".webp": "image/webp",
131938
+ ".bmp": "image/bmp",
131939
+ ".ico": "image/x-icon",
131940
+ ".pdf": "application/pdf",
131941
+ ".xml": "application/xml",
131942
+ ".dita": "application/xml",
131943
+ ".ditamap": "application/xml"
131944
+ };
131945
+ return contentTypes[ext] || "application/octet-stream";
131946
+ }
131947
+ };
131948
+ var POLLING_INTERVAL_MS = 5e3;
131949
+ var POLLING_TIMEOUT_MS = 3e5;
131950
+ var API_REQUEST_TIMEOUT_MS = 3e4;
131951
+ var DOWNLOAD_TIMEOUT_MS = 3e5;
131952
+ var DEFAULT_SCENARIO_NAME = "Doc Detective";
131953
+ function createAuthHeader(username, apiToken) {
131954
+ const credentials = `${username}:${apiToken}`;
131955
+ return Buffer.from(credentials).toString("base64");
131956
+ }
131957
+ function getBaseUrl(organizationId) {
131958
+ return `https://${organizationId}.heretto.com/ezdnxtgen/api/v2`;
131959
+ }
131960
+ function validateDitamapInAssets(assets) {
131961
+ return assets.some((assetPath) => assetPath.startsWith("ot-output/dita/") && assetPath.endsWith(".ditamap"));
131962
+ }
131963
+ function createApiClient(herettoConfig) {
131964
+ const authHeader = createAuthHeader(herettoConfig.username, herettoConfig.apiToken);
131965
+ return import_axios3.default.create({
131966
+ baseURL: getBaseUrl(herettoConfig.organizationId),
131967
+ timeout: API_REQUEST_TIMEOUT_MS,
131968
+ headers: {
131969
+ Authorization: `Basic ${authHeader}`,
131970
+ "Content-Type": "application/json"
131971
+ }
131972
+ });
131973
+ }
131974
+ function createRestApiClient(herettoConfig) {
131975
+ const authHeader = createAuthHeader(herettoConfig.username, herettoConfig.apiToken);
131976
+ return import_axios3.default.create({
131977
+ baseURL: `https://${herettoConfig.organizationId}.heretto.com`,
131978
+ timeout: API_REQUEST_TIMEOUT_MS,
131979
+ headers: {
131980
+ Authorization: `Basic ${authHeader}`,
131981
+ Accept: "application/xml, text/xml, */*"
131982
+ }
131983
+ });
131984
+ }
131985
+ async function findScenario(client, log3, config, scenarioName) {
131986
+ try {
131987
+ const scenariosResp = await client.get("/publishes/scenarios");
131988
+ const scenarios = scenariosResp.data.content || [];
131989
+ const foundScenario = scenarios.find((s) => s.name === scenarioName);
131990
+ if (!foundScenario) {
131991
+ log3(config, "error", `No existing "${scenarioName}" scenario found.`);
131992
+ return null;
131993
+ }
131994
+ const paramsResp = await client.get(`/publishes/scenarios/${foundScenario.id}/parameters`);
131995
+ const scenarioParameters = paramsResp.data;
131996
+ if (!scenarioParameters) {
131997
+ log3(config, "error", `Failed to retrieve scenario details for ID: ${foundScenario.id}`);
131998
+ return null;
131999
+ }
132000
+ log3(config, "debug", `Scenario parameters: ${JSON.stringify(scenarioParameters.content?.map((p) => ({ name: p.name, type: p.type, value: p.value })))}`);
132001
+ const transtypeParam = scenarioParameters.content?.find((param) => param.name === "transtype");
132002
+ if (!transtypeParam || transtypeParam.value !== "dita" && transtypeParam.options?.[0]?.value !== "dita") {
132003
+ log3(config, "error", `Existing "${scenarioName}" scenario: "transtype" must be set to "dita".`);
132004
+ return null;
132005
+ }
132006
+ const toolKitParam = scenarioParameters.content?.find((param) => param.name === "tool-kit-name");
132007
+ if (!toolKitParam || !toolKitParam.value) {
132008
+ log3(config, "error", `Existing "${scenarioName}" scenario has incorrect "tool-kit-name" parameter settings.`);
132009
+ return null;
132010
+ }
132011
+ const fileUuidPickerParam = scenarioParameters.content?.find((param) => param.type === "file_uuid_picker");
132012
+ if (!fileUuidPickerParam || !fileUuidPickerParam.value) {
132013
+ log3(config, "error", `Existing "${scenarioName}" scenario has incorrect "file_uuid_picker" parameter settings. Make sure it has a valid value.`);
132014
+ return null;
132015
+ }
132016
+ log3(config, "debug", `Found existing "${scenarioName}" scenario: ${foundScenario.id}`);
132017
+ return {
132018
+ scenarioId: foundScenario.id,
132019
+ fileId: fileUuidPickerParam.value
132020
+ };
132021
+ } catch (error) {
132022
+ log3(config, "error", `Failed to find publishing scenario: ${error.message}`);
132023
+ return null;
132024
+ }
132025
+ }
132026
+ async function triggerPublishingJob(client, fileId, scenarioId) {
132027
+ const response = await client.post(`/files/${fileId}/publishes`, {
132028
+ scenario: scenarioId,
132029
+ parameters: []
132030
+ });
132031
+ if (!response.data?.jobId) {
132032
+ throw new Error("Publishing job response missing jobId");
132033
+ }
132034
+ return response.data;
132035
+ }
132036
+ async function getJobStatus(client, fileId, jobId) {
132037
+ const response = await client.get(`/files/${fileId}/publishes/${jobId}`);
132038
+ return response.data;
132039
+ }
132040
+ async function getJobAssetDetails(client, fileId, jobId, log3, config) {
132041
+ const allAssets = [];
132042
+ let page = 0;
132043
+ const pageSize = 100;
132044
+ const maxPages = 1e3;
132045
+ let hasMorePages = true;
132046
+ while (hasMorePages && page < maxPages) {
132047
+ const response = await client.get(`/files/${fileId}/publishes/${jobId}/assets`, { params: { page, size: pageSize } });
132048
+ const data = response.data;
132049
+ const content = data.content || [];
132050
+ for (const asset of content) {
132051
+ if (asset.filePath) {
132052
+ allAssets.push(asset.filePath);
132053
+ }
132054
+ }
132055
+ const totalPages = data.totalPages || 1;
132056
+ page++;
132057
+ hasMorePages = page < totalPages;
132058
+ if (page === maxPages && totalPages > maxPages && log3) {
132059
+ log3(config, "warning", `Job ${jobId} asset pagination truncated at ${maxPages} pages (API reports ${totalPages} total). Asset list may be incomplete.`);
132060
+ }
132061
+ }
132062
+ return allAssets;
132063
+ }
132064
+ async function pollJobStatus(client, fileId, jobId, log3, config) {
132065
+ const startTime = Date.now();
132066
+ while (Date.now() - startTime < POLLING_TIMEOUT_MS) {
132067
+ try {
132068
+ const job = await getJobStatus(client, fileId, jobId);
132069
+ log3(config, "debug", `Job ${jobId} status: ${job?.status?.status}`);
132070
+ if (job?.status?.result) {
132071
+ log3(config, "debug", `Job ${jobId} completed with result: ${job.status.result}`);
132072
+ if (job.status.result !== "success") {
132073
+ log3(config, "warning", `Publishing job ${jobId} finished with non-success result: ${job.status.result}. Checking if output files are available...`);
132074
+ }
132075
+ try {
132076
+ const assets = await getJobAssetDetails(client, fileId, jobId, log3, config);
132077
+ log3(config, "debug", `Job ${jobId} has ${assets.length} assets`);
132078
+ if (validateDitamapInAssets(assets)) {
132079
+ log3(config, "debug", `Found .ditamap file in ot-output/dita/`);
132080
+ if (job.status.result !== "success") {
132081
+ log3(config, "info", `Publishing job ${jobId} result was "${job.status.result}" but required output files are present. Proceeding.`);
132082
+ }
132083
+ return job;
132084
+ }
132085
+ log3(config, "warning", `Publishing job ${jobId} completed but no .ditamap file found in ot-output/dita/`);
132086
+ return null;
132087
+ } catch (assetError) {
132088
+ log3(config, "warning", `Failed to validate job assets: ${assetError.message}`);
132089
+ return null;
132090
+ }
132091
+ }
132092
+ await new Promise((resolve) => setTimeout(resolve, POLLING_INTERVAL_MS));
132093
+ } catch (error) {
132094
+ log3(config, "warning", `Error polling job status: ${error.message}`);
132095
+ return null;
132096
+ }
132097
+ }
132098
+ log3(config, "warning", `Publishing job ${jobId} timed out after ${POLLING_TIMEOUT_MS / 1e3} seconds`);
132099
+ return null;
132100
+ }
132101
+ async function downloadAndExtractOutput(client, fileId, jobId, herettoName, log3, config, deps) {
132102
+ const fsModule = deps?.fsModule || import_node_fs4.default;
132103
+ const ZipClass = deps?.ZipClass || import_adm_zip.default;
132104
+ const tempDir = import_node_path5.default.join(import_node_os3.default.tmpdir(), "doc-detective");
132105
+ const hash = import_node_crypto2.default.createHash("md5").update(`${herettoName}_${jobId}`).digest("hex");
132106
+ try {
132107
+ fsModule.mkdirSync(tempDir, { recursive: true });
132108
+ const outputDir = import_node_path5.default.join(tempDir, `heretto_${hash}`);
132109
+ log3(config, "debug", `Downloading publishing job output for ${herettoName}...`);
132110
+ const response = await client.get(`/files/${fileId}/publishes/${jobId}/assets-all`, {
132111
+ responseType: "arraybuffer",
132112
+ timeout: DOWNLOAD_TIMEOUT_MS,
132113
+ headers: { Accept: "application/octet-stream" }
132114
+ });
132115
+ const zipPath = import_node_path5.default.join(tempDir, `heretto_${hash}.zip`);
132116
+ fsModule.writeFileSync(zipPath, response.data);
132117
+ log3(config, "debug", `Extracting output to ${outputDir}...`);
132118
+ const zip = new ZipClass(zipPath);
132119
+ const resolvedOutputDir = import_node_path5.default.resolve(outputDir);
132120
+ for (const entry of zip.getEntries()) {
132121
+ const normalizedName = import_node_path5.default.posix.normalize(entry.entryName.replace(/\\/g, "/"));
132122
+ if (normalizedName.startsWith("..") || normalizedName.includes("/..")) {
132123
+ log3(config, "warning", `Skipping ZIP entry with path traversal: ${entry.entryName}`);
132124
+ continue;
132125
+ }
132126
+ const resolvedPath = import_node_path5.default.resolve(outputDir, normalizedName);
132127
+ if (!resolvedPath.startsWith(resolvedOutputDir + import_node_path5.default.sep) && resolvedPath !== resolvedOutputDir) {
132128
+ log3(config, "warning", `Skipping ZIP entry outside output directory: ${entry.entryName}`);
132129
+ continue;
132130
+ }
132131
+ if (entry.isDirectory) {
132132
+ fsModule.mkdirSync(resolvedPath, { recursive: true });
132133
+ } else {
132134
+ fsModule.mkdirSync(import_node_path5.default.dirname(resolvedPath), { recursive: true });
132135
+ fsModule.writeFileSync(resolvedPath, entry.getData());
132136
+ }
132137
+ }
132138
+ fsModule.unlinkSync(zipPath);
132139
+ log3(config, "info", `Heretto content "${herettoName}" extracted to ${outputDir}`);
132140
+ return outputDir;
132141
+ } catch (error) {
132142
+ try {
132143
+ const zipCleanupPath = import_node_path5.default.join(tempDir, `heretto_${hash}.zip`);
132144
+ if (fsModule.existsSync(zipCleanupPath)) {
132145
+ fsModule.unlinkSync(zipCleanupPath);
132146
+ }
132147
+ } catch {
132148
+ }
132149
+ try {
132150
+ const cleanupOutputDir = import_node_path5.default.join(tempDir, `heretto_${hash}`);
132151
+ if (fsModule.existsSync(cleanupOutputDir)) {
132152
+ fsModule.rmSync(cleanupOutputDir, { recursive: true, force: true });
132153
+ }
132154
+ } catch {
132155
+ }
132156
+ log3(config, "warning", `Failed to download or extract output: ${error.message}`);
132157
+ return null;
132158
+ }
132159
+ }
132160
+ async function getResourceDependencies(restClient, ditamapId, log3, config) {
132161
+ const { XMLParser } = await import("fast-xml-parser");
132162
+ const xmlParser = new XMLParser({
132163
+ ignoreAttributes: false,
132164
+ attributeNamePrefix: "@_"
132165
+ });
132166
+ const pathToUuidMap = {};
132167
+ const REST_API_PATH = "/rest/all-files";
132168
+ try {
132169
+ log3(config, "debug", `Fetching ditamap info for: ${ditamapId}`);
132170
+ const ditamapInfo = await restClient.get(`${REST_API_PATH}/${ditamapId}`);
132171
+ const ditamapParsed = xmlParser.parse(ditamapInfo.data);
132172
+ const ditamapUri = ditamapParsed.resource?.["xmldb-uri"] || ditamapParsed["@_uri"];
132173
+ const ditamapName = ditamapParsed.resource?.name || ditamapParsed["@_name"];
132174
+ const ditamapParentFolder = ditamapParsed.resource?.["folder-uuid"] || ditamapParsed.resource?.["@_folder-uuid"] || ditamapParsed["@_folder-uuid"];
132175
+ if (ditamapUri) {
132176
+ let relativePath = ditamapUri;
132177
+ const orgPathMatch = relativePath?.match(/\/db\/organizations\/[^/]+\/(.+)/);
132178
+ if (orgPathMatch) {
132179
+ relativePath = orgPathMatch[1];
132180
+ }
132181
+ pathToUuidMap[relativePath] = {
132182
+ uuid: ditamapId,
132183
+ fullPath: ditamapUri,
132184
+ name: ditamapName,
132185
+ parentFolderId: ditamapParentFolder,
132186
+ isDitamap: true
132187
+ };
132188
+ pathToUuidMap._ditamapPath = relativePath;
132189
+ pathToUuidMap._ditamapId = ditamapId;
132190
+ pathToUuidMap._ditamapParentFolderId = ditamapParentFolder;
132191
+ }
132192
+ } catch (ditamapError) {
132193
+ log3(config, "warning", `Could not get ditamap info: ${ditamapError.message}`);
132194
+ }
132195
+ try {
132196
+ log3(config, "debug", `Fetching resource dependencies for ditamap: ${ditamapId}`);
132197
+ const response = await restClient.get(`${REST_API_PATH}/${ditamapId}/dependencies`);
132198
+ const parsed = xmlParser.parse(response.data);
132199
+ const extractDependencies = (obj) => {
132200
+ if (!obj)
132201
+ return;
132202
+ let dependencies = obj.dependencies?.dependency || obj.dependency;
132203
+ if (!dependencies) {
132204
+ if (obj["@_id"] && obj["@_uri"]) {
132205
+ dependencies = [obj];
132206
+ } else if (Array.isArray(obj)) {
132207
+ dependencies = obj;
132208
+ }
132209
+ }
132210
+ if (!dependencies)
132211
+ return;
132212
+ if (!Array.isArray(dependencies)) {
132213
+ dependencies = [dependencies];
132214
+ }
132215
+ for (const dep of dependencies) {
132216
+ const uuid = dep["@_id"] || dep["@_uuid"] || dep.id || dep.uuid;
132217
+ const uri = dep["@_uri"] || dep["@_path"] || dep.uri || dep.path || dep["xmldb-uri"];
132218
+ const name = dep["@_name"] || dep.name;
132219
+ const parentFolderId = dep["@_folder-uuid"] || dep["@_parent"] || dep["folder-uuid"];
132220
+ if (uuid && (uri || name)) {
132221
+ let relativePath = uri || name;
132222
+ const orgPathMatch = relativePath?.match(/\/db\/organizations\/[^/]+\/(.+)/);
132223
+ if (orgPathMatch) {
132224
+ relativePath = orgPathMatch[1];
132225
+ }
132226
+ pathToUuidMap[relativePath] = {
132227
+ uuid,
132228
+ fullPath: uri,
132229
+ name: name || import_node_path5.default.basename(relativePath || ""),
132230
+ parentFolderId
132231
+ };
132232
+ }
132233
+ if (dep.dependencies || dep.dependency) {
132234
+ extractDependencies(dep);
132235
+ }
132236
+ }
132237
+ };
132238
+ extractDependencies(parsed);
132239
+ log3(config, "info", `Retrieved ${Object.keys(pathToUuidMap).length} resource dependencies from Heretto`);
132240
+ } catch (error) {
132241
+ const statusCode = error.response?.status;
132242
+ log3(config, "debug", `Dependencies endpoint not available (${statusCode}), will use ditamap info as fallback`);
132243
+ }
132244
+ return pathToUuidMap;
132245
+ }
132246
+ async function loadHerettoContent(herettoConfig, log3, config, deps) {
132247
+ const createApiClientFn = deps?.createApiClientFn || createApiClient;
132248
+ const createRestApiClientFn = deps?.createRestApiClientFn || createRestApiClient;
132249
+ const downloadFn = deps?.downloadFn || downloadAndExtractOutput;
132250
+ const getResourceDependenciesFn = deps?.getResourceDependenciesFn || getResourceDependencies;
132251
+ log3(config, "info", `Loading content from Heretto "${herettoConfig.name}"...`);
132252
+ try {
132253
+ const client = createApiClientFn(herettoConfig);
132254
+ const restClient = createRestApiClientFn(herettoConfig);
132255
+ const scenarioName = herettoConfig.scenarioName || DEFAULT_SCENARIO_NAME;
132256
+ const scenario = await findScenario(client, log3, config, scenarioName);
132257
+ if (!scenario) {
132258
+ log3(config, "warning", `Skipping Heretto "${herettoConfig.name}" - could not find publishing scenario`);
132259
+ return null;
132260
+ }
132261
+ if (herettoConfig.uploadOnChange) {
132262
+ log3(config, "debug", `Fetching resource dependencies for ditamap ${scenario.fileId}...`);
132263
+ const resourceDependencies = await getResourceDependenciesFn(restClient, scenario.fileId, log3, config);
132264
+ herettoConfig.resourceDependencies = {
132265
+ ...resourceDependencies,
132266
+ ...herettoConfig.resourceDependencies || {}
132267
+ };
132268
+ }
132269
+ log3(config, "debug", `Triggering publishing job for file ${scenario.fileId}...`);
132270
+ const job = await triggerPublishingJob(client, scenario.fileId, scenario.scenarioId);
132271
+ log3(config, "debug", `Publishing job started: ${job.jobId}`);
132272
+ log3(config, "info", `Waiting for publishing job to complete...`);
132273
+ const completedJob = await pollJobStatus(client, scenario.fileId, job.jobId, log3, config);
132274
+ if (!completedJob) {
132275
+ log3(config, "warning", `Skipping Heretto "${herettoConfig.name}" - publishing job failed or timed out`);
132276
+ return null;
132277
+ }
132278
+ const outputPath = await downloadFn(client, scenario.fileId, job.jobId, herettoConfig.name, log3, config);
132279
+ return outputPath;
132280
+ } catch (error) {
132281
+ log3(config, "warning", `Failed to load Heretto "${herettoConfig.name}": ${error.message}`);
132282
+ return null;
132283
+ }
132284
+ }
132285
+
130535
132286
  // dist/core/detectTests.js
130536
132287
  async function detectTests2({ config }) {
130537
132288
  const files = await qualifyFiles({ config });
@@ -130543,15 +132294,15 @@ async function detectTests2({ config }) {
130543
132294
  return specs;
130544
132295
  }
130545
132296
  function generateSpecId(filePath) {
130546
- const absolutePath = import_node_path5.default.resolve(filePath);
132297
+ const absolutePath = import_node_path6.default.resolve(filePath);
130547
132298
  const cwd = process.cwd();
130548
132299
  let relativePath;
130549
132300
  if (absolutePath.startsWith(cwd)) {
130550
- relativePath = import_node_path5.default.relative(cwd, absolutePath);
132301
+ relativePath = import_node_path6.default.relative(cwd, absolutePath);
130551
132302
  } else {
130552
132303
  relativePath = absolutePath;
130553
132304
  }
130554
- const normalizedPath = relativePath.split(import_node_path5.default.sep).join("/").replace(/^\.\//, "").replace(/[^a-zA-Z0-9._\-\/]/g, "_");
132305
+ const normalizedPath = relativePath.split(import_node_path6.default.sep).join("/").replace(/^\.\//, "").replace(/[^a-zA-Z0-9._\-\/]/g, "_");
130555
132306
  return normalizedPath;
130556
132307
  }
130557
132308
  async function isValidSourceFile({ config, files, source }) {
@@ -130562,7 +132313,7 @@ async function isValidSourceFile({ config, files, source }) {
130562
132313
  });
130563
132314
  if (files.indexOf(source) >= 0)
130564
132315
  return false;
130565
- if (import_node_path5.default.extname(source) === ".json" || import_node_path5.default.extname(source) === ".yaml" || import_node_path5.default.extname(source) === ".yml") {
132316
+ if (import_node_path6.default.extname(source) === ".json" || import_node_path6.default.extname(source) === ".yaml" || import_node_path6.default.extname(source) === ".yml") {
130566
132317
  const content = await readFile({ fileURLOrPath: source });
130567
132318
  if (typeof content !== "object") {
130568
132319
  log(config, "debug", `${source} isn't a valid test specification. Skipping.`);
@@ -130582,11 +132333,11 @@ async function isValidSourceFile({ config, files, source }) {
130582
132333
  if (test.before) {
130583
132334
  let beforePath = "";
130584
132335
  if (config.relativePathBase === "file") {
130585
- beforePath = import_node_path5.default.resolve(import_node_path5.default.dirname(source), test.before);
132336
+ beforePath = import_node_path6.default.resolve(import_node_path6.default.dirname(source), test.before);
130586
132337
  } else {
130587
- beforePath = import_node_path5.default.resolve(test.before);
132338
+ beforePath = import_node_path6.default.resolve(test.before);
130588
132339
  }
130589
- if (!import_node_fs4.default.existsSync(beforePath)) {
132340
+ if (!import_node_fs5.default.existsSync(beforePath)) {
130590
132341
  log(config, "debug", `${beforePath} is specified to run before a test but isn't a valid file. Skipping ${source}.`);
130591
132342
  return false;
130592
132343
  }
@@ -130594,18 +132345,18 @@ async function isValidSourceFile({ config, files, source }) {
130594
132345
  if (test.after) {
130595
132346
  let afterPath = "";
130596
132347
  if (config.relativePathBase === "file") {
130597
- afterPath = import_node_path5.default.resolve(import_node_path5.default.dirname(source), test.after);
132348
+ afterPath = import_node_path6.default.resolve(import_node_path6.default.dirname(source), test.after);
130598
132349
  } else {
130599
- afterPath = import_node_path5.default.resolve(test.after);
132350
+ afterPath = import_node_path6.default.resolve(test.after);
130600
132351
  }
130601
- if (!import_node_fs4.default.existsSync(afterPath)) {
132352
+ if (!import_node_fs5.default.existsSync(afterPath)) {
130602
132353
  log(config, "debug", `${afterPath} is specified to run after a test but isn't a valid file. Skipping ${source}.`);
130603
132354
  return false;
130604
132355
  }
130605
132356
  }
130606
132357
  }
130607
132358
  }
130608
- const extension = import_node_path5.default.extname(source).substring(1);
132359
+ const extension = import_node_path6.default.extname(source).substring(1);
130609
132360
  if (!allowedExtensions.includes(extension)) {
130610
132361
  log(config, "debug", `${source} extension isn't specified in a \`config.fileTypes\` object. Skipping.`);
130611
132362
  return false;
@@ -130613,12 +132364,12 @@ async function isValidSourceFile({ config, files, source }) {
130613
132364
  return true;
130614
132365
  }
130615
132366
  async function processDitaMap({ config, source }) {
130616
- const hash = import_node_crypto2.default.createHash("md5").update(source).digest("hex");
130617
- const tmpBase = import_node_path5.default.join(import_node_os3.default.tmpdir(), "doc-detective");
130618
- const outputDir = import_node_path5.default.join(tmpBase, `ditamap_${hash}`);
130619
- if (!import_node_fs4.default.existsSync(tmpBase)) {
132367
+ const hash = import_node_crypto3.default.createHash("md5").update(source).digest("hex");
132368
+ const tmpBase = import_node_path6.default.join(import_node_os4.default.tmpdir(), "doc-detective");
132369
+ const outputDir = import_node_path6.default.join(tmpBase, `ditamap_${hash}`);
132370
+ if (!import_node_fs5.default.existsSync(tmpBase)) {
130620
132371
  log(config, "debug", `Creating temp directory: ${tmpBase}`);
130621
- import_node_fs4.default.mkdirSync(tmpBase, { recursive: true });
132372
+ import_node_fs5.default.mkdirSync(tmpBase, { recursive: true });
130622
132373
  }
130623
132374
  const ditaVersion = await spawnCommand("dita", ["--version"]);
130624
132375
  if (ditaVersion.exitCode !== 0) {
@@ -130660,10 +132411,52 @@ async function qualifyFiles({ config }) {
130660
132411
  if (!config._herettoPathMapping) {
130661
132412
  config._herettoPathMapping = {};
130662
132413
  }
130663
- for (let source of sequence) {
132414
+ for (let i = 0; i < sequence.length; i++) {
132415
+ let source = sequence[i];
130664
132416
  log(config, "debug", `source: ${source}`);
130665
132417
  if (source.startsWith("heretto:")) {
130666
- log(config, "warning", `Heretto integration "${source}" is not supported in core. Use the resolver module for Heretto support.`);
132418
+ const herettoName = source.substring(8);
132419
+ const herettoConfig = config?.integrations?.heretto?.find((h) => h.name === herettoName);
132420
+ if (!herettoConfig) {
132421
+ log(config, "warning", `Heretto integration "${herettoName}" not found in config. Skipping.`);
132422
+ continue;
132423
+ }
132424
+ if (!herettoConfig.outputPath) {
132425
+ try {
132426
+ const outputPath = await loadHerettoContent(herettoConfig, log, config);
132427
+ if (outputPath) {
132428
+ herettoConfig.outputPath = outputPath;
132429
+ config._herettoPathMapping[outputPath] = herettoName;
132430
+ sequence.splice(i + 1, 0, outputPath);
132431
+ ignoredDitaMaps.push(outputPath);
132432
+ } else {
132433
+ log(config, "warning", `Failed to load Heretto content for "${herettoName}". Skipping.`);
132434
+ }
132435
+ } catch (error) {
132436
+ log(config, "warning", `Failed to load Heretto content from "${herettoName}": ${error.message}`);
132437
+ }
132438
+ } else {
132439
+ config._herettoPathMapping[herettoConfig.outputPath] = herettoName;
132440
+ if (!ignoredDitaMaps.includes(herettoConfig.outputPath)) {
132441
+ ignoredDitaMaps.push(herettoConfig.outputPath);
132442
+ }
132443
+ if (!sequence.includes(herettoConfig.outputPath)) {
132444
+ sequence.splice(i + 1, 0, herettoConfig.outputPath);
132445
+ }
132446
+ if (herettoConfig.uploadOnChange && !herettoConfig.resourceDependencies) {
132447
+ try {
132448
+ const client = createApiClient(herettoConfig);
132449
+ const scenarioName = herettoConfig.scenarioName || DEFAULT_SCENARIO_NAME;
132450
+ const scenario = await findScenario(client, log, config, scenarioName);
132451
+ if (scenario) {
132452
+ const restClient = createRestApiClient(herettoConfig);
132453
+ herettoConfig.resourceDependencies = await getResourceDependencies(restClient, scenario.fileId, log, config);
132454
+ }
132455
+ } catch (error) {
132456
+ log(config, "warning", `Failed to fetch resource dependencies for "${herettoName}": ${error.message}`);
132457
+ }
132458
+ }
132459
+ }
130667
132460
  continue;
130668
132461
  }
130669
132462
  let isURL = source.startsWith("http://") || source.startsWith("https://");
@@ -130678,13 +132471,13 @@ async function qualifyFiles({ config }) {
130678
132471
  let isFile = false;
130679
132472
  let isDir = false;
130680
132473
  try {
130681
- isFile = import_node_fs4.default.statSync(source).isFile();
130682
- isDir = import_node_fs4.default.statSync(source).isDirectory();
132474
+ isFile = import_node_fs5.default.statSync(source).isFile();
132475
+ isDir = import_node_fs5.default.statSync(source).isDirectory();
130683
132476
  } catch {
130684
132477
  log(config, "warning", `Cannot access path: ${source}. Skipping.`);
130685
132478
  continue;
130686
132479
  }
130687
- if (isFile && import_node_path5.default.extname(source) === ".ditamap" && !ignoredDitaMaps.some((ignored) => source.includes(ignored)) && config.processDitaMaps) {
132480
+ if (isFile && import_node_path6.default.extname(source) === ".ditamap" && !ignoredDitaMaps.some((ignored) => source.includes(ignored)) && config.processDitaMaps) {
130688
132481
  const ditaOutput = await processDitaMap({ config, source });
130689
132482
  if (ditaOutput) {
130690
132483
  const currentIndex = sequence.indexOf(source);
@@ -130694,20 +132487,20 @@ async function qualifyFiles({ config }) {
130694
132487
  continue;
130695
132488
  }
130696
132489
  if (isFile && await isValidSourceFile({ config, files, source })) {
130697
- files.push(import_node_path5.default.resolve(source));
132490
+ files.push(import_node_path6.default.resolve(source));
130698
132491
  } else if (isDir) {
130699
132492
  dirs = [];
130700
132493
  dirs[0] = source;
130701
132494
  for (const dir of dirs) {
130702
- const objects = import_node_fs4.default.readdirSync(dir);
132495
+ const objects = import_node_fs5.default.readdirSync(dir);
130703
132496
  for (const object of objects) {
130704
- const content = import_node_path5.default.resolve(dir + "/" + object);
132497
+ const content = import_node_path6.default.resolve(dir + "/" + object);
130705
132498
  if (content.includes("node_modules"))
130706
132499
  continue;
130707
- const isFile2 = import_node_fs4.default.statSync(content).isFile();
130708
- const isDir2 = import_node_fs4.default.statSync(content).isDirectory();
132500
+ const isFile2 = import_node_fs5.default.statSync(content).isFile();
132501
+ const isDir2 = import_node_fs5.default.statSync(content).isDirectory();
130709
132502
  if (isFile2 && await isValidSourceFile({ config, files, source: content })) {
130710
- files.push(import_node_path5.default.resolve(content));
132503
+ files.push(import_node_path6.default.resolve(content));
130711
132504
  } else if (isDir2 && config.recursive) {
130712
132505
  dirs.push(content);
130713
132506
  }
@@ -130721,12 +132514,12 @@ async function parseTests({ config, files }) {
130721
132514
  let specs = [];
130722
132515
  for (const file of files) {
130723
132516
  log(config, "debug", `file: ${file}`);
130724
- const extension = import_node_path5.default.extname(file).slice(1);
132517
+ const extension = import_node_path6.default.extname(file).slice(1);
130725
132518
  let content = "";
130726
132519
  let rawContent;
130727
132520
  if (extension === "json" || extension === "yaml" || extension === "yml") {
130728
132521
  try {
130729
- rawContent = await import_node_fs4.default.promises.readFile(file, "utf8");
132522
+ rawContent = await import_node_fs5.default.promises.readFile(file, "utf8");
130730
132523
  if (extension === "json") {
130731
132524
  content = JSON.parse(rawContent);
130732
132525
  } else {
@@ -130915,7 +132708,7 @@ async function parseTests({ config, files }) {
130915
132708
  }
130916
132709
 
130917
132710
  // dist/core/resolveTests.js
130918
- var import_node_crypto3 = __toESM(require("node:crypto"), 1);
132711
+ var import_node_crypto4 = __toESM(require("node:crypto"), 1);
130919
132712
  var driverActions = [
130920
132713
  "click",
130921
132714
  "dragAndDrop",
@@ -131014,7 +132807,7 @@ ${JSON.stringify(openApiDocuments, null, 2)}`);
131014
132807
  return openApiDocuments;
131015
132808
  }
131016
132809
  async function resolveContext({ config, test, context }) {
131017
- const contextId = context.contextId || import_node_crypto3.default.randomUUID();
132810
+ const contextId = context.contextId || import_node_crypto4.default.randomUUID();
131018
132811
  log(config, "debug", `RESOLVING CONTEXT ID ${contextId}:
131019
132812
  ${JSON.stringify(context, null, 2)}`);
131020
132813
  const resolvedContext = {
@@ -131029,7 +132822,7 @@ ${JSON.stringify(resolvedContext, null, 2)}`);
131029
132822
  return resolvedContext;
131030
132823
  }
131031
132824
  async function resolveTest({ config, spec, test }) {
131032
- const testId = test.testId || import_node_crypto3.default.randomUUID();
132825
+ const testId = test.testId || import_node_crypto4.default.randomUUID();
131033
132826
  log(config, "debug", `RESOLVING TEST ID ${testId}:
131034
132827
  ${JSON.stringify(test, null, 2)}`);
131035
132828
  const resolvedTest = {
@@ -131061,7 +132854,7 @@ ${JSON.stringify(resolvedTest, null, 2)}`);
131061
132854
  return resolvedTest;
131062
132855
  }
131063
132856
  async function resolveSpec({ config, spec }) {
131064
- const specId = spec.specId || import_node_crypto3.default.randomUUID();
132857
+ const specId = spec.specId || import_node_crypto4.default.randomUUID();
131065
132858
  log(config, "debug", `RESOLVING SPEC ID ${specId}:
131066
132859
  ${JSON.stringify(spec, null, 2)}`);
131067
132860
  const resolvedSpec = {
@@ -131090,7 +132883,7 @@ async function resolveTests({ config, detectedTests }) {
131090
132883
  log(config, "debug", `RESOLVING DETECTED TEST SPECS:
131091
132884
  ${JSON.stringify(detectedTests, null, 2)}`);
131092
132885
  const resolvedTests = {
131093
- resolvedTestsId: import_node_crypto3.default.randomUUID(),
132886
+ resolvedTestsId: import_node_crypto4.default.randomUUID(),
131094
132887
  config,
131095
132888
  specs: []
131096
132889
  };
@@ -131107,8 +132900,8 @@ ${JSON.stringify(resolvedTests, null, 2)}`);
131107
132900
  // dist/core/tests.js
131108
132901
  var import_tree_kill = __toESM(require("tree-kill"), 1);
131109
132902
  var wdio = __toESM(require("webdriverio"), 1);
131110
- var import_node_os6 = __toESM(require("node:os"), 1);
131111
- var import_axios5 = __toESM(require("axios"), 1);
132903
+ var import_node_os7 = __toESM(require("node:os"), 1);
132904
+ var import_axios6 = __toESM(require("axios"), 1);
131112
132905
 
131113
132906
  // dist/core/tests/moveTo.js
131114
132907
  async function instantiateCursor(driver, options = { position: "current" }) {
@@ -132328,8 +134121,8 @@ async function waitForDOMStable(driver, idleTime, timeout) {
132328
134121
  }
132329
134122
 
132330
134123
  // dist/core/tests/runShell.js
132331
- var import_node_fs5 = __toESM(require("node:fs"), 1);
132332
- var import_node_path6 = __toESM(require("node:path"), 1);
134124
+ var import_node_fs6 = __toESM(require("node:fs"), 1);
134125
+ var import_node_path7 = __toESM(require("node:path"), 1);
132333
134126
  async function runShell({ config, step }) {
132334
134127
  const result = {
132335
134128
  status: "PASS",
@@ -132402,24 +134195,24 @@ async function runShell({ config, step }) {
132402
134195
  }
132403
134196
  }
132404
134197
  if (step.runShell.path) {
132405
- const dir = import_node_path6.default.dirname(step.runShell.path);
132406
- if (!import_node_fs5.default.existsSync(dir)) {
132407
- import_node_fs5.default.mkdirSync(dir, { recursive: true });
134198
+ const dir = import_node_path7.default.dirname(step.runShell.path);
134199
+ if (!import_node_fs6.default.existsSync(dir)) {
134200
+ import_node_fs6.default.mkdirSync(dir, { recursive: true });
132408
134201
  }
132409
134202
  let filePath = step.runShell.path;
132410
134203
  log(config, "debug", `Saving stdio to file: ${filePath}`);
132411
- if (!import_node_fs5.default.existsSync(filePath)) {
132412
- import_node_fs5.default.writeFileSync(filePath, result.outputs.stdio.stdout);
134204
+ if (!import_node_fs6.default.existsSync(filePath)) {
134205
+ import_node_fs6.default.writeFileSync(filePath, result.outputs.stdio.stdout);
132413
134206
  } else {
132414
134207
  if (step.runShell.overwrite == "false") {
132415
134208
  result.description = result.description + ` Didn't save output. File already exists.`;
132416
134209
  }
132417
- const existingFile = import_node_fs5.default.readFileSync(filePath, "utf8");
134210
+ const existingFile = import_node_fs6.default.readFileSync(filePath, "utf8");
132418
134211
  const fractionalDiff = calculateFractionalDifference(existingFile, result.outputs.stdio.stdout);
132419
134212
  log(config, "debug", `Fractional difference: ${fractionalDiff}`);
132420
134213
  if (fractionalDiff > step.runShell.maxVariation) {
132421
134214
  if (step.runShell.overwrite == "aboveVariation") {
132422
- import_node_fs5.default.writeFileSync(filePath, result.outputs.stdio.stdout);
134215
+ import_node_fs6.default.writeFileSync(filePath, result.outputs.stdio.stdout);
132423
134216
  result.description += ` Saved output to file.`;
132424
134217
  }
132425
134218
  result.status = "WARNING";
@@ -132427,7 +134220,7 @@ async function runShell({ config, step }) {
132427
134220
  return result;
132428
134221
  }
132429
134222
  if (step.runShell.overwrite == "true") {
132430
- import_node_fs5.default.writeFileSync(filePath, result.outputs.stdio.stdout);
134223
+ import_node_fs6.default.writeFileSync(filePath, result.outputs.stdio.stdout);
132431
134224
  result.description += ` Saved output to file.`;
132432
134225
  }
132433
134226
  }
@@ -132436,7 +134229,103 @@ async function runShell({ config, step }) {
132436
134229
  }
132437
134230
 
132438
134231
  // dist/core/tests/checkLink.js
132439
- var import_axios3 = __toESM(require("axios"), 1);
134232
+ var import_axios4 = __toESM(require("axios"), 1);
134233
+ var DEFAULT_HEADERS = {
134234
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
134235
+ Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
134236
+ "Accept-Language": "en-US,en;q=0.9",
134237
+ "Accept-Encoding": "gzip, deflate, br",
134238
+ "Upgrade-Insecure-Requests": "1",
134239
+ "Sec-Fetch-Site": "none",
134240
+ "Sec-Fetch-Mode": "navigate",
134241
+ "Sec-Fetch-Dest": "document",
134242
+ "Sec-Fetch-User": "?1",
134243
+ "Sec-Ch-Ua": '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"',
134244
+ "Sec-Ch-Ua-Mobile": "?0",
134245
+ "Sec-Ch-Ua-Platform": '"Windows"'
134246
+ };
134247
+ var MAX_ATTEMPTS = 4;
134248
+ var BASE_BACKOFF_MS = 1e3;
134249
+ var MAX_RETRY_AFTER_MS = 1e4;
134250
+ function parseHeaderString(raw) {
134251
+ const out = {};
134252
+ for (const line of raw.split(/\r?\n/)) {
134253
+ const idx = line.indexOf(":");
134254
+ if (idx === -1)
134255
+ continue;
134256
+ const key = line.slice(0, idx).trim();
134257
+ const value = line.slice(idx + 1).trim();
134258
+ if (key && value)
134259
+ out[key] = value;
134260
+ }
134261
+ return out;
134262
+ }
134263
+ function mergeHeaders(userHeaders) {
134264
+ const merged = { ...DEFAULT_HEADERS };
134265
+ if (!userHeaders)
134266
+ return merged;
134267
+ const parsed = typeof userHeaders === "string" ? parseHeaderString(userHeaders) : userHeaders;
134268
+ for (const userKey of Object.keys(parsed)) {
134269
+ const raw = parsed[userKey];
134270
+ if (raw === null || raw === void 0)
134271
+ continue;
134272
+ const value = typeof raw === "string" ? raw : String(raw);
134273
+ const lower = userKey.toLowerCase();
134274
+ for (const existingKey of Object.keys(merged)) {
134275
+ if (existingKey.toLowerCase() === lower)
134276
+ delete merged[existingKey];
134277
+ }
134278
+ merged[userKey] = value;
134279
+ }
134280
+ return merged;
134281
+ }
134282
+ function parseRetryAfter(value) {
134283
+ const candidate = Array.isArray(value) ? value[0] : value;
134284
+ if (typeof candidate !== "string" && typeof candidate !== "number")
134285
+ return null;
134286
+ const str = String(candidate).trim();
134287
+ if (!str)
134288
+ return null;
134289
+ const seconds = Number(str);
134290
+ if (Number.isFinite(seconds)) {
134291
+ return Math.max(0, Math.min(seconds * 1e3, MAX_RETRY_AFTER_MS));
134292
+ }
134293
+ const date = Date.parse(str);
134294
+ if (!Number.isNaN(date)) {
134295
+ return Math.max(0, Math.min(date - Date.now(), MAX_RETRY_AFTER_MS));
134296
+ }
134297
+ return null;
134298
+ }
134299
+ function sleep(ms) {
134300
+ return new Promise((resolve) => setTimeout(resolve, ms));
134301
+ }
134302
+ function shouldRetry(statusCode) {
134303
+ if (statusCode === null)
134304
+ return false;
134305
+ return statusCode === 429 || statusCode >= 500;
134306
+ }
134307
+ async function attemptRequest(method, url, headers) {
134308
+ try {
134309
+ const res = await import_axios4.default.request({
134310
+ method,
134311
+ url,
134312
+ headers,
134313
+ timeout: 1e4,
134314
+ maxRedirects: 5,
134315
+ validateStatus: () => true
134316
+ });
134317
+ const retryAfter = parseRetryAfter(res.headers?.["retry-after"] ?? res.headers?.["Retry-After"]);
134318
+ return { statusCode: res.status, retryAfter };
134319
+ } catch (error) {
134320
+ if (error?.response?.status) {
134321
+ return {
134322
+ statusCode: error.response.status,
134323
+ retryAfter: parseRetryAfter(error.response.headers?.["retry-after"] ?? error.response.headers?.["Retry-After"])
134324
+ };
134325
+ }
134326
+ return { statusCode: null, retryAfter: null };
134327
+ }
134328
+ }
132440
134329
  async function checkLink({ config, step }) {
132441
134330
  let result = { status: "PASS", description: "Checked link." };
132442
134331
  if (typeof step.checkLink === "string") {
@@ -132468,43 +134357,46 @@ async function checkLink({ config, step }) {
132468
134357
  } else if (typeof step.checkLink.statusCodes === "number") {
132469
134358
  step.checkLink.statusCodes = [step.checkLink.statusCodes];
132470
134359
  }
132471
- const requestConfig = {
132472
- headers: {
132473
- "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
132474
- "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
132475
- "Accept-Language": "en-US,en;q=0.9"
132476
- },
132477
- timeout: 1e4,
132478
- // 10 second timeout
132479
- maxRedirects: 5
132480
- };
132481
- let req = await import_axios3.default.get(step.checkLink.url, requestConfig).then((res) => {
132482
- return { statusCode: res.status };
132483
- }).catch((error) => {
132484
- return { error };
132485
- });
132486
- if (req.error) {
132487
- result.status = "FAIL";
132488
- if (req.error.response && req.error.response.status) {
132489
- result.description = `Returned ${req.error.response.status}. Expected one of ${JSON.stringify(step.checkLink.statusCodes)}`;
132490
- } else {
132491
- result.description = `Invalid or unresolvable URL: ${step.checkLink.url}`;
134360
+ const headers = mergeHeaders(step.checkLink.headers);
134361
+ const url = step.checkLink.url;
134362
+ const acceptedCodes = step.checkLink.statusCodes;
134363
+ const isAccepted = (code) => code !== null && acceptedCodes.indexOf(code) >= 0;
134364
+ let last = { statusCode: null, retryAfter: null };
134365
+ for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
134366
+ last = await attemptRequest("get", url, headers);
134367
+ if (isAccepted(last.statusCode))
134368
+ break;
134369
+ if (!shouldRetry(last.statusCode))
134370
+ break;
134371
+ if (attempt < MAX_ATTEMPTS - 1) {
134372
+ const backoff = last.retryAfter ?? BASE_BACKOFF_MS * Math.pow(2, attempt);
134373
+ await sleep(backoff);
134374
+ }
134375
+ }
134376
+ if (!isAccepted(last.statusCode) && (last.statusCode === 429 || last.statusCode === 403)) {
134377
+ const headResult = await attemptRequest("head", url, headers);
134378
+ if (isAccepted(headResult.statusCode)) {
134379
+ last = headResult;
132492
134380
  }
134381
+ }
134382
+ if (last.statusCode === null) {
134383
+ result.status = "FAIL";
134384
+ result.description = `Invalid or unresolvable URL: ${url}`;
132493
134385
  return result;
132494
134386
  }
132495
- if (step.checkLink.statusCodes.indexOf(req.statusCode) >= 0) {
134387
+ if (step.checkLink.statusCodes.indexOf(last.statusCode) >= 0) {
132496
134388
  result.status = "PASS";
132497
- result.description = `Returned ${req.statusCode}`;
134389
+ result.description = `Returned ${last.statusCode}`;
132498
134390
  } else {
132499
134391
  result.status = "FAIL";
132500
- result.description = `Returned ${req.statusCode}. Expected one of ${JSON.stringify(step.checkLink.statusCodes)}`;
134392
+ result.description = `Returned ${last.statusCode}. Expected one of ${JSON.stringify(step.checkLink.statusCodes)}`;
132501
134393
  }
132502
134394
  return result;
132503
134395
  }
132504
134396
 
132505
134397
  // dist/core/tests/saveScreenshot.js
132506
- var import_node_path7 = __toESM(require("node:path"), 1);
132507
- var import_node_fs6 = __toESM(require("node:fs"), 1);
134398
+ var import_node_path8 = __toESM(require("node:path"), 1);
134399
+ var import_node_fs7 = __toESM(require("node:fs"), 1);
132508
134400
  var import_pngjs = require("pngjs");
132509
134401
  var import_sharp = __toESM(require("sharp"), 1);
132510
134402
  var pixelmatch;
@@ -132540,7 +134432,7 @@ async function saveScreenshot({ config, step, driver }) {
132540
134432
  if (typeof step.screenshot.path === "undefined") {
132541
134433
  step.screenshot.path = `${step.stepId}.png`;
132542
134434
  if (step.screenshot.directory) {
132543
- step.screenshot.path = import_node_path7.default.resolve(step.screenshot.directory, step.screenshot.path);
134435
+ step.screenshot.path = import_node_path8.default.resolve(step.screenshot.directory, step.screenshot.path);
132544
134436
  }
132545
134437
  }
132546
134438
  step.screenshot = {
@@ -132557,19 +134449,19 @@ async function saveScreenshot({ config, step, driver }) {
132557
134449
  };
132558
134450
  }
132559
134451
  let filePath = step.screenshot.path;
132560
- const dir = import_node_path7.default.dirname(step.screenshot.path);
132561
- if (!import_node_fs6.default.existsSync(dir)) {
132562
- import_node_fs6.default.mkdirSync(dir, { recursive: true });
134452
+ const dir = import_node_path8.default.dirname(step.screenshot.path);
134453
+ if (!import_node_fs7.default.existsSync(dir)) {
134454
+ import_node_fs7.default.mkdirSync(dir, { recursive: true });
132563
134455
  }
132564
134456
  let existFilePath;
132565
- if (import_node_fs6.default.existsSync(filePath)) {
134457
+ if (import_node_fs7.default.existsSync(filePath)) {
132566
134458
  if (step.screenshot.overwrite == "false") {
132567
134459
  result.status = "SKIPPED";
132568
134460
  result.description = `File already exists: ${filePath}`;
132569
134461
  return result;
132570
134462
  } else {
132571
134463
  existFilePath = filePath;
132572
- filePath = import_node_path7.default.join(dir, `${step.stepId}_${Date.now()}.png`);
134464
+ filePath = import_node_path8.default.join(dir, `${step.stepId}_${Date.now()}.png`);
132573
134465
  }
132574
134466
  }
132575
134467
  if (step.screenshot.crop) {
@@ -132657,8 +134549,8 @@ async function saveScreenshot({ config, step, driver }) {
132657
134549
  } catch (error) {
132658
134550
  result.status = "FAIL";
132659
134551
  result.description = `Couldn't save screenshot. ${error}`;
132660
- if (existFilePath && filePath !== existFilePath && import_node_fs6.default.existsSync(filePath)) {
132661
- import_node_fs6.default.unlinkSync(filePath);
134552
+ if (existFilePath && filePath !== existFilePath && import_node_fs7.default.existsSync(filePath)) {
134553
+ import_node_fs7.default.unlinkSync(filePath);
132662
134554
  }
132663
134555
  return result;
132664
134556
  }
@@ -132711,7 +134603,7 @@ async function saveScreenshot({ config, step, driver }) {
132711
134603
  rect.height = imgMeta.height - rect.y;
132712
134604
  }
132713
134605
  log(config, "debug", { padded_rect: rect });
132714
- const croppedPath = import_node_path7.default.join(dir, `cropped_${step.stepId || Date.now()}.png`);
134606
+ const croppedPath = import_node_path8.default.join(dir, `cropped_${step.stepId || Date.now()}.png`);
132715
134607
  try {
132716
134608
  await (0, import_sharp.default)(filePath).extract({
132717
134609
  left: rect.x,
@@ -132719,12 +134611,12 @@ async function saveScreenshot({ config, step, driver }) {
132719
134611
  width: rect.width,
132720
134612
  height: rect.height
132721
134613
  }).toFile(croppedPath);
132722
- import_node_fs6.default.renameSync(croppedPath, filePath);
134614
+ import_node_fs7.default.renameSync(croppedPath, filePath);
132723
134615
  } catch (error) {
132724
134616
  result.status = "FAIL";
132725
134617
  result.description = `Couldn't crop image. ${error}`;
132726
- if (existFilePath && filePath !== existFilePath && import_node_fs6.default.existsSync(filePath)) {
132727
- import_node_fs6.default.unlinkSync(filePath);
134618
+ if (existFilePath && filePath !== existFilePath && import_node_fs7.default.existsSync(filePath)) {
134619
+ import_node_fs7.default.unlinkSync(filePath);
132728
134620
  }
132729
134621
  return result;
132730
134622
  }
@@ -132732,7 +134624,7 @@ async function saveScreenshot({ config, step, driver }) {
132732
134624
  if (existFilePath) {
132733
134625
  if (step.screenshot.overwrite == "true") {
132734
134626
  result.description += ` Overwrote existing file.`;
132735
- import_node_fs6.default.renameSync(filePath, existFilePath);
134627
+ import_node_fs7.default.renameSync(filePath, existFilePath);
132736
134628
  result.outputs.screenshotPath = existFilePath;
132737
134629
  result.outputs.changed = true;
132738
134630
  if (step.screenshot.sourceIntegration) {
@@ -132742,13 +134634,13 @@ async function saveScreenshot({ config, step, driver }) {
132742
134634
  }
132743
134635
  let fractionalDiff;
132744
134636
  if (step.screenshot.maxVariation != null) {
132745
- const img1 = import_pngjs.PNG.sync.read(import_node_fs6.default.readFileSync(existFilePath));
132746
- const img2 = import_pngjs.PNG.sync.read(import_node_fs6.default.readFileSync(filePath));
134637
+ const img1 = import_pngjs.PNG.sync.read(import_node_fs7.default.readFileSync(existFilePath));
134638
+ const img2 = import_pngjs.PNG.sync.read(import_node_fs7.default.readFileSync(filePath));
132747
134639
  if (Math.round(img1.width / img1.height * 100) / 100 !== Math.round(img2.width / img2.height * 100) / 100) {
132748
134640
  result.status = "FAIL";
132749
134641
  result.description = `Couldn't compare images. Images have different aspect ratios.`;
132750
- if (existFilePath && filePath !== existFilePath && import_node_fs6.default.existsSync(filePath)) {
132751
- import_node_fs6.default.unlinkSync(filePath);
134642
+ if (existFilePath && filePath !== existFilePath && import_node_fs7.default.existsSync(filePath)) {
134643
+ import_node_fs7.default.unlinkSync(filePath);
132752
134644
  }
132753
134645
  return result;
132754
134646
  }
@@ -132779,7 +134671,7 @@ async function saveScreenshot({ config, step, driver }) {
132779
134671
  });
132780
134672
  if (fractionalDiff > step.screenshot.maxVariation) {
132781
134673
  if (step.screenshot.overwrite == "aboveVariation") {
132782
- import_node_fs6.default.renameSync(filePath, existFilePath);
134674
+ import_node_fs7.default.renameSync(filePath, existFilePath);
132783
134675
  }
132784
134676
  result.status = "WARNING";
132785
134677
  result.description += ` The difference between the existing screenshot and new screenshot (${fractionalDiff.toFixed(2)}) is greater than the max accepted variation (${step.screenshot.maxVariation}).`;
@@ -132796,7 +134688,7 @@ async function saveScreenshot({ config, step, driver }) {
132796
134688
  result.outputs.sourceIntegration = step.screenshot.sourceIntegration;
132797
134689
  }
132798
134690
  if (step.screenshot.overwrite != "true") {
132799
- import_node_fs6.default.unlinkSync(filePath);
134691
+ import_node_fs7.default.unlinkSync(filePath);
132800
134692
  }
132801
134693
  }
132802
134694
  }
@@ -132812,9 +134704,9 @@ async function saveScreenshot({ config, step, driver }) {
132812
134704
  }
132813
134705
 
132814
134706
  // dist/core/tests/startRecording.js
132815
- var import_node_path8 = __toESM(require("node:path"), 1);
132816
- var import_node_fs7 = __toESM(require("node:fs"), 1);
132817
- var import_node_os4 = __toESM(require("node:os"), 1);
134707
+ var import_node_path9 = __toESM(require("node:path"), 1);
134708
+ var import_node_fs8 = __toESM(require("node:fs"), 1);
134709
+ var import_node_os5 = __toESM(require("node:os"), 1);
132818
134710
  async function startRecording({ config, context, step, driver }) {
132819
134711
  let result = {
132820
134712
  status: "PASS",
@@ -132836,7 +134728,7 @@ async function startRecording({ config, context, step, driver }) {
132836
134728
  if (typeof step.record.path === "undefined") {
132837
134729
  step.record.path = `${step.stepId}.mp4`;
132838
134730
  if (step.record.directory) {
132839
- step.record.path = import_node_path8.default.resolve(step.record.directory, step.record.path);
134731
+ step.record.path = import_node_path9.default.resolve(step.record.directory, step.record.path);
132840
134732
  }
132841
134733
  }
132842
134734
  step.record = {
@@ -132849,12 +134741,12 @@ async function startRecording({ config, context, step, driver }) {
132849
134741
  return result;
132850
134742
  }
132851
134743
  let filePath = step.record.path;
132852
- const baseName = import_node_path8.default.basename(filePath, import_node_path8.default.extname(filePath));
132853
- const dir = import_node_path8.default.dirname(step.record.path);
132854
- if (!import_node_fs7.default.existsSync(dir)) {
132855
- import_node_fs7.default.mkdirSync(dir, { recursive: true });
134744
+ const baseName = import_node_path9.default.basename(filePath, import_node_path9.default.extname(filePath));
134745
+ const dir = import_node_path9.default.dirname(step.record.path);
134746
+ if (!import_node_fs8.default.existsSync(dir)) {
134747
+ import_node_fs8.default.mkdirSync(dir, { recursive: true });
132856
134748
  }
132857
- if (import_node_fs7.default.existsSync(filePath) && step.record.overwrite == "false") {
134749
+ if (import_node_fs8.default.existsSync(filePath) && step.record.overwrite == "false") {
132858
134750
  result.status = "SKIPPED";
132859
134751
  result.description = `File already exists: ${filePath}`;
132860
134752
  return result;
@@ -132960,7 +134852,7 @@ async function startRecording({ config, context, step, driver }) {
132960
134852
  result.recording = {
132961
134853
  type: "MediaRecorder",
132962
134854
  tab: recorderTab.handle,
132963
- downloadPath: import_node_path8.default.join(import_node_os4.default.tmpdir(), `${baseName}.webm`),
134855
+ downloadPath: import_node_path9.default.join(import_node_os5.default.tmpdir(), `${baseName}.webm`),
132964
134856
  // Where the recording will be downloaded.
132965
134857
  targetPath: filePath
132966
134858
  // Where the recording will be saved.
@@ -132975,8 +134867,8 @@ async function startRecording({ config, context, step, driver }) {
132975
134867
 
132976
134868
  // dist/core/tests/stopRecording.js
132977
134869
  var import_node_child_process2 = require("node:child_process");
132978
- var import_node_path9 = __toESM(require("node:path"), 1);
132979
- var import_node_fs8 = __toESM(require("node:fs"), 1);
134870
+ var import_node_path10 = __toESM(require("node:path"), 1);
134871
+ var import_node_fs9 = __toESM(require("node:fs"), 1);
132980
134872
  var import_ffmpeg = __toESM(require("@ffmpeg-installer/ffmpeg"), 1);
132981
134873
  var ffmpegPath = import_ffmpeg.default.path;
132982
134874
  async function stopRecording({ config, step, driver }) {
@@ -133018,11 +134910,11 @@ async function stopRecording({ config, step, driver }) {
133018
134910
  window.recorder.stop();
133019
134911
  });
133020
134912
  let waitCount = 0;
133021
- while (!import_node_fs8.default.existsSync(config.recording.downloadPath) && waitCount < 60) {
134913
+ while (!import_node_fs9.default.existsSync(config.recording.downloadPath) && waitCount < 60) {
133022
134914
  await new Promise((r) => setTimeout(r, 1e3));
133023
134915
  waitCount++;
133024
134916
  }
133025
- if (!import_node_fs8.default.existsSync(config.recording.downloadPath)) {
134917
+ if (!import_node_fs9.default.existsSync(config.recording.downloadPath)) {
133026
134918
  result.status = "FAIL";
133027
134919
  result.description = "Recording download timed out.";
133028
134920
  return result;
@@ -133037,7 +134929,7 @@ async function stopRecording({ config, step, driver }) {
133037
134929
  const downloadPath = `${config.recording.downloadPath}`;
133038
134930
  const endMessage = `Finished processing file: ${config.recording.targetPath}`;
133039
134931
  const ffmpegArgs = ["-y", "-i", downloadPath, "-pix_fmt", "yuv420p"];
133040
- if (import_node_path9.default.extname(targetPath) === ".gif") {
134932
+ if (import_node_path10.default.extname(targetPath) === ".gif") {
133041
134933
  ffmpegArgs.push("-vf", "scale=iw:-1:flags=lanczos");
133042
134934
  }
133043
134935
  ffmpegArgs.push(targetPath);
@@ -133046,7 +134938,7 @@ async function stopRecording({ config, step, driver }) {
133046
134938
  if (code === 0) {
133047
134939
  if (targetPath !== downloadPath) {
133048
134940
  try {
133049
- import_node_fs8.default.unlinkSync(downloadPath);
134941
+ import_node_fs9.default.unlinkSync(downloadPath);
133050
134942
  } catch {
133051
134943
  }
133052
134944
  }
@@ -133087,8 +134979,8 @@ async function loadVariables({ step }) {
133087
134979
  }
133088
134980
 
133089
134981
  // dist/core/tests/saveCookie.js
133090
- var import_node_path10 = __toESM(require("node:path"), 1);
133091
- var import_node_fs9 = __toESM(require("node:fs"), 1);
134982
+ var import_node_path11 = __toESM(require("node:path"), 1);
134983
+ var import_node_fs10 = __toESM(require("node:fs"), 1);
133092
134984
  async function saveCookie({ config, step, driver }) {
133093
134985
  let result = {
133094
134986
  status: "PASS",
@@ -133105,7 +134997,7 @@ async function saveCookie({ config, step, driver }) {
133105
134997
  let cookieName, filePath, directory, overwrite, domain, variable;
133106
134998
  if (typeof step.saveCookie === "string") {
133107
134999
  if (step.saveCookie.endsWith(".txt")) {
133108
- cookieName = import_node_path10.default.basename(step.saveCookie, ".txt");
135000
+ cookieName = import_node_path11.default.basename(step.saveCookie, ".txt");
133109
135001
  filePath = step.saveCookie;
133110
135002
  } else {
133111
135003
  cookieName = step.saveCookie;
@@ -133154,15 +135046,15 @@ async function saveCookie({ config, step, driver }) {
133154
135046
  }
133155
135047
  if (filePath) {
133156
135048
  const outputDirectory = directory || config.output || process.cwd();
133157
- const fullPath = import_node_path10.default.resolve(outputDirectory, filePath);
133158
- if (import_node_fs9.default.existsSync(fullPath) && !(overwrite === true || overwrite === "true")) {
135049
+ const fullPath = import_node_path11.default.resolve(outputDirectory, filePath);
135050
+ if (import_node_fs10.default.existsSync(fullPath) && !(overwrite === true || overwrite === "true")) {
133159
135051
  result.status = "FAIL";
133160
135052
  result.description = `File '${fullPath}' already exists and overwrite is not enabled.`;
133161
135053
  return result;
133162
135054
  }
133163
- const dir = import_node_path10.default.dirname(fullPath);
133164
- if (!import_node_fs9.default.existsSync(dir)) {
133165
- import_node_fs9.default.mkdirSync(dir, { recursive: true });
135055
+ const dir = import_node_path11.default.dirname(fullPath);
135056
+ if (!import_node_fs10.default.existsSync(dir)) {
135057
+ import_node_fs10.default.mkdirSync(dir, { recursive: true });
133166
135058
  }
133167
135059
  if (targetCookie) {
133168
135060
  const netscapeCookie = formatCookieForNetscape(targetCookie);
@@ -133170,11 +135062,11 @@ async function saveCookie({ config, step, driver }) {
133170
135062
  # This is a cookie file saved by Doc Detective
133171
135063
  ${netscapeCookie}
133172
135064
  `;
133173
- import_node_fs9.default.writeFileSync(fullPath, content, "utf8");
135065
+ import_node_fs10.default.writeFileSync(fullPath, content, "utf8");
133174
135066
  result.description = `Saved cookie '${cookieName}' to '${fullPath}'.`;
133175
135067
  log(config, "debug", `Saved cookie '${cookieName}' to file: ${fullPath}`);
133176
135068
  } else {
133177
- import_node_fs9.default.writeFileSync(fullPath, "# No cookie data\n", "utf8");
135069
+ import_node_fs10.default.writeFileSync(fullPath, "# No cookie data\n", "utf8");
133178
135070
  result.description = `Created empty cookie file at '${fullPath}'.`;
133179
135071
  log(config, "debug", `Created empty cookie file: ${fullPath}`);
133180
135072
  }
@@ -133199,8 +135091,8 @@ function formatCookieForNetscape(cookie) {
133199
135091
  }
133200
135092
 
133201
135093
  // dist/core/tests/loadCookie.js
133202
- var import_node_path11 = __toESM(require("node:path"), 1);
133203
- var import_node_fs10 = __toESM(require("node:fs"), 1);
135094
+ var import_node_path12 = __toESM(require("node:path"), 1);
135095
+ var import_node_fs11 = __toESM(require("node:fs"), 1);
133204
135096
  async function loadCookie({ config, step, driver }) {
133205
135097
  let result = {
133206
135098
  status: "PASS",
@@ -133217,7 +135109,7 @@ async function loadCookie({ config, step, driver }) {
133217
135109
  let cookieName, filePath, directory, domain, variable;
133218
135110
  if (typeof step.loadCookie === "string") {
133219
135111
  if (step.loadCookie.endsWith(".txt")) {
133220
- cookieName = import_node_path11.default.basename(step.loadCookie, ".txt");
135112
+ cookieName = import_node_path12.default.basename(step.loadCookie, ".txt");
133221
135113
  filePath = step.loadCookie;
133222
135114
  } else {
133223
135115
  cookieName = step.loadCookie;
@@ -133230,9 +135122,9 @@ async function loadCookie({ config, step, driver }) {
133230
135122
  domain = step.loadCookie.domain;
133231
135123
  variable = step.loadCookie.variable;
133232
135124
  if (filePath && !cookieName) {
133233
- const ext = import_node_path11.default.extname(filePath).toLowerCase();
135125
+ const ext = import_node_path12.default.extname(filePath).toLowerCase();
133234
135126
  if (ext === ".txt") {
133235
- cookieName = import_node_path11.default.basename(filePath, ext);
135127
+ cookieName = import_node_path12.default.basename(filePath, ext);
133236
135128
  }
133237
135129
  }
133238
135130
  }
@@ -133255,14 +135147,14 @@ async function loadCookie({ config, step, driver }) {
133255
135147
  }
133256
135148
  } else if (filePath) {
133257
135149
  const inputDirectory = directory || config.output || process.cwd();
133258
- const fullPath = import_node_path11.default.resolve(inputDirectory, filePath);
133259
- if (!import_node_fs10.default.existsSync(fullPath)) {
135150
+ const fullPath = import_node_path12.default.resolve(inputDirectory, filePath);
135151
+ if (!import_node_fs11.default.existsSync(fullPath)) {
133260
135152
  result.status = "FAIL";
133261
135153
  result.description = `Cookie file '${fullPath}' not found`;
133262
135154
  return result;
133263
135155
  }
133264
135156
  try {
133265
- const fileContent = import_node_fs10.default.readFileSync(fullPath, "utf8");
135157
+ const fileContent = import_node_fs11.default.readFileSync(fullPath, "utf8");
133266
135158
  const cookies = parseNetscapeCookieFile(fileContent);
133267
135159
  if (cookies.length === 0) {
133268
135160
  result.status = "FAIL";
@@ -133305,6 +135197,15 @@ async function loadCookie({ config, step, driver }) {
133305
135197
  const isHttps = currentUrl.startsWith("https://");
133306
135198
  let sameSite = targetCookie.sameSite || "Lax";
133307
135199
  let secure = targetCookie.secure || false;
135200
+ if (typeof sameSite === "string") {
135201
+ const lower = sameSite.toLowerCase();
135202
+ if (lower === "none")
135203
+ sameSite = "None";
135204
+ else if (lower === "strict")
135205
+ sameSite = "Strict";
135206
+ else
135207
+ sameSite = "Lax";
135208
+ }
133308
135209
  if (sameSite === "None") {
133309
135210
  if (isHttps) {
133310
135211
  secure = true;
@@ -133417,9 +135318,9 @@ function isDomainCompatible(currentDomain, cookieDomain) {
133417
135318
  }
133418
135319
 
133419
135320
  // dist/core/tests/httpRequest.js
133420
- var import_axios4 = __toESM(require("axios"), 1);
133421
- var import_node_fs11 = __toESM(require("node:fs"), 1);
133422
- var import_node_path12 = __toESM(require("node:path"), 1);
135321
+ var import_axios5 = __toESM(require("axios"), 1);
135322
+ var import_node_fs12 = __toESM(require("node:fs"), 1);
135323
+ var import_node_path13 = __toESM(require("node:path"), 1);
133423
135324
  var import_ajv2 = __toESM(require("ajv"), 1);
133424
135325
  var Ajv2 = import_ajv2.default;
133425
135326
  async function httpRequest({ config, step, openApiDefinitions = [] }) {
@@ -133602,7 +135503,7 @@ async function httpRequest({ config, step, openApiDefinitions = [] }) {
133602
135503
  }
133603
135504
  let response = {};
133604
135505
  if (!step?.httpRequest?.openApi?.mockResponse) {
133605
- response = await (0, import_axios4.default)(request).then((response2) => {
135506
+ response = await (0, import_axios5.default)(request).then((response2) => {
133606
135507
  return response2;
133607
135508
  }).catch((error) => {
133608
135509
  return { error };
@@ -133722,25 +135623,25 @@ async function httpRequest({ config, step, openApiDefinitions = [] }) {
133722
135623
  }
133723
135624
  }
133724
135625
  if (step.httpRequest.path) {
133725
- const dir = import_node_path12.default.dirname(step.httpRequest.path);
133726
- if (!import_node_fs11.default.existsSync(dir)) {
133727
- import_node_fs11.default.mkdirSync(dir, { recursive: true });
135626
+ const dir = import_node_path13.default.dirname(step.httpRequest.path);
135627
+ if (!import_node_fs12.default.existsSync(dir)) {
135628
+ import_node_fs12.default.mkdirSync(dir, { recursive: true });
133728
135629
  }
133729
135630
  let filePath = step.httpRequest.path;
133730
135631
  log(config, "debug", `Saving output to file: ${filePath}`);
133731
- if (!import_node_fs11.default.existsSync(filePath)) {
133732
- await import_node_fs11.default.promises.writeFile(filePath, JSON.stringify(response.data, null, 2));
135632
+ if (!import_node_fs12.default.existsSync(filePath)) {
135633
+ await import_node_fs12.default.promises.writeFile(filePath, JSON.stringify(response.data, null, 2));
133733
135634
  result.description += ` Saved output to file.`;
133734
135635
  } else {
133735
135636
  if (step.httpRequest.overwrite == "false") {
133736
135637
  result.description += ` Didn't save output. File already exists.`;
133737
135638
  }
133738
- const existingFile = import_node_fs11.default.readFileSync(filePath, "utf8");
135639
+ const existingFile = import_node_fs12.default.readFileSync(filePath, "utf8");
133739
135640
  const fractionalDiff = calculateFractionalDifference(existingFile, JSON.stringify(response.data, null, 2));
133740
135641
  log(config, "debug", `Fractional difference: ${fractionalDiff}`);
133741
135642
  if (fractionalDiff > step.httpRequest.maxVariation) {
133742
135643
  if (step.httpRequest.overwrite == "aboveVariation") {
133743
- await import_node_fs11.default.promises.writeFile(filePath, JSON.stringify(response.data, null, 2));
135644
+ await import_node_fs12.default.promises.writeFile(filePath, JSON.stringify(response.data, null, 2));
133744
135645
  result.description += ` Saved response to file.`;
133745
135646
  }
133746
135647
  result.status = "WARNING";
@@ -133748,7 +135649,7 @@ async function httpRequest({ config, step, openApiDefinitions = [] }) {
133748
135649
  return result;
133749
135650
  }
133750
135651
  if (step.httpRequest.overwrite == "true") {
133751
- import_node_fs11.default.writeFileSync(filePath, JSON.stringify(response.data, null, 2));
135652
+ import_node_fs12.default.writeFileSync(filePath, JSON.stringify(response.data, null, 2));
133752
135653
  result.description += ` Saved response to file.`;
133753
135654
  }
133754
135655
  }
@@ -133922,9 +135823,9 @@ async function clickElement({ config, step, driver }) {
133922
135823
  }
133923
135824
 
133924
135825
  // dist/core/tests/runCode.js
133925
- var import_node_fs12 = __toESM(require("node:fs"), 1);
133926
- var import_node_path13 = __toESM(require("node:path"), 1);
133927
- var import_node_os5 = __toESM(require("node:os"), 1);
135826
+ var import_node_fs13 = __toESM(require("node:fs"), 1);
135827
+ var import_node_path14 = __toESM(require("node:path"), 1);
135828
+ var import_node_os6 = __toESM(require("node:os"), 1);
133928
135829
  function createTempScript(code, language) {
133929
135830
  let extension;
133930
135831
  switch (language) {
@@ -133943,10 +135844,10 @@ function createTempScript(code, language) {
133943
135844
  default:
133944
135845
  extension = "";
133945
135846
  }
133946
- const tmpDir = import_node_os5.default.tmpdir();
133947
- const tmpFile = import_node_path13.default.join(tmpDir, `doc-detective-${Date.now()}${extension}`);
135847
+ const tmpDir = import_node_os6.default.tmpdir();
135848
+ const tmpFile = import_node_path14.default.join(tmpDir, `doc-detective-${Date.now()}${extension}`);
133948
135849
  try {
133949
- import_node_fs12.default.writeFileSync(tmpFile, code);
135850
+ import_node_fs13.default.writeFileSync(tmpFile, code);
133950
135851
  } catch (error) {
133951
135852
  throw new Error(`Failed to create temporary script: ${error.message}`);
133952
135853
  }
@@ -134009,7 +135910,7 @@ async function runCode({ config, step }) {
134009
135910
  result.description = `Command ${command} is unavailable. Make sure it's installed and in your PATH.`;
134010
135911
  return result;
134011
135912
  }
134012
- if (import_node_os5.default.platform() === "win32" && command === "bash") {
135913
+ if (import_node_os6.default.platform() === "win32" && command === "bash") {
134013
135914
  result.status = "FAIL";
134014
135915
  result.description = `runCode currently doesn't support bash on Windows. Use a different command, a different language, or a runShell step.`;
134015
135916
  return result;
@@ -134030,7 +135931,7 @@ async function runCode({ config, step }) {
134030
135931
  result.description = error.message;
134031
135932
  } finally {
134032
135933
  try {
134033
- import_node_fs12.default.unlinkSync(scriptPath);
135934
+ import_node_fs13.default.unlinkSync(scriptPath);
134034
135935
  log(config, "debug", `Removed temporary script: ${scriptPath}`);
134035
135936
  } catch (error) {
134036
135937
  log(config, "warn", `Failed to remove temporary script: ${scriptPath}`);
@@ -134160,7 +136061,7 @@ async function dragAndDropElement({ config, step, driver }) {
134160
136061
  // dist/core/tests.js
134161
136062
  var import_node_path15 = __toESM(require("node:path"), 1);
134162
136063
  var import_node_child_process3 = require("node:child_process");
134163
- var import_node_crypto4 = require("node:crypto");
136064
+ var import_node_crypto5 = require("node:crypto");
134164
136065
 
134165
136066
  // dist/core/expressions.js
134166
136067
  var import_jq_web = __toESM(require("jq-web"), 1);
@@ -134409,789 +136310,6 @@ function preprocessExpression(expression) {
134409
136310
  return expression;
134410
136311
  }
134411
136312
 
134412
- // dist/core/integrations/heretto.js
134413
- var import_node_fs13 = __toESM(require("node:fs"), 1);
134414
- var import_node_path14 = __toESM(require("node:path"), 1);
134415
- var import_node_https = __toESM(require("node:https"), 1);
134416
- var import_node_http = __toESM(require("node:http"), 1);
134417
- var HerettoUploader = class {
134418
- /**
134419
- * Checks if this uploader can handle the given source integration.
134420
- * @param {Object} sourceIntegration - Source integration metadata
134421
- * @returns {boolean} True if this uploader handles Heretto integrations
134422
- */
134423
- canHandle(sourceIntegration) {
134424
- return sourceIntegration?.type === "heretto";
134425
- }
134426
- /**
134427
- * Uploads a file to Heretto CMS.
134428
- * @param {Object} options - Upload options
134429
- * @param {Object} options.config - Doc Detective config
134430
- * @param {Object} options.integrationConfig - Heretto integration config
134431
- * @param {string} options.localFilePath - Local file path to upload
134432
- * @param {Object} options.sourceIntegration - Source integration metadata
134433
- * @param {Function} options.log - Logging function
134434
- * @returns {Promise<Object>} Upload result with status and description
134435
- */
134436
- async upload({ config, integrationConfig, localFilePath, sourceIntegration, log: log3 }) {
134437
- const result = {
134438
- status: "FAIL",
134439
- description: ""
134440
- };
134441
- if (!integrationConfig) {
134442
- result.description = "No Heretto integration configuration found";
134443
- return result;
134444
- }
134445
- if (!integrationConfig.organizationId || !integrationConfig.apiToken) {
134446
- result.description = "Heretto integration missing organizationId or apiToken";
134447
- return result;
134448
- }
134449
- const apiBaseUrl = `https://${integrationConfig.organizationId}.heretto.com`;
134450
- let fileId = sourceIntegration.fileId;
134451
- let parentFolderId = sourceIntegration.parentFolderId;
134452
- const filename = import_node_path14.default.basename(sourceIntegration.filePath);
134453
- const relativeFilePath = sourceIntegration.filePath;
134454
- if (!fileId && integrationConfig.resourceDependencies) {
134455
- const resolvedFile = this.resolveFromDependencies({
134456
- resourceDependencies: integrationConfig.resourceDependencies,
134457
- filePath: relativeFilePath,
134458
- filename,
134459
- log: (level, msg) => log3(config, level, msg)
134460
- });
134461
- if (resolvedFile) {
134462
- fileId = resolvedFile.uuid;
134463
- if (!parentFolderId && resolvedFile.parentFolderId) {
134464
- parentFolderId = resolvedFile.parentFolderId;
134465
- }
134466
- log3(config, "debug", `Resolved from dependencies: ${relativeFilePath} -> ${fileId}`);
134467
- }
134468
- }
134469
- if (!fileId) {
134470
- log3(config, "debug", `No fileId found, resolving correct folder for: ${sourceIntegration.filePath}`);
134471
- try {
134472
- if (!parentFolderId && integrationConfig.resourceDependencies) {
134473
- const folderResolution = this.findParentFolderFromDependencies({
134474
- resourceDependencies: integrationConfig.resourceDependencies,
134475
- filePath: relativeFilePath,
134476
- log: (level, msg) => log3(config, level, msg)
134477
- });
134478
- parentFolderId = folderResolution.folderId;
134479
- if (!parentFolderId && folderResolution.ditamapParentFolderId && folderResolution.targetFolderName) {
134480
- log3(config, "debug", `Searching for folder '${folderResolution.targetFolderName}' in ditamap's parent folder`);
134481
- parentFolderId = await this.getChildFolderByName({
134482
- apiBaseUrl,
134483
- apiToken: integrationConfig.apiToken,
134484
- username: integrationConfig.username || "",
134485
- parentFolderId: folderResolution.ditamapParentFolderId,
134486
- folderName: folderResolution.targetFolderName,
134487
- log: (level, msg) => log3(config, level, msg)
134488
- });
134489
- }
134490
- }
134491
- if (!parentFolderId && relativeFilePath) {
134492
- const parentDirPath = import_node_path14.default.dirname(relativeFilePath);
134493
- if (parentDirPath && parentDirPath !== ".") {
134494
- const folderName = import_node_path14.default.basename(parentDirPath);
134495
- log3(config, "debug", `Searching for parent folder by name: ${folderName}`);
134496
- parentFolderId = await this.searchFolderByName({
134497
- apiBaseUrl,
134498
- apiToken: integrationConfig.apiToken,
134499
- username: integrationConfig.username || "",
134500
- folderName,
134501
- log: (level, msg) => log3(config, level, msg)
134502
- });
134503
- }
134504
- }
134505
- if (parentFolderId) {
134506
- log3(config, "debug", `Looking for file '${filename}' in target folder ${parentFolderId}`);
134507
- fileId = await this.getFileInFolder({
134508
- apiBaseUrl,
134509
- apiToken: integrationConfig.apiToken,
134510
- username: integrationConfig.username || "",
134511
- folderId: parentFolderId,
134512
- filename,
134513
- log: (level, msg) => log3(config, level, msg)
134514
- });
134515
- if (fileId) {
134516
- log3(config, "debug", `Found existing file in target folder with ID: ${fileId}`);
134517
- } else {
134518
- log3(config, "debug", `File not in target folder, creating new document`);
134519
- const mimeType = this.getContentType(localFilePath);
134520
- const createResult = await this.createDocument({
134521
- apiBaseUrl,
134522
- apiToken: integrationConfig.apiToken,
134523
- username: integrationConfig.username || "",
134524
- parentFolderId,
134525
- filename,
134526
- mimeType,
134527
- log: (level, msg) => log3(config, level, msg)
134528
- });
134529
- if (createResult.created) {
134530
- fileId = createResult.documentId;
134531
- log3(config, "info", `Created new document in Heretto with ID: ${fileId}`);
134532
- } else if (createResult.existsInFolder) {
134533
- log3(config, "debug", `File exists in folder (race condition), searching for its ID`);
134534
- fileId = await this.getFileInFolder({
134535
- apiBaseUrl,
134536
- apiToken: integrationConfig.apiToken,
134537
- username: integrationConfig.username || "",
134538
- folderId: parentFolderId,
134539
- filename,
134540
- log: (level, msg) => log3(config, level, msg)
134541
- });
134542
- if (!fileId) {
134543
- result.description = `File exists in folder but could not get its ID: ${filename}`;
134544
- return result;
134545
- }
134546
- } else {
134547
- result.description = `Failed to create document in Heretto: ${filename}`;
134548
- return result;
134549
- }
134550
- }
134551
- } else {
134552
- log3(config, "debug", `No target folder found, searching globally for file: ${filename}`);
134553
- fileId = await this.searchFileByName({
134554
- apiBaseUrl,
134555
- apiToken: integrationConfig.apiToken,
134556
- username: integrationConfig.username || "",
134557
- filename,
134558
- log: (level, msg) => log3(config, level, msg)
134559
- });
134560
- if (!fileId) {
134561
- result.description = `Could not find file or parent folder in Heretto: ${sourceIntegration.filePath}`;
134562
- return result;
134563
- }
134564
- }
134565
- } catch (error) {
134566
- result.description = `Error searching/creating file: ${error.message}`;
134567
- return result;
134568
- }
134569
- }
134570
- if (!import_node_fs13.default.existsSync(localFilePath)) {
134571
- result.description = `Local file not found: ${localFilePath}`;
134572
- return result;
134573
- }
134574
- const fileContent = import_node_fs13.default.readFileSync(localFilePath);
134575
- const contentType = this.getContentType(localFilePath);
134576
- try {
134577
- await this.uploadFile({
134578
- apiBaseUrl,
134579
- apiToken: integrationConfig.apiToken,
134580
- username: integrationConfig.username || "",
134581
- documentId: fileId,
134582
- content: fileContent,
134583
- contentType,
134584
- log: (level, msg) => log3(config, level, msg)
134585
- });
134586
- result.status = "PASS";
134587
- result.description = `Successfully uploaded to Heretto (document ID: ${fileId})`;
134588
- } catch (error) {
134589
- result.description = `Upload failed: ${error.message}`;
134590
- }
134591
- return result;
134592
- }
134593
- /**
134594
- * Resolves a file path to its UUID using the resource dependencies map.
134595
- * @param {Object} options - Resolution options
134596
- * @param {Object.<string, {uuid: string, parentFolderId: string}>} options.resourceDependencies - Map of resource paths to resource metadata
134597
- * @param {string} options.filePath - Original file path to resolve
134598
- * @param {string} options.filename - Filename extracted from the file path
134599
- * @param {Function} options.log - Logging function
134600
- * @returns {{uuid: string, parentFolderId: string}|null} File info with uuid and parentFolderId, or null if not found
134601
- */
134602
- resolveFromDependencies({ resourceDependencies, filePath, filename, log: log3 }) {
134603
- if (!resourceDependencies)
134604
- return null;
134605
- const normalizedPath = import_node_path14.default.posix.normalize(filePath.replace(/\\/g, "/")).replace(/^\.\.\/+/g, "").replace(/^\.\//, "");
134606
- for (const [depPath, info] of Object.entries(resourceDependencies)) {
134607
- if (depPath.startsWith("_"))
134608
- continue;
134609
- const normalizedDepPath = depPath.replace(/\\/g, "/");
134610
- if (normalizedDepPath === normalizedPath || normalizedDepPath.endsWith("/" + normalizedPath) || normalizedDepPath.endsWith(normalizedPath)) {
134611
- log3("debug", `Found exact path match in dependencies: ${depPath}`);
134612
- return info;
134613
- }
134614
- }
134615
- const parentDir = import_node_path14.default.dirname(normalizedPath);
134616
- const parentFolderName = import_node_path14.default.basename(parentDir);
134617
- for (const [depPath, info] of Object.entries(resourceDependencies)) {
134618
- if (depPath.startsWith("_"))
134619
- continue;
134620
- const depFilename = import_node_path14.default.basename(depPath);
134621
- const depParentDir = import_node_path14.default.dirname(depPath);
134622
- const depParentFolderName = import_node_path14.default.basename(depParentDir);
134623
- if (depFilename === filename && depParentFolderName === parentFolderName) {
134624
- log3("debug", `Found filename+folder match in dependencies: ${depPath}`);
134625
- return info;
134626
- }
134627
- }
134628
- for (const [depPath, info] of Object.entries(resourceDependencies)) {
134629
- if (depPath.startsWith("_"))
134630
- continue;
134631
- const depFilename = import_node_path14.default.basename(depPath);
134632
- if (depFilename === filename) {
134633
- log3("debug", `Found filename match in dependencies: ${depPath}`);
134634
- return info;
134635
- }
134636
- }
134637
- log3("debug", `No match found in dependencies for: ${filePath}`);
134638
- return null;
134639
- }
134640
- /**
134641
- * Finds the parent folder ID for a file path using resource dependencies.
134642
- * Returns the target folder name for API lookup if not found in dependencies.
134643
- * @param {Object} options - Resolution options
134644
- * @param {Object.<string, {uuid: string, parentFolderId: string}>} options.resourceDependencies - Map of resource paths to resource metadata.
134645
- * Keys are relative file paths, values are objects with uuid and parentFolderId.
134646
- * Special keys starting with '_' (e.g., '_ditamapParentFolderId') store internal metadata.
134647
- * @param {string} options.filePath - File path to find parent folder for (can be relative with ../ or ./ prefixes)
134648
- * @param {Function} options.log - Logging function with signature (level, message)
134649
- * @returns {{folderId: string|null, targetFolderName: string|null, ditamapParentFolderId: string|null}} Resolution result containing:
134650
- * - folderId: The parent folder UUID if found in dependencies, null otherwise
134651
- * - targetFolderName: The name of the target folder extracted from the file path
134652
- * - ditamapParentFolderId: The ditamap's parent folder ID from dependencies (for API fallback lookup)
134653
- */
134654
- findParentFolderFromDependencies({ resourceDependencies, filePath, log: log3 }) {
134655
- const result = {
134656
- folderId: null,
134657
- targetFolderName: null,
134658
- ditamapParentFolderId: null
134659
- };
134660
- if (!resourceDependencies)
134661
- return result;
134662
- const normalizedPath = import_node_path14.default.posix.normalize(filePath.replace(/\\/g, "/")).replace(/^(\.\.\/)+/g, "").replace(/^\.\//, "");
134663
- const parentDir = import_node_path14.default.dirname(normalizedPath);
134664
- const targetFolderName = import_node_path14.default.basename(parentDir);
134665
- result.targetFolderName = targetFolderName;
134666
- result.ditamapParentFolderId = resourceDependencies._ditamapParentFolderId || null;
134667
- log3("debug", `Looking for parent folder '${targetFolderName}' in dependencies`);
134668
- for (const [depPath, info] of Object.entries(resourceDependencies)) {
134669
- if (depPath.startsWith("_"))
134670
- continue;
134671
- const depParentDir = import_node_path14.default.dirname(depPath);
134672
- const depFolderName = import_node_path14.default.basename(depParentDir);
134673
- if (depFolderName === targetFolderName && info.parentFolderId) {
134674
- log3("debug", `Found sibling file ${depPath} with parent folder ID: ${info.parentFolderId}`);
134675
- result.folderId = info.parentFolderId;
134676
- return result;
134677
- }
134678
- }
134679
- for (const [depPath, info] of Object.entries(resourceDependencies)) {
134680
- if (depPath.startsWith("_"))
134681
- continue;
134682
- if (depPath.endsWith("/" + targetFolderName) || depPath === targetFolderName) {
134683
- log3("debug", `Found folder ${depPath} with ID: ${info.uuid}`);
134684
- result.folderId = info.uuid;
134685
- return result;
134686
- }
134687
- }
134688
- log3("debug", `Could not find parent folder '${targetFolderName}' in dependencies, will search via API`);
134689
- return result;
134690
- }
134691
- /**
134692
- * Gets a child folder within a parent folder by name.
134693
- * @param {Object} options - Search options
134694
- * @returns {Promise<string|null>} Child folder ID if found, null otherwise
134695
- */
134696
- async getChildFolderByName({ apiBaseUrl, apiToken, username, parentFolderId, folderName, log: log3 }) {
134697
- const folderUrl = new URL(`/rest/all-files/${parentFolderId}`, apiBaseUrl);
134698
- return new Promise((resolve, reject) => {
134699
- const protocol = folderUrl.protocol === "https:" ? import_node_https.default : import_node_http.default;
134700
- const authString = Buffer.from(`${username}:${apiToken}`).toString("base64");
134701
- const options = {
134702
- hostname: folderUrl.hostname,
134703
- port: folderUrl.port || (folderUrl.protocol === "https:" ? 443 : 80),
134704
- path: folderUrl.pathname,
134705
- method: "GET",
134706
- headers: {
134707
- "Authorization": `Basic ${authString}`,
134708
- "Accept": "application/xml"
134709
- }
134710
- };
134711
- log3("debug", `Looking for child folder '${folderName}' in parent ${parentFolderId}`);
134712
- const req = protocol.request(options, (res) => {
134713
- let data = "";
134714
- res.on("data", (chunk) => {
134715
- data += chunk;
134716
- });
134717
- res.on("end", () => {
134718
- if (res.statusCode === 200) {
134719
- try {
134720
- const escapedFolderName = folderName.replace(/[.*+?^${}()|[\]\\/]/g, "\\$&");
134721
- const folderMatch = data.match(new RegExp(`<folder\\s+name="${escapedFolderName}"\\s+id="([^"]+)"`, "i"));
134722
- if (folderMatch && folderMatch[1]) {
134723
- log3("debug", `Found child folder '${folderName}' with ID: ${folderMatch[1]}`);
134724
- resolve(folderMatch[1]);
134725
- } else {
134726
- log3("debug", `Child folder '${folderName}' not found in parent ${parentFolderId}`);
134727
- resolve(null);
134728
- }
134729
- } catch (parseError) {
134730
- log3("debug", `Error parsing folder contents: ${parseError.message}`);
134731
- resolve(null);
134732
- }
134733
- } else {
134734
- log3("debug", `Failed to get parent folder contents: ${res.statusCode}`);
134735
- resolve(null);
134736
- }
134737
- });
134738
- });
134739
- req.setTimeout(3e4, () => {
134740
- req.destroy(new Error("Request timeout"));
134741
- });
134742
- req.on("error", (error) => {
134743
- log3("debug", `Error getting folder contents: ${error.message}`);
134744
- resolve(null);
134745
- });
134746
- req.end();
134747
- });
134748
- }
134749
- /**
134750
- * Creates a new document in Heretto.
134751
- * @param {Object} options - Creation options
134752
- * @returns {Promise<Object>} Result with created: boolean, documentId: string (if successful or already exists)
134753
- */
134754
- async createDocument({ apiBaseUrl, apiToken, username, parentFolderId, filename, mimeType, log: log3 }) {
134755
- const createUrl = new URL(`/rest/all-files/${parentFolderId}`, apiBaseUrl);
134756
- const createBody = `<resource><name>${this.escapeXml(filename)}</name><mime-type>${mimeType}</mime-type></resource>`;
134757
- return new Promise((resolve, reject) => {
134758
- const protocol = createUrl.protocol === "https:" ? import_node_https.default : import_node_http.default;
134759
- const authString = Buffer.from(`${username}:${apiToken}`).toString("base64");
134760
- const options = {
134761
- hostname: createUrl.hostname,
134762
- port: createUrl.port || (createUrl.protocol === "https:" ? 443 : 80),
134763
- path: createUrl.pathname,
134764
- method: "POST",
134765
- headers: {
134766
- "Content-Type": "application/xml",
134767
- "Authorization": `Basic ${authString}`,
134768
- "Content-Length": Buffer.byteLength(createBody)
134769
- }
134770
- };
134771
- log3("debug", `Creating document at ${createUrl.toString()}`);
134772
- const req = protocol.request(options, (res) => {
134773
- let data = "";
134774
- res.on("data", (chunk) => {
134775
- data += chunk;
134776
- });
134777
- res.on("end", () => {
134778
- if (res.statusCode === 200 || res.statusCode === 201) {
134779
- try {
134780
- const idMatch = data.match(/id="([^"]+)"/);
134781
- if (idMatch && idMatch[1]) {
134782
- log3("debug", `Document created successfully with ID: ${idMatch[1]}`);
134783
- resolve({ created: true, documentId: idMatch[1] });
134784
- } else {
134785
- log3("warning", `Document created but could not parse ID from response: ${data}`);
134786
- reject(new Error("Could not parse document ID from create response"));
134787
- }
134788
- } catch (parseError) {
134789
- reject(new Error(`Failed to parse create response: ${parseError.message}`));
134790
- }
134791
- } else if (res.statusCode === 400 && data.includes("already exists")) {
134792
- log3("debug", `Document already exists in folder, will search for existing file`);
134793
- resolve({ created: false, existsInFolder: true, parentFolderId });
134794
- } else {
134795
- reject(new Error(`Create document failed with status ${res.statusCode}: ${data}`));
134796
- }
134797
- });
134798
- });
134799
- req.on("error", (error) => {
134800
- reject(new Error(`Create document request error: ${error.message}`));
134801
- });
134802
- req.setTimeout(3e4, () => {
134803
- req.destroy(new Error("Request timeout"));
134804
- });
134805
- req.write(createBody);
134806
- req.end();
134807
- });
134808
- }
134809
- /**
134810
- * Gets file information from a specific folder.
134811
- * @param {Object} options - Options
134812
- * @returns {Promise<string|null>} File ID if found, null otherwise
134813
- */
134814
- async getFileInFolder({ apiBaseUrl, apiToken, username, folderId, filename, log: log3 }) {
134815
- const folderUrl = new URL(`/rest/all-files/${folderId}`, apiBaseUrl);
134816
- return new Promise((resolve, reject) => {
134817
- const protocol = folderUrl.protocol === "https:" ? import_node_https.default : import_node_http.default;
134818
- const authString = Buffer.from(`${username}:${apiToken}`).toString("base64");
134819
- const options = {
134820
- hostname: folderUrl.hostname,
134821
- port: folderUrl.port || (folderUrl.protocol === "https:" ? 443 : 80),
134822
- path: folderUrl.pathname,
134823
- method: "GET",
134824
- headers: {
134825
- "Authorization": `Basic ${authString}`,
134826
- "Accept": "application/xml"
134827
- }
134828
- };
134829
- log3("debug", `Getting folder contents: ${folderUrl.toString()}`);
134830
- const req = protocol.request(options, (res) => {
134831
- let data = "";
134832
- res.on("data", (chunk) => {
134833
- data += chunk;
134834
- });
134835
- res.on("end", () => {
134836
- if (res.statusCode === 200) {
134837
- try {
134838
- const escapedFilename = filename.replace(/[.*+?^${}()|[\]\\/]/g, "\\$&");
134839
- const nameIdMatch = data.match(new RegExp(`id="([^"]+)"[^>]*name="${escapedFilename}"`, "i"));
134840
- const idNameMatch = data.match(new RegExp(`name="${escapedFilename}"[^>]*id="([^"]+)"`, "i"));
134841
- const match = nameIdMatch || idNameMatch;
134842
- if (match && match[1]) {
134843
- log3("debug", `Found file ${filename} with ID: ${match[1]}`);
134844
- resolve(match[1]);
134845
- } else {
134846
- log3("debug", `File ${filename} not found in folder ${folderId}`);
134847
- resolve(null);
134848
- }
134849
- } catch (parseError) {
134850
- log3("debug", `Error parsing folder contents: ${parseError.message}`);
134851
- resolve(null);
134852
- }
134853
- } else {
134854
- log3("debug", `Failed to get folder contents: ${res.statusCode}`);
134855
- resolve(null);
134856
- }
134857
- });
134858
- });
134859
- req.on("error", (error) => {
134860
- log3("debug", `Error getting folder contents: ${error.message}`);
134861
- resolve(null);
134862
- });
134863
- req.setTimeout(3e4, () => {
134864
- req.destroy(new Error("Request timeout"));
134865
- });
134866
- req.end();
134867
- });
134868
- }
134869
- /**
134870
- * Escapes special characters for XML.
134871
- * @param {string} str - String to escape
134872
- * @returns {string} Escaped string
134873
- */
134874
- escapeXml(str) {
134875
- return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
134876
- }
134877
- /**
134878
- * Searches for a folder in Heretto by name.
134879
- * @param {Object} options - Search options
134880
- * @returns {Promise<string|null>} Folder ID if found, null otherwise
134881
- */
134882
- async searchFolderByName({ apiBaseUrl, apiToken, username, folderName, log: log3 }) {
134883
- const searchUrl = new URL("/ezdnxtgen/api/search", apiBaseUrl);
134884
- const searchBody = JSON.stringify({
134885
- queryString: folderName,
134886
- searchResultType: "FOLDERS_ONLY"
134887
- });
134888
- return new Promise((resolve, reject) => {
134889
- const protocol = searchUrl.protocol === "https:" ? import_node_https.default : import_node_http.default;
134890
- const authString = Buffer.from(`${username}:${apiToken}`).toString("base64");
134891
- const options = {
134892
- hostname: searchUrl.hostname,
134893
- port: searchUrl.port || (searchUrl.protocol === "https:" ? 443 : 80),
134894
- path: searchUrl.pathname,
134895
- method: "POST",
134896
- headers: {
134897
- "Content-Type": "application/json",
134898
- "Authorization": `Basic ${authString}`,
134899
- "Content-Length": Buffer.byteLength(searchBody)
134900
- }
134901
- };
134902
- log3("debug", `Searching for folder: ${folderName}`);
134903
- const req = protocol.request(options, (res) => {
134904
- let data = "";
134905
- res.on("data", (chunk) => {
134906
- data += chunk;
134907
- });
134908
- res.on("end", () => {
134909
- if (res.statusCode >= 200 && res.statusCode < 300) {
134910
- try {
134911
- if (!data || data.trim() === "") {
134912
- log3("debug", "Folder search returned empty response - no results found");
134913
- resolve(null);
134914
- return;
134915
- }
134916
- const result = JSON.parse(data);
134917
- if (result.searchResults && result.searchResults.length > 0) {
134918
- const match = result.searchResults.find((r) => r.name === folderName || r.title === folderName);
134919
- if (match) {
134920
- log3("debug", `Found folder: ${folderName} with ID: ${match.uuid || match.id}`);
134921
- resolve(match.uuid || match.id);
134922
- } else {
134923
- log3("debug", `Exact folder match not found, using first result: ${result.searchResults[0].uuid || result.searchResults[0].id}`);
134924
- resolve(result.searchResults[0].uuid || result.searchResults[0].id);
134925
- }
134926
- } else {
134927
- log3("debug", `No folders found matching: ${folderName}`);
134928
- resolve(null);
134929
- }
134930
- } catch (parseError) {
134931
- reject(new Error(`Failed to parse folder search response: ${parseError.message}`));
134932
- }
134933
- } else {
134934
- reject(new Error(`Folder search request failed with status ${res.statusCode}: ${data}`));
134935
- }
134936
- });
134937
- });
134938
- req.on("error", (error) => {
134939
- reject(new Error(`Folder search request error: ${error.message}`));
134940
- });
134941
- req.setTimeout(3e4, () => {
134942
- req.destroy(new Error("Request timeout"));
134943
- });
134944
- req.write(searchBody);
134945
- req.end();
134946
- });
134947
- }
134948
- /**
134949
- * Searches for a file in Heretto by filename.
134950
- * @param {Object} options - Search options
134951
- * @returns {Promise<string|null>} Document ID if found, null otherwise
134952
- */
134953
- async searchFileByName({ apiBaseUrl, apiToken, username, filename, log: log3 }) {
134954
- const searchUrl = new URL("/ezdnxtgen/api/search", apiBaseUrl);
134955
- const searchBody = JSON.stringify({
134956
- queryString: filename,
134957
- searchResultType: "FILES_ONLY"
134958
- });
134959
- return new Promise((resolve, reject) => {
134960
- const protocol = searchUrl.protocol === "https:" ? import_node_https.default : import_node_http.default;
134961
- const authString = Buffer.from(`${username}:${apiToken}`).toString("base64");
134962
- const options = {
134963
- hostname: searchUrl.hostname,
134964
- port: searchUrl.port || (searchUrl.protocol === "https:" ? 443 : 80),
134965
- path: searchUrl.pathname,
134966
- method: "POST",
134967
- headers: {
134968
- "Content-Type": "application/json",
134969
- "Authorization": `Basic ${authString}`,
134970
- "Content-Length": Buffer.byteLength(searchBody)
134971
- }
134972
- };
134973
- const req = protocol.request(options, (res) => {
134974
- let data = "";
134975
- res.on("data", (chunk) => {
134976
- data += chunk;
134977
- });
134978
- res.on("end", () => {
134979
- if (res.statusCode >= 200 && res.statusCode < 300) {
134980
- try {
134981
- if (!data || data.trim() === "") {
134982
- log3("debug", "Search returned empty response - no results found");
134983
- resolve(null);
134984
- return;
134985
- }
134986
- const result = JSON.parse(data);
134987
- if (result.searchResults && result.searchResults.length > 0) {
134988
- const match = result.searchResults.find((r) => r.name === filename || r.title === filename);
134989
- if (match) {
134990
- resolve(match.uuid || match.id);
134991
- } else {
134992
- resolve(result.searchResults[0].uuid || result.searchResults[0].id);
134993
- }
134994
- } else {
134995
- resolve(null);
134996
- }
134997
- } catch (parseError) {
134998
- reject(new Error(`Failed to parse search response: ${parseError.message}`));
134999
- }
135000
- } else {
135001
- reject(new Error(`Search request failed with status ${res.statusCode}: ${data}`));
135002
- }
135003
- });
135004
- });
135005
- req.on("error", (error) => {
135006
- reject(new Error(`Search request error: ${error.message}`));
135007
- });
135008
- req.setTimeout(3e4, () => {
135009
- req.destroy(new Error("Request timeout"));
135010
- });
135011
- req.write(searchBody);
135012
- req.end();
135013
- });
135014
- }
135015
- /**
135016
- * Uploads file content to Heretto.
135017
- * @param {Object} options - Upload options
135018
- * @returns {Promise<void>}
135019
- */
135020
- async uploadFile({ apiBaseUrl, apiToken, username, documentId, content, contentType, log: log3 }) {
135021
- const uploadUrl = new URL(`/rest/all-files/${documentId}/content`, apiBaseUrl);
135022
- return new Promise((resolve, reject) => {
135023
- const protocol = uploadUrl.protocol === "https:" ? import_node_https.default : import_node_http.default;
135024
- const authString = Buffer.from(`${username}:${apiToken}`).toString("base64");
135025
- const options = {
135026
- hostname: uploadUrl.hostname,
135027
- port: uploadUrl.port || (uploadUrl.protocol === "https:" ? 443 : 80),
135028
- path: uploadUrl.pathname,
135029
- method: "PUT",
135030
- headers: {
135031
- "Content-Type": contentType,
135032
- "Authorization": `Basic ${authString}`,
135033
- "Content-Length": Buffer.byteLength(content)
135034
- }
135035
- };
135036
- log3("debug", `Uploading to ${uploadUrl.toString()}`);
135037
- const req = protocol.request(options, (res) => {
135038
- let data = "";
135039
- res.on("data", (chunk) => {
135040
- data += chunk;
135041
- });
135042
- res.on("end", () => {
135043
- if (res.statusCode >= 200 && res.statusCode < 300) {
135044
- log3("debug", `Upload successful: ${res.statusCode}`);
135045
- resolve();
135046
- } else {
135047
- reject(new Error(`Upload failed with status ${res.statusCode}: ${data}`));
135048
- }
135049
- });
135050
- });
135051
- req.on("error", (error) => {
135052
- reject(new Error(`Upload request error: ${error.message}`));
135053
- });
135054
- req.setTimeout(3e4, () => {
135055
- req.destroy(new Error("Request timeout"));
135056
- });
135057
- req.write(content);
135058
- req.end();
135059
- });
135060
- }
135061
- /**
135062
- * Gets document information from Heretto.
135063
- * @param {Object} options - Options
135064
- * @returns {Promise<Object>} Document info including id, name, mimeType, folderUuid, uri
135065
- */
135066
- async getDocumentInfo({ apiBaseUrl, apiToken, username, documentId, log: log3 }) {
135067
- const docUrl = new URL(`/rest/all-files/${documentId}`, apiBaseUrl);
135068
- return new Promise((resolve, reject) => {
135069
- const protocol = docUrl.protocol === "https:" ? import_node_https.default : import_node_http.default;
135070
- const authString = Buffer.from(`${username}:${apiToken}`).toString("base64");
135071
- const options = {
135072
- hostname: docUrl.hostname,
135073
- port: docUrl.port || (docUrl.protocol === "https:" ? 443 : 80),
135074
- path: docUrl.pathname,
135075
- method: "GET",
135076
- headers: {
135077
- "Authorization": `Basic ${authString}`,
135078
- "Accept": "application/xml"
135079
- }
135080
- };
135081
- log3("debug", `Getting document info: ${docUrl.toString()}`);
135082
- const req = protocol.request(options, (res) => {
135083
- let data = "";
135084
- res.on("data", (chunk) => {
135085
- data += chunk;
135086
- });
135087
- res.on("end", () => {
135088
- if (res.statusCode === 200) {
135089
- try {
135090
- const resourceMatch = data.match(/<resource\s+([^>]+)>/);
135091
- let id = null;
135092
- let folderUuid = null;
135093
- if (resourceMatch) {
135094
- const attrs = resourceMatch[1];
135095
- const idMatch = attrs.match(/\bid="([^"]+)"/);
135096
- const folderMatch = attrs.match(/\bfolder-uuid="([^"]+)"/);
135097
- id = idMatch ? idMatch[1] : null;
135098
- folderUuid = folderMatch ? folderMatch[1] : null;
135099
- }
135100
- const nameMatch = data.match(/<name>([^<]+)<\/name>/);
135101
- const mimeMatch = data.match(/<mime-type>([^<]+)<\/mime-type>/);
135102
- const uriMatch = data.match(/<xmldb-uri>([^<]+)<\/xmldb-uri>/);
135103
- resolve({
135104
- id,
135105
- name: nameMatch ? nameMatch[1] : null,
135106
- mimeType: mimeMatch ? mimeMatch[1] : null,
135107
- folderUuid,
135108
- uri: uriMatch ? uriMatch[1] : null,
135109
- rawXml: data
135110
- });
135111
- } catch (parseError) {
135112
- reject(new Error(`Failed to parse document info: ${parseError.message}`));
135113
- }
135114
- } else {
135115
- reject(new Error(`Get document info failed with status ${res.statusCode}: ${data}`));
135116
- }
135117
- });
135118
- });
135119
- req.on("error", (error) => {
135120
- reject(new Error(`Get document info request error: ${error.message}`));
135121
- });
135122
- req.setTimeout(3e4, () => {
135123
- req.destroy(new Error("Request timeout"));
135124
- });
135125
- req.end();
135126
- });
135127
- }
135128
- /**
135129
- * Gets document content from Heretto.
135130
- * @param {Object} options - Options
135131
- * @returns {Promise<Buffer>} Document content as buffer
135132
- */
135133
- async getDocumentContent({ apiBaseUrl, apiToken, username, documentId, log: log3 }) {
135134
- const contentUrl = new URL(`/rest/all-files/${documentId}/content`, apiBaseUrl);
135135
- return new Promise((resolve, reject) => {
135136
- const protocol = contentUrl.protocol === "https:" ? import_node_https.default : import_node_http.default;
135137
- const authString = Buffer.from(`${username}:${apiToken}`).toString("base64");
135138
- const options = {
135139
- hostname: contentUrl.hostname,
135140
- port: contentUrl.port || (contentUrl.protocol === "https:" ? 443 : 80),
135141
- path: contentUrl.pathname,
135142
- method: "GET",
135143
- headers: {
135144
- "Authorization": `Basic ${authString}`
135145
- }
135146
- };
135147
- log3("debug", `Getting document content: ${contentUrl.toString()}`);
135148
- const req = protocol.request(options, (res) => {
135149
- const chunks = [];
135150
- res.on("data", (chunk) => {
135151
- chunks.push(chunk);
135152
- });
135153
- res.on("end", () => {
135154
- if (res.statusCode === 200) {
135155
- resolve(Buffer.concat(chunks));
135156
- } else {
135157
- reject(new Error(`Get document content failed with status ${res.statusCode}`));
135158
- }
135159
- });
135160
- });
135161
- req.on("error", (error) => {
135162
- reject(new Error(`Get document content request error: ${error.message}`));
135163
- });
135164
- req.setTimeout(3e4, () => {
135165
- req.destroy(new Error("Request timeout"));
135166
- });
135167
- req.end();
135168
- });
135169
- }
135170
- /**
135171
- * Determines the content type based on file extension.
135172
- * @param {string} filePath - File path
135173
- * @returns {string} MIME content type
135174
- */
135175
- getContentType(filePath) {
135176
- const ext = import_node_path14.default.extname(filePath).toLowerCase();
135177
- const contentTypes = {
135178
- ".png": "image/png",
135179
- ".jpg": "image/jpeg",
135180
- ".jpeg": "image/jpeg",
135181
- ".gif": "image/gif",
135182
- ".svg": "image/svg+xml",
135183
- ".webp": "image/webp",
135184
- ".bmp": "image/bmp",
135185
- ".ico": "image/x-icon",
135186
- ".pdf": "application/pdf",
135187
- ".xml": "application/xml",
135188
- ".dita": "application/xml",
135189
- ".ditamap": "application/xml"
135190
- };
135191
- return contentTypes[ext] || "application/octet-stream";
135192
- }
135193
- };
135194
-
135195
136313
  // dist/core/integrations/index.js
135196
136314
  var uploaders = [
135197
136315
  new HerettoUploader()
@@ -135425,7 +136543,7 @@ function getDriverCapabilities({ runnerDetails, name, options }) {
135425
136543
  // Reference: https://chromedriver.chromium.org/capabilities#h.p_ID_102
135426
136544
  args,
135427
136545
  prefs: {
135428
- "download.default_directory": import_node_os6.default.tmpdir(),
136546
+ "download.default_directory": import_node_os7.default.tmpdir(),
135429
136547
  "download.prompt_for_download": false,
135430
136548
  "download.directory_upgrade": true
135431
136549
  },
@@ -135521,7 +136639,7 @@ async function runViaApi({ resolvedTests, apiKey, config = {} }) {
135521
136639
  };
135522
136640
  let createResponse;
135523
136641
  try {
135524
- createResponse = await import_axios5.default.post(apiUrl, resolvedTests, axiosConfig);
136642
+ createResponse = await import_axios6.default.post(apiUrl, resolvedTests, axiosConfig);
135525
136643
  } catch (error) {
135526
136644
  return {
135527
136645
  status: error.response?.status,
@@ -135534,7 +136652,7 @@ async function runViaApi({ resolvedTests, apiKey, config = {} }) {
135534
136652
  const runId = createResponse.data.run.runId;
135535
136653
  let startResponse;
135536
136654
  try {
135537
- startResponse = await import_axios5.default.post(`${apiUrl}/${runId}/start`, {}, axiosConfig);
136655
+ startResponse = await import_axios6.default.post(`${apiUrl}/${runId}/start`, {}, axiosConfig);
135538
136656
  } catch (error) {
135539
136657
  return {
135540
136658
  status: error.response?.status,
@@ -135558,7 +136676,7 @@ async function runViaApi({ resolvedTests, apiKey, config = {} }) {
135558
136676
  };
135559
136677
  }
135560
136678
  try {
135561
- response = await import_axios5.default.get(`${apiUrl}/${runId}`, axiosConfig);
136679
+ response = await import_axios6.default.get(`${apiUrl}/${runId}`, axiosConfig);
135562
136680
  } catch (error) {
135563
136681
  return {
135564
136682
  status: error.response?.status,
@@ -135674,7 +136792,7 @@ async function runSpecs({ resolvedTests }) {
135674
136792
  context.browser = getDefaultBrowser({ runnerDetails });
135675
136793
  }
135676
136794
  let contextReport = {
135677
- contextId: context.contextId || (0, import_node_crypto4.randomUUID)(),
136795
+ contextId: context.contextId || (0, import_node_crypto5.randomUUID)(),
135678
136796
  platform: context.platform,
135679
136797
  browser: context.browser,
135680
136798
  steps: []
@@ -135752,7 +136870,7 @@ ${JSON.stringify(context, null, 2)}`);
135752
136870
  let stepExecutionFailed = false;
135753
136871
  for (let step of context.steps) {
135754
136872
  if (!step.stepId)
135755
- step.stepId = (0, import_node_crypto4.randomUUID)();
136873
+ step.stepId = (0, import_node_crypto5.randomUUID)();
135756
136874
  log(config, "debug", `STEP:
135757
136875
  ${JSON.stringify(step, null, 2)}`);
135758
136876
  if (step.unsafe && runnerDetails.allowUnsafeSteps === false) {
@@ -135807,7 +136925,7 @@ ${JSON.stringify(stepResult, null, 2)}`);
135807
136925
  const stopRecordStep = {
135808
136926
  stopRecord: true,
135809
136927
  description: "Stopping recording",
135810
- stepId: (0, import_node_crypto4.randomUUID)()
136928
+ stepId: (0, import_node_crypto5.randomUUID)()
135811
136929
  };
135812
136930
  const stepResult = await runStep({
135813
136931
  config,
@@ -136011,7 +137129,7 @@ async function appiumIsReady(timeoutMs = 12e4) {
136011
137129
  }
136012
137130
  await new Promise((resolve) => setTimeout(resolve, 1e3));
136013
137131
  try {
136014
- let resp = await import_axios5.default.get("http://127.0.0.1:4723/status");
137132
+ let resp = await import_axios6.default.get("http://127.0.0.1:4723/status");
136015
137133
  if (resp.status === 200)
136016
137134
  isReady = true;
136017
137135
  } catch {
@@ -136100,7 +137218,7 @@ async function getRunner(options = {}) {
136100
137218
  }
136101
137219
 
136102
137220
  // dist/core/telem.js
136103
- var import_node_os7 = __toESM(require("node:os"), 1);
137221
+ var import_node_os8 = __toESM(require("node:os"), 1);
136104
137222
  var import_posthog_node = require("posthog-node");
136105
137223
  var import_node_module = require("node:module");
136106
137224
  var require2 = (0, import_node_module.createRequire)(importMetaUrl);
@@ -136125,11 +137243,11 @@ function sendTelemetry(config, command, results) {
136125
137243
  telemetryData.dist_interface = telemetryData.dist_interface || "package";
136126
137244
  telemetryData.core_version = pkg.version;
136127
137245
  telemetryData.dist_version = telemetryData.dist_version || telemetryData.core_version;
136128
- telemetryData.core_platform = platformMap2[import_node_os7.default.platform()] || import_node_os7.default.platform();
137246
+ telemetryData.core_platform = platformMap2[import_node_os8.default.platform()] || import_node_os8.default.platform();
136129
137247
  telemetryData.dist_platform = telemetryData.dist_platform || telemetryData.core_platform;
136130
- telemetryData.core_platform_version = import_node_os7.default.release();
137248
+ telemetryData.core_platform_version = import_node_os8.default.release();
136131
137249
  telemetryData.dist_platform_version = telemetryData.dist_platform_version || telemetryData.core_platform_version;
136132
- telemetryData.core_platform_arch = import_node_os7.default.arch();
137250
+ telemetryData.core_platform_arch = import_node_os8.default.arch();
136133
137251
  telemetryData.dist_platform_arch = telemetryData.dist_platform_arch || telemetryData.core_platform_arch;
136134
137252
  telemetryData.core_deployment = telemetryData.core_deployment || "node";
136135
137253
  telemetryData.dist_deployment = telemetryData.dist_deployment || telemetryData.core_deployment;