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.
- package/dist/agents/adapters/claude-code.d.ts +77 -0
- package/dist/agents/adapters/claude-code.d.ts.map +1 -0
- package/dist/agents/adapters/claude-code.js +461 -0
- package/dist/agents/adapters/claude-code.js.map +1 -0
- package/dist/agents/adapters/codex.d.ts +64 -0
- package/dist/agents/adapters/codex.d.ts.map +1 -0
- package/dist/agents/adapters/codex.js +299 -0
- package/dist/agents/adapters/codex.js.map +1 -0
- package/dist/agents/adapters/copilot-cli.d.ts +29 -0
- package/dist/agents/adapters/copilot-cli.d.ts.map +1 -0
- package/dist/agents/adapters/copilot-cli.js +195 -0
- package/dist/agents/adapters/copilot-cli.js.map +1 -0
- package/dist/agents/adapters/gemini-cli.d.ts +29 -0
- package/dist/agents/adapters/gemini-cli.d.ts.map +1 -0
- package/dist/agents/adapters/gemini-cli.js +207 -0
- package/dist/agents/adapters/gemini-cli.js.map +1 -0
- package/dist/agents/adapters/opencode.d.ts +67 -0
- package/dist/agents/adapters/opencode.d.ts.map +1 -0
- package/dist/agents/adapters/opencode.js +341 -0
- package/dist/agents/adapters/opencode.js.map +1 -0
- package/dist/agents/adapters/qwen-code.d.ts +30 -0
- package/dist/agents/adapters/qwen-code.d.ts.map +1 -0
- package/dist/agents/adapters/qwen-code.js +212 -0
- package/dist/agents/adapters/qwen-code.js.map +1 -0
- package/dist/agents/command.d.ts +11 -0
- package/dist/agents/command.d.ts.map +1 -0
- package/dist/agents/command.js +41 -0
- package/dist/agents/command.js.map +1 -0
- package/dist/agents/fetcher.d.ts +30 -0
- package/dist/agents/fetcher.d.ts.map +1 -0
- package/dist/agents/fetcher.js +112 -0
- package/dist/agents/fetcher.js.map +1 -0
- package/dist/agents/prompts.d.ts +24 -0
- package/dist/agents/prompts.d.ts.map +1 -0
- package/dist/agents/prompts.js +74 -0
- package/dist/agents/prompts.js.map +1 -0
- package/dist/agents/registry.d.ts +4 -0
- package/dist/agents/registry.d.ts.map +1 -0
- package/dist/agents/registry.js +25 -0
- package/dist/agents/registry.js.map +1 -0
- package/dist/agents/runner.d.ts +13 -0
- package/dist/agents/runner.d.ts.map +1 -0
- package/dist/agents/runner.js +155 -0
- package/dist/agents/runner.js.map +1 -0
- package/dist/agents/spawn-helper.d.ts +33 -0
- package/dist/agents/spawn-helper.d.ts.map +1 -0
- package/dist/agents/spawn-helper.js +98 -0
- package/dist/agents/spawn-helper.js.map +1 -0
- package/dist/agents/types.d.ts +41 -0
- package/dist/agents/types.d.ts.map +1 -0
- package/dist/agents/types.js +2 -0
- package/dist/agents/types.js.map +1 -0
- package/dist/cli.js +42 -10
- package/dist/cli.js.map +1 -1
- package/dist/common/src/detectTests.d.ts +101 -0
- package/dist/common/src/detectTests.d.ts.map +1 -0
- package/dist/common/src/detectTests.js +693 -0
- package/dist/common/src/detectTests.js.map +1 -0
- package/dist/common/src/fileTypes.d.ts +35 -0
- package/dist/common/src/fileTypes.d.ts.map +1 -0
- package/dist/common/src/fileTypes.js +303 -0
- package/dist/common/src/fileTypes.js.map +1 -0
- package/dist/common/src/index.d.ts +10 -0
- package/dist/common/src/index.d.ts.map +1 -0
- package/dist/common/src/index.js +5 -0
- package/dist/common/src/index.js.map +1 -0
- package/dist/common/src/schemas/index.d.ts +5 -0
- package/dist/common/src/schemas/index.d.ts.map +1 -0
- package/dist/common/src/schemas/index.js +3 -0
- package/dist/common/src/schemas/index.js.map +1 -0
- package/dist/common/src/schemas/schemas.json +128711 -0
- package/dist/common/src/types/generated/checkLink_v3.d.ts +42 -0
- package/dist/common/src/types/generated/checkLink_v3.d.ts.map +1 -0
- package/dist/common/src/types/generated/checkLink_v3.js +7 -0
- package/dist/common/src/types/generated/checkLink_v3.js.map +1 -0
- package/dist/common/src/types/generated/click_v3.d.ts +16 -0
- package/dist/common/src/types/generated/click_v3.d.ts.map +1 -0
- package/dist/common/src/types/generated/click_v3.js +7 -0
- package/dist/common/src/types/generated/click_v3.js.map +1 -0
- package/dist/common/src/types/generated/config_v3.d.ts +402 -0
- package/dist/common/src/types/generated/config_v3.d.ts.map +1 -0
- package/dist/common/src/types/generated/config_v3.js +7 -0
- package/dist/common/src/types/generated/config_v3.js.map +1 -0
- package/dist/common/src/types/generated/context_v3.d.ts +108 -0
- package/dist/common/src/types/generated/context_v3.d.ts.map +1 -0
- package/dist/common/src/types/generated/context_v3.js +7 -0
- package/dist/common/src/types/generated/context_v3.js.map +1 -0
- package/dist/common/src/types/generated/dragAndDrop_v3.d.ts +37 -0
- package/dist/common/src/types/generated/dragAndDrop_v3.d.ts.map +1 -0
- package/dist/common/src/types/generated/dragAndDrop_v3.js +7 -0
- package/dist/common/src/types/generated/dragAndDrop_v3.js.map +1 -0
- package/dist/common/src/types/generated/endRecord_v3.d.ts +9 -0
- package/dist/common/src/types/generated/endRecord_v3.d.ts.map +1 -0
- package/dist/common/src/types/generated/endRecord_v3.js +7 -0
- package/dist/common/src/types/generated/endRecord_v3.js.map +1 -0
- package/dist/common/src/types/generated/find_v3.d.ts +16 -0
- package/dist/common/src/types/generated/find_v3.d.ts.map +1 -0
- package/dist/common/src/types/generated/find_v3.js +7 -0
- package/dist/common/src/types/generated/find_v3.js.map +1 -0
- package/dist/common/src/types/generated/goTo_v3.d.ts +46 -0
- package/dist/common/src/types/generated/goTo_v3.d.ts.map +1 -0
- package/dist/common/src/types/generated/goTo_v3.js +7 -0
- package/dist/common/src/types/generated/goTo_v3.js.map +1 -0
- package/dist/common/src/types/generated/httpRequest_v3.d.ts +16 -0
- package/dist/common/src/types/generated/httpRequest_v3.d.ts.map +1 -0
- package/dist/common/src/types/generated/httpRequest_v3.js +7 -0
- package/dist/common/src/types/generated/httpRequest_v3.js.map +1 -0
- package/dist/common/src/types/generated/loadCookie_v3.d.ts +16 -0
- package/dist/common/src/types/generated/loadCookie_v3.d.ts.map +1 -0
- package/dist/common/src/types/generated/loadCookie_v3.js +7 -0
- package/dist/common/src/types/generated/loadCookie_v3.js.map +1 -0
- package/dist/common/src/types/generated/loadVariables_v3.d.ts +9 -0
- package/dist/common/src/types/generated/loadVariables_v3.d.ts.map +1 -0
- package/dist/common/src/types/generated/loadVariables_v3.js +7 -0
- package/dist/common/src/types/generated/loadVariables_v3.js.map +1 -0
- package/dist/common/src/types/generated/openApi_v3.d.ts +62 -0
- package/dist/common/src/types/generated/openApi_v3.d.ts.map +1 -0
- package/dist/common/src/types/generated/openApi_v3.js +7 -0
- package/dist/common/src/types/generated/openApi_v3.js.map +1 -0
- package/dist/common/src/types/generated/record_v3.d.ts +32 -0
- package/dist/common/src/types/generated/record_v3.d.ts.map +1 -0
- package/dist/common/src/types/generated/record_v3.js +7 -0
- package/dist/common/src/types/generated/record_v3.js.map +1 -0
- package/dist/common/src/types/generated/report_v3.d.ts +174 -0
- package/dist/common/src/types/generated/report_v3.d.ts.map +1 -0
- package/dist/common/src/types/generated/report_v3.js +7 -0
- package/dist/common/src/types/generated/report_v3.js.map +1 -0
- package/dist/common/src/types/generated/resolvedTests_v3.d.ts +575 -0
- package/dist/common/src/types/generated/resolvedTests_v3.d.ts.map +1 -0
- package/dist/common/src/types/generated/resolvedTests_v3.js +7 -0
- package/dist/common/src/types/generated/resolvedTests_v3.js.map +1 -0
- package/dist/common/src/types/generated/runCode_v3.d.ts +57 -0
- package/dist/common/src/types/generated/runCode_v3.d.ts.map +1 -0
- package/dist/common/src/types/generated/runCode_v3.js +7 -0
- package/dist/common/src/types/generated/runCode_v3.js.map +1 -0
- package/dist/common/src/types/generated/runShell_v3.d.ts +56 -0
- package/dist/common/src/types/generated/runShell_v3.d.ts.map +1 -0
- package/dist/common/src/types/generated/runShell_v3.js +7 -0
- package/dist/common/src/types/generated/runShell_v3.js.map +1 -0
- package/dist/common/src/types/generated/saveCookie_v3.d.ts +16 -0
- package/dist/common/src/types/generated/saveCookie_v3.d.ts.map +1 -0
- package/dist/common/src/types/generated/saveCookie_v3.js +7 -0
- package/dist/common/src/types/generated/saveCookie_v3.js.map +1 -0
- package/dist/common/src/types/generated/screenshot_v3.d.ts +74 -0
- package/dist/common/src/types/generated/screenshot_v3.d.ts.map +1 -0
- package/dist/common/src/types/generated/screenshot_v3.js +7 -0
- package/dist/common/src/types/generated/screenshot_v3.js.map +1 -0
- package/dist/common/src/types/generated/sourceIntegration_v3.d.ts +30 -0
- package/dist/common/src/types/generated/sourceIntegration_v3.d.ts.map +1 -0
- package/dist/common/src/types/generated/sourceIntegration_v3.js +7 -0
- package/dist/common/src/types/generated/sourceIntegration_v3.js.map +1 -0
- package/dist/common/src/types/generated/spec_v3.d.ts +159 -0
- package/dist/common/src/types/generated/spec_v3.d.ts.map +1 -0
- package/dist/common/src/types/generated/spec_v3.js +7 -0
- package/dist/common/src/types/generated/spec_v3.js.map +1 -0
- package/dist/common/src/types/generated/step_v3.d.ts +1573 -0
- package/dist/common/src/types/generated/step_v3.d.ts.map +1 -0
- package/dist/common/src/types/generated/step_v3.js +7 -0
- package/dist/common/src/types/generated/step_v3.js.map +1 -0
- package/dist/common/src/types/generated/stopRecord_v3.d.ts +9 -0
- package/dist/common/src/types/generated/stopRecord_v3.d.ts.map +1 -0
- package/dist/common/src/types/generated/stopRecord_v3.js +7 -0
- package/dist/common/src/types/generated/stopRecord_v3.js.map +1 -0
- package/dist/common/src/types/generated/test_v3.d.ts +3521 -0
- package/dist/common/src/types/generated/test_v3.d.ts.map +1 -0
- package/dist/common/src/types/generated/test_v3.js +7 -0
- package/dist/common/src/types/generated/test_v3.js.map +1 -0
- package/dist/common/src/types/generated/type_v3.d.ts +54 -0
- package/dist/common/src/types/generated/type_v3.d.ts.map +1 -0
- package/dist/common/src/types/generated/type_v3.js +7 -0
- package/dist/common/src/types/generated/type_v3.js.map +1 -0
- package/dist/common/src/types/generated/wait_v3.d.ts +12 -0
- package/dist/common/src/types/generated/wait_v3.d.ts.map +1 -0
- package/dist/common/src/types/generated/wait_v3.js +7 -0
- package/dist/common/src/types/generated/wait_v3.js.map +1 -0
- package/dist/common/src/validate.d.ts +41 -0
- package/dist/common/src/validate.d.ts.map +1 -0
- package/dist/common/src/validate.js +557 -0
- package/dist/common/src/validate.js.map +1 -0
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +10 -0
- package/dist/core/config.js.map +1 -1
- package/dist/core/detectTests.d.ts.map +1 -1
- package/dist/core/detectTests.js +50 -2
- package/dist/core/detectTests.js.map +1 -1
- package/dist/core/integrations/heretto.d.ts +32 -0
- package/dist/core/integrations/heretto.d.ts.map +1 -1
- package/dist/core/integrations/heretto.js +368 -0
- package/dist/core/integrations/heretto.js.map +1 -1
- package/dist/core/tests/checkLink.d.ts.map +1 -1
- package/dist/core/tests/checkLink.js +136 -29
- package/dist/core/tests/checkLink.js.map +1 -1
- package/dist/core/tests/loadCookie.d.ts.map +1 -1
- package/dist/core/tests/loadCookie.js +12 -2
- package/dist/core/tests/loadCookie.js.map +1 -1
- package/dist/index.cjs +2083 -965
- package/dist/reporters/htmlReporter.d.ts +2 -0
- package/dist/reporters/htmlReporter.d.ts.map +1 -0
- package/dist/reporters/htmlReporter.js +1589 -0
- package/dist/reporters/htmlReporter.js.map +1 -0
- package/dist/utils.d.ts +2 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +43 -10
- package/dist/utils.js.map +1 -1
- package/package.json +146 -135
- package/.doc-detective.json +0 -1
- package/CONTRIBUTIONS.md +0 -27
- 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(`
|
|
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
|
|
129733
|
-
var
|
|
129734
|
-
var
|
|
129735
|
-
var
|
|
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(/'|'/g, "'").replace(/"|"/g, '"').replace(/</g, "<").replace(/>/g, ">").replace(/&/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
|
-
|
|
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, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
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 =
|
|
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 =
|
|
132301
|
+
relativePath = import_node_path6.default.relative(cwd, absolutePath);
|
|
130551
132302
|
} else {
|
|
130552
132303
|
relativePath = absolutePath;
|
|
130553
132304
|
}
|
|
130554
|
-
const normalizedPath = relativePath.split(
|
|
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 (
|
|
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 =
|
|
132336
|
+
beforePath = import_node_path6.default.resolve(import_node_path6.default.dirname(source), test.before);
|
|
130586
132337
|
} else {
|
|
130587
|
-
beforePath =
|
|
132338
|
+
beforePath = import_node_path6.default.resolve(test.before);
|
|
130588
132339
|
}
|
|
130589
|
-
if (!
|
|
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 =
|
|
132348
|
+
afterPath = import_node_path6.default.resolve(import_node_path6.default.dirname(source), test.after);
|
|
130598
132349
|
} else {
|
|
130599
|
-
afterPath =
|
|
132350
|
+
afterPath = import_node_path6.default.resolve(test.after);
|
|
130600
132351
|
}
|
|
130601
|
-
if (!
|
|
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 =
|
|
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 =
|
|
130617
|
-
const tmpBase =
|
|
130618
|
-
const outputDir =
|
|
130619
|
-
if (!
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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 =
|
|
130682
|
-
isDir =
|
|
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 &&
|
|
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(
|
|
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 =
|
|
132495
|
+
const objects = import_node_fs5.default.readdirSync(dir);
|
|
130703
132496
|
for (const object of objects) {
|
|
130704
|
-
const content =
|
|
132497
|
+
const content = import_node_path6.default.resolve(dir + "/" + object);
|
|
130705
132498
|
if (content.includes("node_modules"))
|
|
130706
132499
|
continue;
|
|
130707
|
-
const isFile2 =
|
|
130708
|
-
const isDir2 =
|
|
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(
|
|
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 =
|
|
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
|
|
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
|
|
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 ||
|
|
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 ||
|
|
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 ||
|
|
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:
|
|
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
|
|
131111
|
-
var
|
|
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
|
|
132332
|
-
var
|
|
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 =
|
|
132406
|
-
if (!
|
|
132407
|
-
|
|
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 (!
|
|
132412
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
132472
|
-
|
|
132473
|
-
|
|
132474
|
-
|
|
132475
|
-
|
|
132476
|
-
|
|
132477
|
-
|
|
132478
|
-
|
|
132479
|
-
|
|
132480
|
-
|
|
132481
|
-
|
|
132482
|
-
|
|
132483
|
-
|
|
132484
|
-
|
|
132485
|
-
|
|
132486
|
-
|
|
132487
|
-
|
|
132488
|
-
|
|
132489
|
-
|
|
132490
|
-
|
|
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(
|
|
134387
|
+
if (step.checkLink.statusCodes.indexOf(last.statusCode) >= 0) {
|
|
132496
134388
|
result.status = "PASS";
|
|
132497
|
-
result.description = `Returned ${
|
|
134389
|
+
result.description = `Returned ${last.statusCode}`;
|
|
132498
134390
|
} else {
|
|
132499
134391
|
result.status = "FAIL";
|
|
132500
|
-
result.description = `Returned ${
|
|
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
|
|
132507
|
-
var
|
|
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 =
|
|
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 =
|
|
132561
|
-
if (!
|
|
132562
|
-
|
|
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 (
|
|
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 =
|
|
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 &&
|
|
132661
|
-
|
|
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 =
|
|
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
|
-
|
|
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 &&
|
|
132727
|
-
|
|
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
|
-
|
|
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(
|
|
132746
|
-
const img2 = import_pngjs.PNG.sync.read(
|
|
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 &&
|
|
132751
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
132816
|
-
var
|
|
132817
|
-
var
|
|
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 =
|
|
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 =
|
|
132853
|
-
const dir =
|
|
132854
|
-
if (!
|
|
132855
|
-
|
|
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 (
|
|
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:
|
|
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
|
|
132979
|
-
var
|
|
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 (!
|
|
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 (!
|
|
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 (
|
|
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
|
-
|
|
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
|
|
133091
|
-
var
|
|
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 =
|
|
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 =
|
|
133158
|
-
if (
|
|
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 =
|
|
133164
|
-
if (!
|
|
133165
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
133203
|
-
var
|
|
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 =
|
|
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 =
|
|
135125
|
+
const ext = import_node_path12.default.extname(filePath).toLowerCase();
|
|
133234
135126
|
if (ext === ".txt") {
|
|
133235
|
-
cookieName =
|
|
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 =
|
|
133259
|
-
if (!
|
|
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 =
|
|
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
|
|
133421
|
-
var
|
|
133422
|
-
var
|
|
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,
|
|
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 =
|
|
133726
|
-
if (!
|
|
133727
|
-
|
|
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 (!
|
|
133732
|
-
await
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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
|
|
133926
|
-
var
|
|
133927
|
-
var
|
|
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 =
|
|
133947
|
-
const tmpFile =
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
|
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, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
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":
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
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
|
|
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[
|
|
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 =
|
|
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 =
|
|
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;
|