doc-detective 4.15.3 → 4.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/common/src/schemas/schemas.json +280 -0
- package/dist/common/src/types/generated/config_v3.d.ts +16 -0
- package/dist/common/src/types/generated/config_v3.d.ts.map +1 -1
- package/dist/common/src/types/generated/context_v3.d.ts +12 -0
- package/dist/common/src/types/generated/context_v3.d.ts.map +1 -1
- package/dist/common/src/types/generated/report_v3.d.ts +12 -0
- package/dist/common/src/types/generated/report_v3.d.ts.map +1 -1
- package/dist/common/src/types/generated/resolvedTests_v3.d.ts +28 -0
- package/dist/common/src/types/generated/resolvedTests_v3.d.ts.map +1 -1
- package/dist/common/src/types/generated/spec_v3.d.ts +12 -0
- package/dist/common/src/types/generated/spec_v3.d.ts.map +1 -1
- package/dist/common/src/types/generated/test_v3.d.ts +16 -0
- package/dist/common/src/types/generated/test_v3.d.ts.map +1 -1
- package/dist/core/config.d.ts +28 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +90 -13
- package/dist/core/config.js.map +1 -1
- package/dist/core/resolveTests.d.ts.map +1 -1
- package/dist/core/resolveTests.js +5 -0
- package/dist/core/resolveTests.js.map +1 -1
- package/dist/core/tests.d.ts +65 -2
- package/dist/core/tests.d.ts.map +1 -1
- package/dist/core/tests.js +312 -77
- package/dist/core/tests.js.map +1 -1
- package/dist/index.cjs +1190 -569
- package/dist/runtime/browsers.d.ts +39 -0
- package/dist/runtime/browsers.d.ts.map +1 -1
- package/dist/runtime/browsers.js +269 -39
- package/dist/runtime/browsers.js.map +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +18 -0
- package/dist/utils.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1037,6 +1037,11 @@ var init_schemas = __esm({
|
|
|
1037
1037
|
],
|
|
1038
1038
|
$comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
|
|
1039
1039
|
},
|
|
1040
|
+
explicit: {
|
|
1041
|
+
type: "boolean",
|
|
1042
|
+
description: "Set automatically during context resolution: `true` when the author explicitly requested this browser (as opposed to it being auto-selected as the default). The runner's cross-browser fallback uses it to decide whether substituting another engine reports `PASS` (auto-selected) or `WARNING` (explicitly pinned).",
|
|
1043
|
+
readOnly: true
|
|
1044
|
+
},
|
|
1040
1045
|
headless: {
|
|
1041
1046
|
type: "boolean",
|
|
1042
1047
|
description: "If `true`, runs the browser in headless mode.",
|
|
@@ -1111,6 +1116,11 @@ var init_schemas = __esm({
|
|
|
1111
1116
|
],
|
|
1112
1117
|
$comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
|
|
1113
1118
|
},
|
|
1119
|
+
explicit: {
|
|
1120
|
+
type: "boolean",
|
|
1121
|
+
description: "Set automatically during context resolution: `true` when the author explicitly requested this browser (as opposed to it being auto-selected as the default). The runner's cross-browser fallback uses it to decide whether substituting another engine reports `PASS` (auto-selected) or `WARNING` (explicitly pinned).",
|
|
1122
|
+
readOnly: true
|
|
1123
|
+
},
|
|
1114
1124
|
headless: {
|
|
1115
1125
|
type: "boolean",
|
|
1116
1126
|
description: "If `true`, runs the browser in headless mode.",
|
|
@@ -1155,6 +1165,15 @@ var init_schemas = __esm({
|
|
|
1155
1165
|
}
|
|
1156
1166
|
}
|
|
1157
1167
|
]
|
|
1168
|
+
},
|
|
1169
|
+
browserFallback: {
|
|
1170
|
+
description: "Per-context override for the config-level [`browserFallback`](config) policy that governs whether a context whose browser can't start a driver session falls back to another available browser. Accepts the same values \u2014 `auto`, `explicit`, `off` \u2014 and, when set, takes precedence over the config-level value for the contexts this entry expands into. Omit it to inherit the config-level policy (which itself defaults to `auto`).",
|
|
1171
|
+
type: "string",
|
|
1172
|
+
enum: [
|
|
1173
|
+
"auto",
|
|
1174
|
+
"explicit",
|
|
1175
|
+
"off"
|
|
1176
|
+
]
|
|
1158
1177
|
}
|
|
1159
1178
|
},
|
|
1160
1179
|
components: {
|
|
@@ -1197,6 +1216,11 @@ var init_schemas = __esm({
|
|
|
1197
1216
|
],
|
|
1198
1217
|
$comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
|
|
1199
1218
|
},
|
|
1219
|
+
explicit: {
|
|
1220
|
+
type: "boolean",
|
|
1221
|
+
description: "Set automatically during context resolution: `true` when the author explicitly requested this browser (as opposed to it being auto-selected as the default). The runner's cross-browser fallback uses it to decide whether substituting another engine reports `PASS` (auto-selected) or `WARNING` (explicitly pinned).",
|
|
1222
|
+
readOnly: true
|
|
1223
|
+
},
|
|
1200
1224
|
headless: {
|
|
1201
1225
|
type: "boolean",
|
|
1202
1226
|
description: "If `true`, runs the browser in headless mode.",
|
|
@@ -29464,6 +29488,16 @@ var init_schemas = __esm({
|
|
|
29464
29488
|
type: "string",
|
|
29465
29489
|
minLength: 1,
|
|
29466
29490
|
pattern: "\\S"
|
|
29491
|
+
},
|
|
29492
|
+
browserFallback: {
|
|
29493
|
+
description: "Controls whether a context whose browser cannot start a driver session falls back to another available browser instead of being skipped. Drivers are validated by execution (not just presence) so a present-but-broken driver \u2014 for example a partially downloaded geckodriver \u2014 no longer silently skips Firefox coverage. `auto` (default): fall back to any other available browser for both auto-selected and explicitly requested browsers; a fallback away from an explicitly requested browser reports the context as `WARNING` rather than `PASS`. `explicit`: fall back only when the browser was auto-selected; an explicitly requested browser whose driver is broken is skipped with a diagnostic reason. `off`: never fall back across browsers (driver validation and diagnostic skip reasons still apply). Equivalent to `--browser-fallback` on the CLI.",
|
|
29494
|
+
type: "string",
|
|
29495
|
+
enum: [
|
|
29496
|
+
"auto",
|
|
29497
|
+
"explicit",
|
|
29498
|
+
"off"
|
|
29499
|
+
],
|
|
29500
|
+
default: "auto"
|
|
29467
29501
|
}
|
|
29468
29502
|
},
|
|
29469
29503
|
components: {
|
|
@@ -56977,6 +57011,11 @@ var init_schemas = __esm({
|
|
|
56977
57011
|
],
|
|
56978
57012
|
$comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
|
|
56979
57013
|
},
|
|
57014
|
+
explicit: {
|
|
57015
|
+
type: "boolean",
|
|
57016
|
+
description: "Set automatically during context resolution: `true` when the author explicitly requested this browser (as opposed to it being auto-selected as the default). The runner's cross-browser fallback uses it to decide whether substituting another engine reports `PASS` (auto-selected) or `WARNING` (explicitly pinned).",
|
|
57017
|
+
readOnly: true
|
|
57018
|
+
},
|
|
56980
57019
|
headless: {
|
|
56981
57020
|
type: "boolean",
|
|
56982
57021
|
description: "If `true`, runs the browser in headless mode.",
|
|
@@ -57051,6 +57090,11 @@ var init_schemas = __esm({
|
|
|
57051
57090
|
],
|
|
57052
57091
|
$comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
|
|
57053
57092
|
},
|
|
57093
|
+
explicit: {
|
|
57094
|
+
type: "boolean",
|
|
57095
|
+
description: "Set automatically during context resolution: `true` when the author explicitly requested this browser (as opposed to it being auto-selected as the default). The runner's cross-browser fallback uses it to decide whether substituting another engine reports `PASS` (auto-selected) or `WARNING` (explicitly pinned).",
|
|
57096
|
+
readOnly: true
|
|
57097
|
+
},
|
|
57054
57098
|
headless: {
|
|
57055
57099
|
type: "boolean",
|
|
57056
57100
|
description: "If `true`, runs the browser in headless mode.",
|
|
@@ -57095,6 +57139,15 @@ var init_schemas = __esm({
|
|
|
57095
57139
|
}
|
|
57096
57140
|
}
|
|
57097
57141
|
]
|
|
57142
|
+
},
|
|
57143
|
+
browserFallback: {
|
|
57144
|
+
description: "Per-context override for the config-level [`browserFallback`](config) policy that governs whether a context whose browser can't start a driver session falls back to another available browser. Accepts the same values \u2014 `auto`, `explicit`, `off` \u2014 and, when set, takes precedence over the config-level value for the contexts this entry expands into. Omit it to inherit the config-level policy (which itself defaults to `auto`).",
|
|
57145
|
+
type: "string",
|
|
57146
|
+
enum: [
|
|
57147
|
+
"auto",
|
|
57148
|
+
"explicit",
|
|
57149
|
+
"off"
|
|
57150
|
+
]
|
|
57098
57151
|
}
|
|
57099
57152
|
},
|
|
57100
57153
|
components: {
|
|
@@ -57137,6 +57190,11 @@ var init_schemas = __esm({
|
|
|
57137
57190
|
],
|
|
57138
57191
|
$comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
|
|
57139
57192
|
},
|
|
57193
|
+
explicit: {
|
|
57194
|
+
type: "boolean",
|
|
57195
|
+
description: "Set automatically during context resolution: `true` when the author explicitly requested this browser (as opposed to it being auto-selected as the default). The runner's cross-browser fallback uses it to decide whether substituting another engine reports `PASS` (auto-selected) or `WARNING` (explicitly pinned).",
|
|
57196
|
+
readOnly: true
|
|
57197
|
+
},
|
|
57140
57198
|
headless: {
|
|
57141
57199
|
type: "boolean",
|
|
57142
57200
|
description: "If `true`, runs the browser in headless mode.",
|
|
@@ -62241,6 +62299,11 @@ var init_schemas = __esm({
|
|
|
62241
62299
|
],
|
|
62242
62300
|
$comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
|
|
62243
62301
|
},
|
|
62302
|
+
explicit: {
|
|
62303
|
+
type: "boolean",
|
|
62304
|
+
description: "Set automatically during context resolution: `true` when the author explicitly requested this browser (as opposed to it being auto-selected as the default). The runner's cross-browser fallback uses it to decide whether substituting another engine reports `PASS` (auto-selected) or `WARNING` (explicitly pinned).",
|
|
62305
|
+
readOnly: true
|
|
62306
|
+
},
|
|
62244
62307
|
headless: {
|
|
62245
62308
|
type: "boolean",
|
|
62246
62309
|
description: "If `true`, runs the browser in headless mode.",
|
|
@@ -62315,6 +62378,11 @@ var init_schemas = __esm({
|
|
|
62315
62378
|
],
|
|
62316
62379
|
$comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
|
|
62317
62380
|
},
|
|
62381
|
+
explicit: {
|
|
62382
|
+
type: "boolean",
|
|
62383
|
+
description: "Set automatically during context resolution: `true` when the author explicitly requested this browser (as opposed to it being auto-selected as the default). The runner's cross-browser fallback uses it to decide whether substituting another engine reports `PASS` (auto-selected) or `WARNING` (explicitly pinned).",
|
|
62384
|
+
readOnly: true
|
|
62385
|
+
},
|
|
62318
62386
|
headless: {
|
|
62319
62387
|
type: "boolean",
|
|
62320
62388
|
description: "If `true`, runs the browser in headless mode.",
|
|
@@ -62359,6 +62427,15 @@ var init_schemas = __esm({
|
|
|
62359
62427
|
}
|
|
62360
62428
|
}
|
|
62361
62429
|
]
|
|
62430
|
+
},
|
|
62431
|
+
browserFallback: {
|
|
62432
|
+
description: "Per-context override for the config-level [`browserFallback`](config) policy that governs whether a context whose browser can't start a driver session falls back to another available browser. Accepts the same values \u2014 `auto`, `explicit`, `off` \u2014 and, when set, takes precedence over the config-level value for the contexts this entry expands into. Omit it to inherit the config-level policy (which itself defaults to `auto`).",
|
|
62433
|
+
type: "string",
|
|
62434
|
+
enum: [
|
|
62435
|
+
"auto",
|
|
62436
|
+
"explicit",
|
|
62437
|
+
"off"
|
|
62438
|
+
]
|
|
62362
62439
|
}
|
|
62363
62440
|
},
|
|
62364
62441
|
components: {
|
|
@@ -62401,6 +62478,11 @@ var init_schemas = __esm({
|
|
|
62401
62478
|
],
|
|
62402
62479
|
$comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
|
|
62403
62480
|
},
|
|
62481
|
+
explicit: {
|
|
62482
|
+
type: "boolean",
|
|
62483
|
+
description: "Set automatically during context resolution: `true` when the author explicitly requested this browser (as opposed to it being auto-selected as the default). The runner's cross-browser fallback uses it to decide whether substituting another engine reports `PASS` (auto-selected) or `WARNING` (explicitly pinned).",
|
|
62484
|
+
readOnly: true
|
|
62485
|
+
},
|
|
62404
62486
|
headless: {
|
|
62405
62487
|
type: "boolean",
|
|
62406
62488
|
description: "If `true`, runs the browser in headless mode.",
|
|
@@ -90668,6 +90750,16 @@ var init_schemas = __esm({
|
|
|
90668
90750
|
type: "string",
|
|
90669
90751
|
minLength: 1,
|
|
90670
90752
|
pattern: "\\S"
|
|
90753
|
+
},
|
|
90754
|
+
browserFallback: {
|
|
90755
|
+
description: "Controls whether a context whose browser cannot start a driver session falls back to another available browser instead of being skipped. Drivers are validated by execution (not just presence) so a present-but-broken driver \u2014 for example a partially downloaded geckodriver \u2014 no longer silently skips Firefox coverage. `auto` (default): fall back to any other available browser for both auto-selected and explicitly requested browsers; a fallback away from an explicitly requested browser reports the context as `WARNING` rather than `PASS`. `explicit`: fall back only when the browser was auto-selected; an explicitly requested browser whose driver is broken is skipped with a diagnostic reason. `off`: never fall back across browsers (driver validation and diagnostic skip reasons still apply). Equivalent to `--browser-fallback` on the CLI.",
|
|
90756
|
+
type: "string",
|
|
90757
|
+
enum: [
|
|
90758
|
+
"auto",
|
|
90759
|
+
"explicit",
|
|
90760
|
+
"off"
|
|
90761
|
+
],
|
|
90762
|
+
default: "auto"
|
|
90671
90763
|
}
|
|
90672
90764
|
},
|
|
90673
90765
|
components: {
|
|
@@ -118243,6 +118335,11 @@ var init_schemas = __esm({
|
|
|
118243
118335
|
],
|
|
118244
118336
|
$comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
|
|
118245
118337
|
},
|
|
118338
|
+
explicit: {
|
|
118339
|
+
type: "boolean",
|
|
118340
|
+
description: "Set automatically during context resolution: `true` when the author explicitly requested this browser (as opposed to it being auto-selected as the default). The runner's cross-browser fallback uses it to decide whether substituting another engine reports `PASS` (auto-selected) or `WARNING` (explicitly pinned).",
|
|
118341
|
+
readOnly: true
|
|
118342
|
+
},
|
|
118246
118343
|
headless: {
|
|
118247
118344
|
type: "boolean",
|
|
118248
118345
|
description: "If `true`, runs the browser in headless mode.",
|
|
@@ -118317,6 +118414,11 @@ var init_schemas = __esm({
|
|
|
118317
118414
|
],
|
|
118318
118415
|
$comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
|
|
118319
118416
|
},
|
|
118417
|
+
explicit: {
|
|
118418
|
+
type: "boolean",
|
|
118419
|
+
description: "Set automatically during context resolution: `true` when the author explicitly requested this browser (as opposed to it being auto-selected as the default). The runner's cross-browser fallback uses it to decide whether substituting another engine reports `PASS` (auto-selected) or `WARNING` (explicitly pinned).",
|
|
118420
|
+
readOnly: true
|
|
118421
|
+
},
|
|
118320
118422
|
headless: {
|
|
118321
118423
|
type: "boolean",
|
|
118322
118424
|
description: "If `true`, runs the browser in headless mode.",
|
|
@@ -118361,6 +118463,15 @@ var init_schemas = __esm({
|
|
|
118361
118463
|
}
|
|
118362
118464
|
}
|
|
118363
118465
|
]
|
|
118466
|
+
},
|
|
118467
|
+
browserFallback: {
|
|
118468
|
+
description: "Per-context override for the config-level [`browserFallback`](config) policy that governs whether a context whose browser can't start a driver session falls back to another available browser. Accepts the same values \u2014 `auto`, `explicit`, `off` \u2014 and, when set, takes precedence over the config-level value for the contexts this entry expands into. Omit it to inherit the config-level policy (which itself defaults to `auto`).",
|
|
118469
|
+
type: "string",
|
|
118470
|
+
enum: [
|
|
118471
|
+
"auto",
|
|
118472
|
+
"explicit",
|
|
118473
|
+
"off"
|
|
118474
|
+
]
|
|
118364
118475
|
}
|
|
118365
118476
|
},
|
|
118366
118477
|
components: {
|
|
@@ -118403,6 +118514,11 @@ var init_schemas = __esm({
|
|
|
118403
118514
|
],
|
|
118404
118515
|
$comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
|
|
118405
118516
|
},
|
|
118517
|
+
explicit: {
|
|
118518
|
+
type: "boolean",
|
|
118519
|
+
description: "Set automatically during context resolution: `true` when the author explicitly requested this browser (as opposed to it being auto-selected as the default). The runner's cross-browser fallback uses it to decide whether substituting another engine reports `PASS` (auto-selected) or `WARNING` (explicitly pinned).",
|
|
118520
|
+
readOnly: true
|
|
118521
|
+
},
|
|
118406
118522
|
headless: {
|
|
118407
118523
|
type: "boolean",
|
|
118408
118524
|
description: "If `true`, runs the browser in headless mode.",
|
|
@@ -118852,6 +118968,11 @@ var init_schemas = __esm({
|
|
|
118852
118968
|
],
|
|
118853
118969
|
$comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
|
|
118854
118970
|
},
|
|
118971
|
+
explicit: {
|
|
118972
|
+
type: "boolean",
|
|
118973
|
+
description: "Set automatically during context resolution: `true` when the author explicitly requested this browser (as opposed to it being auto-selected as the default). The runner's cross-browser fallback uses it to decide whether substituting another engine reports `PASS` (auto-selected) or `WARNING` (explicitly pinned).",
|
|
118974
|
+
readOnly: true
|
|
118975
|
+
},
|
|
118855
118976
|
headless: {
|
|
118856
118977
|
type: "boolean",
|
|
118857
118978
|
description: "If `true`, runs the browser in headless mode.",
|
|
@@ -118926,6 +119047,11 @@ var init_schemas = __esm({
|
|
|
118926
119047
|
],
|
|
118927
119048
|
$comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
|
|
118928
119049
|
},
|
|
119050
|
+
explicit: {
|
|
119051
|
+
type: "boolean",
|
|
119052
|
+
description: "Set automatically during context resolution: `true` when the author explicitly requested this browser (as opposed to it being auto-selected as the default). The runner's cross-browser fallback uses it to decide whether substituting another engine reports `PASS` (auto-selected) or `WARNING` (explicitly pinned).",
|
|
119053
|
+
readOnly: true
|
|
119054
|
+
},
|
|
118929
119055
|
headless: {
|
|
118930
119056
|
type: "boolean",
|
|
118931
119057
|
description: "If `true`, runs the browser in headless mode.",
|
|
@@ -118970,6 +119096,15 @@ var init_schemas = __esm({
|
|
|
118970
119096
|
}
|
|
118971
119097
|
}
|
|
118972
119098
|
]
|
|
119099
|
+
},
|
|
119100
|
+
browserFallback: {
|
|
119101
|
+
description: "Per-context override for the config-level [`browserFallback`](config) policy that governs whether a context whose browser can't start a driver session falls back to another available browser. Accepts the same values \u2014 `auto`, `explicit`, `off` \u2014 and, when set, takes precedence over the config-level value for the contexts this entry expands into. Omit it to inherit the config-level policy (which itself defaults to `auto`).",
|
|
119102
|
+
type: "string",
|
|
119103
|
+
enum: [
|
|
119104
|
+
"auto",
|
|
119105
|
+
"explicit",
|
|
119106
|
+
"off"
|
|
119107
|
+
]
|
|
118973
119108
|
}
|
|
118974
119109
|
},
|
|
118975
119110
|
components: {
|
|
@@ -119012,6 +119147,11 @@ var init_schemas = __esm({
|
|
|
119012
119147
|
],
|
|
119013
119148
|
$comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
|
|
119014
119149
|
},
|
|
119150
|
+
explicit: {
|
|
119151
|
+
type: "boolean",
|
|
119152
|
+
description: "Set automatically during context resolution: `true` when the author explicitly requested this browser (as opposed to it being auto-selected as the default). The runner's cross-browser fallback uses it to decide whether substituting another engine reports `PASS` (auto-selected) or `WARNING` (explicitly pinned).",
|
|
119153
|
+
readOnly: true
|
|
119154
|
+
},
|
|
119015
119155
|
headless: {
|
|
119016
119156
|
type: "boolean",
|
|
119017
119157
|
description: "If `true`, runs the browser in headless mode.",
|
|
@@ -147114,6 +147254,11 @@ var init_schemas = __esm({
|
|
|
147114
147254
|
],
|
|
147115
147255
|
$comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
|
|
147116
147256
|
},
|
|
147257
|
+
explicit: {
|
|
147258
|
+
type: "boolean",
|
|
147259
|
+
description: "Set automatically during context resolution: `true` when the author explicitly requested this browser (as opposed to it being auto-selected as the default). The runner's cross-browser fallback uses it to decide whether substituting another engine reports `PASS` (auto-selected) or `WARNING` (explicitly pinned).",
|
|
147260
|
+
readOnly: true
|
|
147261
|
+
},
|
|
147117
147262
|
headless: {
|
|
147118
147263
|
type: "boolean",
|
|
147119
147264
|
description: "If `true`, runs the browser in headless mode.",
|
|
@@ -175285,6 +175430,11 @@ var init_schemas = __esm({
|
|
|
175285
175430
|
],
|
|
175286
175431
|
$comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
|
|
175287
175432
|
},
|
|
175433
|
+
explicit: {
|
|
175434
|
+
type: "boolean",
|
|
175435
|
+
description: "Set automatically during context resolution: `true` when the author explicitly requested this browser (as opposed to it being auto-selected as the default). The runner's cross-browser fallback uses it to decide whether substituting another engine reports `PASS` (auto-selected) or `WARNING` (explicitly pinned).",
|
|
175436
|
+
readOnly: true
|
|
175437
|
+
},
|
|
175288
175438
|
headless: {
|
|
175289
175439
|
type: "boolean",
|
|
175290
175440
|
description: "If `true`, runs the browser in headless mode.",
|
|
@@ -175359,6 +175509,11 @@ var init_schemas = __esm({
|
|
|
175359
175509
|
],
|
|
175360
175510
|
$comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
|
|
175361
175511
|
},
|
|
175512
|
+
explicit: {
|
|
175513
|
+
type: "boolean",
|
|
175514
|
+
description: "Set automatically during context resolution: `true` when the author explicitly requested this browser (as opposed to it being auto-selected as the default). The runner's cross-browser fallback uses it to decide whether substituting another engine reports `PASS` (auto-selected) or `WARNING` (explicitly pinned).",
|
|
175515
|
+
readOnly: true
|
|
175516
|
+
},
|
|
175362
175517
|
headless: {
|
|
175363
175518
|
type: "boolean",
|
|
175364
175519
|
description: "If `true`, runs the browser in headless mode.",
|
|
@@ -175403,6 +175558,15 @@ var init_schemas = __esm({
|
|
|
175403
175558
|
}
|
|
175404
175559
|
}
|
|
175405
175560
|
]
|
|
175561
|
+
},
|
|
175562
|
+
browserFallback: {
|
|
175563
|
+
description: "Per-context override for the config-level [`browserFallback`](config) policy that governs whether a context whose browser can't start a driver session falls back to another available browser. Accepts the same values \u2014 `auto`, `explicit`, `off` \u2014 and, when set, takes precedence over the config-level value for the contexts this entry expands into. Omit it to inherit the config-level policy (which itself defaults to `auto`).",
|
|
175564
|
+
type: "string",
|
|
175565
|
+
enum: [
|
|
175566
|
+
"auto",
|
|
175567
|
+
"explicit",
|
|
175568
|
+
"off"
|
|
175569
|
+
]
|
|
175406
175570
|
}
|
|
175407
175571
|
},
|
|
175408
175572
|
components: {
|
|
@@ -175445,6 +175609,11 @@ var init_schemas = __esm({
|
|
|
175445
175609
|
],
|
|
175446
175610
|
$comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
|
|
175447
175611
|
},
|
|
175612
|
+
explicit: {
|
|
175613
|
+
type: "boolean",
|
|
175614
|
+
description: "Set automatically during context resolution: `true` when the author explicitly requested this browser (as opposed to it being auto-selected as the default). The runner's cross-browser fallback uses it to decide whether substituting another engine reports `PASS` (auto-selected) or `WARNING` (explicitly pinned).",
|
|
175615
|
+
readOnly: true
|
|
175616
|
+
},
|
|
175448
175617
|
headless: {
|
|
175449
175618
|
type: "boolean",
|
|
175450
175619
|
description: "If `true`, runs the browser in headless mode.",
|
|
@@ -175894,6 +176063,11 @@ var init_schemas = __esm({
|
|
|
175894
176063
|
],
|
|
175895
176064
|
$comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
|
|
175896
176065
|
},
|
|
176066
|
+
explicit: {
|
|
176067
|
+
type: "boolean",
|
|
176068
|
+
description: "Set automatically during context resolution: `true` when the author explicitly requested this browser (as opposed to it being auto-selected as the default). The runner's cross-browser fallback uses it to decide whether substituting another engine reports `PASS` (auto-selected) or `WARNING` (explicitly pinned).",
|
|
176069
|
+
readOnly: true
|
|
176070
|
+
},
|
|
175897
176071
|
headless: {
|
|
175898
176072
|
type: "boolean",
|
|
175899
176073
|
description: "If `true`, runs the browser in headless mode.",
|
|
@@ -175968,6 +176142,11 @@ var init_schemas = __esm({
|
|
|
175968
176142
|
],
|
|
175969
176143
|
$comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
|
|
175970
176144
|
},
|
|
176145
|
+
explicit: {
|
|
176146
|
+
type: "boolean",
|
|
176147
|
+
description: "Set automatically during context resolution: `true` when the author explicitly requested this browser (as opposed to it being auto-selected as the default). The runner's cross-browser fallback uses it to decide whether substituting another engine reports `PASS` (auto-selected) or `WARNING` (explicitly pinned).",
|
|
176148
|
+
readOnly: true
|
|
176149
|
+
},
|
|
175971
176150
|
headless: {
|
|
175972
176151
|
type: "boolean",
|
|
175973
176152
|
description: "If `true`, runs the browser in headless mode.",
|
|
@@ -176012,6 +176191,15 @@ var init_schemas = __esm({
|
|
|
176012
176191
|
}
|
|
176013
176192
|
}
|
|
176014
176193
|
]
|
|
176194
|
+
},
|
|
176195
|
+
browserFallback: {
|
|
176196
|
+
description: "Per-context override for the config-level [`browserFallback`](config) policy that governs whether a context whose browser can't start a driver session falls back to another available browser. Accepts the same values \u2014 `auto`, `explicit`, `off` \u2014 and, when set, takes precedence over the config-level value for the contexts this entry expands into. Omit it to inherit the config-level policy (which itself defaults to `auto`).",
|
|
176197
|
+
type: "string",
|
|
176198
|
+
enum: [
|
|
176199
|
+
"auto",
|
|
176200
|
+
"explicit",
|
|
176201
|
+
"off"
|
|
176202
|
+
]
|
|
176015
176203
|
}
|
|
176016
176204
|
},
|
|
176017
176205
|
components: {
|
|
@@ -176054,6 +176242,11 @@ var init_schemas = __esm({
|
|
|
176054
176242
|
],
|
|
176055
176243
|
$comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
|
|
176056
176244
|
},
|
|
176245
|
+
explicit: {
|
|
176246
|
+
type: "boolean",
|
|
176247
|
+
description: "Set automatically during context resolution: `true` when the author explicitly requested this browser (as opposed to it being auto-selected as the default). The runner's cross-browser fallback uses it to decide whether substituting another engine reports `PASS` (auto-selected) or `WARNING` (explicitly pinned).",
|
|
176248
|
+
readOnly: true
|
|
176249
|
+
},
|
|
176057
176250
|
headless: {
|
|
176058
176251
|
type: "boolean",
|
|
176059
176252
|
description: "If `true`, runs the browser in headless mode.",
|
|
@@ -204156,6 +204349,11 @@ var init_schemas = __esm({
|
|
|
204156
204349
|
],
|
|
204157
204350
|
$comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
|
|
204158
204351
|
},
|
|
204352
|
+
explicit: {
|
|
204353
|
+
type: "boolean",
|
|
204354
|
+
description: "Set automatically during context resolution: `true` when the author explicitly requested this browser (as opposed to it being auto-selected as the default). The runner's cross-browser fallback uses it to decide whether substituting another engine reports `PASS` (auto-selected) or `WARNING` (explicitly pinned).",
|
|
204355
|
+
readOnly: true
|
|
204356
|
+
},
|
|
204159
204357
|
headless: {
|
|
204160
204358
|
type: "boolean",
|
|
204161
204359
|
description: "If `true`, runs the browser in headless mode.",
|
|
@@ -234283,6 +234481,11 @@ var init_schemas = __esm({
|
|
|
234283
234481
|
],
|
|
234284
234482
|
$comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
|
|
234285
234483
|
},
|
|
234484
|
+
explicit: {
|
|
234485
|
+
type: "boolean",
|
|
234486
|
+
description: "Set automatically during context resolution: `true` when the author explicitly requested this browser (as opposed to it being auto-selected as the default). The runner's cross-browser fallback uses it to decide whether substituting another engine reports `PASS` (auto-selected) or `WARNING` (explicitly pinned).",
|
|
234487
|
+
readOnly: true
|
|
234488
|
+
},
|
|
234286
234489
|
headless: {
|
|
234287
234490
|
type: "boolean",
|
|
234288
234491
|
description: "If `true`, runs the browser in headless mode.",
|
|
@@ -234357,6 +234560,11 @@ var init_schemas = __esm({
|
|
|
234357
234560
|
],
|
|
234358
234561
|
$comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
|
|
234359
234562
|
},
|
|
234563
|
+
explicit: {
|
|
234564
|
+
type: "boolean",
|
|
234565
|
+
description: "Set automatically during context resolution: `true` when the author explicitly requested this browser (as opposed to it being auto-selected as the default). The runner's cross-browser fallback uses it to decide whether substituting another engine reports `PASS` (auto-selected) or `WARNING` (explicitly pinned).",
|
|
234566
|
+
readOnly: true
|
|
234567
|
+
},
|
|
234360
234568
|
headless: {
|
|
234361
234569
|
type: "boolean",
|
|
234362
234570
|
description: "If `true`, runs the browser in headless mode.",
|
|
@@ -234401,6 +234609,15 @@ var init_schemas = __esm({
|
|
|
234401
234609
|
}
|
|
234402
234610
|
}
|
|
234403
234611
|
]
|
|
234612
|
+
},
|
|
234613
|
+
browserFallback: {
|
|
234614
|
+
description: "Per-context override for the config-level [`browserFallback`](config) policy that governs whether a context whose browser can't start a driver session falls back to another available browser. Accepts the same values \u2014 `auto`, `explicit`, `off` \u2014 and, when set, takes precedence over the config-level value for the contexts this entry expands into. Omit it to inherit the config-level policy (which itself defaults to `auto`).",
|
|
234615
|
+
type: "string",
|
|
234616
|
+
enum: [
|
|
234617
|
+
"auto",
|
|
234618
|
+
"explicit",
|
|
234619
|
+
"off"
|
|
234620
|
+
]
|
|
234404
234621
|
}
|
|
234405
234622
|
},
|
|
234406
234623
|
components: {
|
|
@@ -234443,6 +234660,11 @@ var init_schemas = __esm({
|
|
|
234443
234660
|
],
|
|
234444
234661
|
$comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
|
|
234445
234662
|
},
|
|
234663
|
+
explicit: {
|
|
234664
|
+
type: "boolean",
|
|
234665
|
+
description: "Set automatically during context resolution: `true` when the author explicitly requested this browser (as opposed to it being auto-selected as the default). The runner's cross-browser fallback uses it to decide whether substituting another engine reports `PASS` (auto-selected) or `WARNING` (explicitly pinned).",
|
|
234666
|
+
readOnly: true
|
|
234667
|
+
},
|
|
234446
234668
|
headless: {
|
|
234447
234669
|
type: "boolean",
|
|
234448
234670
|
description: "If `true`, runs the browser in headless mode.",
|
|
@@ -234892,6 +235114,11 @@ var init_schemas = __esm({
|
|
|
234892
235114
|
],
|
|
234893
235115
|
$comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
|
|
234894
235116
|
},
|
|
235117
|
+
explicit: {
|
|
235118
|
+
type: "boolean",
|
|
235119
|
+
description: "Set automatically during context resolution: `true` when the author explicitly requested this browser (as opposed to it being auto-selected as the default). The runner's cross-browser fallback uses it to decide whether substituting another engine reports `PASS` (auto-selected) or `WARNING` (explicitly pinned).",
|
|
235120
|
+
readOnly: true
|
|
235121
|
+
},
|
|
234895
235122
|
headless: {
|
|
234896
235123
|
type: "boolean",
|
|
234897
235124
|
description: "If `true`, runs the browser in headless mode.",
|
|
@@ -234966,6 +235193,11 @@ var init_schemas = __esm({
|
|
|
234966
235193
|
],
|
|
234967
235194
|
$comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
|
|
234968
235195
|
},
|
|
235196
|
+
explicit: {
|
|
235197
|
+
type: "boolean",
|
|
235198
|
+
description: "Set automatically during context resolution: `true` when the author explicitly requested this browser (as opposed to it being auto-selected as the default). The runner's cross-browser fallback uses it to decide whether substituting another engine reports `PASS` (auto-selected) or `WARNING` (explicitly pinned).",
|
|
235199
|
+
readOnly: true
|
|
235200
|
+
},
|
|
234969
235201
|
headless: {
|
|
234970
235202
|
type: "boolean",
|
|
234971
235203
|
description: "If `true`, runs the browser in headless mode.",
|
|
@@ -235010,6 +235242,15 @@ var init_schemas = __esm({
|
|
|
235010
235242
|
}
|
|
235011
235243
|
}
|
|
235012
235244
|
]
|
|
235245
|
+
},
|
|
235246
|
+
browserFallback: {
|
|
235247
|
+
description: "Per-context override for the config-level [`browserFallback`](config) policy that governs whether a context whose browser can't start a driver session falls back to another available browser. Accepts the same values \u2014 `auto`, `explicit`, `off` \u2014 and, when set, takes precedence over the config-level value for the contexts this entry expands into. Omit it to inherit the config-level policy (which itself defaults to `auto`).",
|
|
235248
|
+
type: "string",
|
|
235249
|
+
enum: [
|
|
235250
|
+
"auto",
|
|
235251
|
+
"explicit",
|
|
235252
|
+
"off"
|
|
235253
|
+
]
|
|
235013
235254
|
}
|
|
235014
235255
|
},
|
|
235015
235256
|
components: {
|
|
@@ -235052,6 +235293,11 @@ var init_schemas = __esm({
|
|
|
235052
235293
|
],
|
|
235053
235294
|
$comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
|
|
235054
235295
|
},
|
|
235296
|
+
explicit: {
|
|
235297
|
+
type: "boolean",
|
|
235298
|
+
description: "Set automatically during context resolution: `true` when the author explicitly requested this browser (as opposed to it being auto-selected as the default). The runner's cross-browser fallback uses it to decide whether substituting another engine reports `PASS` (auto-selected) or `WARNING` (explicitly pinned).",
|
|
235299
|
+
readOnly: true
|
|
235300
|
+
},
|
|
235055
235301
|
headless: {
|
|
235056
235302
|
type: "boolean",
|
|
235057
235303
|
description: "If `true`, runs the browser in headless mode.",
|
|
@@ -263154,6 +263400,11 @@ var init_schemas = __esm({
|
|
|
263154
263400
|
],
|
|
263155
263401
|
$comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
|
|
263156
263402
|
},
|
|
263403
|
+
explicit: {
|
|
263404
|
+
type: "boolean",
|
|
263405
|
+
description: "Set automatically during context resolution: `true` when the author explicitly requested this browser (as opposed to it being auto-selected as the default). The runner's cross-browser fallback uses it to decide whether substituting another engine reports `PASS` (auto-selected) or `WARNING` (explicitly pinned).",
|
|
263406
|
+
readOnly: true
|
|
263407
|
+
},
|
|
263157
263408
|
headless: {
|
|
263158
263409
|
type: "boolean",
|
|
263159
263410
|
description: "If `true`, runs the browser in headless mode.",
|
|
@@ -318224,6 +318475,11 @@ var init_schemas = __esm({
|
|
|
318224
318475
|
],
|
|
318225
318476
|
$comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
|
|
318226
318477
|
},
|
|
318478
|
+
explicit: {
|
|
318479
|
+
type: "boolean",
|
|
318480
|
+
description: "Set automatically during context resolution: `true` when the author explicitly requested this browser (as opposed to it being auto-selected as the default). The runner's cross-browser fallback uses it to decide whether substituting another engine reports `PASS` (auto-selected) or `WARNING` (explicitly pinned).",
|
|
318481
|
+
readOnly: true
|
|
318482
|
+
},
|
|
318227
318483
|
headless: {
|
|
318228
318484
|
type: "boolean",
|
|
318229
318485
|
description: "If `true`, runs the browser in headless mode.",
|
|
@@ -318298,6 +318554,11 @@ var init_schemas = __esm({
|
|
|
318298
318554
|
],
|
|
318299
318555
|
$comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
|
|
318300
318556
|
},
|
|
318557
|
+
explicit: {
|
|
318558
|
+
type: "boolean",
|
|
318559
|
+
description: "Set automatically during context resolution: `true` when the author explicitly requested this browser (as opposed to it being auto-selected as the default). The runner's cross-browser fallback uses it to decide whether substituting another engine reports `PASS` (auto-selected) or `WARNING` (explicitly pinned).",
|
|
318560
|
+
readOnly: true
|
|
318561
|
+
},
|
|
318301
318562
|
headless: {
|
|
318302
318563
|
type: "boolean",
|
|
318303
318564
|
description: "If `true`, runs the browser in headless mode.",
|
|
@@ -318342,6 +318603,15 @@ var init_schemas = __esm({
|
|
|
318342
318603
|
}
|
|
318343
318604
|
}
|
|
318344
318605
|
]
|
|
318606
|
+
},
|
|
318607
|
+
browserFallback: {
|
|
318608
|
+
description: "Per-context override for the config-level [`browserFallback`](config) policy that governs whether a context whose browser can't start a driver session falls back to another available browser. Accepts the same values \u2014 `auto`, `explicit`, `off` \u2014 and, when set, takes precedence over the config-level value for the contexts this entry expands into. Omit it to inherit the config-level policy (which itself defaults to `auto`).",
|
|
318609
|
+
type: "string",
|
|
318610
|
+
enum: [
|
|
318611
|
+
"auto",
|
|
318612
|
+
"explicit",
|
|
318613
|
+
"off"
|
|
318614
|
+
]
|
|
318345
318615
|
}
|
|
318346
318616
|
},
|
|
318347
318617
|
components: {
|
|
@@ -318384,6 +318654,11 @@ var init_schemas = __esm({
|
|
|
318384
318654
|
],
|
|
318385
318655
|
$comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
|
|
318386
318656
|
},
|
|
318657
|
+
explicit: {
|
|
318658
|
+
type: "boolean",
|
|
318659
|
+
description: "Set automatically during context resolution: `true` when the author explicitly requested this browser (as opposed to it being auto-selected as the default). The runner's cross-browser fallback uses it to decide whether substituting another engine reports `PASS` (auto-selected) or `WARNING` (explicitly pinned).",
|
|
318660
|
+
readOnly: true
|
|
318661
|
+
},
|
|
318387
318662
|
headless: {
|
|
318388
318663
|
type: "boolean",
|
|
318389
318664
|
description: "If `true`, runs the browser in headless mode.",
|
|
@@ -346486,6 +346761,11 @@ var init_schemas = __esm({
|
|
|
346486
346761
|
],
|
|
346487
346762
|
$comment: "`safari` is just a shortcut for `webkit`. Included for visibility and to reduce questions."
|
|
346488
346763
|
},
|
|
346764
|
+
explicit: {
|
|
346765
|
+
type: "boolean",
|
|
346766
|
+
description: "Set automatically during context resolution: `true` when the author explicitly requested this browser (as opposed to it being auto-selected as the default). The runner's cross-browser fallback uses it to decide whether substituting another engine reports `PASS` (auto-selected) or `WARNING` (explicitly pinned).",
|
|
346767
|
+
readOnly: true
|
|
346768
|
+
},
|
|
346489
346769
|
headless: {
|
|
346490
346770
|
type: "boolean",
|
|
346491
346771
|
description: "If `true`, runs the browser in headless mode.",
|
|
@@ -385247,16 +385527,410 @@ var init_utils = __esm({
|
|
|
385247
385527
|
}
|
|
385248
385528
|
});
|
|
385249
385529
|
|
|
385530
|
+
// dist/runtime/browsers.js
|
|
385531
|
+
var browsers_exports = {};
|
|
385532
|
+
__export(browsers_exports, {
|
|
385533
|
+
BROWSER_CHANNELS: () => BROWSER_CHANNELS,
|
|
385534
|
+
ensureBrowserInstalled: () => ensureBrowserInstalled,
|
|
385535
|
+
geckodriverBinaryInCache: () => geckodriverBinaryInCache,
|
|
385536
|
+
getInstalledBrowsers: () => getInstalledBrowsers,
|
|
385537
|
+
requiredBrowserAssets: () => requiredBrowserAssets,
|
|
385538
|
+
verifyDriverBinary: () => verifyDriverBinary
|
|
385539
|
+
});
|
|
385540
|
+
function isAllowedDriverPath(binaryPath) {
|
|
385541
|
+
return typeof binaryPath === "string" && import_node_path5.default.isAbsolute(binaryPath) && ALLOWED_DRIVER_PATH.test(binaryPath);
|
|
385542
|
+
}
|
|
385543
|
+
function parseDriverVersion(output) {
|
|
385544
|
+
const m = output.match(/(\d+\.\d+(?:\.\d+)*)/);
|
|
385545
|
+
return m ? m[1] : void 0;
|
|
385546
|
+
}
|
|
385547
|
+
async function verifyDriverBinary(driverName, binaryPath, options = {}) {
|
|
385548
|
+
if (!binaryPath || typeof binaryPath !== "string") {
|
|
385549
|
+
return { ok: false, error: "No driver binary path to verify." };
|
|
385550
|
+
}
|
|
385551
|
+
if (!isAllowedDriverPath(binaryPath)) {
|
|
385552
|
+
return {
|
|
385553
|
+
ok: false,
|
|
385554
|
+
error: `Refusing to execute '${binaryPath}': not a recognized driver binary path.`
|
|
385555
|
+
};
|
|
385556
|
+
}
|
|
385557
|
+
const key = String(driverName ?? "").toLowerCase();
|
|
385558
|
+
const args = DRIVER_VERSION_ARGS[key] ?? ["--version"];
|
|
385559
|
+
const exec = options.exec ?? defaultDriverExec;
|
|
385560
|
+
const timeoutMs = options.timeoutMs ?? DRIVER_VERIFY_TIMEOUT_MS;
|
|
385561
|
+
let res;
|
|
385562
|
+
try {
|
|
385563
|
+
res = await exec(binaryPath, args, timeoutMs);
|
|
385564
|
+
} catch (err) {
|
|
385565
|
+
return { ok: false, error: `Failed to execute ${driverName}: ${String(err)}` };
|
|
385566
|
+
}
|
|
385567
|
+
if (res.code === null) {
|
|
385568
|
+
const detail = (res.stderr || res.stdout || "").trim();
|
|
385569
|
+
return {
|
|
385570
|
+
ok: false,
|
|
385571
|
+
error: `${driverName} could not be executed (spawn failed)${detail ? `: ${detail}` : ""}`
|
|
385572
|
+
};
|
|
385573
|
+
}
|
|
385574
|
+
if (res.code !== 0) {
|
|
385575
|
+
const detail = (res.stderr || res.stdout || "").trim();
|
|
385576
|
+
return {
|
|
385577
|
+
ok: false,
|
|
385578
|
+
error: `${driverName} exited with code ${res.code}${detail ? `: ${detail}` : ""}`
|
|
385579
|
+
};
|
|
385580
|
+
}
|
|
385581
|
+
const version = parseDriverVersion(`${res.stdout}
|
|
385582
|
+
${res.stderr}`);
|
|
385583
|
+
if (!version) {
|
|
385584
|
+
return {
|
|
385585
|
+
ok: false,
|
|
385586
|
+
error: `${driverName} ran but did not report a parseable version (likely a partial or corrupt download).`
|
|
385587
|
+
};
|
|
385588
|
+
}
|
|
385589
|
+
return { ok: true, version };
|
|
385590
|
+
}
|
|
385591
|
+
function requiredBrowserAssets(name) {
|
|
385592
|
+
switch ((name ?? "").toLowerCase()) {
|
|
385593
|
+
case "chrome":
|
|
385594
|
+
return ["chrome", "chromedriver"];
|
|
385595
|
+
case "firefox":
|
|
385596
|
+
return ["firefox", "geckodriver"];
|
|
385597
|
+
default:
|
|
385598
|
+
return [];
|
|
385599
|
+
}
|
|
385600
|
+
}
|
|
385601
|
+
async function loadPuppeteerBrowsers(deps, ctx) {
|
|
385602
|
+
if (deps.browsersModule)
|
|
385603
|
+
return deps.browsersModule;
|
|
385604
|
+
return await loadHeavyDep("@puppeteer/browsers", { ctx, deps: { logger: deps.logger } });
|
|
385605
|
+
}
|
|
385606
|
+
async function loadGeckodriver(deps, ctx) {
|
|
385607
|
+
if (deps.geckodriverModule)
|
|
385608
|
+
return deps.geckodriverModule;
|
|
385609
|
+
return await loadHeavyDep("geckodriver", { ctx, deps: { logger: deps.logger } });
|
|
385610
|
+
}
|
|
385611
|
+
function isStillFresh(latestCheckedAt, now) {
|
|
385612
|
+
if (!latestCheckedAt)
|
|
385613
|
+
return false;
|
|
385614
|
+
const t = Date.parse(latestCheckedAt);
|
|
385615
|
+
if (Number.isNaN(t))
|
|
385616
|
+
return false;
|
|
385617
|
+
return now.getTime() - t < FRESHNESS_TTL_MS;
|
|
385618
|
+
}
|
|
385619
|
+
async function resolveChannelBuildId(browsersModule, assetName, platform) {
|
|
385620
|
+
const channel = BROWSER_CHANNELS[assetName];
|
|
385621
|
+
return await withTimeout(Promise.resolve(browsersModule.resolveBuildId(assetName, platform, channel)), RESOLVE_TIMEOUT_MS, `resolveBuildId(${assetName}, ${channel})`);
|
|
385622
|
+
}
|
|
385623
|
+
function withTimeout(p, ms, label) {
|
|
385624
|
+
return new Promise((resolve, reject) => {
|
|
385625
|
+
const t = setTimeout(() => reject(new Error(`${label} timed out after ${ms}ms`)), ms);
|
|
385626
|
+
if (typeof t.unref === "function")
|
|
385627
|
+
t.unref();
|
|
385628
|
+
p.then((v) => {
|
|
385629
|
+
clearTimeout(t);
|
|
385630
|
+
resolve(v);
|
|
385631
|
+
}, (err) => {
|
|
385632
|
+
clearTimeout(t);
|
|
385633
|
+
reject(err);
|
|
385634
|
+
});
|
|
385635
|
+
});
|
|
385636
|
+
}
|
|
385637
|
+
async function ensureBrowserInstalled(name, options = {}) {
|
|
385638
|
+
const { ctx = {} } = options;
|
|
385639
|
+
const dedupeKey = `${name}:${getBrowsersDir(ctx)}`;
|
|
385640
|
+
const inFlight = inFlightInstalls.get(dedupeKey);
|
|
385641
|
+
if (inFlight)
|
|
385642
|
+
return inFlight;
|
|
385643
|
+
const p = ensureBrowserInstalledImpl(name, options).finally(() => {
|
|
385644
|
+
inFlightInstalls.delete(dedupeKey);
|
|
385645
|
+
});
|
|
385646
|
+
inFlightInstalls.set(dedupeKey, p);
|
|
385647
|
+
return p;
|
|
385648
|
+
}
|
|
385649
|
+
async function ensureBrowserInstalledImpl(name, options = {}) {
|
|
385650
|
+
const { ctx = {}, deps = {}, force = false } = options;
|
|
385651
|
+
const logger = deps.logger ?? defaultLogger2;
|
|
385652
|
+
const now = (deps.now ?? (() => /* @__PURE__ */ new Date()))();
|
|
385653
|
+
const cacheDir = getBrowsersDir(ctx);
|
|
385654
|
+
const record = readInstalledRecord(ctx);
|
|
385655
|
+
const existing = record.browsers[name];
|
|
385656
|
+
if (name === "geckodriver") {
|
|
385657
|
+
return await ensureGeckodriver({ ctx, deps: { ...deps, logger }, force, now }, record, existing, logger);
|
|
385658
|
+
}
|
|
385659
|
+
const browsersModule = await loadPuppeteerBrowsers(deps, ctx);
|
|
385660
|
+
const platform = browsersModule.detectBrowserPlatform ? browsersModule.detectBrowserPlatform() : void 0;
|
|
385661
|
+
if (!platform) {
|
|
385662
|
+
throw new Error(`Unable to determine browser platform on ${process.platform}/${process.arch}. @puppeteer/browsers does not recognize this OS; ${name} cannot be installed automatically. Set DOC_DETECTIVE_CACHE_DIR to a pre-warmed cache or skip browser-driven steps.`);
|
|
385663
|
+
}
|
|
385664
|
+
if (!force && existing && isStillFresh(existing.latestCheckedAt, now)) {
|
|
385665
|
+
if (existing.latestKnownVersion === existing.installedVersion) {
|
|
385666
|
+
const path24 = await locateExecutable(browsersModule, name, existing.installedVersion, cacheDir, platform);
|
|
385667
|
+
return { path: path24, version: existing.installedVersion, outdated: false };
|
|
385668
|
+
}
|
|
385669
|
+
const path23 = await locateExecutable(browsersModule, name, existing.installedVersion, cacheDir, platform);
|
|
385670
|
+
logger(`${name} ${existing.installedVersion} installed in ${cacheDir}; "${BROWSER_CHANNELS[name]}" channel is now ${existing.latestKnownVersion}. Run \`doc-detective install browsers ${name} --force\` to update.`, "warn");
|
|
385671
|
+
return { path: path23, version: existing.installedVersion, outdated: true };
|
|
385672
|
+
}
|
|
385673
|
+
let latest;
|
|
385674
|
+
try {
|
|
385675
|
+
latest = await resolveChannelBuildId(browsersModule, name, platform);
|
|
385676
|
+
} catch (err) {
|
|
385677
|
+
logger(`Channel resolution for ${name} skipped: ${String(err)}`, "debug");
|
|
385678
|
+
if (existing) {
|
|
385679
|
+
const path23 = await locateExecutable(browsersModule, name, existing.installedVersion, cacheDir, platform);
|
|
385680
|
+
return { path: path23, version: existing.installedVersion, outdated: false };
|
|
385681
|
+
}
|
|
385682
|
+
throw err;
|
|
385683
|
+
}
|
|
385684
|
+
if (!force && existing && existing.installedVersion === latest) {
|
|
385685
|
+
record.browsers[name] = {
|
|
385686
|
+
...existing,
|
|
385687
|
+
latestKnownVersion: latest,
|
|
385688
|
+
latestCheckedAt: now.toISOString()
|
|
385689
|
+
};
|
|
385690
|
+
writeInstalledRecord(record, ctx);
|
|
385691
|
+
const path23 = await locateExecutable(browsersModule, name, latest, cacheDir, platform);
|
|
385692
|
+
return { path: path23, version: latest, outdated: false };
|
|
385693
|
+
}
|
|
385694
|
+
if (!force && existing) {
|
|
385695
|
+
record.browsers[name] = {
|
|
385696
|
+
...existing,
|
|
385697
|
+
latestKnownVersion: latest,
|
|
385698
|
+
latestCheckedAt: now.toISOString()
|
|
385699
|
+
};
|
|
385700
|
+
writeInstalledRecord(record, ctx);
|
|
385701
|
+
logger(`${name} ${existing.installedVersion} installed in ${cacheDir}; "${BROWSER_CHANNELS[name]}" channel is now ${latest}. Run \`doc-detective install browsers ${name} --force\` to update.`, "warn");
|
|
385702
|
+
const path23 = await locateExecutable(browsersModule, name, existing.installedVersion, cacheDir, platform);
|
|
385703
|
+
return { path: path23, version: existing.installedVersion, outdated: true };
|
|
385704
|
+
}
|
|
385705
|
+
logger(`Installing ${name} ${latest} into ${cacheDir}`, "info");
|
|
385706
|
+
await browsersModule.install({
|
|
385707
|
+
browser: name,
|
|
385708
|
+
buildId: latest,
|
|
385709
|
+
cacheDir
|
|
385710
|
+
});
|
|
385711
|
+
let path22 = await locateExecutable(browsersModule, name, latest, cacheDir, platform);
|
|
385712
|
+
if (name === "chromedriver" && isAllowedDriverPath(path22)) {
|
|
385713
|
+
let verify = await verifyDriverBinary(name, path22, { exec: deps.verifyExec });
|
|
385714
|
+
if (!verify.ok) {
|
|
385715
|
+
logger(`Installed ${name} ${latest} failed validation (${verify.error}); reinstalling once.`, "warn");
|
|
385716
|
+
try {
|
|
385717
|
+
await browsersModule.uninstall({ browser: name, buildId: latest, cacheDir });
|
|
385718
|
+
} catch (err) {
|
|
385719
|
+
logger(`Failed to prune broken ${name} ${latest}: ${String(err)}`, "debug");
|
|
385720
|
+
}
|
|
385721
|
+
await browsersModule.install({ browser: name, buildId: latest, cacheDir });
|
|
385722
|
+
path22 = await locateExecutable(browsersModule, name, latest, cacheDir, platform);
|
|
385723
|
+
verify = await verifyDriverBinary(name, path22, { exec: deps.verifyExec });
|
|
385724
|
+
}
|
|
385725
|
+
if (!verify.ok) {
|
|
385726
|
+
throw new Error(`${name} ${latest} is present but non-functional after a reinstall (${verify.error}). It may be a partial or corrupt download; delete ${path22} or reinstall.`);
|
|
385727
|
+
}
|
|
385728
|
+
}
|
|
385729
|
+
if (existing && existing.installedVersion !== latest) {
|
|
385730
|
+
try {
|
|
385731
|
+
await browsersModule.uninstall({
|
|
385732
|
+
browser: name,
|
|
385733
|
+
buildId: existing.installedVersion,
|
|
385734
|
+
cacheDir
|
|
385735
|
+
});
|
|
385736
|
+
} catch (err) {
|
|
385737
|
+
logger(`Failed to prune old ${name} buildId ${existing.installedVersion}: ${String(err)}`, "debug");
|
|
385738
|
+
}
|
|
385739
|
+
}
|
|
385740
|
+
record.browsers[name] = {
|
|
385741
|
+
installedVersion: latest,
|
|
385742
|
+
installedAt: now.toISOString(),
|
|
385743
|
+
latestKnownVersion: latest,
|
|
385744
|
+
latestCheckedAt: now.toISOString()
|
|
385745
|
+
};
|
|
385746
|
+
writeInstalledRecord(record, ctx);
|
|
385747
|
+
return { path: path22, version: latest, outdated: false };
|
|
385748
|
+
}
|
|
385749
|
+
async function locateExecutable(browsersModule, name, buildId, cacheDir, platform) {
|
|
385750
|
+
if (typeof browsersModule.computeExecutablePath === "function") {
|
|
385751
|
+
return browsersModule.computeExecutablePath({
|
|
385752
|
+
browser: name,
|
|
385753
|
+
buildId,
|
|
385754
|
+
cacheDir,
|
|
385755
|
+
platform
|
|
385756
|
+
});
|
|
385757
|
+
}
|
|
385758
|
+
return cacheDir;
|
|
385759
|
+
}
|
|
385760
|
+
async function withGeckodriverCacheDir(cacheDir, fn) {
|
|
385761
|
+
const previous = geckodriverEnvQueue;
|
|
385762
|
+
let release;
|
|
385763
|
+
geckodriverEnvQueue = new Promise((resolve) => {
|
|
385764
|
+
release = resolve;
|
|
385765
|
+
});
|
|
385766
|
+
await previous;
|
|
385767
|
+
const prevEnv = process.env.GECKODRIVER_CACHE_DIR;
|
|
385768
|
+
process.env.GECKODRIVER_CACHE_DIR = cacheDir;
|
|
385769
|
+
try {
|
|
385770
|
+
return await fn();
|
|
385771
|
+
} finally {
|
|
385772
|
+
if (prevEnv === void 0)
|
|
385773
|
+
delete process.env.GECKODRIVER_CACHE_DIR;
|
|
385774
|
+
else
|
|
385775
|
+
process.env.GECKODRIVER_CACHE_DIR = prevEnv;
|
|
385776
|
+
release();
|
|
385777
|
+
}
|
|
385778
|
+
}
|
|
385779
|
+
function isPathInside(child, dir) {
|
|
385780
|
+
const resolvedDir = import_node_path5.default.resolve(dir);
|
|
385781
|
+
const resolvedChild = import_node_path5.default.resolve(child);
|
|
385782
|
+
return resolvedChild === resolvedDir || resolvedChild.startsWith(resolvedDir + import_node_path5.default.sep);
|
|
385783
|
+
}
|
|
385784
|
+
function geckodriverBinaryInCache(cacheDir) {
|
|
385785
|
+
const binName = process.platform === "win32" ? "geckodriver.exe" : "geckodriver";
|
|
385786
|
+
const rootCandidate = import_node_path5.default.join(cacheDir, binName);
|
|
385787
|
+
try {
|
|
385788
|
+
if (import_node_fs5.default.existsSync(rootCandidate))
|
|
385789
|
+
return rootCandidate;
|
|
385790
|
+
} catch {
|
|
385791
|
+
}
|
|
385792
|
+
try {
|
|
385793
|
+
for (const entry of import_node_fs5.default.readdirSync(cacheDir, { withFileTypes: true })) {
|
|
385794
|
+
if (!entry.isDirectory())
|
|
385795
|
+
continue;
|
|
385796
|
+
const nested = import_node_path5.default.join(cacheDir, entry.name, binName);
|
|
385797
|
+
if (import_node_fs5.default.existsSync(nested))
|
|
385798
|
+
return nested;
|
|
385799
|
+
}
|
|
385800
|
+
} catch {
|
|
385801
|
+
}
|
|
385802
|
+
return void 0;
|
|
385803
|
+
}
|
|
385804
|
+
async function ensureGeckodriver(ctxBag, record, existing, logger) {
|
|
385805
|
+
const cacheDir = getBrowsersDir(ctxBag.ctx);
|
|
385806
|
+
const resolveBinaryPath = (gecko) => {
|
|
385807
|
+
const fromModule = gecko && typeof gecko.path === "string" && gecko.path.length > 0 && isPathInside(gecko.path, cacheDir) ? gecko.path : null;
|
|
385808
|
+
return fromModule ?? geckodriverBinaryInCache(cacheDir) ?? cacheDir;
|
|
385809
|
+
};
|
|
385810
|
+
if (!ctxBag.force && existing && isStillFresh(existing.latestCheckedAt, ctxBag.now)) {
|
|
385811
|
+
const gecko = ctxBag.deps.geckodriverModule ?? await withGeckodriverCacheDir(cacheDir, () => loadGeckodriver(ctxBag.deps, ctxBag.ctx));
|
|
385812
|
+
return {
|
|
385813
|
+
path: resolveBinaryPath(gecko),
|
|
385814
|
+
version: existing.installedVersion,
|
|
385815
|
+
// Only mark outdated when we actually KNOW a different latest
|
|
385816
|
+
// version exists. If `latestKnownVersion` is undefined (e.g., the
|
|
385817
|
+
// installer couldn't determine a version and we recorded
|
|
385818
|
+
// `installedVersion: "unknown"` with `latestKnownVersion: undefined`),
|
|
385819
|
+
// a naive `!==` comparison would flag the entry as outdated on
|
|
385820
|
+
// every run.
|
|
385821
|
+
outdated: typeof existing.latestKnownVersion === "string" && existing.latestKnownVersion.length > 0 && existing.latestKnownVersion !== existing.installedVersion
|
|
385822
|
+
};
|
|
385823
|
+
}
|
|
385824
|
+
return await withGeckodriverCacheDir(cacheDir, async () => {
|
|
385825
|
+
const gecko = await loadGeckodriver(ctxBag.deps, ctxBag.ctx);
|
|
385826
|
+
logger(`Installing geckodriver into ${cacheDir}`, "info");
|
|
385827
|
+
await gecko.download();
|
|
385828
|
+
let binaryPath = resolveBinaryPath(gecko);
|
|
385829
|
+
let verify = await verifyDriverBinary("geckodriver", binaryPath, {
|
|
385830
|
+
exec: ctxBag.deps.verifyExec
|
|
385831
|
+
});
|
|
385832
|
+
if (!verify.ok) {
|
|
385833
|
+
logger(`Downloaded geckodriver failed validation (${verify.error}); re-downloading once.`, "warn");
|
|
385834
|
+
try {
|
|
385835
|
+
if (binaryPath && binaryPath !== cacheDir && import_node_fs5.default.existsSync(binaryPath)) {
|
|
385836
|
+
import_node_fs5.default.rmSync(binaryPath, { force: true });
|
|
385837
|
+
}
|
|
385838
|
+
} catch {
|
|
385839
|
+
}
|
|
385840
|
+
await gecko.download();
|
|
385841
|
+
binaryPath = resolveBinaryPath(gecko);
|
|
385842
|
+
verify = await verifyDriverBinary("geckodriver", binaryPath, {
|
|
385843
|
+
exec: ctxBag.deps.verifyExec
|
|
385844
|
+
});
|
|
385845
|
+
}
|
|
385846
|
+
if (!verify.ok) {
|
|
385847
|
+
throw new Error(`geckodriver is present but non-functional after a re-download (${verify.error}). It may be a partial or corrupt download; delete ${binaryPath} or reinstall.`);
|
|
385848
|
+
}
|
|
385849
|
+
const installedVersion = verify.version;
|
|
385850
|
+
record.browsers.geckodriver = {
|
|
385851
|
+
installedVersion,
|
|
385852
|
+
installedAt: ctxBag.now.toISOString(),
|
|
385853
|
+
latestKnownVersion: installedVersion,
|
|
385854
|
+
latestCheckedAt: ctxBag.now.toISOString()
|
|
385855
|
+
};
|
|
385856
|
+
writeInstalledRecord(record, ctxBag.ctx);
|
|
385857
|
+
return {
|
|
385858
|
+
path: binaryPath,
|
|
385859
|
+
version: installedVersion,
|
|
385860
|
+
outdated: false
|
|
385861
|
+
};
|
|
385862
|
+
});
|
|
385863
|
+
}
|
|
385864
|
+
function getInstalledBrowsers(ctx = {}) {
|
|
385865
|
+
const record = readInstalledRecord(ctx);
|
|
385866
|
+
return Object.entries(record.browsers).map(([name, entry]) => ({
|
|
385867
|
+
name,
|
|
385868
|
+
installedVersion: entry.installedVersion,
|
|
385869
|
+
installedAt: entry.installedAt,
|
|
385870
|
+
latestKnownVersion: entry.latestKnownVersion
|
|
385871
|
+
}));
|
|
385872
|
+
}
|
|
385873
|
+
var import_node_child_process3, import_node_fs5, import_node_path5, DRIVER_VERSION_ARGS, DRIVER_VERIFY_TIMEOUT_MS, ALLOWED_DRIVER_PATH, defaultDriverExec, BROWSER_CHANNELS, FRESHNESS_TTL_MS, RESOLVE_TIMEOUT_MS, RUNTIME_DEBUG2, defaultLogger2, inFlightInstalls, geckodriverEnvQueue;
|
|
385874
|
+
var init_browsers = __esm({
|
|
385875
|
+
"dist/runtime/browsers.js"() {
|
|
385876
|
+
"use strict";
|
|
385877
|
+
import_node_child_process3 = require("node:child_process");
|
|
385878
|
+
import_node_fs5 = __toESM(require("node:fs"), 1);
|
|
385879
|
+
import_node_path5 = __toESM(require("node:path"), 1);
|
|
385880
|
+
init_cacheDir();
|
|
385881
|
+
init_loader();
|
|
385882
|
+
DRIVER_VERSION_ARGS = {
|
|
385883
|
+
geckodriver: ["--version"],
|
|
385884
|
+
chromedriver: ["--version"],
|
|
385885
|
+
safaridriver: ["--version"]
|
|
385886
|
+
};
|
|
385887
|
+
DRIVER_VERIFY_TIMEOUT_MS = 1e4;
|
|
385888
|
+
ALLOWED_DRIVER_PATH = /[\\/](?:geckodriver|chromedriver|safaridriver)(?:\.exe)?$/i;
|
|
385889
|
+
defaultDriverExec = (binaryPath, args, timeoutMs) => new Promise((resolve) => {
|
|
385890
|
+
(0, import_node_child_process3.execFile)(binaryPath, args, { timeout: timeoutMs, windowsHide: true }, (err, stdout, stderr) => {
|
|
385891
|
+
const out = typeof stdout === "string" ? stdout : String(stdout ?? "");
|
|
385892
|
+
const errOut = typeof stderr === "string" ? stderr : String(stderr ?? "");
|
|
385893
|
+
if (err && typeof err.code === "number") {
|
|
385894
|
+
resolve({ code: err.code, stdout: out, stderr: errOut });
|
|
385895
|
+
} else if (err) {
|
|
385896
|
+
resolve({ code: null, stdout: out, stderr: `${errOut}${String(err)}` });
|
|
385897
|
+
} else {
|
|
385898
|
+
resolve({ code: 0, stdout: out, stderr: errOut });
|
|
385899
|
+
}
|
|
385900
|
+
});
|
|
385901
|
+
});
|
|
385902
|
+
BROWSER_CHANNELS = {
|
|
385903
|
+
chrome: "stable",
|
|
385904
|
+
firefox: "latest",
|
|
385905
|
+
chromedriver: "stable",
|
|
385906
|
+
geckodriver: "latest"
|
|
385907
|
+
};
|
|
385908
|
+
FRESHNESS_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
385909
|
+
RESOLVE_TIMEOUT_MS = 5e3;
|
|
385910
|
+
RUNTIME_DEBUG2 = process.env.DOC_DETECTIVE_RUNTIME_DEBUG === "1";
|
|
385911
|
+
defaultLogger2 = (msg, level = "info") => {
|
|
385912
|
+
if (level === "debug" && !RUNTIME_DEBUG2)
|
|
385913
|
+
return;
|
|
385914
|
+
if (level === "error")
|
|
385915
|
+
console.error(msg);
|
|
385916
|
+
else
|
|
385917
|
+
console.log(msg);
|
|
385918
|
+
};
|
|
385919
|
+
inFlightInstalls = /* @__PURE__ */ new Map();
|
|
385920
|
+
geckodriverEnvQueue = Promise.resolve();
|
|
385921
|
+
}
|
|
385922
|
+
});
|
|
385923
|
+
|
|
385250
385924
|
// dist/core/appium.js
|
|
385251
385925
|
function appiumHomeForDriverPath(driverEntry) {
|
|
385252
|
-
const marker = `${
|
|
385926
|
+
const marker = `${import_node_path6.default.sep}node_modules${import_node_path6.default.sep}`;
|
|
385253
385927
|
const idx = driverEntry.lastIndexOf(marker);
|
|
385254
385928
|
return idx === -1 ? null : driverEntry.slice(0, idx);
|
|
385255
385929
|
}
|
|
385256
385930
|
function setAppiumHome(ctx = {}) {
|
|
385257
|
-
const runtimeAppium =
|
|
385258
|
-
if ((0,
|
|
385259
|
-
process.env.APPIUM_HOME =
|
|
385931
|
+
const runtimeAppium = import_node_path6.default.join(getRuntimeDir({ cacheDir: ctx.cacheDir }), "node_modules", "appium");
|
|
385932
|
+
if ((0, import_node_fs6.existsSync)(runtimeAppium)) {
|
|
385933
|
+
process.env.APPIUM_HOME = import_node_path6.default.join(getRuntimeDir({ cacheDir: ctx.cacheDir }));
|
|
385260
385934
|
return;
|
|
385261
385935
|
}
|
|
385262
385936
|
for (const driverName of ["appium-chromium-driver", "appium-geckodriver"]) {
|
|
@@ -385269,27 +385943,27 @@ function setAppiumHome(ctx = {}) {
|
|
|
385269
385943
|
return;
|
|
385270
385944
|
}
|
|
385271
385945
|
}
|
|
385272
|
-
const corePath =
|
|
385946
|
+
const corePath = import_node_path6.default.join(__dirname3, "../../node_modules");
|
|
385273
385947
|
const pathArray = corePath.split("node_modules");
|
|
385274
385948
|
let appiumParentPath = pathArray[0];
|
|
385275
385949
|
for (let i = 1; i < pathArray.length; i++) {
|
|
385276
|
-
if ((0,
|
|
385950
|
+
if ((0, import_node_fs6.existsSync)(import_node_path6.default.join(appiumParentPath, "node_modules", "appium"))) {
|
|
385277
385951
|
break;
|
|
385278
385952
|
}
|
|
385279
|
-
appiumParentPath =
|
|
385953
|
+
appiumParentPath = import_node_path6.default.join(appiumParentPath, "node_modules", pathArray[i]);
|
|
385280
385954
|
}
|
|
385281
385955
|
process.env.APPIUM_HOME = appiumParentPath;
|
|
385282
385956
|
}
|
|
385283
|
-
var
|
|
385957
|
+
var import_node_path6, import_node_fs6, import_node_url4, __dirname3;
|
|
385284
385958
|
var init_appium = __esm({
|
|
385285
385959
|
"dist/core/appium.js"() {
|
|
385286
385960
|
"use strict";
|
|
385287
|
-
|
|
385288
|
-
|
|
385961
|
+
import_node_path6 = __toESM(require("node:path"), 1);
|
|
385962
|
+
import_node_fs6 = require("node:fs");
|
|
385289
385963
|
import_node_url4 = require("node:url");
|
|
385290
385964
|
init_cacheDir();
|
|
385291
385965
|
init_loader();
|
|
385292
|
-
__dirname3 =
|
|
385966
|
+
__dirname3 = import_node_path6.default.dirname((0, import_node_url4.fileURLToPath)(importMetaUrl));
|
|
385293
385967
|
}
|
|
385294
385968
|
});
|
|
385295
385969
|
|
|
@@ -385321,7 +385995,7 @@ async function readFile({ fileURLOrPath }) {
|
|
|
385321
385995
|
}
|
|
385322
385996
|
} else {
|
|
385323
385997
|
try {
|
|
385324
|
-
content = await
|
|
385998
|
+
content = await import_node_fs7.default.promises.readFile(fileURLOrPath, "utf8");
|
|
385325
385999
|
} catch (error) {
|
|
385326
386000
|
if (error.code === "ENOENT") {
|
|
385327
386001
|
console.warn(`File not found: ${fileURLOrPath}`);
|
|
@@ -385392,13 +386066,13 @@ async function resolvePaths({ config, object, filePath, nested = false, objectTy
|
|
|
385392
386066
|
if (relativePath.startsWith("https://") || relativePath.startsWith("http://") || relativePath.startsWith("heretto:")) {
|
|
385393
386067
|
return relativePath;
|
|
385394
386068
|
}
|
|
385395
|
-
if (
|
|
386069
|
+
if (import_node_path7.default.isAbsolute(relativePath)) {
|
|
385396
386070
|
return relativePath;
|
|
385397
386071
|
}
|
|
385398
|
-
const fileExists =
|
|
385399
|
-
const isFile = fileExists ?
|
|
385400
|
-
const basePath = isFile ?
|
|
385401
|
-
return baseType === "file" ?
|
|
386072
|
+
const fileExists = import_node_fs7.default.existsSync(filePath2);
|
|
386073
|
+
const isFile = fileExists ? import_node_fs7.default.lstatSync(filePath2).isFile() : import_node_path7.default.parse(filePath2).ext !== "";
|
|
386074
|
+
const basePath = isFile ? import_node_path7.default.dirname(filePath2) : filePath2;
|
|
386075
|
+
return baseType === "file" ? import_node_path7.default.resolve(basePath, relativePath) : import_node_path7.default.resolve(relativePath);
|
|
385402
386076
|
}
|
|
385403
386077
|
const relativePathBase = config.relativePathBase;
|
|
385404
386078
|
let pathProperties;
|
|
@@ -385447,7 +386121,7 @@ async function resolvePaths({ config, object, filePath, nested = false, objectTy
|
|
|
385447
386121
|
objectType
|
|
385448
386122
|
});
|
|
385449
386123
|
} else if (typeof item === "string" && pathProperties.includes(property)) {
|
|
385450
|
-
const resolved = property === "path" && object.directory &&
|
|
386124
|
+
const resolved = property === "path" && object.directory && import_node_path7.default.isAbsolute(object.directory) ? resolve(relativePathBase, item, object.directory) : resolve(relativePathBase, item, filePath);
|
|
385451
386125
|
object[property][i] = resolved;
|
|
385452
386126
|
}
|
|
385453
386127
|
}
|
|
@@ -385465,7 +386139,7 @@ async function resolvePaths({ config, object, filePath, nested = false, objectTy
|
|
|
385465
386139
|
}
|
|
385466
386140
|
if (pathProperties.includes(property)) {
|
|
385467
386141
|
if (property === "path" && object.directory) {
|
|
385468
|
-
const directory =
|
|
386142
|
+
const directory = import_node_path7.default.isAbsolute(object.directory) ? object.directory : resolve(relativePathBase, object.directory, filePath);
|
|
385469
386143
|
object[property] = resolve(relativePathBase, object[property], directory);
|
|
385470
386144
|
} else {
|
|
385471
386145
|
object[property] = resolve(relativePathBase, object[property], filePath);
|
|
@@ -385475,12 +386149,12 @@ async function resolvePaths({ config, object, filePath, nested = false, objectTy
|
|
|
385475
386149
|
}
|
|
385476
386150
|
return object;
|
|
385477
386151
|
}
|
|
385478
|
-
var
|
|
386152
|
+
var import_node_fs7, import_node_path7, import_yaml, import_axios2;
|
|
385479
386153
|
var init_files = __esm({
|
|
385480
386154
|
"dist/core/files.js"() {
|
|
385481
386155
|
"use strict";
|
|
385482
|
-
|
|
385483
|
-
|
|
386156
|
+
import_node_fs7 = __toESM(require("node:fs"), 1);
|
|
386157
|
+
import_node_path7 = __toESM(require("node:path"), 1);
|
|
385484
386158
|
import_yaml = __toESM(require("yaml"), 1);
|
|
385485
386159
|
import_axios2 = __toESM(require("axios"), 1);
|
|
385486
386160
|
init_validate();
|
|
@@ -385503,10 +386177,10 @@ async function getOperation(definition = {}, operationId = "", responseCode = ""
|
|
|
385503
386177
|
if (!operationId) {
|
|
385504
386178
|
throw new Error("OperationId is required.");
|
|
385505
386179
|
}
|
|
385506
|
-
for (const
|
|
385507
|
-
for (const method in definition.paths[
|
|
385508
|
-
if (definition.paths[
|
|
385509
|
-
const operation = definition.paths[
|
|
386180
|
+
for (const path22 in definition.paths) {
|
|
386181
|
+
for (const method in definition.paths[path22]) {
|
|
386182
|
+
if (definition.paths[path22][method].operationId === operationId) {
|
|
386183
|
+
const operation = definition.paths[path22][method];
|
|
385510
386184
|
if (!server) {
|
|
385511
386185
|
if (definition.servers && definition.servers.length > 0) {
|
|
385512
386186
|
server = definition.servers[0].url;
|
|
@@ -385514,9 +386188,9 @@ async function getOperation(definition = {}, operationId = "", responseCode = ""
|
|
|
385514
386188
|
throw new Error("No server URL provided and no servers defined in the OpenAPI definition.");
|
|
385515
386189
|
}
|
|
385516
386190
|
}
|
|
385517
|
-
const example = await compileExample(operation, server +
|
|
386191
|
+
const example = await compileExample(operation, server + path22, responseCode, exampleKey);
|
|
385518
386192
|
const schemas2 = getSchemas(operation, responseCode);
|
|
385519
|
-
return { path:
|
|
386193
|
+
return { path: path22, method, definition: operation, schemas: schemas2, example };
|
|
385520
386194
|
}
|
|
385521
386195
|
}
|
|
385522
386196
|
}
|
|
@@ -385537,15 +386211,15 @@ function getSchemas(definition = {}, responseCode = "") {
|
|
|
385537
386211
|
schemas2.response = definition.responses[responseCode].content[Object.keys(definition.responses[responseCode].content)[0]].schema;
|
|
385538
386212
|
return schemas2;
|
|
385539
386213
|
}
|
|
385540
|
-
async function compileExample(operation = {},
|
|
386214
|
+
async function compileExample(operation = {}, path22 = "", responseCode = "", exampleKey = "") {
|
|
385541
386215
|
if (!operation) {
|
|
385542
386216
|
throw new Error("Operation is required.");
|
|
385543
386217
|
}
|
|
385544
|
-
if (!
|
|
386218
|
+
if (!path22) {
|
|
385545
386219
|
throw new Error("Path is required.");
|
|
385546
386220
|
}
|
|
385547
386221
|
let example = {
|
|
385548
|
-
url:
|
|
386222
|
+
url: path22,
|
|
385549
386223
|
request: { parameters: {}, headers: {}, body: {} },
|
|
385550
386224
|
response: { headers: {}, body: {} }
|
|
385551
386225
|
};
|
|
@@ -385712,8 +386386,25 @@ __export(config_exports, {
|
|
|
385712
386386
|
getBrowserDiagnostics: () => getBrowserDiagnostics,
|
|
385713
386387
|
getEnvironment: () => getEnvironment,
|
|
385714
386388
|
resolveConcurrentRunners: () => resolveConcurrentRunners,
|
|
385715
|
-
setConfig: () => setConfig
|
|
386389
|
+
setConfig: () => setConfig,
|
|
386390
|
+
verifyAppDrivers: () => verifyAppDrivers
|
|
385716
386391
|
});
|
|
386392
|
+
async function verifyAppDrivers(descriptors, { verify, logger }) {
|
|
386393
|
+
const out = [];
|
|
386394
|
+
for (const d of descriptors) {
|
|
386395
|
+
if (!d.driverName || !d.driverPath) {
|
|
386396
|
+
out.push(d.app);
|
|
386397
|
+
continue;
|
|
386398
|
+
}
|
|
386399
|
+
const res = await verify(d.driverName, d.driverPath);
|
|
386400
|
+
if (res.ok) {
|
|
386401
|
+
out.push(d.app);
|
|
386402
|
+
} else if (logger) {
|
|
386403
|
+
logger(`Excluding ${d.app?.name ?? d.driverName} from available browsers: its ${d.driverName} driver is present but did not validate (${res.error ?? "no error reported"}). Possible causes include a partial or corrupt download, a permissions issue, or a missing dependency.`, "warning");
|
|
386404
|
+
}
|
|
386405
|
+
}
|
|
386406
|
+
return out;
|
|
386407
|
+
}
|
|
385717
386408
|
function deepMerge(target, override) {
|
|
385718
386409
|
const result = { ...target };
|
|
385719
386410
|
for (const key in override) {
|
|
@@ -385907,7 +386598,7 @@ function clearAppCache(config) {
|
|
|
385907
386598
|
async function probeBrowserEnvironment({ config, browsersDir }) {
|
|
385908
386599
|
setAppiumHome({ cacheDir: config?.cacheDir });
|
|
385909
386600
|
const cwd = process.cwd();
|
|
385910
|
-
process.chdir(
|
|
386601
|
+
process.chdir(import_node_path8.default.join(__dirname4, "../.."));
|
|
385911
386602
|
let installedBrowsers = [];
|
|
385912
386603
|
let browserDetectionFailed = false;
|
|
385913
386604
|
let appiumDriverOutput = "";
|
|
@@ -385942,7 +386633,7 @@ async function probeBrowserEnvironment({ config, browsersDir }) {
|
|
|
385942
386633
|
});
|
|
385943
386634
|
return;
|
|
385944
386635
|
}
|
|
385945
|
-
const child = (0,
|
|
386636
|
+
const child = (0, import_node_child_process4.spawn)(process.execPath, [appiumEntry, "driver", "list"], { env: process.env });
|
|
385946
386637
|
let stdout = "";
|
|
385947
386638
|
let stderr = "";
|
|
385948
386639
|
child.stdout?.on("data", (c) => {
|
|
@@ -385979,44 +386670,69 @@ async function getAvailableApps({ config }) {
|
|
|
385979
386670
|
if (hit)
|
|
385980
386671
|
return hit;
|
|
385981
386672
|
const { installedBrowsers, appiumDriverOutput, browserDetectionFailed } = await probeBrowserEnvironment({ config, browsersDir });
|
|
385982
|
-
const
|
|
386673
|
+
const descriptors = [];
|
|
385983
386674
|
const chrome = installedBrowsers.find((browser) => browser.browser === "chrome");
|
|
385984
386675
|
const chromeVersion = chrome?.buildId;
|
|
385985
386676
|
const chromedriver = installedBrowsers.find((browser) => browser.browser === "chromedriver");
|
|
385986
386677
|
const appiumChromium = appiumDriverOutput.match(/\n.*chromium.*installed \(npm\).*\n/);
|
|
385987
386678
|
if (chrome && chromedriver && appiumChromium) {
|
|
385988
|
-
|
|
385989
|
-
|
|
385990
|
-
|
|
385991
|
-
|
|
385992
|
-
|
|
386679
|
+
descriptors.push({
|
|
386680
|
+
app: {
|
|
386681
|
+
name: "chrome",
|
|
386682
|
+
version: chromeVersion,
|
|
386683
|
+
path: chrome.executablePath,
|
|
386684
|
+
driver: chromedriver.executablePath
|
|
386685
|
+
},
|
|
386686
|
+
driverName: "chromedriver",
|
|
386687
|
+
driverPath: chromedriver.executablePath
|
|
385993
386688
|
});
|
|
385994
386689
|
}
|
|
385995
386690
|
const firefox = installedBrowsers.find((browser) => browser.browser === "firefox");
|
|
385996
386691
|
const appiumFirefox = appiumDriverOutput.match(/\n.*gecko.*installed \(npm\).*\n/);
|
|
385997
386692
|
if (firefox && appiumFirefox) {
|
|
385998
|
-
|
|
385999
|
-
|
|
386000
|
-
|
|
386001
|
-
|
|
386693
|
+
const geckodriverPath = resolveGeckodriverBinaryPath(config, browsersDir);
|
|
386694
|
+
descriptors.push({
|
|
386695
|
+
app: {
|
|
386696
|
+
name: "firefox",
|
|
386697
|
+
version: firefox.buildId,
|
|
386698
|
+
path: firefox.executablePath
|
|
386699
|
+
},
|
|
386700
|
+
driverName: geckodriverPath ? "geckodriver" : void 0,
|
|
386701
|
+
driverPath: geckodriverPath
|
|
386002
386702
|
});
|
|
386003
386703
|
}
|
|
386004
386704
|
if (config.environment.platform === "mac") {
|
|
386005
386705
|
const safariVersion = await spawnCommand("defaults read /Applications/Safari.app/Contents/Info.plist CFBundleShortVersionString");
|
|
386006
386706
|
const appiumSafari = appiumDriverOutput.match(/\n.*safari.*installed \(npm\).*\n/);
|
|
386007
386707
|
if (safariVersion.exitCode === 0 && appiumSafari) {
|
|
386008
|
-
|
|
386708
|
+
descriptors.push({
|
|
386709
|
+
app: { name: "safari", version: safariVersion.stdout.trim(), path: "" },
|
|
386710
|
+
driverName: "safaridriver",
|
|
386711
|
+
driverPath: "/usr/bin/safaridriver"
|
|
386712
|
+
});
|
|
386009
386713
|
}
|
|
386010
386714
|
}
|
|
386715
|
+
const apps = await verifyAppDrivers(descriptors, {
|
|
386716
|
+
verify: (driverName, driverPath) => verifyDriverBinary(driverName, driverPath),
|
|
386717
|
+
logger: (msg, level) => log(config, level ?? "warning", msg)
|
|
386718
|
+
});
|
|
386011
386719
|
if (!browserDetectionFailed)
|
|
386012
386720
|
cachedAppsByDir.set(key, apps);
|
|
386013
386721
|
return apps;
|
|
386014
386722
|
}
|
|
386723
|
+
function resolveGeckodriverBinaryPath(config, browsersDir) {
|
|
386724
|
+
const installed = resolveHeavyDepPath("geckodriver", {
|
|
386725
|
+
cacheDir: config?.cacheDir
|
|
386726
|
+
});
|
|
386727
|
+
if (!installed)
|
|
386728
|
+
return void 0;
|
|
386729
|
+
return geckodriverBinaryInCache(browsersDir);
|
|
386730
|
+
}
|
|
386015
386731
|
function probeSafariVersion(timeoutMs) {
|
|
386016
386732
|
return new Promise((resolve) => {
|
|
386017
386733
|
let child;
|
|
386018
386734
|
try {
|
|
386019
|
-
child = (0,
|
|
386735
|
+
child = (0, import_node_child_process4.spawn)("defaults", [
|
|
386020
386736
|
"read",
|
|
386021
386737
|
"/Applications/Safari.app/Contents/Info.plist",
|
|
386022
386738
|
"CFBundleShortVersionString"
|
|
@@ -386097,7 +386813,7 @@ async function getBrowserDiagnostics({ config }) {
|
|
|
386097
386813
|
const version = await probeSafariVersion(4e3);
|
|
386098
386814
|
safariApp = version !== null;
|
|
386099
386815
|
safariVersion = version ?? void 0;
|
|
386100
|
-
safaridriver =
|
|
386816
|
+
safaridriver = import_node_fs8.default.existsSync("/usr/bin/safaridriver");
|
|
386101
386817
|
} catch {
|
|
386102
386818
|
detectionFailed = true;
|
|
386103
386819
|
}
|
|
@@ -386119,22 +386835,23 @@ async function getBrowserDiagnostics({ config }) {
|
|
|
386119
386835
|
});
|
|
386120
386836
|
return { browsers, detectionFailed };
|
|
386121
386837
|
}
|
|
386122
|
-
var import_node_os3,
|
|
386838
|
+
var import_node_os3, import_node_fs8, import_node_path8, import_node_child_process4, import_node_url5, __dirname4, platformMap, defaultFileTypes, cachedAppsByDir;
|
|
386123
386839
|
var init_config = __esm({
|
|
386124
386840
|
"dist/core/config.js"() {
|
|
386125
386841
|
"use strict";
|
|
386126
386842
|
import_node_os3 = __toESM(require("node:os"), 1);
|
|
386127
|
-
|
|
386843
|
+
import_node_fs8 = __toESM(require("node:fs"), 1);
|
|
386128
386844
|
init_validate();
|
|
386129
386845
|
init_utils();
|
|
386130
|
-
|
|
386131
|
-
|
|
386846
|
+
import_node_path8 = __toESM(require("node:path"), 1);
|
|
386847
|
+
import_node_child_process4 = require("node:child_process");
|
|
386132
386848
|
init_loader();
|
|
386133
386849
|
init_cacheDir();
|
|
386850
|
+
init_browsers();
|
|
386134
386851
|
init_appium();
|
|
386135
386852
|
init_openapi();
|
|
386136
386853
|
import_node_url5 = require("node:url");
|
|
386137
|
-
__dirname4 =
|
|
386854
|
+
__dirname4 = import_node_path8.default.dirname((0, import_node_url5.fileURLToPath)(importMetaUrl));
|
|
386138
386855
|
platformMap = {
|
|
386139
386856
|
darwin: "mac",
|
|
386140
386857
|
linux: "linux",
|
|
@@ -386436,252 +387153,6 @@ var init_browserStepKeys = __esm({
|
|
|
386436
387153
|
}
|
|
386437
387154
|
});
|
|
386438
387155
|
|
|
386439
|
-
// dist/runtime/browsers.js
|
|
386440
|
-
var browsers_exports = {};
|
|
386441
|
-
__export(browsers_exports, {
|
|
386442
|
-
BROWSER_CHANNELS: () => BROWSER_CHANNELS,
|
|
386443
|
-
ensureBrowserInstalled: () => ensureBrowserInstalled,
|
|
386444
|
-
getInstalledBrowsers: () => getInstalledBrowsers,
|
|
386445
|
-
requiredBrowserAssets: () => requiredBrowserAssets
|
|
386446
|
-
});
|
|
386447
|
-
function requiredBrowserAssets(name) {
|
|
386448
|
-
switch ((name ?? "").toLowerCase()) {
|
|
386449
|
-
case "chrome":
|
|
386450
|
-
return ["chrome", "chromedriver"];
|
|
386451
|
-
case "firefox":
|
|
386452
|
-
return ["firefox", "geckodriver"];
|
|
386453
|
-
default:
|
|
386454
|
-
return [];
|
|
386455
|
-
}
|
|
386456
|
-
}
|
|
386457
|
-
async function loadPuppeteerBrowsers(deps, ctx) {
|
|
386458
|
-
if (deps.browsersModule)
|
|
386459
|
-
return deps.browsersModule;
|
|
386460
|
-
return await loadHeavyDep("@puppeteer/browsers", { ctx, deps: { logger: deps.logger } });
|
|
386461
|
-
}
|
|
386462
|
-
async function loadGeckodriver(deps, ctx) {
|
|
386463
|
-
if (deps.geckodriverModule)
|
|
386464
|
-
return deps.geckodriverModule;
|
|
386465
|
-
return await loadHeavyDep("geckodriver", { ctx, deps: { logger: deps.logger } });
|
|
386466
|
-
}
|
|
386467
|
-
function isStillFresh(latestCheckedAt, now) {
|
|
386468
|
-
if (!latestCheckedAt)
|
|
386469
|
-
return false;
|
|
386470
|
-
const t = Date.parse(latestCheckedAt);
|
|
386471
|
-
if (Number.isNaN(t))
|
|
386472
|
-
return false;
|
|
386473
|
-
return now.getTime() - t < FRESHNESS_TTL_MS;
|
|
386474
|
-
}
|
|
386475
|
-
async function resolveChannelBuildId(browsersModule, assetName, platform) {
|
|
386476
|
-
const channel = BROWSER_CHANNELS[assetName];
|
|
386477
|
-
return await withTimeout(Promise.resolve(browsersModule.resolveBuildId(assetName, platform, channel)), RESOLVE_TIMEOUT_MS, `resolveBuildId(${assetName}, ${channel})`);
|
|
386478
|
-
}
|
|
386479
|
-
function withTimeout(p, ms, label) {
|
|
386480
|
-
return new Promise((resolve, reject) => {
|
|
386481
|
-
const t = setTimeout(() => reject(new Error(`${label} timed out after ${ms}ms`)), ms);
|
|
386482
|
-
if (typeof t.unref === "function")
|
|
386483
|
-
t.unref();
|
|
386484
|
-
p.then((v) => {
|
|
386485
|
-
clearTimeout(t);
|
|
386486
|
-
resolve(v);
|
|
386487
|
-
}, (err) => {
|
|
386488
|
-
clearTimeout(t);
|
|
386489
|
-
reject(err);
|
|
386490
|
-
});
|
|
386491
|
-
});
|
|
386492
|
-
}
|
|
386493
|
-
async function ensureBrowserInstalled(name, options = {}) {
|
|
386494
|
-
const { ctx = {} } = options;
|
|
386495
|
-
const dedupeKey = `${name}:${getBrowsersDir(ctx)}`;
|
|
386496
|
-
const inFlight = inFlightInstalls.get(dedupeKey);
|
|
386497
|
-
if (inFlight)
|
|
386498
|
-
return inFlight;
|
|
386499
|
-
const p = ensureBrowserInstalledImpl(name, options).finally(() => {
|
|
386500
|
-
inFlightInstalls.delete(dedupeKey);
|
|
386501
|
-
});
|
|
386502
|
-
inFlightInstalls.set(dedupeKey, p);
|
|
386503
|
-
return p;
|
|
386504
|
-
}
|
|
386505
|
-
async function ensureBrowserInstalledImpl(name, options = {}) {
|
|
386506
|
-
const { ctx = {}, deps = {}, force = false } = options;
|
|
386507
|
-
const logger = deps.logger ?? defaultLogger2;
|
|
386508
|
-
const now = (deps.now ?? (() => /* @__PURE__ */ new Date()))();
|
|
386509
|
-
const cacheDir = getBrowsersDir(ctx);
|
|
386510
|
-
const record = readInstalledRecord(ctx);
|
|
386511
|
-
const existing = record.browsers[name];
|
|
386512
|
-
if (name === "geckodriver") {
|
|
386513
|
-
return await ensureGeckodriver({ ctx, deps: { ...deps, logger }, force, now }, record, existing, logger);
|
|
386514
|
-
}
|
|
386515
|
-
const browsersModule = await loadPuppeteerBrowsers(deps, ctx);
|
|
386516
|
-
const platform = browsersModule.detectBrowserPlatform ? browsersModule.detectBrowserPlatform() : void 0;
|
|
386517
|
-
if (!platform) {
|
|
386518
|
-
throw new Error(`Unable to determine browser platform on ${process.platform}/${process.arch}. @puppeteer/browsers does not recognize this OS; ${name} cannot be installed automatically. Set DOC_DETECTIVE_CACHE_DIR to a pre-warmed cache or skip browser-driven steps.`);
|
|
386519
|
-
}
|
|
386520
|
-
if (!force && existing && isStillFresh(existing.latestCheckedAt, now)) {
|
|
386521
|
-
if (existing.latestKnownVersion === existing.installedVersion) {
|
|
386522
|
-
const path23 = await locateExecutable(browsersModule, name, existing.installedVersion, cacheDir, platform);
|
|
386523
|
-
return { path: path23, version: existing.installedVersion, outdated: false };
|
|
386524
|
-
}
|
|
386525
|
-
const path22 = await locateExecutable(browsersModule, name, existing.installedVersion, cacheDir, platform);
|
|
386526
|
-
logger(`${name} ${existing.installedVersion} installed in ${cacheDir}; "${BROWSER_CHANNELS[name]}" channel is now ${existing.latestKnownVersion}. Run \`doc-detective install browsers ${name} --force\` to update.`, "warn");
|
|
386527
|
-
return { path: path22, version: existing.installedVersion, outdated: true };
|
|
386528
|
-
}
|
|
386529
|
-
let latest;
|
|
386530
|
-
try {
|
|
386531
|
-
latest = await resolveChannelBuildId(browsersModule, name, platform);
|
|
386532
|
-
} catch (err) {
|
|
386533
|
-
logger(`Channel resolution for ${name} skipped: ${String(err)}`, "debug");
|
|
386534
|
-
if (existing) {
|
|
386535
|
-
const path22 = await locateExecutable(browsersModule, name, existing.installedVersion, cacheDir, platform);
|
|
386536
|
-
return { path: path22, version: existing.installedVersion, outdated: false };
|
|
386537
|
-
}
|
|
386538
|
-
throw err;
|
|
386539
|
-
}
|
|
386540
|
-
if (!force && existing && existing.installedVersion === latest) {
|
|
386541
|
-
record.browsers[name] = {
|
|
386542
|
-
...existing,
|
|
386543
|
-
latestKnownVersion: latest,
|
|
386544
|
-
latestCheckedAt: now.toISOString()
|
|
386545
|
-
};
|
|
386546
|
-
writeInstalledRecord(record, ctx);
|
|
386547
|
-
const path22 = await locateExecutable(browsersModule, name, latest, cacheDir, platform);
|
|
386548
|
-
return { path: path22, version: latest, outdated: false };
|
|
386549
|
-
}
|
|
386550
|
-
if (!force && existing) {
|
|
386551
|
-
record.browsers[name] = {
|
|
386552
|
-
...existing,
|
|
386553
|
-
latestKnownVersion: latest,
|
|
386554
|
-
latestCheckedAt: now.toISOString()
|
|
386555
|
-
};
|
|
386556
|
-
writeInstalledRecord(record, ctx);
|
|
386557
|
-
logger(`${name} ${existing.installedVersion} installed in ${cacheDir}; "${BROWSER_CHANNELS[name]}" channel is now ${latest}. Run \`doc-detective install browsers ${name} --force\` to update.`, "warn");
|
|
386558
|
-
const path22 = await locateExecutable(browsersModule, name, existing.installedVersion, cacheDir, platform);
|
|
386559
|
-
return { path: path22, version: existing.installedVersion, outdated: true };
|
|
386560
|
-
}
|
|
386561
|
-
logger(`Installing ${name} ${latest} into ${cacheDir}`, "info");
|
|
386562
|
-
await browsersModule.install({
|
|
386563
|
-
browser: name,
|
|
386564
|
-
buildId: latest,
|
|
386565
|
-
cacheDir
|
|
386566
|
-
});
|
|
386567
|
-
if (existing && existing.installedVersion !== latest) {
|
|
386568
|
-
try {
|
|
386569
|
-
await browsersModule.uninstall({
|
|
386570
|
-
browser: name,
|
|
386571
|
-
buildId: existing.installedVersion,
|
|
386572
|
-
cacheDir
|
|
386573
|
-
});
|
|
386574
|
-
} catch (err) {
|
|
386575
|
-
logger(`Failed to prune old ${name} buildId ${existing.installedVersion}: ${String(err)}`, "debug");
|
|
386576
|
-
}
|
|
386577
|
-
}
|
|
386578
|
-
record.browsers[name] = {
|
|
386579
|
-
installedVersion: latest,
|
|
386580
|
-
installedAt: now.toISOString(),
|
|
386581
|
-
latestKnownVersion: latest,
|
|
386582
|
-
latestCheckedAt: now.toISOString()
|
|
386583
|
-
};
|
|
386584
|
-
writeInstalledRecord(record, ctx);
|
|
386585
|
-
const path21 = await locateExecutable(browsersModule, name, latest, cacheDir, platform);
|
|
386586
|
-
return { path: path21, version: latest, outdated: false };
|
|
386587
|
-
}
|
|
386588
|
-
async function locateExecutable(browsersModule, name, buildId, cacheDir, platform) {
|
|
386589
|
-
if (typeof browsersModule.computeExecutablePath === "function") {
|
|
386590
|
-
return browsersModule.computeExecutablePath({
|
|
386591
|
-
browser: name,
|
|
386592
|
-
buildId,
|
|
386593
|
-
cacheDir,
|
|
386594
|
-
platform
|
|
386595
|
-
});
|
|
386596
|
-
}
|
|
386597
|
-
return cacheDir;
|
|
386598
|
-
}
|
|
386599
|
-
async function ensureGeckodriver(ctxBag, record, existing, logger) {
|
|
386600
|
-
const cacheDir = getBrowsersDir(ctxBag.ctx);
|
|
386601
|
-
const resolveBinaryPath = (gecko2) => {
|
|
386602
|
-
const fromModule = gecko2 && typeof gecko2.path === "string" && gecko2.path.length > 0 ? gecko2.path : null;
|
|
386603
|
-
return fromModule ?? cacheDir;
|
|
386604
|
-
};
|
|
386605
|
-
if (!ctxBag.force && existing && isStillFresh(existing.latestCheckedAt, ctxBag.now)) {
|
|
386606
|
-
const gecko2 = ctxBag.deps.geckodriverModule ?? await loadGeckodriver(ctxBag.deps, ctxBag.ctx);
|
|
386607
|
-
return {
|
|
386608
|
-
path: resolveBinaryPath(gecko2),
|
|
386609
|
-
version: existing.installedVersion,
|
|
386610
|
-
// Only mark outdated when we actually KNOW a different latest
|
|
386611
|
-
// version exists. If `latestKnownVersion` is undefined (e.g., the
|
|
386612
|
-
// installer couldn't determine a version and we recorded
|
|
386613
|
-
// `installedVersion: "unknown"` with `latestKnownVersion: undefined`),
|
|
386614
|
-
// a naive `!==` comparison would flag the entry as outdated on
|
|
386615
|
-
// every run.
|
|
386616
|
-
outdated: typeof existing.latestKnownVersion === "string" && existing.latestKnownVersion.length > 0 && existing.latestKnownVersion !== existing.installedVersion
|
|
386617
|
-
};
|
|
386618
|
-
}
|
|
386619
|
-
const gecko = await loadGeckodriver(ctxBag.deps, ctxBag.ctx);
|
|
386620
|
-
const prevEnv = process.env.GECKODRIVER_CACHE_DIR;
|
|
386621
|
-
process.env.GECKODRIVER_CACHE_DIR = cacheDir;
|
|
386622
|
-
try {
|
|
386623
|
-
logger(`Installing geckodriver into ${cacheDir}`, "info");
|
|
386624
|
-
const downloadResult = await gecko.download();
|
|
386625
|
-
const resolvedVersion = (typeof gecko.GECKODRIVER_VERSION === "string" ? gecko.GECKODRIVER_VERSION : void 0) || (downloadResult && typeof downloadResult.version === "string" ? downloadResult.version : void 0);
|
|
386626
|
-
if (!resolvedVersion) {
|
|
386627
|
-
logger("Could not determine geckodriver version after install. Freshness checks for geckodriver will be unreliable until the next forced reinstall.", "warn");
|
|
386628
|
-
}
|
|
386629
|
-
const installedVersion = resolvedVersion ?? "unknown";
|
|
386630
|
-
record.browsers.geckodriver = {
|
|
386631
|
-
installedVersion,
|
|
386632
|
-
installedAt: ctxBag.now.toISOString(),
|
|
386633
|
-
latestKnownVersion: resolvedVersion,
|
|
386634
|
-
latestCheckedAt: ctxBag.now.toISOString()
|
|
386635
|
-
};
|
|
386636
|
-
writeInstalledRecord(record, ctxBag.ctx);
|
|
386637
|
-
return {
|
|
386638
|
-
path: resolveBinaryPath(gecko),
|
|
386639
|
-
version: installedVersion,
|
|
386640
|
-
outdated: false
|
|
386641
|
-
};
|
|
386642
|
-
} finally {
|
|
386643
|
-
if (prevEnv === void 0)
|
|
386644
|
-
delete process.env.GECKODRIVER_CACHE_DIR;
|
|
386645
|
-
else
|
|
386646
|
-
process.env.GECKODRIVER_CACHE_DIR = prevEnv;
|
|
386647
|
-
}
|
|
386648
|
-
}
|
|
386649
|
-
function getInstalledBrowsers(ctx = {}) {
|
|
386650
|
-
const record = readInstalledRecord(ctx);
|
|
386651
|
-
return Object.entries(record.browsers).map(([name, entry]) => ({
|
|
386652
|
-
name,
|
|
386653
|
-
installedVersion: entry.installedVersion,
|
|
386654
|
-
installedAt: entry.installedAt,
|
|
386655
|
-
latestKnownVersion: entry.latestKnownVersion
|
|
386656
|
-
}));
|
|
386657
|
-
}
|
|
386658
|
-
var BROWSER_CHANNELS, FRESHNESS_TTL_MS, RESOLVE_TIMEOUT_MS, RUNTIME_DEBUG2, defaultLogger2, inFlightInstalls;
|
|
386659
|
-
var init_browsers = __esm({
|
|
386660
|
-
"dist/runtime/browsers.js"() {
|
|
386661
|
-
"use strict";
|
|
386662
|
-
init_cacheDir();
|
|
386663
|
-
init_loader();
|
|
386664
|
-
BROWSER_CHANNELS = {
|
|
386665
|
-
chrome: "stable",
|
|
386666
|
-
firefox: "latest",
|
|
386667
|
-
chromedriver: "stable",
|
|
386668
|
-
geckodriver: "latest"
|
|
386669
|
-
};
|
|
386670
|
-
FRESHNESS_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
386671
|
-
RESOLVE_TIMEOUT_MS = 5e3;
|
|
386672
|
-
RUNTIME_DEBUG2 = process.env.DOC_DETECTIVE_RUNTIME_DEBUG === "1";
|
|
386673
|
-
defaultLogger2 = (msg, level = "info") => {
|
|
386674
|
-
if (level === "debug" && !RUNTIME_DEBUG2)
|
|
386675
|
-
return;
|
|
386676
|
-
if (level === "error")
|
|
386677
|
-
console.error(msg);
|
|
386678
|
-
else
|
|
386679
|
-
console.log(msg);
|
|
386680
|
-
};
|
|
386681
|
-
inFlightInstalls = /* @__PURE__ */ new Map();
|
|
386682
|
-
}
|
|
386683
|
-
});
|
|
386684
|
-
|
|
386685
387156
|
// dist/runtime/inferRuntimeNeeds.js
|
|
386686
387157
|
var inferRuntimeNeeds_exports = {};
|
|
386687
387158
|
__export(inferRuntimeNeeds_exports, {
|
|
@@ -386819,8 +387290,8 @@ module.exports = __toCommonJS(index_exports);
|
|
|
386819
387290
|
init_config();
|
|
386820
387291
|
|
|
386821
387292
|
// dist/core/detectTests.js
|
|
386822
|
-
var
|
|
386823
|
-
var
|
|
387293
|
+
var import_node_fs10 = __toESM(require("node:fs"), 1);
|
|
387294
|
+
var import_node_path10 = __toESM(require("node:path"), 1);
|
|
386824
387295
|
var import_node_os5 = __toESM(require("node:os"), 1);
|
|
386825
387296
|
var import_node_crypto3 = __toESM(require("node:crypto"), 1);
|
|
386826
387297
|
var import_yaml3 = __toESM(require("yaml"), 1);
|
|
@@ -387693,8 +388164,8 @@ init_files();
|
|
|
387693
388164
|
init_utils();
|
|
387694
388165
|
|
|
387695
388166
|
// dist/core/integrations/heretto.js
|
|
387696
|
-
var
|
|
387697
|
-
var
|
|
388167
|
+
var import_node_fs9 = __toESM(require("node:fs"), 1);
|
|
388168
|
+
var import_node_path9 = __toESM(require("node:path"), 1);
|
|
387698
388169
|
var import_node_https = __toESM(require("node:https"), 1);
|
|
387699
388170
|
var import_node_http = __toESM(require("node:http"), 1);
|
|
387700
388171
|
var import_axios3 = __toESM(require("axios"), 1);
|
|
@@ -387736,7 +388207,7 @@ var HerettoUploader = class {
|
|
|
387736
388207
|
const apiBaseUrl = `https://${integrationConfig.organizationId}.heretto.com`;
|
|
387737
388208
|
let fileId = sourceIntegration.fileId;
|
|
387738
388209
|
let parentFolderId = sourceIntegration.parentFolderId;
|
|
387739
|
-
const filename =
|
|
388210
|
+
const filename = import_node_path9.default.basename(sourceIntegration.filePath);
|
|
387740
388211
|
const relativeFilePath = sourceIntegration.filePath;
|
|
387741
388212
|
if (!fileId && integrationConfig.resourceDependencies) {
|
|
387742
388213
|
const resolvedFile = this.resolveFromDependencies({
|
|
@@ -387776,9 +388247,9 @@ var HerettoUploader = class {
|
|
|
387776
388247
|
}
|
|
387777
388248
|
}
|
|
387778
388249
|
if (!parentFolderId && relativeFilePath) {
|
|
387779
|
-
const parentDirPath =
|
|
388250
|
+
const parentDirPath = import_node_path9.default.dirname(relativeFilePath);
|
|
387780
388251
|
if (parentDirPath && parentDirPath !== ".") {
|
|
387781
|
-
const folderName =
|
|
388252
|
+
const folderName = import_node_path9.default.basename(parentDirPath);
|
|
387782
388253
|
log3(config, "debug", `Searching for parent folder by name: ${folderName}`);
|
|
387783
388254
|
parentFolderId = await this.searchFolderByName({
|
|
387784
388255
|
apiBaseUrl,
|
|
@@ -387854,11 +388325,11 @@ var HerettoUploader = class {
|
|
|
387854
388325
|
return result;
|
|
387855
388326
|
}
|
|
387856
388327
|
}
|
|
387857
|
-
if (!
|
|
388328
|
+
if (!import_node_fs9.default.existsSync(localFilePath)) {
|
|
387858
388329
|
result.description = `Local file not found: ${localFilePath}`;
|
|
387859
388330
|
return result;
|
|
387860
388331
|
}
|
|
387861
|
-
const fileContent =
|
|
388332
|
+
const fileContent = import_node_fs9.default.readFileSync(localFilePath);
|
|
387862
388333
|
const contentType = this.getContentType(localFilePath);
|
|
387863
388334
|
try {
|
|
387864
388335
|
await this.uploadFile({
|
|
@@ -387889,7 +388360,7 @@ var HerettoUploader = class {
|
|
|
387889
388360
|
resolveFromDependencies({ resourceDependencies, filePath, filename, log: log3 }) {
|
|
387890
388361
|
if (!resourceDependencies)
|
|
387891
388362
|
return null;
|
|
387892
|
-
const normalizedPath =
|
|
388363
|
+
const normalizedPath = import_node_path9.default.posix.normalize(filePath.replace(/\\/g, "/")).replace(/^\.\.\/+/g, "").replace(/^\.\//, "");
|
|
387893
388364
|
for (const [depPath, info] of Object.entries(resourceDependencies)) {
|
|
387894
388365
|
if (depPath.startsWith("_"))
|
|
387895
388366
|
continue;
|
|
@@ -387899,14 +388370,14 @@ var HerettoUploader = class {
|
|
|
387899
388370
|
return info;
|
|
387900
388371
|
}
|
|
387901
388372
|
}
|
|
387902
|
-
const parentDir =
|
|
387903
|
-
const parentFolderName =
|
|
388373
|
+
const parentDir = import_node_path9.default.dirname(normalizedPath);
|
|
388374
|
+
const parentFolderName = import_node_path9.default.basename(parentDir);
|
|
387904
388375
|
for (const [depPath, info] of Object.entries(resourceDependencies)) {
|
|
387905
388376
|
if (depPath.startsWith("_"))
|
|
387906
388377
|
continue;
|
|
387907
|
-
const depFilename =
|
|
387908
|
-
const depParentDir =
|
|
387909
|
-
const depParentFolderName =
|
|
388378
|
+
const depFilename = import_node_path9.default.basename(depPath);
|
|
388379
|
+
const depParentDir = import_node_path9.default.dirname(depPath);
|
|
388380
|
+
const depParentFolderName = import_node_path9.default.basename(depParentDir);
|
|
387910
388381
|
if (depFilename === filename && depParentFolderName === parentFolderName) {
|
|
387911
388382
|
log3("debug", `Found filename+folder match in dependencies: ${depPath}`);
|
|
387912
388383
|
return info;
|
|
@@ -387915,7 +388386,7 @@ var HerettoUploader = class {
|
|
|
387915
388386
|
for (const [depPath, info] of Object.entries(resourceDependencies)) {
|
|
387916
388387
|
if (depPath.startsWith("_"))
|
|
387917
388388
|
continue;
|
|
387918
|
-
const depFilename =
|
|
388389
|
+
const depFilename = import_node_path9.default.basename(depPath);
|
|
387919
388390
|
if (depFilename === filename) {
|
|
387920
388391
|
log3("debug", `Found filename match in dependencies: ${depPath}`);
|
|
387921
388392
|
return info;
|
|
@@ -387946,17 +388417,17 @@ var HerettoUploader = class {
|
|
|
387946
388417
|
};
|
|
387947
388418
|
if (!resourceDependencies)
|
|
387948
388419
|
return result;
|
|
387949
|
-
const normalizedPath =
|
|
387950
|
-
const parentDir =
|
|
387951
|
-
const targetFolderName =
|
|
388420
|
+
const normalizedPath = import_node_path9.default.posix.normalize(filePath.replace(/\\/g, "/")).replace(/^(\.\.\/)+/g, "").replace(/^\.\//, "");
|
|
388421
|
+
const parentDir = import_node_path9.default.dirname(normalizedPath);
|
|
388422
|
+
const targetFolderName = import_node_path9.default.basename(parentDir);
|
|
387952
388423
|
result.targetFolderName = targetFolderName;
|
|
387953
388424
|
result.ditamapParentFolderId = resourceDependencies._ditamapParentFolderId || null;
|
|
387954
388425
|
log3("debug", `Looking for parent folder '${targetFolderName}' in dependencies`);
|
|
387955
388426
|
for (const [depPath, info] of Object.entries(resourceDependencies)) {
|
|
387956
388427
|
if (depPath.startsWith("_"))
|
|
387957
388428
|
continue;
|
|
387958
|
-
const depParentDir =
|
|
387959
|
-
const depFolderName =
|
|
388429
|
+
const depParentDir = import_node_path9.default.dirname(depPath);
|
|
388430
|
+
const depFolderName = import_node_path9.default.basename(depParentDir);
|
|
387960
388431
|
if (depFolderName === targetFolderName && info.parentFolderId) {
|
|
387961
388432
|
log3("debug", `Found sibling file ${depPath} with parent folder ID: ${info.parentFolderId}`);
|
|
387962
388433
|
result.folderId = info.parentFolderId;
|
|
@@ -388460,7 +388931,7 @@ var HerettoUploader = class {
|
|
|
388460
388931
|
* @returns {string} MIME content type
|
|
388461
388932
|
*/
|
|
388462
388933
|
getContentType(filePath) {
|
|
388463
|
-
const ext =
|
|
388934
|
+
const ext = import_node_path9.default.extname(filePath).toLowerCase();
|
|
388464
388935
|
const contentTypes = {
|
|
388465
388936
|
".png": "image/png",
|
|
388466
388937
|
".jpg": "image/jpeg",
|
|
@@ -388632,39 +389103,39 @@ async function pollJobStatus(client, fileId, jobId, log3, config) {
|
|
|
388632
389103
|
return null;
|
|
388633
389104
|
}
|
|
388634
389105
|
async function downloadAndExtractOutput(client, fileId, jobId, herettoName, log3, config, deps) {
|
|
388635
|
-
const fsModule = deps?.fsModule ||
|
|
389106
|
+
const fsModule = deps?.fsModule || import_node_fs9.default;
|
|
388636
389107
|
const ZipClass = deps?.ZipClass || import_adm_zip.default;
|
|
388637
|
-
const tempDir =
|
|
389108
|
+
const tempDir = import_node_path9.default.join(import_node_os4.default.tmpdir(), "doc-detective");
|
|
388638
389109
|
const hash = import_node_crypto2.default.createHash("md5").update(`${herettoName}_${jobId}`).digest("hex");
|
|
388639
389110
|
try {
|
|
388640
389111
|
fsModule.mkdirSync(tempDir, { recursive: true });
|
|
388641
|
-
const outputDir =
|
|
389112
|
+
const outputDir = import_node_path9.default.join(tempDir, `heretto_${hash}`);
|
|
388642
389113
|
log3(config, "debug", `Downloading publishing job output for ${herettoName}...`);
|
|
388643
389114
|
const response = await client.get(`/files/${fileId}/publishes/${jobId}/assets-all`, {
|
|
388644
389115
|
responseType: "arraybuffer",
|
|
388645
389116
|
timeout: DOWNLOAD_TIMEOUT_MS,
|
|
388646
389117
|
headers: { Accept: "application/octet-stream" }
|
|
388647
389118
|
});
|
|
388648
|
-
const zipPath =
|
|
389119
|
+
const zipPath = import_node_path9.default.join(tempDir, `heretto_${hash}.zip`);
|
|
388649
389120
|
fsModule.writeFileSync(zipPath, response.data);
|
|
388650
389121
|
log3(config, "debug", `Extracting output to ${outputDir}...`);
|
|
388651
389122
|
const zip = new ZipClass(zipPath);
|
|
388652
|
-
const resolvedOutputDir =
|
|
389123
|
+
const resolvedOutputDir = import_node_path9.default.resolve(outputDir);
|
|
388653
389124
|
for (const entry of zip.getEntries()) {
|
|
388654
|
-
const normalizedName =
|
|
389125
|
+
const normalizedName = import_node_path9.default.posix.normalize(entry.entryName.replace(/\\/g, "/"));
|
|
388655
389126
|
if (normalizedName.startsWith("..") || normalizedName.includes("/..")) {
|
|
388656
389127
|
log3(config, "warning", `Skipping ZIP entry with path traversal: ${entry.entryName}`);
|
|
388657
389128
|
continue;
|
|
388658
389129
|
}
|
|
388659
|
-
const resolvedPath =
|
|
388660
|
-
if (!resolvedPath.startsWith(resolvedOutputDir +
|
|
389130
|
+
const resolvedPath = import_node_path9.default.resolve(outputDir, normalizedName);
|
|
389131
|
+
if (!resolvedPath.startsWith(resolvedOutputDir + import_node_path9.default.sep) && resolvedPath !== resolvedOutputDir) {
|
|
388661
389132
|
log3(config, "warning", `Skipping ZIP entry outside output directory: ${entry.entryName}`);
|
|
388662
389133
|
continue;
|
|
388663
389134
|
}
|
|
388664
389135
|
if (entry.isDirectory) {
|
|
388665
389136
|
fsModule.mkdirSync(resolvedPath, { recursive: true });
|
|
388666
389137
|
} else {
|
|
388667
|
-
fsModule.mkdirSync(
|
|
389138
|
+
fsModule.mkdirSync(import_node_path9.default.dirname(resolvedPath), { recursive: true });
|
|
388668
389139
|
fsModule.writeFileSync(resolvedPath, entry.getData());
|
|
388669
389140
|
}
|
|
388670
389141
|
}
|
|
@@ -388673,14 +389144,14 @@ async function downloadAndExtractOutput(client, fileId, jobId, herettoName, log3
|
|
|
388673
389144
|
return outputDir;
|
|
388674
389145
|
} catch (error) {
|
|
388675
389146
|
try {
|
|
388676
|
-
const zipCleanupPath =
|
|
389147
|
+
const zipCleanupPath = import_node_path9.default.join(tempDir, `heretto_${hash}.zip`);
|
|
388677
389148
|
if (fsModule.existsSync(zipCleanupPath)) {
|
|
388678
389149
|
fsModule.unlinkSync(zipCleanupPath);
|
|
388679
389150
|
}
|
|
388680
389151
|
} catch {
|
|
388681
389152
|
}
|
|
388682
389153
|
try {
|
|
388683
|
-
const cleanupOutputDir =
|
|
389154
|
+
const cleanupOutputDir = import_node_path9.default.join(tempDir, `heretto_${hash}`);
|
|
388684
389155
|
if (fsModule.existsSync(cleanupOutputDir)) {
|
|
388685
389156
|
fsModule.rmSync(cleanupOutputDir, { recursive: true, force: true });
|
|
388686
389157
|
}
|
|
@@ -388759,7 +389230,7 @@ async function getResourceDependencies(restClient, ditamapId, log3, config) {
|
|
|
388759
389230
|
pathToUuidMap[relativePath] = {
|
|
388760
389231
|
uuid,
|
|
388761
389232
|
fullPath: uri,
|
|
388762
|
-
name: name ||
|
|
389233
|
+
name: name || import_node_path9.default.basename(relativePath || ""),
|
|
388763
389234
|
parentFolderId
|
|
388764
389235
|
};
|
|
388765
389236
|
}
|
|
@@ -388827,12 +389298,12 @@ async function detectTests2({ config }) {
|
|
|
388827
389298
|
return specs;
|
|
388828
389299
|
}
|
|
388829
389300
|
function generateSpecId(filePath) {
|
|
388830
|
-
const absolutePath =
|
|
389301
|
+
const absolutePath = import_node_path10.default.resolve(filePath);
|
|
388831
389302
|
const cwd = process.cwd();
|
|
388832
|
-
const candidate =
|
|
388833
|
-
const isInsideCwd = candidate.length > 0 && !candidate.startsWith("..") && !
|
|
389303
|
+
const candidate = import_node_path10.default.relative(cwd, absolutePath);
|
|
389304
|
+
const isInsideCwd = candidate.length > 0 && !candidate.startsWith("..") && !import_node_path10.default.isAbsolute(candidate);
|
|
388834
389305
|
const relativePath = isInsideCwd ? candidate : absolutePath;
|
|
388835
|
-
const normalizedPath = relativePath.split(
|
|
389306
|
+
const normalizedPath = relativePath.split(import_node_path10.default.sep).join("/").replace(/^\.\//, "").replace(/[^a-zA-Z0-9._\-\/]/g, "_");
|
|
388836
389307
|
return normalizedPath;
|
|
388837
389308
|
}
|
|
388838
389309
|
async function isValidSourceFile({ config, files, source }) {
|
|
@@ -388843,7 +389314,7 @@ async function isValidSourceFile({ config, files, source }) {
|
|
|
388843
389314
|
});
|
|
388844
389315
|
if (files.indexOf(source) >= 0)
|
|
388845
389316
|
return false;
|
|
388846
|
-
if (
|
|
389317
|
+
if (import_node_path10.default.extname(source) === ".json" || import_node_path10.default.extname(source) === ".yaml" || import_node_path10.default.extname(source) === ".yml") {
|
|
388847
389318
|
const content = await readFile({ fileURLOrPath: source });
|
|
388848
389319
|
if (typeof content !== "object") {
|
|
388849
389320
|
log(config, "debug", `${source} isn't a valid test specification. Skipping.`);
|
|
@@ -388863,11 +389334,11 @@ async function isValidSourceFile({ config, files, source }) {
|
|
|
388863
389334
|
if (test.before) {
|
|
388864
389335
|
let beforePath = "";
|
|
388865
389336
|
if (config.relativePathBase === "file") {
|
|
388866
|
-
beforePath =
|
|
389337
|
+
beforePath = import_node_path10.default.resolve(import_node_path10.default.dirname(source), test.before);
|
|
388867
389338
|
} else {
|
|
388868
|
-
beforePath =
|
|
389339
|
+
beforePath = import_node_path10.default.resolve(test.before);
|
|
388869
389340
|
}
|
|
388870
|
-
if (!
|
|
389341
|
+
if (!import_node_fs10.default.existsSync(beforePath)) {
|
|
388871
389342
|
log(config, "debug", `${beforePath} is specified to run before a test but isn't a valid file. Skipping ${source}.`);
|
|
388872
389343
|
return false;
|
|
388873
389344
|
}
|
|
@@ -388875,18 +389346,18 @@ async function isValidSourceFile({ config, files, source }) {
|
|
|
388875
389346
|
if (test.after) {
|
|
388876
389347
|
let afterPath = "";
|
|
388877
389348
|
if (config.relativePathBase === "file") {
|
|
388878
|
-
afterPath =
|
|
389349
|
+
afterPath = import_node_path10.default.resolve(import_node_path10.default.dirname(source), test.after);
|
|
388879
389350
|
} else {
|
|
388880
|
-
afterPath =
|
|
389351
|
+
afterPath = import_node_path10.default.resolve(test.after);
|
|
388881
389352
|
}
|
|
388882
|
-
if (!
|
|
389353
|
+
if (!import_node_fs10.default.existsSync(afterPath)) {
|
|
388883
389354
|
log(config, "debug", `${afterPath} is specified to run after a test but isn't a valid file. Skipping ${source}.`);
|
|
388884
389355
|
return false;
|
|
388885
389356
|
}
|
|
388886
389357
|
}
|
|
388887
389358
|
}
|
|
388888
389359
|
}
|
|
388889
|
-
const extension =
|
|
389360
|
+
const extension = import_node_path10.default.extname(source).substring(1);
|
|
388890
389361
|
if (!allowedExtensions.includes(extension)) {
|
|
388891
389362
|
log(config, "debug", `${source} extension isn't specified in a \`config.fileTypes\` object. Skipping.`);
|
|
388892
389363
|
return false;
|
|
@@ -388895,11 +389366,11 @@ async function isValidSourceFile({ config, files, source }) {
|
|
|
388895
389366
|
}
|
|
388896
389367
|
async function processDitaMap({ config, source }) {
|
|
388897
389368
|
const hash = import_node_crypto3.default.createHash("md5").update(source).digest("hex");
|
|
388898
|
-
const tmpBase =
|
|
388899
|
-
const outputDir =
|
|
388900
|
-
if (!
|
|
389369
|
+
const tmpBase = import_node_path10.default.join(import_node_os5.default.tmpdir(), "doc-detective");
|
|
389370
|
+
const outputDir = import_node_path10.default.join(tmpBase, `ditamap_${hash}`);
|
|
389371
|
+
if (!import_node_fs10.default.existsSync(tmpBase)) {
|
|
388901
389372
|
log(config, "debug", `Creating temp directory: ${tmpBase}`);
|
|
388902
|
-
|
|
389373
|
+
import_node_fs10.default.mkdirSync(tmpBase, { recursive: true });
|
|
388903
389374
|
}
|
|
388904
389375
|
const ditaVersion = await spawnCommand("dita", ["--version"]);
|
|
388905
389376
|
if (ditaVersion.exitCode !== 0) {
|
|
@@ -389006,13 +389477,13 @@ async function qualifyFiles({ config }) {
|
|
|
389006
389477
|
let isFile = false;
|
|
389007
389478
|
let isDir = false;
|
|
389008
389479
|
try {
|
|
389009
|
-
isFile =
|
|
389010
|
-
isDir =
|
|
389480
|
+
isFile = import_node_fs10.default.statSync(source).isFile();
|
|
389481
|
+
isDir = import_node_fs10.default.statSync(source).isDirectory();
|
|
389011
389482
|
} catch {
|
|
389012
389483
|
log(config, "warning", `Cannot access path: ${source}. Skipping.`);
|
|
389013
389484
|
continue;
|
|
389014
389485
|
}
|
|
389015
|
-
if (isFile &&
|
|
389486
|
+
if (isFile && import_node_path10.default.extname(source) === ".ditamap" && !ignoredDitaMaps.some((ignored) => source.includes(ignored)) && config.processDitaMaps) {
|
|
389016
389487
|
const ditaOutput = await processDitaMap({ config, source });
|
|
389017
389488
|
if (ditaOutput) {
|
|
389018
389489
|
const currentIndex = sequence.findIndex((e) => e.source === source);
|
|
@@ -389021,7 +389492,7 @@ async function qualifyFiles({ config }) {
|
|
|
389021
389492
|
}
|
|
389022
389493
|
continue;
|
|
389023
389494
|
}
|
|
389024
|
-
const resolved =
|
|
389495
|
+
const resolved = import_node_path10.default.resolve(source);
|
|
389025
389496
|
if (isFile && await isValidSourceFile({ config, files, source: resolved })) {
|
|
389026
389497
|
files.push(resolved);
|
|
389027
389498
|
if (!phaseByFile.has(resolved))
|
|
@@ -389030,15 +389501,15 @@ async function qualifyFiles({ config }) {
|
|
|
389030
389501
|
dirs = [];
|
|
389031
389502
|
dirs[0] = source;
|
|
389032
389503
|
for (const dir of dirs) {
|
|
389033
|
-
const objects =
|
|
389504
|
+
const objects = import_node_fs10.default.readdirSync(dir);
|
|
389034
389505
|
for (const object of objects) {
|
|
389035
|
-
const content =
|
|
389506
|
+
const content = import_node_path10.default.resolve(dir + "/" + object);
|
|
389036
389507
|
if (content.includes("node_modules"))
|
|
389037
389508
|
continue;
|
|
389038
|
-
const isFile2 =
|
|
389039
|
-
const isDir2 =
|
|
389509
|
+
const isFile2 = import_node_fs10.default.statSync(content).isFile();
|
|
389510
|
+
const isDir2 = import_node_fs10.default.statSync(content).isDirectory();
|
|
389040
389511
|
if (isFile2 && await isValidSourceFile({ config, files, source: content })) {
|
|
389041
|
-
const resolved2 =
|
|
389512
|
+
const resolved2 = import_node_path10.default.resolve(content);
|
|
389042
389513
|
files.push(resolved2);
|
|
389043
389514
|
if (!phaseByFile.has(resolved2))
|
|
389044
389515
|
phaseByFile.set(resolved2, phase);
|
|
@@ -389055,12 +389526,12 @@ async function parseTests({ config, files }) {
|
|
|
389055
389526
|
let specs = [];
|
|
389056
389527
|
for (const file of files) {
|
|
389057
389528
|
log(config, "debug", `file: ${file}`);
|
|
389058
|
-
const extension =
|
|
389529
|
+
const extension = import_node_path10.default.extname(file).slice(1);
|
|
389059
389530
|
let content = "";
|
|
389060
389531
|
let rawContent;
|
|
389061
389532
|
if (extension === "json" || extension === "yaml" || extension === "yml") {
|
|
389062
389533
|
try {
|
|
389063
|
-
rawContent = await
|
|
389534
|
+
rawContent = await import_node_fs10.default.promises.readFile(file, "utf8");
|
|
389064
389535
|
if (extension === "json") {
|
|
389065
389536
|
content = JSON.parse(rawContent);
|
|
389066
389537
|
} else {
|
|
@@ -389078,7 +389549,7 @@ async function parseTests({ config, files }) {
|
|
|
389078
389549
|
content.specId = generateSpecId(file);
|
|
389079
389550
|
if (!content.contentPath)
|
|
389080
389551
|
content.contentPath = file;
|
|
389081
|
-
const specPhase = config._phaseByFile?.get(
|
|
389552
|
+
const specPhase = config._phaseByFile?.get(import_node_path10.default.resolve(file)) ?? "main";
|
|
389082
389553
|
content._phase = specPhase;
|
|
389083
389554
|
if (Array.isArray(content.tests)) {
|
|
389084
389555
|
const usedTestIds = new Set(content.tests.map((test) => test?.testId).filter((id) => id));
|
|
@@ -389246,7 +389717,7 @@ async function parseTests({ config, files }) {
|
|
|
389246
389717
|
let spec = {
|
|
389247
389718
|
specId: id,
|
|
389248
389719
|
contentPath: file,
|
|
389249
|
-
_phase: config._phaseByFile?.get(
|
|
389720
|
+
_phase: config._phaseByFile?.get(import_node_path10.default.resolve(file)) ?? "main",
|
|
389250
389721
|
tests: []
|
|
389251
389722
|
};
|
|
389252
389723
|
const fileType = config.fileTypes.find((fileType2) => fileType2.extensions.includes(extension));
|
|
@@ -389295,7 +389766,7 @@ async function parseTests({ config, files }) {
|
|
|
389295
389766
|
object: spec,
|
|
389296
389767
|
filePath: file
|
|
389297
389768
|
});
|
|
389298
|
-
spec._phase = config._phaseByFile?.get(
|
|
389769
|
+
spec._phase = config._phaseByFile?.get(import_node_path10.default.resolve(file)) ?? "main";
|
|
389299
389770
|
specs.push(spec);
|
|
389300
389771
|
}
|
|
389301
389772
|
}
|
|
@@ -389329,6 +389800,7 @@ function resolveContexts({ contexts, test, config }) {
|
|
|
389329
389800
|
}
|
|
389330
389801
|
if (browser.name === "safari")
|
|
389331
389802
|
browser.name = "webkit";
|
|
389803
|
+
browser.explicit = true;
|
|
389332
389804
|
return browser;
|
|
389333
389805
|
});
|
|
389334
389806
|
}
|
|
@@ -389508,7 +389980,7 @@ init_utils();
|
|
|
389508
389980
|
|
|
389509
389981
|
// dist/core/tests.js
|
|
389510
389982
|
var import_tree_kill3 = __toESM(require("tree-kill"), 1);
|
|
389511
|
-
var
|
|
389983
|
+
var import_node_fs22 = __toESM(require("node:fs"), 1);
|
|
389512
389984
|
init_loader();
|
|
389513
389985
|
init_browsers();
|
|
389514
389986
|
init_browserStepKeys();
|
|
@@ -390065,10 +390537,10 @@ init_validate();
|
|
|
390065
390537
|
init_loader();
|
|
390066
390538
|
|
|
390067
390539
|
// dist/core/tests/ffmpegRecorder.js
|
|
390068
|
-
var
|
|
390540
|
+
var import_node_child_process5 = require("node:child_process");
|
|
390069
390541
|
var import_node_os6 = __toESM(require("node:os"), 1);
|
|
390070
|
-
var
|
|
390071
|
-
var
|
|
390542
|
+
var import_node_path11 = __toESM(require("node:path"), 1);
|
|
390543
|
+
var import_node_fs11 = __toESM(require("node:fs"), 1);
|
|
390072
390544
|
var import_node_crypto5 = __toESM(require("node:crypto"), 1);
|
|
390073
390545
|
init_loader();
|
|
390074
390546
|
init_utils();
|
|
@@ -390147,7 +390619,7 @@ function browserCaptureTitle(contextId) {
|
|
|
390147
390619
|
return `RECORD_ME_${safeContextId(contextId)}`;
|
|
390148
390620
|
}
|
|
390149
390621
|
function browserDownloadDir(contextId) {
|
|
390150
|
-
return
|
|
390622
|
+
return import_node_path11.default.join(import_node_os6.default.tmpdir(), "doc-detective", "recordings", safeContextId(contextId));
|
|
390151
390623
|
}
|
|
390152
390624
|
function engineFields(record) {
|
|
390153
390625
|
const engine = record && typeof record === "object" ? record.engine : void 0;
|
|
@@ -390384,7 +390856,7 @@ async function detectMacScreenIndex(ffmpegPath) {
|
|
|
390384
390856
|
resolve(v);
|
|
390385
390857
|
};
|
|
390386
390858
|
try {
|
|
390387
|
-
proc = (0,
|
|
390859
|
+
proc = (0, import_node_child_process5.spawn)(ffmpegPath, ["-f", "avfoundation", "-list_devices", "true", "-i", ""], { stdio: ["ignore", "ignore", "pipe"] });
|
|
390388
390860
|
proc.stderr?.on("data", (d) => {
|
|
390389
390861
|
out += d.toString();
|
|
390390
390862
|
});
|
|
@@ -390413,7 +390885,7 @@ async function detectX11ScreenSize(display) {
|
|
|
390413
390885
|
};
|
|
390414
390886
|
try {
|
|
390415
390887
|
const env = display ? { ...process.env, DISPLAY: display } : process.env;
|
|
390416
|
-
proc = (0,
|
|
390888
|
+
proc = (0, import_node_child_process5.spawn)("xdpyinfo", [], { env, stdio: ["ignore", "pipe", "ignore"] });
|
|
390417
390889
|
proc.stdout?.on("data", (d) => {
|
|
390418
390890
|
out += d.toString();
|
|
390419
390891
|
});
|
|
@@ -390431,7 +390903,7 @@ async function detectX11ScreenSize(display) {
|
|
|
390431
390903
|
async function checkSystemBinary(name) {
|
|
390432
390904
|
return new Promise((resolve) => {
|
|
390433
390905
|
try {
|
|
390434
|
-
const proc = (0,
|
|
390906
|
+
const proc = (0, import_node_child_process5.spawn)(name, ["-help"], { stdio: "ignore" });
|
|
390435
390907
|
proc.on("error", () => resolve(false));
|
|
390436
390908
|
proc.on("close", () => resolve(true));
|
|
390437
390909
|
} catch {
|
|
@@ -390448,7 +390920,7 @@ async function startXvfb(display, opts = {}) {
|
|
|
390448
390920
|
const w = opts.width ?? defW;
|
|
390449
390921
|
const h = opts.height ?? defH;
|
|
390450
390922
|
const startMs = Date.now();
|
|
390451
|
-
const proc = (0,
|
|
390923
|
+
const proc = (0, import_node_child_process5.spawn)("Xvfb", [display, "-screen", "0", `${w}x${h}x24`, "-nolisten", "tcp"], { stdio: "ignore" });
|
|
390452
390924
|
let spawnErr = null;
|
|
390453
390925
|
proc.on("error", (e) => {
|
|
390454
390926
|
spawnErr = e;
|
|
@@ -390460,7 +390932,7 @@ async function startXvfb(display, opts = {}) {
|
|
|
390460
390932
|
if (proc.exitCode !== null)
|
|
390461
390933
|
throw new Error(`Xvfb exited early on ${display} (code ${proc.exitCode})`);
|
|
390462
390934
|
try {
|
|
390463
|
-
if (
|
|
390935
|
+
if (import_node_fs11.default.statSync(lock).mtimeMs >= startMs)
|
|
390464
390936
|
return proc;
|
|
390465
390937
|
} catch {
|
|
390466
390938
|
}
|
|
@@ -390545,11 +391017,11 @@ function hasUnresolvedMetaReference(expression, context) {
|
|
|
390545
391017
|
}
|
|
390546
391018
|
return false;
|
|
390547
391019
|
}
|
|
390548
|
-
function getMetaValue(
|
|
391020
|
+
function getMetaValue(path22, context) {
|
|
390549
391021
|
if (!context) {
|
|
390550
391022
|
return void 0;
|
|
390551
391023
|
}
|
|
390552
|
-
const [basePath, jsonPointer] =
|
|
391024
|
+
const [basePath, jsonPointer] = path22.split("#");
|
|
390553
391025
|
const resolvedPath = resolvePathTemplateVariables(basePath, context);
|
|
390554
391026
|
let value = getNestedProperty(context, resolvedPath);
|
|
390555
391027
|
if (jsonPointer && value) {
|
|
@@ -390566,9 +391038,9 @@ function getMetaValue(path21, context) {
|
|
|
390566
391038
|
}
|
|
390567
391039
|
return value;
|
|
390568
391040
|
}
|
|
390569
|
-
function resolvePathTemplateVariables(
|
|
391041
|
+
function resolvePathTemplateVariables(path22, context) {
|
|
390570
391042
|
const templateRegex = /\{\{(\w+)\}\}/g;
|
|
390571
|
-
return
|
|
391043
|
+
return path22.replace(templateRegex, (match, varName) => {
|
|
390572
391044
|
if (context && context.id && varName === "id") {
|
|
390573
391045
|
return context.id;
|
|
390574
391046
|
}
|
|
@@ -390605,10 +391077,10 @@ async function resolveEmbeddedExpressions(str, context) {
|
|
|
390605
391077
|
parts.push(str.slice(lastIdx));
|
|
390606
391078
|
return parts.join("");
|
|
390607
391079
|
}
|
|
390608
|
-
function getNestedProperty(obj,
|
|
390609
|
-
if (!obj || !
|
|
391080
|
+
function getNestedProperty(obj, path22) {
|
|
391081
|
+
if (!obj || !path22)
|
|
390610
391082
|
return void 0;
|
|
390611
|
-
const parts =
|
|
391083
|
+
const parts = path22.split(".");
|
|
390612
391084
|
let current = obj;
|
|
390613
391085
|
for (const part of parts) {
|
|
390614
391086
|
if (current === null || current === void 0)
|
|
@@ -391866,8 +392338,8 @@ async function waitForDOMStable(driver, idleTime, timeout) {
|
|
|
391866
392338
|
init_validate();
|
|
391867
392339
|
init_utils();
|
|
391868
392340
|
var import_tree_kill = __toESM(require("tree-kill"), 1);
|
|
391869
|
-
var
|
|
391870
|
-
var
|
|
392341
|
+
var import_node_fs12 = __toESM(require("node:fs"), 1);
|
|
392342
|
+
var import_node_path12 = __toESM(require("node:path"), 1);
|
|
391871
392343
|
async function runShell({ config, step, processRegistry }) {
|
|
391872
392344
|
const result = {
|
|
391873
392345
|
status: "PASS",
|
|
@@ -392009,19 +392481,19 @@ async function runShell({ config, step, processRegistry }) {
|
|
|
392009
392481
|
descriptions.push(stdioMatched ? `Found expected output (${step.runShell.stdio}) in stdio.` : isRegex ? `Couldn't find expected output (${step.runShell.stdio}) in actual output (stdout or stderr).` : `Couldn't find expected output (${step.runShell.stdio}) in stdio (stdout or stderr).`);
|
|
392010
392482
|
}
|
|
392011
392483
|
if (step.runShell.path) {
|
|
392012
|
-
const dir =
|
|
392013
|
-
if (!
|
|
392014
|
-
|
|
392484
|
+
const dir = import_node_path12.default.dirname(step.runShell.path);
|
|
392485
|
+
if (!import_node_fs12.default.existsSync(dir)) {
|
|
392486
|
+
import_node_fs12.default.mkdirSync(dir, { recursive: true });
|
|
392015
392487
|
}
|
|
392016
392488
|
let filePath = step.runShell.path;
|
|
392017
392489
|
log(config, "debug", `Saving stdio to file: ${filePath}`);
|
|
392018
|
-
if (!
|
|
392019
|
-
|
|
392490
|
+
if (!import_node_fs12.default.existsSync(filePath)) {
|
|
392491
|
+
import_node_fs12.default.writeFileSync(filePath, result.outputs.stdio.stdout);
|
|
392020
392492
|
} else {
|
|
392021
392493
|
if (step.runShell.overwrite == "false") {
|
|
392022
392494
|
descriptions.push(`Didn't save output. File already exists.`);
|
|
392023
392495
|
}
|
|
392024
|
-
const existingFile =
|
|
392496
|
+
const existingFile = import_node_fs12.default.readFileSync(filePath, "utf8");
|
|
392025
392497
|
const fractionalDiff = calculateFractionalDifference(existingFile, result.outputs.stdio.stdout);
|
|
392026
392498
|
log(config, "debug", `Fractional difference: ${fractionalDiff}`);
|
|
392027
392499
|
result.outputs.variation = fractionalDiff;
|
|
@@ -392031,13 +392503,13 @@ async function runShell({ config, step, processRegistry }) {
|
|
|
392031
392503
|
});
|
|
392032
392504
|
if (fractionalDiff > step.runShell.maxVariation) {
|
|
392033
392505
|
if (step.runShell.overwrite == "aboveVariation") {
|
|
392034
|
-
|
|
392506
|
+
import_node_fs12.default.writeFileSync(filePath, result.outputs.stdio.stdout);
|
|
392035
392507
|
descriptions.push(`Saved output to file.`);
|
|
392036
392508
|
}
|
|
392037
392509
|
descriptions.push(`The difference between the existing output and the new output (${fractionalDiff.toFixed(2)}) is greater than the max accepted variation (${step.runShell.maxVariation}).`);
|
|
392038
392510
|
} else {
|
|
392039
392511
|
if (step.runShell.overwrite == "true") {
|
|
392040
|
-
|
|
392512
|
+
import_node_fs12.default.writeFileSync(filePath, result.outputs.stdio.stdout);
|
|
392041
392513
|
descriptions.push(`Saved output to file.`);
|
|
392042
392514
|
}
|
|
392043
392515
|
descriptions.push(`Saved-file variation (${fractionalDiff.toFixed(2)}) is within the max accepted variation (${step.runShell.maxVariation}).`);
|
|
@@ -392235,8 +392707,8 @@ async function checkLink({ config, step }) {
|
|
|
392235
392707
|
// dist/core/tests/saveScreenshot.js
|
|
392236
392708
|
init_validate();
|
|
392237
392709
|
init_utils();
|
|
392238
|
-
var
|
|
392239
|
-
var
|
|
392710
|
+
var import_node_path13 = __toESM(require("node:path"), 1);
|
|
392711
|
+
var import_node_fs13 = __toESM(require("node:fs"), 1);
|
|
392240
392712
|
init_loader();
|
|
392241
392713
|
var _pngjs = null;
|
|
392242
392714
|
var _sharp = null;
|
|
@@ -392332,7 +392804,7 @@ async function saveScreenshot({ config, step, driver }) {
|
|
|
392332
392804
|
if (typeof step.screenshot.path === "undefined") {
|
|
392333
392805
|
step.screenshot.path = `${step.stepId}.png`;
|
|
392334
392806
|
if (step.screenshot.directory) {
|
|
392335
|
-
step.screenshot.path =
|
|
392807
|
+
step.screenshot.path = import_node_path13.default.resolve(step.screenshot.directory, step.screenshot.path);
|
|
392336
392808
|
}
|
|
392337
392809
|
}
|
|
392338
392810
|
step.screenshot = {
|
|
@@ -392368,17 +392840,17 @@ async function saveScreenshot({ config, step, driver }) {
|
|
|
392368
392840
|
} catch {
|
|
392369
392841
|
urlPathname = originalUrlPath;
|
|
392370
392842
|
}
|
|
392371
|
-
const rawBase =
|
|
392843
|
+
const rawBase = import_node_path13.default.basename(urlPathname.split("?")[0].split("#")[0].replace(/\\/g, "/"));
|
|
392372
392844
|
const safeBase = sanitizeFilesystemName(rawBase, `${step.stepId}.png`);
|
|
392373
|
-
dir =
|
|
392374
|
-
if (!
|
|
392375
|
-
|
|
392845
|
+
dir = import_node_path13.default.join(process.cwd(), "doc-detective-runs", getOrInitRunTimestamp(config));
|
|
392846
|
+
if (!import_node_fs13.default.existsSync(dir)) {
|
|
392847
|
+
import_node_fs13.default.mkdirSync(dir, { recursive: true });
|
|
392376
392848
|
}
|
|
392377
392849
|
const captureId = `${step.stepId || "screenshot"}_${Date.now()}`;
|
|
392378
|
-
filePath =
|
|
392379
|
-
const resolvedDir =
|
|
392380
|
-
const resolvedFile =
|
|
392381
|
-
if (!resolvedFile.startsWith(resolvedDir +
|
|
392850
|
+
filePath = import_node_path13.default.join(dir, `${captureId}_${safeBase}`);
|
|
392851
|
+
const resolvedDir = import_node_path13.default.resolve(dir);
|
|
392852
|
+
const resolvedFile = import_node_path13.default.resolve(filePath);
|
|
392853
|
+
if (!resolvedFile.startsWith(resolvedDir + import_node_path13.default.sep)) {
|
|
392382
392854
|
result.status = "FAIL";
|
|
392383
392855
|
result.description = `Refusing to write screenshot outside run folder: ${resolvedFile}`;
|
|
392384
392856
|
return result;
|
|
@@ -392387,11 +392859,11 @@ async function saveScreenshot({ config, step, driver }) {
|
|
|
392387
392859
|
log(config, "debug", `Screenshot path is a URL (${redactedUrl}); overwrite is ignored, running comparison only.`);
|
|
392388
392860
|
}
|
|
392389
392861
|
} else {
|
|
392390
|
-
dir =
|
|
392391
|
-
if (!
|
|
392392
|
-
|
|
392862
|
+
dir = import_node_path13.default.dirname(step.screenshot.path);
|
|
392863
|
+
if (!import_node_fs13.default.existsSync(dir)) {
|
|
392864
|
+
import_node_fs13.default.mkdirSync(dir, { recursive: true });
|
|
392393
392865
|
}
|
|
392394
|
-
if (
|
|
392866
|
+
if (import_node_fs13.default.existsSync(filePath)) {
|
|
392395
392867
|
if (step.screenshot.overwrite == "false") {
|
|
392396
392868
|
result.status = "SKIPPED";
|
|
392397
392869
|
result.description = `File already exists: ${filePath}`;
|
|
@@ -392399,7 +392871,7 @@ async function saveScreenshot({ config, step, driver }) {
|
|
|
392399
392871
|
return result;
|
|
392400
392872
|
} else {
|
|
392401
392873
|
existFilePath = filePath;
|
|
392402
|
-
filePath =
|
|
392874
|
+
filePath = import_node_path13.default.join(dir, `${step.stepId}_${Date.now()}.png`);
|
|
392403
392875
|
}
|
|
392404
392876
|
}
|
|
392405
392877
|
}
|
|
@@ -392497,8 +392969,8 @@ async function saveScreenshot({ config, step, driver }) {
|
|
|
392497
392969
|
} catch (error) {
|
|
392498
392970
|
result.status = "FAIL";
|
|
392499
392971
|
result.description = `Couldn't save screenshot. ${error}`;
|
|
392500
|
-
if (existFilePath && filePath !== existFilePath &&
|
|
392501
|
-
|
|
392972
|
+
if (existFilePath && filePath !== existFilePath && import_node_fs13.default.existsSync(filePath)) {
|
|
392973
|
+
import_node_fs13.default.unlinkSync(filePath);
|
|
392502
392974
|
}
|
|
392503
392975
|
return result;
|
|
392504
392976
|
} finally {
|
|
@@ -392553,7 +393025,7 @@ async function saveScreenshot({ config, step, driver }) {
|
|
|
392553
393025
|
rect.width = clamped.width;
|
|
392554
393026
|
rect.height = clamped.height;
|
|
392555
393027
|
log(config, "debug", { padded_rect: rect });
|
|
392556
|
-
const croppedPath =
|
|
393028
|
+
const croppedPath = import_node_path13.default.join(dir, `cropped_${step.stepId || Date.now()}.png`);
|
|
392557
393029
|
try {
|
|
392558
393030
|
await sharp(filePath).extract({
|
|
392559
393031
|
left: rect.x,
|
|
@@ -392561,12 +393033,12 @@ async function saveScreenshot({ config, step, driver }) {
|
|
|
392561
393033
|
width: rect.width,
|
|
392562
393034
|
height: rect.height
|
|
392563
393035
|
}).toFile(croppedPath);
|
|
392564
|
-
|
|
393036
|
+
import_node_fs13.default.renameSync(croppedPath, filePath);
|
|
392565
393037
|
} catch (error) {
|
|
392566
393038
|
result.status = "FAIL";
|
|
392567
393039
|
result.description = `Couldn't crop image. ${error}`;
|
|
392568
|
-
if (existFilePath && filePath !== existFilePath &&
|
|
392569
|
-
|
|
393040
|
+
if (existFilePath && filePath !== existFilePath && import_node_fs13.default.existsSync(filePath)) {
|
|
393041
|
+
import_node_fs13.default.unlinkSync(filePath);
|
|
392570
393042
|
}
|
|
392571
393043
|
return result;
|
|
392572
393044
|
}
|
|
@@ -392574,7 +393046,7 @@ async function saveScreenshot({ config, step, driver }) {
|
|
|
392574
393046
|
if (existFilePath) {
|
|
392575
393047
|
if (step.screenshot.overwrite == "true" && !isUrlPath) {
|
|
392576
393048
|
result.description += ` Overwrote existing file.`;
|
|
392577
|
-
|
|
393049
|
+
import_node_fs13.default.renameSync(filePath, existFilePath);
|
|
392578
393050
|
result.outputs.screenshotPath = existFilePath;
|
|
392579
393051
|
result.outputs.changed = true;
|
|
392580
393052
|
if (step.screenshot.sourceIntegration) {
|
|
@@ -392587,13 +393059,13 @@ async function saveScreenshot({ config, step, driver }) {
|
|
|
392587
393059
|
let img1;
|
|
392588
393060
|
let img2;
|
|
392589
393061
|
try {
|
|
392590
|
-
img1 = PNG.sync.read(
|
|
392591
|
-
img2 = PNG.sync.read(
|
|
393062
|
+
img1 = PNG.sync.read(import_node_fs13.default.readFileSync(existFilePath));
|
|
393063
|
+
img2 = PNG.sync.read(import_node_fs13.default.readFileSync(filePath));
|
|
392592
393064
|
} catch (error) {
|
|
392593
393065
|
result.status = "FAIL";
|
|
392594
393066
|
result.description = isUrlPath ? `Couldn't decode PNG for comparison. The URL reference (${redactedUrl}) may not be a valid PNG. ${error}` : `Couldn't decode PNG for comparison. ${error}`;
|
|
392595
|
-
if (!isUrlPath && filePath !== existFilePath &&
|
|
392596
|
-
|
|
393067
|
+
if (!isUrlPath && filePath !== existFilePath && import_node_fs13.default.existsSync(filePath)) {
|
|
393068
|
+
import_node_fs13.default.unlinkSync(filePath);
|
|
392597
393069
|
}
|
|
392598
393070
|
return result;
|
|
392599
393071
|
}
|
|
@@ -392605,8 +393077,8 @@ async function saveScreenshot({ config, step, driver }) {
|
|
|
392605
393077
|
});
|
|
392606
393078
|
if (!aspectRatioMatch) {
|
|
392607
393079
|
result.description = `Couldn't compare images. Images have different aspect ratios.`;
|
|
392608
|
-
if (!isUrlPath && filePath !== existFilePath &&
|
|
392609
|
-
|
|
393080
|
+
if (!isUrlPath && filePath !== existFilePath && import_node_fs13.default.existsSync(filePath)) {
|
|
393081
|
+
import_node_fs13.default.unlinkSync(filePath);
|
|
392610
393082
|
}
|
|
392611
393083
|
return await evaluateApplicable();
|
|
392612
393084
|
}
|
|
@@ -392633,8 +393105,8 @@ async function saveScreenshot({ config, step, driver }) {
|
|
|
392633
393105
|
} catch (error) {
|
|
392634
393106
|
result.status = "FAIL";
|
|
392635
393107
|
result.description = `Couldn't load screenshot comparison dependency (pixelmatch). ${error?.message ?? error}`;
|
|
392636
|
-
if (!isUrlPath && filePath !== existFilePath &&
|
|
392637
|
-
|
|
393108
|
+
if (!isUrlPath && filePath !== existFilePath && import_node_fs13.default.existsSync(filePath)) {
|
|
393109
|
+
import_node_fs13.default.unlinkSync(filePath);
|
|
392638
393110
|
}
|
|
392639
393111
|
return result;
|
|
392640
393112
|
}
|
|
@@ -392652,7 +393124,7 @@ async function saveScreenshot({ config, step, driver }) {
|
|
|
392652
393124
|
});
|
|
392653
393125
|
if (fractionalDiff > step.screenshot.maxVariation) {
|
|
392654
393126
|
if (step.screenshot.overwrite == "aboveVariation" && !isUrlPath) {
|
|
392655
|
-
|
|
393127
|
+
import_node_fs13.default.renameSync(filePath, existFilePath);
|
|
392656
393128
|
}
|
|
392657
393129
|
result.description += ` The difference between the existing screenshot and new screenshot (${fractionalDiff.toFixed(2)}) is greater than the max accepted variation (${step.screenshot.maxVariation}).`;
|
|
392658
393130
|
if (isUrlPath) {
|
|
@@ -392677,7 +393149,7 @@ async function saveScreenshot({ config, step, driver }) {
|
|
|
392677
393149
|
result.outputs.sourceIntegration = step.screenshot.sourceIntegration;
|
|
392678
393150
|
}
|
|
392679
393151
|
if (step.screenshot.overwrite != "true") {
|
|
392680
|
-
|
|
393152
|
+
import_node_fs13.default.unlinkSync(filePath);
|
|
392681
393153
|
}
|
|
392682
393154
|
}
|
|
392683
393155
|
}
|
|
@@ -392696,10 +393168,10 @@ async function saveScreenshot({ config, step, driver }) {
|
|
|
392696
393168
|
// dist/core/tests/startRecording.js
|
|
392697
393169
|
init_validate();
|
|
392698
393170
|
init_utils();
|
|
392699
|
-
var
|
|
393171
|
+
var import_node_child_process6 = require("node:child_process");
|
|
392700
393172
|
var import_node_crypto6 = require("node:crypto");
|
|
392701
|
-
var
|
|
392702
|
-
var
|
|
393173
|
+
var import_node_path14 = __toESM(require("node:path"), 1);
|
|
393174
|
+
var import_node_fs14 = __toESM(require("node:fs"), 1);
|
|
392703
393175
|
var import_node_os7 = __toESM(require("node:os"), 1);
|
|
392704
393176
|
async function startRecording({ config, context, step, driver }) {
|
|
392705
393177
|
let result = {
|
|
@@ -392727,7 +393199,7 @@ async function startRecording({ config, context, step, driver }) {
|
|
|
392727
393199
|
if (typeof step.record.path === "undefined") {
|
|
392728
393200
|
step.record.path = `${step.stepId}.mp4`;
|
|
392729
393201
|
if (step.record.directory) {
|
|
392730
|
-
step.record.path =
|
|
393202
|
+
step.record.path = import_node_path14.default.resolve(step.record.directory, step.record.path);
|
|
392731
393203
|
}
|
|
392732
393204
|
}
|
|
392733
393205
|
step.record = {
|
|
@@ -392735,18 +393207,18 @@ async function startRecording({ config, context, step, driver }) {
|
|
|
392735
393207
|
overwrite: step.record.overwrite || "false"
|
|
392736
393208
|
};
|
|
392737
393209
|
let filePath = step.record.path;
|
|
392738
|
-
const baseName =
|
|
392739
|
-
const dir =
|
|
392740
|
-
if (!
|
|
392741
|
-
|
|
393210
|
+
const baseName = import_node_path14.default.basename(filePath, import_node_path14.default.extname(filePath));
|
|
393211
|
+
const dir = import_node_path14.default.dirname(step.record.path);
|
|
393212
|
+
if (!import_node_fs14.default.existsSync(dir)) {
|
|
393213
|
+
import_node_fs14.default.mkdirSync(dir, { recursive: true });
|
|
392742
393214
|
}
|
|
392743
|
-
if (
|
|
393215
|
+
if (import_node_fs14.default.existsSync(filePath) && step.record.overwrite == "false") {
|
|
392744
393216
|
result.status = "SKIPPED";
|
|
392745
393217
|
result.description = `File already exists: ${filePath}`;
|
|
392746
393218
|
return result;
|
|
392747
393219
|
}
|
|
392748
393220
|
const normalizeActiveTarget = (p) => {
|
|
392749
|
-
const resolved =
|
|
393221
|
+
const resolved = import_node_path14.default.resolve(p);
|
|
392750
393222
|
return process.platform === "win32" || process.platform === "darwin" ? resolved.toLowerCase() : resolved;
|
|
392751
393223
|
};
|
|
392752
393224
|
const normalizedTarget = normalizeActiveTarget(filePath);
|
|
@@ -392774,13 +393246,13 @@ async function startRecording({ config, context, step, driver }) {
|
|
|
392774
393246
|
}
|
|
392775
393247
|
const captureTitle = browserCaptureTitle(context.contextId);
|
|
392776
393248
|
const downloadDir = browserDownloadDir(context.contextId);
|
|
392777
|
-
if (!
|
|
392778
|
-
|
|
393249
|
+
if (!import_node_fs14.default.existsSync(downloadDir)) {
|
|
393250
|
+
import_node_fs14.default.mkdirSync(downloadDir, { recursive: true });
|
|
392779
393251
|
}
|
|
392780
|
-
const downloadPath =
|
|
392781
|
-
if (
|
|
393252
|
+
const downloadPath = import_node_path14.default.join(downloadDir, `${baseName}.webm`);
|
|
393253
|
+
if (import_node_fs14.default.existsSync(downloadPath)) {
|
|
392782
393254
|
try {
|
|
392783
|
-
|
|
393255
|
+
import_node_fs14.default.unlinkSync(downloadPath);
|
|
392784
393256
|
} catch {
|
|
392785
393257
|
}
|
|
392786
393258
|
}
|
|
@@ -392907,10 +393379,10 @@ async function startRecording({ config, context, step, driver }) {
|
|
|
392907
393379
|
} catch {
|
|
392908
393380
|
}
|
|
392909
393381
|
}
|
|
392910
|
-
const tempDir =
|
|
392911
|
-
if (!
|
|
392912
|
-
|
|
392913
|
-
const tempPath =
|
|
393382
|
+
const tempDir = import_node_path14.default.join(import_node_os7.default.tmpdir(), "doc-detective", "recordings");
|
|
393383
|
+
if (!import_node_fs14.default.existsSync(tempDir))
|
|
393384
|
+
import_node_fs14.default.mkdirSync(tempDir, { recursive: true });
|
|
393385
|
+
const tempPath = import_node_path14.default.join(tempDir, `${safeContextId(context.contextId)}-${baseName}-${(0, import_node_crypto6.randomUUID)().slice(0, 8)}.mkv`);
|
|
392914
393386
|
let ffmpegPath;
|
|
392915
393387
|
try {
|
|
392916
393388
|
ffmpegPath = await getFfmpegPath({ cacheDir: config?.cacheDir });
|
|
@@ -392939,7 +393411,7 @@ async function startRecording({ config, context, step, driver }) {
|
|
|
392939
393411
|
screenSize
|
|
392940
393412
|
});
|
|
392941
393413
|
log(config, "debug", `ffmpeg recording: platform=${process.platform} target=${plan.target}${screenIndex !== void 0 ? ` screen=${screenIndex}` : ""}${context.__display ? ` display=${context.__display}` : ""} -> ${tempPath}`);
|
|
392942
|
-
const proc = (0,
|
|
393414
|
+
const proc = (0, import_node_child_process6.spawn)(ffmpegPath, args, { stdio: ["pipe", "ignore", "pipe"] });
|
|
392943
393415
|
let spawnError = null;
|
|
392944
393416
|
let stderrTail = "";
|
|
392945
393417
|
proc.stderr?.on("data", (d) => {
|
|
@@ -392968,9 +393440,9 @@ async function startRecording({ config, context, step, driver }) {
|
|
|
392968
393440
|
// dist/core/tests/stopRecording.js
|
|
392969
393441
|
init_validate();
|
|
392970
393442
|
init_utils();
|
|
392971
|
-
var
|
|
392972
|
-
var
|
|
392973
|
-
var
|
|
393443
|
+
var import_node_child_process7 = require("node:child_process");
|
|
393444
|
+
var import_node_path15 = __toESM(require("node:path"), 1);
|
|
393445
|
+
var import_node_fs15 = __toESM(require("node:fs"), 1);
|
|
392974
393446
|
async function stopRecording({ config, step, driver }) {
|
|
392975
393447
|
let result = {
|
|
392976
393448
|
status: "PASS",
|
|
@@ -393107,7 +393579,7 @@ async function transcode({ config, sourcePath, targetPath, deleteSource, crop })
|
|
|
393107
393579
|
const cy = `max(0\\,min(${crop.y}\\,ih-${ch}))`;
|
|
393108
393580
|
filters.push(`crop=w=${cw}:h=${ch}:x=${cx}:y=${cy}`);
|
|
393109
393581
|
}
|
|
393110
|
-
if (
|
|
393582
|
+
if (import_node_path15.default.extname(targetPath) === ".gif") {
|
|
393111
393583
|
filters.push("scale=iw:-1:flags=lanczos");
|
|
393112
393584
|
}
|
|
393113
393585
|
if (filters.length > 0) {
|
|
@@ -393116,7 +393588,7 @@ async function transcode({ config, sourcePath, targetPath, deleteSource, crop })
|
|
|
393116
393588
|
ffmpegArgs.push(`${targetPath}`);
|
|
393117
393589
|
const ffmpegPath = await getFfmpegPath({ cacheDir: config?.cacheDir });
|
|
393118
393590
|
await new Promise((resolve, reject) => {
|
|
393119
|
-
const child = (0,
|
|
393591
|
+
const child = (0, import_node_child_process7.spawn)(ffmpegPath, ffmpegArgs);
|
|
393120
393592
|
let stderr = "";
|
|
393121
393593
|
child.stderr?.on("data", (d) => {
|
|
393122
393594
|
stderr = (stderr + d.toString()).slice(-2e3);
|
|
@@ -393125,7 +393597,7 @@ async function transcode({ config, sourcePath, targetPath, deleteSource, crop })
|
|
|
393125
393597
|
if (code === 0) {
|
|
393126
393598
|
if (deleteSource && sourcePath !== targetPath) {
|
|
393127
393599
|
try {
|
|
393128
|
-
|
|
393600
|
+
import_node_fs15.default.unlinkSync(sourcePath);
|
|
393129
393601
|
} catch {
|
|
393130
393602
|
}
|
|
393131
393603
|
}
|
|
@@ -393144,7 +393616,7 @@ async function waitForStableFile(filePath, maxSeconds) {
|
|
|
393144
393616
|
for (let i = 0; i < deadline; i++) {
|
|
393145
393617
|
let size = -1;
|
|
393146
393618
|
try {
|
|
393147
|
-
size =
|
|
393619
|
+
size = import_node_fs15.default.statSync(filePath).size;
|
|
393148
393620
|
} catch {
|
|
393149
393621
|
size = -1;
|
|
393150
393622
|
}
|
|
@@ -393184,8 +393656,8 @@ async function loadVariables({ step }) {
|
|
|
393184
393656
|
// dist/core/tests/saveCookie.js
|
|
393185
393657
|
init_validate();
|
|
393186
393658
|
init_utils();
|
|
393187
|
-
var
|
|
393188
|
-
var
|
|
393659
|
+
var import_node_path16 = __toESM(require("node:path"), 1);
|
|
393660
|
+
var import_node_fs16 = __toESM(require("node:fs"), 1);
|
|
393189
393661
|
async function saveCookie({ config, step, driver }) {
|
|
393190
393662
|
let result = {
|
|
393191
393663
|
status: "PASS",
|
|
@@ -393202,7 +393674,7 @@ async function saveCookie({ config, step, driver }) {
|
|
|
393202
393674
|
let cookieName, filePath, directory, overwrite, domain, variable;
|
|
393203
393675
|
if (typeof step.saveCookie === "string") {
|
|
393204
393676
|
if (step.saveCookie.endsWith(".txt")) {
|
|
393205
|
-
cookieName =
|
|
393677
|
+
cookieName = import_node_path16.default.basename(step.saveCookie, ".txt");
|
|
393206
393678
|
filePath = step.saveCookie;
|
|
393207
393679
|
} else {
|
|
393208
393680
|
cookieName = step.saveCookie;
|
|
@@ -393251,15 +393723,15 @@ async function saveCookie({ config, step, driver }) {
|
|
|
393251
393723
|
}
|
|
393252
393724
|
if (filePath) {
|
|
393253
393725
|
const outputDirectory = directory || config.output || process.cwd();
|
|
393254
|
-
const fullPath =
|
|
393255
|
-
if (
|
|
393726
|
+
const fullPath = import_node_path16.default.resolve(outputDirectory, filePath);
|
|
393727
|
+
if (import_node_fs16.default.existsSync(fullPath) && !(overwrite === true || overwrite === "true")) {
|
|
393256
393728
|
result.status = "FAIL";
|
|
393257
393729
|
result.description = `File '${fullPath}' already exists and overwrite is not enabled.`;
|
|
393258
393730
|
return result;
|
|
393259
393731
|
}
|
|
393260
|
-
const dir =
|
|
393261
|
-
if (!
|
|
393262
|
-
|
|
393732
|
+
const dir = import_node_path16.default.dirname(fullPath);
|
|
393733
|
+
if (!import_node_fs16.default.existsSync(dir)) {
|
|
393734
|
+
import_node_fs16.default.mkdirSync(dir, { recursive: true });
|
|
393263
393735
|
}
|
|
393264
393736
|
if (targetCookie) {
|
|
393265
393737
|
const netscapeCookie = formatCookieForNetscape(targetCookie);
|
|
@@ -393267,11 +393739,11 @@ async function saveCookie({ config, step, driver }) {
|
|
|
393267
393739
|
# This is a cookie file saved by Doc Detective
|
|
393268
393740
|
${netscapeCookie}
|
|
393269
393741
|
`;
|
|
393270
|
-
|
|
393742
|
+
import_node_fs16.default.writeFileSync(fullPath, content, "utf8");
|
|
393271
393743
|
result.description = `Saved cookie '${cookieName}' to '${fullPath}'.`;
|
|
393272
393744
|
log(config, "debug", `Saved cookie '${cookieName}' to file: ${fullPath}`);
|
|
393273
393745
|
} else {
|
|
393274
|
-
|
|
393746
|
+
import_node_fs16.default.writeFileSync(fullPath, "# No cookie data\n", "utf8");
|
|
393275
393747
|
result.description = `Created empty cookie file at '${fullPath}'.`;
|
|
393276
393748
|
log(config, "debug", `Created empty cookie file: ${fullPath}`);
|
|
393277
393749
|
}
|
|
@@ -393298,8 +393770,8 @@ function formatCookieForNetscape(cookie) {
|
|
|
393298
393770
|
// dist/core/tests/loadCookie.js
|
|
393299
393771
|
init_validate();
|
|
393300
393772
|
init_utils();
|
|
393301
|
-
var
|
|
393302
|
-
var
|
|
393773
|
+
var import_node_path17 = __toESM(require("node:path"), 1);
|
|
393774
|
+
var import_node_fs17 = __toESM(require("node:fs"), 1);
|
|
393303
393775
|
async function loadCookie({ config, step, driver }) {
|
|
393304
393776
|
let result = {
|
|
393305
393777
|
status: "PASS",
|
|
@@ -393316,7 +393788,7 @@ async function loadCookie({ config, step, driver }) {
|
|
|
393316
393788
|
let cookieName, filePath, directory, domain, variable;
|
|
393317
393789
|
if (typeof step.loadCookie === "string") {
|
|
393318
393790
|
if (step.loadCookie.endsWith(".txt")) {
|
|
393319
|
-
cookieName =
|
|
393791
|
+
cookieName = import_node_path17.default.basename(step.loadCookie, ".txt");
|
|
393320
393792
|
filePath = step.loadCookie;
|
|
393321
393793
|
} else {
|
|
393322
393794
|
cookieName = step.loadCookie;
|
|
@@ -393329,9 +393801,9 @@ async function loadCookie({ config, step, driver }) {
|
|
|
393329
393801
|
domain = step.loadCookie.domain;
|
|
393330
393802
|
variable = step.loadCookie.variable;
|
|
393331
393803
|
if (filePath && !cookieName) {
|
|
393332
|
-
const ext =
|
|
393804
|
+
const ext = import_node_path17.default.extname(filePath).toLowerCase();
|
|
393333
393805
|
if (ext === ".txt") {
|
|
393334
|
-
cookieName =
|
|
393806
|
+
cookieName = import_node_path17.default.basename(filePath, ext);
|
|
393335
393807
|
}
|
|
393336
393808
|
}
|
|
393337
393809
|
}
|
|
@@ -393354,14 +393826,14 @@ async function loadCookie({ config, step, driver }) {
|
|
|
393354
393826
|
}
|
|
393355
393827
|
} else if (filePath) {
|
|
393356
393828
|
const inputDirectory = directory || config.output || process.cwd();
|
|
393357
|
-
const fullPath =
|
|
393358
|
-
if (!
|
|
393829
|
+
const fullPath = import_node_path17.default.resolve(inputDirectory, filePath);
|
|
393830
|
+
if (!import_node_fs17.default.existsSync(fullPath)) {
|
|
393359
393831
|
result.status = "FAIL";
|
|
393360
393832
|
result.description = `Cookie file '${fullPath}' not found`;
|
|
393361
393833
|
return result;
|
|
393362
393834
|
}
|
|
393363
393835
|
try {
|
|
393364
|
-
const fileContent =
|
|
393836
|
+
const fileContent = import_node_fs17.default.readFileSync(fullPath, "utf8");
|
|
393365
393837
|
const cookies = parseNetscapeCookieFile(fileContent);
|
|
393366
393838
|
if (cookies.length === 0) {
|
|
393367
393839
|
result.status = "FAIL";
|
|
@@ -393527,8 +393999,8 @@ function isDomainCompatible(currentDomain, cookieDomain) {
|
|
|
393527
393999
|
// dist/core/tests/httpRequest.js
|
|
393528
394000
|
init_validate();
|
|
393529
394001
|
var import_axios5 = __toESM(require("axios"), 1);
|
|
393530
|
-
var
|
|
393531
|
-
var
|
|
394002
|
+
var import_node_fs18 = __toESM(require("node:fs"), 1);
|
|
394003
|
+
var import_node_path18 = __toESM(require("node:path"), 1);
|
|
393532
394004
|
var import_ajv2 = __toESM(require("ajv"), 1);
|
|
393533
394005
|
init_openapi();
|
|
393534
394006
|
init_utils();
|
|
@@ -393555,9 +394027,9 @@ async function httpRequest({ config, step, openApiDefinitions = [] }) {
|
|
|
393555
394027
|
delete step.httpRequest.openApi.definition;
|
|
393556
394028
|
} else if (openApiDefinitions.length > 0) {
|
|
393557
394029
|
for (const openApiConfig of openApiDefinitions) {
|
|
393558
|
-
for (const
|
|
393559
|
-
for (const method in openApiConfig.definition.paths[
|
|
393560
|
-
if (openApiConfig.definition.paths[
|
|
394030
|
+
for (const path22 in openApiConfig.definition.paths) {
|
|
394031
|
+
for (const method in openApiConfig.definition.paths[path22]) {
|
|
394032
|
+
if (openApiConfig.definition.paths[path22][method].operationId === step.httpRequest.openApi.operationId) {
|
|
393561
394033
|
openApiDefinition = openApiConfig.definition;
|
|
393562
394034
|
step.httpRequest.openApi = {
|
|
393563
394035
|
...openApiConfig,
|
|
@@ -393859,20 +394331,20 @@ async function httpRequest({ config, step, openApiDefinitions = [] }) {
|
|
|
393859
394331
|
}
|
|
393860
394332
|
}
|
|
393861
394333
|
if (step.httpRequest.path) {
|
|
393862
|
-
const dir =
|
|
393863
|
-
if (!
|
|
393864
|
-
|
|
394334
|
+
const dir = import_node_path18.default.dirname(step.httpRequest.path);
|
|
394335
|
+
if (!import_node_fs18.default.existsSync(dir)) {
|
|
394336
|
+
import_node_fs18.default.mkdirSync(dir, { recursive: true });
|
|
393865
394337
|
}
|
|
393866
394338
|
let filePath = step.httpRequest.path;
|
|
393867
394339
|
log(config, "debug", `Saving output to file: ${filePath}`);
|
|
393868
|
-
if (!
|
|
393869
|
-
await
|
|
394340
|
+
if (!import_node_fs18.default.existsSync(filePath)) {
|
|
394341
|
+
await import_node_fs18.default.promises.writeFile(filePath, JSON.stringify(response.data, null, 2));
|
|
393870
394342
|
descriptions.push(`Saved output to file.`);
|
|
393871
394343
|
} else {
|
|
393872
394344
|
if (step.httpRequest.overwrite == "false") {
|
|
393873
394345
|
descriptions.push(`Didn't save output. File already exists.`);
|
|
393874
394346
|
}
|
|
393875
|
-
const existingFile =
|
|
394347
|
+
const existingFile = import_node_fs18.default.readFileSync(filePath, "utf8");
|
|
393876
394348
|
const fractionalDiff = calculateFractionalDifference(existingFile, JSON.stringify(response.data, null, 2));
|
|
393877
394349
|
log(config, "debug", `Fractional difference: ${fractionalDiff}`);
|
|
393878
394350
|
result.outputs.variation = fractionalDiff;
|
|
@@ -393882,13 +394354,13 @@ async function httpRequest({ config, step, openApiDefinitions = [] }) {
|
|
|
393882
394354
|
});
|
|
393883
394355
|
if (fractionalDiff > step.httpRequest.maxVariation) {
|
|
393884
394356
|
if (step.httpRequest.overwrite == "aboveVariation") {
|
|
393885
|
-
await
|
|
394357
|
+
await import_node_fs18.default.promises.writeFile(filePath, JSON.stringify(response.data, null, 2));
|
|
393886
394358
|
descriptions.push(`Saved response to file.`);
|
|
393887
394359
|
}
|
|
393888
394360
|
descriptions.push(`The difference between the existing saved response and the new response (${fractionalDiff.toFixed(2)}) is greater than the max accepted variation (${step.httpRequest.maxVariation}).`);
|
|
393889
394361
|
} else {
|
|
393890
394362
|
if (step.httpRequest.overwrite == "true") {
|
|
393891
|
-
|
|
394363
|
+
import_node_fs18.default.writeFileSync(filePath, JSON.stringify(response.data, null, 2));
|
|
393892
394364
|
descriptions.push(`Saved response to file.`);
|
|
393893
394365
|
}
|
|
393894
394366
|
}
|
|
@@ -393901,8 +394373,8 @@ async function httpRequest({ config, step, openApiDefinitions = [] }) {
|
|
|
393901
394373
|
result.description = descriptions.join(" ").trim();
|
|
393902
394374
|
return result;
|
|
393903
394375
|
}
|
|
393904
|
-
function fieldExistsAtPath(obj,
|
|
393905
|
-
const segments =
|
|
394376
|
+
function fieldExistsAtPath(obj, path22) {
|
|
394377
|
+
const segments = path22.match(/[^.[\]]+/g);
|
|
393906
394378
|
if (!segments) {
|
|
393907
394379
|
return false;
|
|
393908
394380
|
}
|
|
@@ -394071,8 +394543,8 @@ async function clickElement({ config, step, driver }) {
|
|
|
394071
394543
|
// dist/core/tests/runCode.js
|
|
394072
394544
|
init_validate();
|
|
394073
394545
|
init_utils();
|
|
394074
|
-
var
|
|
394075
|
-
var
|
|
394546
|
+
var import_node_fs19 = __toESM(require("node:fs"), 1);
|
|
394547
|
+
var import_node_path19 = __toESM(require("node:path"), 1);
|
|
394076
394548
|
var import_node_os8 = __toESM(require("node:os"), 1);
|
|
394077
394549
|
function createTempScript(code, language) {
|
|
394078
394550
|
let extension;
|
|
@@ -394093,9 +394565,9 @@ function createTempScript(code, language) {
|
|
|
394093
394565
|
extension = "";
|
|
394094
394566
|
}
|
|
394095
394567
|
const tmpDir = import_node_os8.default.tmpdir();
|
|
394096
|
-
const tmpFile =
|
|
394568
|
+
const tmpFile = import_node_path19.default.join(tmpDir, `doc-detective-${Date.now()}${extension}`);
|
|
394097
394569
|
try {
|
|
394098
|
-
|
|
394570
|
+
import_node_fs19.default.writeFileSync(tmpFile, code);
|
|
394099
394571
|
} catch (error) {
|
|
394100
394572
|
throw new Error(`Failed to create temporary script: ${error.message}`);
|
|
394101
394573
|
}
|
|
@@ -394176,7 +394648,7 @@ async function runCode({ config, step, processRegistry }) {
|
|
|
394176
394648
|
if (typeof step.runCode.stdio !== "undefined")
|
|
394177
394649
|
runShellOptions.stdio = step.runCode.stdio;
|
|
394178
394650
|
if (typeof step.runCode.path !== "undefined") {
|
|
394179
|
-
runShellOptions.path = step.runCode.directory ?
|
|
394651
|
+
runShellOptions.path = step.runCode.directory ? import_node_path19.default.join(step.runCode.directory, step.runCode.path) : step.runCode.path;
|
|
394180
394652
|
}
|
|
394181
394653
|
if (step.runCode.background) {
|
|
394182
394654
|
runShellOptions.background = step.runCode.background;
|
|
@@ -394204,7 +394676,7 @@ async function runCode({ config, step, processRegistry }) {
|
|
|
394204
394676
|
} finally {
|
|
394205
394677
|
if (!deferTempCleanup) {
|
|
394206
394678
|
try {
|
|
394207
|
-
|
|
394679
|
+
import_node_fs19.default.unlinkSync(scriptPath);
|
|
394208
394680
|
log(config, "debug", `Removed temporary script: ${scriptPath}`);
|
|
394209
394681
|
} catch (error) {
|
|
394210
394682
|
log(config, "warning", `Failed to remove temporary script: ${scriptPath}`);
|
|
@@ -394218,7 +394690,7 @@ async function runCode({ config, step, processRegistry }) {
|
|
|
394218
394690
|
init_validate();
|
|
394219
394691
|
init_utils();
|
|
394220
394692
|
var import_tree_kill2 = __toESM(require("tree-kill"), 1);
|
|
394221
|
-
var
|
|
394693
|
+
var import_node_fs20 = __toESM(require("node:fs"), 1);
|
|
394222
394694
|
function resolveSurfaceNames(ref) {
|
|
394223
394695
|
const items = Array.isArray(ref) ? ref : [ref];
|
|
394224
394696
|
const names = [];
|
|
@@ -394265,7 +394737,7 @@ async function closeSurface({ config, step, processRegistry }) {
|
|
|
394265
394737
|
}
|
|
394266
394738
|
if (entry.tempPath) {
|
|
394267
394739
|
try {
|
|
394268
|
-
|
|
394740
|
+
import_node_fs20.default.unlinkSync(entry.tempPath);
|
|
394269
394741
|
} catch {
|
|
394270
394742
|
}
|
|
394271
394743
|
}
|
|
@@ -394291,8 +394763,8 @@ async function closeSurface({ config, step, processRegistry }) {
|
|
|
394291
394763
|
// dist/core/tests/runBrowserScript.js
|
|
394292
394764
|
init_validate();
|
|
394293
394765
|
init_utils();
|
|
394294
|
-
var
|
|
394295
|
-
var
|
|
394766
|
+
var import_node_fs21 = __toESM(require("node:fs"), 1);
|
|
394767
|
+
var import_node_path20 = __toESM(require("node:path"), 1);
|
|
394296
394768
|
async function runBrowserScript({ config, step, driver }) {
|
|
394297
394769
|
const result = {
|
|
394298
394770
|
status: "PASS",
|
|
@@ -394358,16 +394830,16 @@ async function runBrowserScript({ config, step, driver }) {
|
|
|
394358
394830
|
}
|
|
394359
394831
|
if (step.runBrowserScript.path) {
|
|
394360
394832
|
try {
|
|
394361
|
-
const dir =
|
|
394362
|
-
if (!
|
|
394363
|
-
|
|
394833
|
+
const dir = import_node_path20.default.dirname(step.runBrowserScript.path);
|
|
394834
|
+
if (!import_node_fs21.default.existsSync(dir)) {
|
|
394835
|
+
import_node_fs21.default.mkdirSync(dir, { recursive: true });
|
|
394364
394836
|
}
|
|
394365
394837
|
const filePath = step.runBrowserScript.path;
|
|
394366
394838
|
log(config, "debug", `Saving script result to file: ${filePath}`);
|
|
394367
|
-
if (!
|
|
394368
|
-
|
|
394839
|
+
if (!import_node_fs21.default.existsSync(filePath)) {
|
|
394840
|
+
import_node_fs21.default.writeFileSync(filePath, serialized);
|
|
394369
394841
|
} else {
|
|
394370
|
-
const existingFile =
|
|
394842
|
+
const existingFile = import_node_fs21.default.readFileSync(filePath, "utf8");
|
|
394371
394843
|
const fractionalDiff = calculateFractionalDifference(existingFile, serialized);
|
|
394372
394844
|
log(config, "debug", `Fractional difference: ${fractionalDiff}`);
|
|
394373
394845
|
result.outputs.variation = fractionalDiff;
|
|
@@ -394377,7 +394849,7 @@ async function runBrowserScript({ config, step, driver }) {
|
|
|
394377
394849
|
});
|
|
394378
394850
|
if (fractionalDiff > step.runBrowserScript.maxVariation) {
|
|
394379
394851
|
if (step.runBrowserScript.overwrite == "aboveVariation" || step.runBrowserScript.overwrite == "true") {
|
|
394380
|
-
|
|
394852
|
+
import_node_fs21.default.writeFileSync(filePath, serialized);
|
|
394381
394853
|
descriptions.push(`Saved output to file.`);
|
|
394382
394854
|
} else {
|
|
394383
394855
|
descriptions.push(`Didn't overwrite the existing file.`);
|
|
@@ -394385,7 +394857,7 @@ async function runBrowserScript({ config, step, driver }) {
|
|
|
394385
394857
|
descriptions.push(`The difference between the existing output and the new output (${fractionalDiff.toFixed(2)}) is greater than the max accepted variation (${step.runBrowserScript.maxVariation}).`);
|
|
394386
394858
|
} else {
|
|
394387
394859
|
if (step.runBrowserScript.overwrite == "true") {
|
|
394388
|
-
|
|
394860
|
+
import_node_fs21.default.writeFileSync(filePath, serialized);
|
|
394389
394861
|
descriptions.push(`Saved output to file.`);
|
|
394390
394862
|
}
|
|
394391
394863
|
}
|
|
@@ -394534,8 +395006,8 @@ async function dragAndDropElement({ config, step, driver }) {
|
|
|
394534
395006
|
}
|
|
394535
395007
|
|
|
394536
395008
|
// dist/core/tests.js
|
|
394537
|
-
var
|
|
394538
|
-
var
|
|
395009
|
+
var import_node_path21 = __toESM(require("node:path"), 1);
|
|
395010
|
+
var import_node_child_process8 = require("node:child_process");
|
|
394539
395011
|
var import_node_crypto7 = require("node:crypto");
|
|
394540
395012
|
init_appium();
|
|
394541
395013
|
init_config();
|
|
@@ -394688,7 +395160,7 @@ function getIntegrationConfig(config, sourceIntegration) {
|
|
|
394688
395160
|
var import_node_http2 = __toESM(require("node:http"), 1);
|
|
394689
395161
|
var import_node_https2 = __toESM(require("node:https"), 1);
|
|
394690
395162
|
var import_node_url6 = require("node:url");
|
|
394691
|
-
var __dirname5 =
|
|
395163
|
+
var __dirname5 = import_node_path21.default.dirname((0, import_node_url6.fileURLToPath)(importMetaUrl));
|
|
394692
395164
|
var KNOWN_BROWSERS = ["firefox", "chrome", "safari", "webkit"];
|
|
394693
395165
|
function combinationKey(context) {
|
|
394694
395166
|
const rawName = context?.browser?.name;
|
|
@@ -394835,6 +395307,49 @@ function getDefaultBrowser({ runnerDetails }) {
|
|
|
394835
395307
|
}
|
|
394836
395308
|
return browser;
|
|
394837
395309
|
}
|
|
395310
|
+
function normalizeBrowserName(name) {
|
|
395311
|
+
return name === "webkit" ? "safari" : name ?? "";
|
|
395312
|
+
}
|
|
395313
|
+
function buildFallbackCandidates({ requestedName, explicit, policy, availableApps }) {
|
|
395314
|
+
const available = new Set((availableApps || []).map((a) => a.name));
|
|
395315
|
+
const requestedNorm = normalizeBrowserName(requestedName);
|
|
395316
|
+
const candidates = [];
|
|
395317
|
+
if (available.has(requestedNorm))
|
|
395318
|
+
candidates.push(requestedName);
|
|
395319
|
+
const fallbackAllowed = policy === "auto" || policy === "explicit" && !explicit;
|
|
395320
|
+
if (fallbackAllowed) {
|
|
395321
|
+
for (const name of ["firefox", "chrome", "safari"]) {
|
|
395322
|
+
if (name === requestedNorm)
|
|
395323
|
+
continue;
|
|
395324
|
+
if (available.has(name))
|
|
395325
|
+
candidates.push(name);
|
|
395326
|
+
}
|
|
395327
|
+
}
|
|
395328
|
+
return candidates;
|
|
395329
|
+
}
|
|
395330
|
+
function resolveBrowserFallbackPolicy({ context, config }) {
|
|
395331
|
+
return context?.browserFallback || config?.browserFallback || "auto";
|
|
395332
|
+
}
|
|
395333
|
+
function shouldRepairBeforeFallback({ candidateName, requestedName, installAttempts }) {
|
|
395334
|
+
if (normalizeBrowserName(candidateName) !== normalizeBrowserName(requestedName)) {
|
|
395335
|
+
return false;
|
|
395336
|
+
}
|
|
395337
|
+
if (requiredBrowserAssets(candidateName).length === 0)
|
|
395338
|
+
return false;
|
|
395339
|
+
return !installAttempts.has((candidateName ?? "<none>").toLowerCase());
|
|
395340
|
+
}
|
|
395341
|
+
function driverSkipDiagnostic({ requestedName, platform, platformMatches, attemptedFallback, lastError }) {
|
|
395342
|
+
if (!platformMatches) {
|
|
395343
|
+
return `Skipping context on '${platform}': this context targets a different platform.`;
|
|
395344
|
+
}
|
|
395345
|
+
const driverHint = normalizeBrowserName(requestedName) === "firefox" ? "geckodriver" : normalizeBrowserName(requestedName) === "chrome" ? "chromedriver" : normalizeBrowserName(requestedName) === "safari" ? "safaridriver" : "driver";
|
|
395346
|
+
let msg = `Skipping context: could not start a browser session for '${requestedName}' on '${platform}'`;
|
|
395347
|
+
msg += attemptedFallback ? `, and no other available browser could start either.` : ` and cross-browser fallback is disabled or unavailable.`;
|
|
395348
|
+
if (lastError)
|
|
395349
|
+
msg += ` Last error: ${lastError}`;
|
|
395350
|
+
msg += ` A present-but-broken driver (for example a partially downloaded ${driverHint}) can cause this; reinstall the driver or its browser.`;
|
|
395351
|
+
return msg;
|
|
395352
|
+
}
|
|
394838
395353
|
async function setViewportSize(context, driver) {
|
|
394839
395354
|
if (context.browser?.viewport?.width || context.browser?.viewport?.height) {
|
|
394840
395355
|
const viewportSize = await driver.execute("return { width: window.innerWidth, height: window.innerHeight }", []);
|
|
@@ -394983,7 +395498,7 @@ async function runSpecs({ resolvedTests }) {
|
|
|
394983
395498
|
const runDir = getRunOutputDir(config, {
|
|
394984
395499
|
create: runArchivesArtifacts(config, specs)
|
|
394985
395500
|
});
|
|
394986
|
-
const runId =
|
|
395501
|
+
const runId = import_node_path21.default.basename(runDir).replace(/^run-/, "");
|
|
394987
395502
|
const report = {
|
|
394988
395503
|
runId,
|
|
394989
395504
|
runDir,
|
|
@@ -395218,7 +395733,7 @@ async function runSpecs({ resolvedTests }) {
|
|
|
395218
395733
|
}
|
|
395219
395734
|
if (entry?.tempPath) {
|
|
395220
395735
|
try {
|
|
395221
|
-
|
|
395736
|
+
import_node_fs22.default.unlinkSync(entry.tempPath);
|
|
395222
395737
|
} catch {
|
|
395223
395738
|
}
|
|
395224
395739
|
}
|
|
@@ -395607,7 +396122,9 @@ async function warmUpContexts({ jobs, config, runnerDetails, appiumPool, install
|
|
|
395607
396122
|
deps: {
|
|
395608
396123
|
ensureBrowser: (asset, options) => ensureBrowserInstalled(asset, options),
|
|
395609
396124
|
log
|
|
395610
|
-
}
|
|
396125
|
+
},
|
|
396126
|
+
// Repair a present-but-broken driver, not just install-if-missing.
|
|
396127
|
+
repair: true
|
|
395611
396128
|
});
|
|
395612
396129
|
if (firstAttempt && (outcome === "installed" || outcome === "failed")) {
|
|
395613
396130
|
clearAppCache(config);
|
|
@@ -395775,7 +396292,7 @@ function buildAutoRecordStep({ config, spec, test, context }) {
|
|
|
395775
396292
|
return null;
|
|
395776
396293
|
const runDir = getRunOutputDir(config, { create: false });
|
|
395777
396294
|
const fileName = `${capPathSegment(sanitizeFilesystemName(String(context.contextId ?? ""), "context"))}.mp4`;
|
|
395778
|
-
const recordPath =
|
|
396295
|
+
const recordPath = import_node_path21.default.join(runDir, "recordings", capPathSegment(sanitizeFilesystemName(String(spec.specId ?? ""), "spec")), capPathSegment(sanitizeFilesystemName(String(test.testId ?? ""), "test")), fileName);
|
|
395779
396296
|
return {
|
|
395780
396297
|
record: { path: recordPath, overwrite: "true", engine: "ffmpeg" },
|
|
395781
396298
|
description: "Automatic full-context recording",
|
|
@@ -395793,7 +396310,7 @@ async function captureAutoScreenshot({ config, driver, spec, test, context, step
|
|
|
395793
396310
|
const action = BROWSER_STEP_KEYS.find((key) => typeof step[key] !== "undefined") || "step";
|
|
395794
396311
|
const sanitizedTestId = sanitizeFilesystemName(String(test.testId ?? ""), "test");
|
|
395795
396312
|
const runDir = getRunOutputDir(config);
|
|
395796
|
-
const dir =
|
|
396313
|
+
const dir = import_node_path21.default.join(runDir, "screenshots", capPathSegment(sanitizeFilesystemName(String(spec.specId ?? ""), "spec")), capPathSegment(sanitizedTestId), capPathSegment(sanitizeFilesystemName(String(context.contextId ?? ""), "context")));
|
|
395797
396314
|
const stepIdString = sanitizeFilesystemName(String(step.stepId ?? ""), "step");
|
|
395798
396315
|
const stepRef = capPathSegment(stepIdString.startsWith(`${sanitizedTestId}~`) ? stepIdString.slice(sanitizedTestId.length + 1) : stepIdString);
|
|
395799
396316
|
const pad = Math.max(2, String(stepCount).length);
|
|
@@ -395802,7 +396319,7 @@ async function captureAutoScreenshot({ config, driver, spec, test, context, step
|
|
|
395802
396319
|
stepId: `${step.stepId}_auto`,
|
|
395803
396320
|
description: "Automatic post-step screenshot",
|
|
395804
396321
|
screenshot: {
|
|
395805
|
-
path:
|
|
396322
|
+
path: import_node_path21.default.join(dir, fileName),
|
|
395806
396323
|
overwrite: "true"
|
|
395807
396324
|
}
|
|
395808
396325
|
};
|
|
@@ -395815,7 +396332,7 @@ async function captureAutoScreenshot({ config, driver, spec, test, context, step
|
|
|
395815
396332
|
log(config, "warning", `Auto screenshot failed after step ${step.stepId}: ${captureResult.description}`);
|
|
395816
396333
|
return null;
|
|
395817
396334
|
}
|
|
395818
|
-
return
|
|
396335
|
+
return import_node_path21.default.relative(runDir, screenshotStep.screenshot.path).split(import_node_path21.default.sep).join("/");
|
|
395819
396336
|
} catch (error) {
|
|
395820
396337
|
log(config, "warning", `Auto screenshot failed after step ${step.stepId}: ${error?.message ?? error}`);
|
|
395821
396338
|
return null;
|
|
@@ -395859,6 +396376,7 @@ async function runContext({ config, spec, test, context, runnerDetails, appiumPo
|
|
|
395859
396376
|
platform
|
|
395860
396377
|
});
|
|
395861
396378
|
let freshInstallRedetected = false;
|
|
396379
|
+
let freshInstallOutcome;
|
|
395862
396380
|
if (!supportedContext && context.platform === platform && // Mirror isSupportedContext's own guard: isDriverRequired iterates
|
|
395863
396381
|
// context.steps, so a malformed context without a steps array would
|
|
395864
396382
|
// otherwise crash here instead of skipping cleanly.
|
|
@@ -395871,10 +396389,15 @@ async function runContext({ config, spec, test, context, runnerDetails, appiumPo
|
|
|
395871
396389
|
deps: {
|
|
395872
396390
|
ensureBrowser: (asset, options) => ensureBrowserInstalled(asset, options),
|
|
395873
396391
|
log
|
|
395874
|
-
}
|
|
396392
|
+
},
|
|
396393
|
+
// The browser is unavailable here — possibly because its driver is
|
|
396394
|
+
// present-but-broken — so repair (force a clean driver reinstall +
|
|
396395
|
+
// re-validation), not just install-if-missing.
|
|
396396
|
+
repair: true
|
|
395875
396397
|
});
|
|
395876
396398
|
if (firstAttempt && (outcome === "installed" || outcome === "failed")) {
|
|
395877
396399
|
freshInstallRedetected = true;
|
|
396400
|
+
freshInstallOutcome = outcome;
|
|
395878
396401
|
clearAppCache(config);
|
|
395879
396402
|
availableApps = await getAvailableApps({ config });
|
|
395880
396403
|
runnerDetails.availableApps = availableApps;
|
|
@@ -395885,9 +396408,35 @@ async function runContext({ config, spec, test, context, runnerDetails, appiumPo
|
|
|
395885
396408
|
});
|
|
395886
396409
|
}
|
|
395887
396410
|
}
|
|
395888
|
-
|
|
395889
|
-
|
|
395890
|
-
|
|
396411
|
+
const platformMatches = context.platform === platform;
|
|
396412
|
+
const requestedBrowserName = context.browser?.name;
|
|
396413
|
+
const explicitlyRequested = context.browser?.explicit === true;
|
|
396414
|
+
const fallbackPolicy = resolveBrowserFallbackPolicy({ context, config });
|
|
396415
|
+
const driverRequired = isDriverRequired({ test: context });
|
|
396416
|
+
const candidateEngines = platformMatches && driverRequired ? buildFallbackCandidates({
|
|
396417
|
+
requestedName: requestedBrowserName,
|
|
396418
|
+
explicit: explicitlyRequested,
|
|
396419
|
+
policy: fallbackPolicy,
|
|
396420
|
+
availableApps
|
|
396421
|
+
}) : [];
|
|
396422
|
+
if (driverRequired && candidateEngines.length === 0) {
|
|
396423
|
+
const errorMessage = freshInstallRedetected ? freshInstallOutcome === "installed" ? `Skipping context '${requestedBrowserName}' on '${context.platform}': the missing browser dependency was installed but still could not be detected.` : `Skipping context '${requestedBrowserName}' on '${context.platform}': the on-demand install/repair of its browser dependency failed.` : driverSkipDiagnostic({
|
|
396424
|
+
requestedName: requestedBrowserName ?? "<none>",
|
|
396425
|
+
// driverSkipDiagnostic treats `platform` as the *current* runner
|
|
396426
|
+
// platform (as in the !startedName path), so pass that — not the
|
|
396427
|
+
// context's target platform — or the mismatch message mislabels it.
|
|
396428
|
+
platform,
|
|
396429
|
+
platformMatches,
|
|
396430
|
+
attemptedFallback: false
|
|
396431
|
+
});
|
|
396432
|
+
clog(platformMatches ? "warning" : "info", errorMessage);
|
|
396433
|
+
contextReport.result = "SKIPPED";
|
|
396434
|
+
contextReport.resultDescription = errorMessage;
|
|
396435
|
+
return contextReport;
|
|
396436
|
+
}
|
|
396437
|
+
if (!driverRequired && !platformMatches) {
|
|
396438
|
+
const errorMessage = `Skipping context. The current system doesn't support this context: {"platform": "${context.platform}", "apps": ${JSON.stringify(context.apps)}}`;
|
|
396439
|
+
clog("info", errorMessage);
|
|
395891
396440
|
contextReport.result = "SKIPPED";
|
|
395892
396441
|
contextReport.resultDescription = errorMessage;
|
|
395893
396442
|
return contextReport;
|
|
@@ -395896,20 +396445,13 @@ async function runContext({ config, spec, test, context, runnerDetails, appiumPo
|
|
|
395896
396445
|
${JSON.stringify(context, null, 2)}`);
|
|
395897
396446
|
let driver;
|
|
395898
396447
|
let appiumPort;
|
|
395899
|
-
|
|
396448
|
+
let fellBackNote = "";
|
|
396449
|
+
let fellBackPinned = false;
|
|
395900
396450
|
if (driverRequired && !appiumPool) {
|
|
395901
396451
|
throw new Error("Driver requested but no Appium server pool was created; driverJobCount and isDriverRequired(context) disagreed; this is a bug.");
|
|
395902
396452
|
}
|
|
395903
|
-
const combo = combinationKey(context);
|
|
395904
396453
|
try {
|
|
395905
396454
|
if (driverRequired) {
|
|
395906
|
-
if (warmUpDecision(warmUpResults.get(combo)) === "skip") {
|
|
395907
|
-
const errorMessage = `Skipping context '${context.browser?.name}' on '${context.platform}': this context combination could not start a driver earlier in this run.`;
|
|
395908
|
-
clog("warning", errorMessage);
|
|
395909
|
-
contextReport.result = "SKIPPED";
|
|
395910
|
-
contextReport.resultDescription = errorMessage;
|
|
395911
|
-
return contextReport;
|
|
395912
|
-
}
|
|
395913
396455
|
appiumPort = await appiumPool.acquire();
|
|
395914
396456
|
if (portToDisplay) {
|
|
395915
396457
|
const display = portToDisplay.get(appiumPort);
|
|
@@ -395922,49 +396464,118 @@ ${JSON.stringify(context, null, 2)}`);
|
|
|
395922
396464
|
captureSourceTitle: browserCaptureTitle(context.contextId),
|
|
395923
396465
|
downloadDir: browserDownloadDir(context.contextId)
|
|
395924
396466
|
};
|
|
395925
|
-
|
|
395926
|
-
|
|
395927
|
-
|
|
395928
|
-
|
|
395929
|
-
|
|
395930
|
-
|
|
395931
|
-
|
|
395932
|
-
|
|
395933
|
-
|
|
395934
|
-
|
|
395935
|
-
|
|
395936
|
-
|
|
395937
|
-
|
|
395938
|
-
|
|
395939
|
-
|
|
396467
|
+
const startDriverForBrowser = async (browserName) => {
|
|
396468
|
+
const wantHeadless = context.browser?.headless !== false;
|
|
396469
|
+
const buildCaps = (headless) => getDriverCapabilities({
|
|
396470
|
+
runnerDetails,
|
|
396471
|
+
name: browserName,
|
|
396472
|
+
options: {
|
|
396473
|
+
width: context.browser?.window?.width || 1200,
|
|
396474
|
+
height: context.browser?.window?.height || 800,
|
|
396475
|
+
headless,
|
|
396476
|
+
...recordOptions
|
|
396477
|
+
}
|
|
396478
|
+
});
|
|
396479
|
+
const startFailure = () => {
|
|
396480
|
+
let error = `Failed to start context '${browserName}' on '${platform}'.`;
|
|
396481
|
+
if (browserName === "safari" || browserName === "webkit") {
|
|
396482
|
+
error += " Make sure you've run `safaridriver --enable` in a terminal and enabled 'Allow Remote Automation' in Safari's Develop menu.";
|
|
396483
|
+
}
|
|
396484
|
+
return { ok: false, error };
|
|
396485
|
+
};
|
|
395940
396486
|
try {
|
|
395941
|
-
|
|
395942
|
-
|
|
395943
|
-
|
|
395944
|
-
|
|
395945
|
-
|
|
395946
|
-
|
|
395947
|
-
|
|
395948
|
-
|
|
395949
|
-
|
|
395950
|
-
|
|
395951
|
-
|
|
396487
|
+
const d = await driverStart(buildCaps(wantHeadless), appiumPort, 4, {
|
|
396488
|
+
cacheDir: config?.cacheDir
|
|
396489
|
+
});
|
|
396490
|
+
return { ok: true, driver: d, headless: wantHeadless };
|
|
396491
|
+
} catch {
|
|
396492
|
+
if (wantHeadless)
|
|
396493
|
+
return startFailure();
|
|
396494
|
+
try {
|
|
396495
|
+
clog("warning", `Failed to start context '${browserName}' on '${platform}'. Retrying as headless.`);
|
|
396496
|
+
const d = await driverStart(buildCaps(true), appiumPort, 4, {
|
|
396497
|
+
cacheDir: config?.cacheDir
|
|
396498
|
+
});
|
|
396499
|
+
return { ok: true, driver: d, headless: true };
|
|
396500
|
+
} catch {
|
|
396501
|
+
return startFailure();
|
|
396502
|
+
}
|
|
396503
|
+
}
|
|
396504
|
+
};
|
|
396505
|
+
let startedName;
|
|
396506
|
+
let startedHeadless = false;
|
|
396507
|
+
let lastError = "";
|
|
396508
|
+
for (const candidateName of candidateEngines) {
|
|
396509
|
+
const candidateCombo = combinationKey({
|
|
396510
|
+
platform: context.platform,
|
|
396511
|
+
browser: { name: candidateName }
|
|
396512
|
+
});
|
|
396513
|
+
if (warmUpDecision(warmUpResults.get(candidateCombo)) === "skip") {
|
|
396514
|
+
lastError = `context combination '${candidateName}' on '${platform}' could not start a driver earlier in this run.`;
|
|
396515
|
+
continue;
|
|
396516
|
+
}
|
|
396517
|
+
let res = await startDriverForBrowser(candidateName);
|
|
396518
|
+
if (!res.ok && shouldRepairBeforeFallback({
|
|
396519
|
+
candidateName,
|
|
396520
|
+
requestedName: requestedBrowserName,
|
|
396521
|
+
installAttempts
|
|
396522
|
+
})) {
|
|
396523
|
+
const outcome = await ensureContextBrowserInstalled({
|
|
396524
|
+
browserName: candidateName,
|
|
396525
|
+
config,
|
|
396526
|
+
installAttempts,
|
|
396527
|
+
deps: {
|
|
396528
|
+
ensureBrowser: (asset, options) => ensureBrowserInstalled(asset, options),
|
|
396529
|
+
log
|
|
396530
|
+
},
|
|
396531
|
+
repair: true
|
|
395952
396532
|
});
|
|
395953
|
-
|
|
395954
|
-
|
|
395955
|
-
|
|
395956
|
-
|
|
395957
|
-
errorMessage = errorMessage + " Make sure you've run `safaridriver --enable` in a terminal and enabled 'Allow Remote Automation' in Safari's Develop menu.";
|
|
395958
|
-
clog("error", errorMessage);
|
|
395959
|
-
if (!warmUpResults.has(combo))
|
|
395960
|
-
warmUpResults.set(combo, "failed");
|
|
395961
|
-
contextReport.result = "SKIPPED";
|
|
395962
|
-
contextReport.resultDescription = errorMessage;
|
|
395963
|
-
return contextReport;
|
|
396533
|
+
if (outcome === "installed") {
|
|
396534
|
+
clog("info", `Repaired '${candidateName}' driver after a start failure; retrying before falling back.`);
|
|
396535
|
+
res = await startDriverForBrowser(candidateName);
|
|
396536
|
+
}
|
|
395964
396537
|
}
|
|
396538
|
+
if (res.ok) {
|
|
396539
|
+
driver = res.driver;
|
|
396540
|
+
startedName = candidateName;
|
|
396541
|
+
startedHeadless = res.headless;
|
|
396542
|
+
if (!warmUpResults.has(candidateCombo))
|
|
396543
|
+
warmUpResults.set(candidateCombo, "ok");
|
|
396544
|
+
break;
|
|
396545
|
+
}
|
|
396546
|
+
clog("error", res.error);
|
|
396547
|
+
if (!warmUpResults.has(candidateCombo))
|
|
396548
|
+
warmUpResults.set(candidateCombo, "failed");
|
|
396549
|
+
lastError = res.error;
|
|
396550
|
+
}
|
|
396551
|
+
if (!startedName) {
|
|
396552
|
+
const errorMessage = driverSkipDiagnostic({
|
|
396553
|
+
requestedName: requestedBrowserName ?? "<none>",
|
|
396554
|
+
platform,
|
|
396555
|
+
platformMatches: true,
|
|
396556
|
+
attemptedFallback: candidateEngines.length > 1,
|
|
396557
|
+
lastError
|
|
396558
|
+
});
|
|
396559
|
+
clog("error", errorMessage);
|
|
396560
|
+
contextReport.result = "SKIPPED";
|
|
396561
|
+
contextReport.resultDescription = errorMessage;
|
|
396562
|
+
return contextReport;
|
|
396563
|
+
}
|
|
396564
|
+
if (startedHeadless) {
|
|
396565
|
+
context.browser = { ...context.browser, headless: true };
|
|
396566
|
+
contextReport.browser = context.browser;
|
|
396567
|
+
}
|
|
396568
|
+
if (normalizeBrowserName(startedName) !== normalizeBrowserName(requestedBrowserName)) {
|
|
396569
|
+
fellBackNote = `${requestedBrowserName} unavailable; ran on ${startedName}.`;
|
|
396570
|
+
fellBackPinned = explicitlyRequested;
|
|
396571
|
+
context.browser = { ...context.browser, name: startedName };
|
|
396572
|
+
contextReport.browser = context.browser;
|
|
396573
|
+
contextReport.fallback = {
|
|
396574
|
+
requested: requestedBrowserName,
|
|
396575
|
+
used: startedName
|
|
396576
|
+
};
|
|
396577
|
+
clog("warning", fellBackNote);
|
|
395965
396578
|
}
|
|
395966
|
-
if (!warmUpResults.has(combo))
|
|
395967
|
-
warmUpResults.set(combo, "ok");
|
|
395968
396579
|
if (context.browser?.viewport?.width || context.browser?.viewport?.height) {
|
|
395969
396580
|
await setViewportSize(context, driver);
|
|
395970
396581
|
} else if (context.browser?.window?.width || context.browser?.window?.height) {
|
|
@@ -396214,6 +396825,12 @@ ${JSON.stringify(r, null, 2)}`);
|
|
|
396214
396825
|
}
|
|
396215
396826
|
}
|
|
396216
396827
|
contextReport.result = rollUpResults(contextReport.steps);
|
|
396828
|
+
if (fellBackNote) {
|
|
396829
|
+
if (fellBackPinned && contextReport.result === "PASS") {
|
|
396830
|
+
contextReport.result = "WARNING";
|
|
396831
|
+
}
|
|
396832
|
+
contextReport.resultDescription = contextReport.resultDescription ? `${fellBackNote} ${contextReport.resultDescription}` : fellBackNote;
|
|
396833
|
+
}
|
|
396217
396834
|
return contextReport;
|
|
396218
396835
|
}
|
|
396219
396836
|
async function stopAllRecordings({ config, context, driver, contextReport }) {
|
|
@@ -396381,9 +396998,9 @@ async function startAppiumServer(appiumEntry, config, display) {
|
|
|
396381
396998
|
const port = await findFreePort();
|
|
396382
396999
|
log(config, "debug", `Starting Appium on port ${port}`);
|
|
396383
397000
|
const env = display ? { ...process.env, DISPLAY: display } : process.env;
|
|
396384
|
-
const proc = (0,
|
|
397001
|
+
const proc = (0, import_node_child_process8.spawn)(process.execPath, [appiumEntry, "-a", "127.0.0.1", "-p", String(port)], {
|
|
396385
397002
|
windowsHide: true,
|
|
396386
|
-
cwd:
|
|
397003
|
+
cwd: import_node_path21.default.join(__dirname5, "../.."),
|
|
396387
397004
|
env
|
|
396388
397005
|
});
|
|
396389
397006
|
proc.on("error", (err) => {
|
|
@@ -396482,7 +397099,7 @@ async function ensureChromeAvailable(config, deps) {
|
|
|
396482
397099
|
}
|
|
396483
397100
|
return availableApps;
|
|
396484
397101
|
}
|
|
396485
|
-
async function ensureContextBrowserInstalled({ browserName, config, installAttempts, deps }) {
|
|
397102
|
+
async function ensureContextBrowserInstalled({ browserName, config, installAttempts, deps, repair = false }) {
|
|
396486
397103
|
const key = (browserName ?? "<none>").toLowerCase();
|
|
396487
397104
|
const cached = installAttempts.get(key);
|
|
396488
397105
|
if (cached)
|
|
@@ -396495,9 +397112,13 @@ async function ensureContextBrowserInstalled({ browserName, config, installAttem
|
|
|
396495
397112
|
const ctx = { cacheDir: config?.cacheDir };
|
|
396496
397113
|
const logger = (msg, level = "info") => deps.log?.(config, level === "warn" ? "warning" : level, msg);
|
|
396497
397114
|
try {
|
|
396498
|
-
deps.log?.(config, "info", `Browser '${browserName}' is not available; attempting on-demand install of: ${assets.join(", ")}.`);
|
|
397115
|
+
deps.log?.(config, "info", `Browser '${browserName}' is not available; attempting on-demand ${repair ? "repair" : "install"} of: ${assets.join(", ")}.`);
|
|
396499
397116
|
for (const asset of assets) {
|
|
396500
|
-
await deps.ensureBrowser(asset, {
|
|
397117
|
+
await deps.ensureBrowser(asset, {
|
|
397118
|
+
ctx,
|
|
397119
|
+
deps: { logger },
|
|
397120
|
+
force: !!repair
|
|
397121
|
+
});
|
|
396501
397122
|
}
|
|
396502
397123
|
installAttempts.set(key, "installed");
|
|
396503
397124
|
return "installed";
|
|
@@ -396528,9 +397149,9 @@ async function getRunner(options = {}) {
|
|
|
396528
397149
|
if (!appiumEntry) {
|
|
396529
397150
|
throw new Error("appium is not installed. Run `doc-detective install runtime appium` to install it.");
|
|
396530
397151
|
}
|
|
396531
|
-
const appium = (0,
|
|
397152
|
+
const appium = (0, import_node_child_process8.spawn)(process.execPath, [appiumEntry, "-a", "127.0.0.1", "-p", String(appiumPort)], {
|
|
396532
397153
|
windowsHide: true,
|
|
396533
|
-
cwd:
|
|
397154
|
+
cwd: import_node_path21.default.join(__dirname5, "../..")
|
|
396534
397155
|
});
|
|
396535
397156
|
appium.on("error", (err) => {
|
|
396536
397157
|
log(config, "warning", `Appium process error: ${err?.stack ?? err?.message ?? String(err)}`);
|