@opensteer/engine-abp 0.7.0 → 0.8.1

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/README.md CHANGED
@@ -8,3 +8,7 @@ building directly against the engine package.
8
8
  ```bash
9
9
  pnpm add @opensteer/engine-abp
10
10
  ```
11
+
12
+ `agent-browser-protocol` downloads its bundled ABP browser during install. In offline
13
+ or custom environments, point Opensteer at an existing binary with
14
+ `launch.browserExecutablePath` or `ABP_BROWSER_PATH`.
package/dist/index.cjs CHANGED
@@ -1776,6 +1776,9 @@ function resolveDefaultAbpWrapperExecutablePath() {
1776
1776
  }
1777
1777
  return resolveExecutablePath([path.join(root, "dist", "bin", "abp.js")]);
1778
1778
  }
1779
+ function resolveDefaultAbpExecutablePath() {
1780
+ return resolveDefaultAbpBrowserExecutablePath() ?? resolveDefaultAbpWrapperExecutablePath();
1781
+ }
1779
1782
  function buildAbpLaunchCommand(options) {
1780
1783
  if (options.abpExecutablePath !== void 0 && options.browserExecutablePath !== void 0) {
1781
1784
  throw createBrowserCoreError(
@@ -3171,8 +3174,260 @@ var CLASSIFY_POINTER_HIT_DECLARATION = String.raw`function(hitNode, point) {
3171
3174
  ambiguous,
3172
3175
  };
3173
3176
  }`;
3177
+ var LIVE_REPLAY_PATH_MATCH_ATTRIBUTE_PRIORITY = {
3178
+ stablePrimaryExact: 150,
3179
+ stablePrimaryPrefix: 130,
3180
+ attrExact: 100,
3181
+ attrPrefix: 80,
3182
+ tagOnly: 10
3183
+ };
3184
+ var LIVE_REPLAY_PATH_STABLE_PRIMARY_ATTR_KEYS = [
3185
+ "data-testid",
3186
+ "data-test",
3187
+ "data-qa",
3188
+ "data-cy",
3189
+ "name",
3190
+ "role",
3191
+ "type",
3192
+ "aria-label",
3193
+ "title",
3194
+ "placeholder"
3195
+ ];
3196
+ var LIVE_REPLAY_PATH_DEFERRED_MATCH_ATTR_KEYS = [
3197
+ "href",
3198
+ "src",
3199
+ "srcset",
3200
+ "imagesrcset",
3201
+ "ping",
3202
+ "value",
3203
+ "for",
3204
+ "aria-controls",
3205
+ "aria-labelledby",
3206
+ "aria-describedby"
3207
+ ];
3208
+ var LIVE_REPLAY_PATH_POLICY = {
3209
+ matchAttributePriority: LIVE_REPLAY_PATH_MATCH_ATTRIBUTE_PRIORITY,
3210
+ stablePrimaryAttrKeys: LIVE_REPLAY_PATH_STABLE_PRIMARY_ATTR_KEYS,
3211
+ deferredMatchAttrKeys: LIVE_REPLAY_PATH_DEFERRED_MATCH_ATTR_KEYS
3212
+ };
3213
+ var BUILD_LIVE_REPLAY_PATH_DECLARATION = String.raw`function(policy, source) {
3214
+ const buildReplayPath = (0, eval)(source);
3215
+ return buildReplayPath(this, policy);
3216
+ }`;
3217
+ var BUILD_LIVE_REPLAY_PATH_SOURCE = String.raw`(target, policy) => {
3218
+ const MAX_ATTRIBUTE_VALUE_LENGTH = 300;
3219
+
3220
+ function isValidAttrKey(key) {
3221
+ const trimmed = String(key || "").trim();
3222
+ if (!trimmed) return false;
3223
+ if (/[\s"'<>/]/.test(trimmed)) return false;
3224
+ return /^[A-Za-z_][A-Za-z0-9_:\-.]*$/.test(trimmed);
3225
+ }
3226
+
3227
+ function isMediaTag(tag) {
3228
+ return new Set(["img", "video", "source", "iframe"]).has(String(tag || "").toLowerCase());
3229
+ }
3230
+
3231
+ function shouldKeepAttr(tag, key, value) {
3232
+ const normalized = String(key || "").trim().toLowerCase();
3233
+ if (!normalized || !String(value || "").trim()) return false;
3234
+ if (!isValidAttrKey(key)) return false;
3235
+ if (normalized === "c") return false;
3236
+ if (/^on[a-z]/i.test(normalized)) return false;
3237
+ if (new Set(["style", "nonce", "integrity", "crossorigin", "referrerpolicy", "autocomplete"]).has(normalized)) {
3238
+ return false;
3239
+ }
3240
+ if (normalized.startsWith("data-os-") || normalized.startsWith("data-opensteer-")) {
3241
+ return false;
3242
+ }
3243
+ if (
3244
+ isMediaTag(tag) &&
3245
+ new Set([
3246
+ "data-src",
3247
+ "data-lazy-src",
3248
+ "data-original",
3249
+ "data-lazy",
3250
+ "data-image",
3251
+ "data-url",
3252
+ "data-srcset",
3253
+ "data-lazy-srcset",
3254
+ "data-was-processed",
3255
+ ]).has(normalized)
3256
+ ) {
3257
+ return false;
3258
+ }
3259
+ return true;
3260
+ }
3261
+
3262
+ function collectAttrs(node) {
3263
+ const tag = node.tagName.toLowerCase();
3264
+ const attrs = {};
3265
+ for (const attr of Array.from(node.attributes)) {
3266
+ if (!shouldKeepAttr(tag, attr.name, attr.value)) {
3267
+ continue;
3268
+ }
3269
+ const value = String(attr.value || "");
3270
+ if (!value.trim()) continue;
3271
+ if (value.length > MAX_ATTRIBUTE_VALUE_LENGTH) continue;
3272
+ attrs[attr.name] = value;
3273
+ }
3274
+ return attrs;
3275
+ }
3276
+
3277
+ function getSiblings(node, root) {
3278
+ if (node.parentElement) return Array.from(node.parentElement.children);
3279
+ return Array.from(root.children || []);
3280
+ }
3281
+
3282
+ function cssEscape(value) {
3283
+ return String(value).replace(/\\/g, "\\\\").replace(/"/g, '\\"');
3284
+ }
3285
+
3286
+ function stablePrimaryKey(attrs) {
3287
+ for (const key of policy.stablePrimaryAttrKeys || []) {
3288
+ if (attrs[key]) return key;
3289
+ }
3290
+ return null;
3291
+ }
3292
+
3293
+ function countMatches(root, tag, attrKey, attrValue, mode) {
3294
+ const scope = root instanceof ShadowRoot ? root : root.ownerDocument;
3295
+ if (!scope || typeof scope.querySelectorAll !== "function") return 0;
3296
+ const escapedTag = String(tag || "*");
3297
+ let selector = escapedTag;
3298
+ if (attrKey && attrValue) {
3299
+ const operator = mode === "prefix" ? "^=" : "=";
3300
+ selector += "[" + attrKey + operator + "\\"" + cssEscape(attrValue) + "\\"]";
3301
+ }
3302
+ try {
3303
+ return scope.querySelectorAll(selector).length;
3304
+ } catch {
3305
+ return 0;
3306
+ }
3307
+ }
3308
+
3309
+ function chooseAttribute(tag, attrs, root) {
3310
+ const stableKey = stablePrimaryKey(attrs);
3311
+ if (stableKey) {
3312
+ const exactCount = countMatches(root, tag, stableKey, attrs[stableKey], "exact");
3313
+ if (exactCount === 1) {
3314
+ return {
3315
+ key: stableKey,
3316
+ value: attrs[stableKey],
3317
+ match: "exact",
3318
+ };
3319
+ }
3320
+ }
3321
+
3322
+ const entries = Object.entries(attrs)
3323
+ .filter(([key]) => !policy.deferredMatchAttrKeys.includes(key))
3324
+ .concat(Object.entries(attrs).filter(([key]) => policy.deferredMatchAttrKeys.includes(key)));
3325
+
3326
+ for (const [key, value] of entries) {
3327
+ const exactCount = countMatches(root, tag, key, value, "exact");
3328
+ if (exactCount === 1) {
3329
+ return { key, value, match: "exact" };
3330
+ }
3331
+ if (value.length >= 4) {
3332
+ const prefixLength = Math.min(Math.max(4, Math.floor(value.length / 2)), value.length);
3333
+ const prefix = value.slice(0, prefixLength);
3334
+ const prefixCount = countMatches(root, tag, key, prefix, "prefix");
3335
+ if (prefixCount === 1) {
3336
+ return { key, value: prefix, match: "prefix" };
3337
+ }
3338
+ }
3339
+ }
3340
+
3341
+ return null;
3342
+ }
3343
+
3344
+ function buildChain(node) {
3345
+ const nodes = [];
3346
+ let current = node;
3347
+ while (current && current instanceof Element) {
3348
+ nodes.unshift(current);
3349
+ current = current.parentElement;
3350
+ }
3351
+ return nodes;
3352
+ }
3353
+
3354
+ function finalizePath(chain, root) {
3355
+ const result = [];
3356
+ for (const node of chain) {
3357
+ const tag = node.tagName.toLowerCase();
3358
+ const attrs = collectAttrs(node);
3359
+ const attribute = chooseAttribute(tag, attrs, root);
3360
+ if (attribute) {
3361
+ result.push({
3362
+ tag,
3363
+ attributes: [
3364
+ {
3365
+ name: attribute.key,
3366
+ value: attribute.value,
3367
+ match: attribute.match,
3368
+ },
3369
+ ],
3370
+ });
3371
+ continue;
3372
+ }
3373
+
3374
+ const siblings = getSiblings(node, root).filter(
3375
+ (candidate) => candidate.tagName.toLowerCase() === tag,
3376
+ );
3377
+ const index = siblings.indexOf(node);
3378
+ result.push({
3379
+ tag,
3380
+ index: siblings.length <= 1 || index < 0 ? undefined : index,
3381
+ });
3382
+ }
3383
+
3384
+ return {
3385
+ nodes: result,
3386
+ };
3387
+ }
3388
+
3389
+ if (!(target instanceof Element)) return null;
3390
+
3391
+ const context = [];
3392
+ let currentRoot = target.getRootNode() instanceof ShadowRoot ? target.getRootNode() : document;
3393
+ const targetChain = buildChain(target);
3394
+ const finalizedTarget = finalizePath(targetChain, currentRoot);
3395
+ if (!finalizedTarget) return null;
3396
+
3397
+ while (currentRoot instanceof ShadowRoot) {
3398
+ const host = currentRoot.host;
3399
+ const hostRoot = host.getRootNode() instanceof ShadowRoot ? host.getRootNode() : document;
3400
+ const hostChain = buildChain(host);
3401
+ const finalizedHost = finalizePath(hostChain, hostRoot);
3402
+ if (!finalizedHost) return null;
3403
+ context.unshift({
3404
+ kind: "shadow",
3405
+ host: finalizedHost.nodes,
3406
+ });
3407
+ currentRoot = hostRoot;
3408
+ }
3409
+
3410
+ return {
3411
+ resolution: "deterministic",
3412
+ context,
3413
+ nodes: finalizedTarget.nodes,
3414
+ };
3415
+ }`;
3174
3416
  function createAbpDomActionBridge(context) {
3175
3417
  return {
3418
+ async buildReplayPath(locator) {
3419
+ const { controller, document, backendNodeId } = await prepareLiveNodeContext(
3420
+ context,
3421
+ locator
3422
+ );
3423
+ return withTemporaryExecutionResume(context, controller, async () => {
3424
+ const raw = await callNodeValueFunction(controller, document, locator, backendNodeId, {
3425
+ functionDeclaration: BUILD_LIVE_REPLAY_PATH_DECLARATION,
3426
+ arguments: [{ value: LIVE_REPLAY_PATH_POLICY }, { value: BUILD_LIVE_REPLAY_PATH_SOURCE }]
3427
+ });
3428
+ return requireReplayPath(raw, locator);
3429
+ });
3430
+ },
3176
3431
  async inspectActionTarget(locator) {
3177
3432
  const { controller, document, backendNodeId } = await prepareLiveNodeContext(
3178
3433
  context,
@@ -3410,6 +3665,24 @@ async function callNodeFunctionWithNodeArgument(controller, document, locator, b
3410
3665
  await releaseObject(controller, objectId);
3411
3666
  }
3412
3667
  }
3668
+ async function callNodeValueFunction(controller, document, locator, backendNodeId, input) {
3669
+ let objectId;
3670
+ try {
3671
+ objectId = await resolveNodeObjectId(controller, document, locator, backendNodeId);
3672
+ const evaluated = await controller.cdp.send("Runtime.callFunctionOn", {
3673
+ objectId,
3674
+ functionDeclaration: input.functionDeclaration,
3675
+ ...input.arguments === void 0 ? {} : { arguments: [...input.arguments] },
3676
+ returnByValue: true,
3677
+ awaitPromise: true
3678
+ });
3679
+ return evaluated.result?.value;
3680
+ } catch (error) {
3681
+ rethrowNodeLookupError2(document, locator, error);
3682
+ } finally {
3683
+ await releaseObject(controller, objectId);
3684
+ }
3685
+ }
3413
3686
  async function resolveNodeObjectId(controller, document, locator, backendNodeId) {
3414
3687
  try {
3415
3688
  const resolved = await controller.cdp.send("DOM.resolveNode", {
@@ -3504,6 +3777,14 @@ function normalizePointerHitAssessment(value, canonicalTarget) {
3504
3777
  canonicalTarget
3505
3778
  };
3506
3779
  }
3780
+ function requireReplayPath(value, locator) {
3781
+ if (!value || typeof value !== "object" || Array.isArray(value) || value.resolution !== "deterministic") {
3782
+ throw new Error(
3783
+ `live DOM replay path builder returned an invalid result for ${locator.nodeRef}`
3784
+ );
3785
+ }
3786
+ return value;
3787
+ }
3507
3788
  function rethrowNodeLookupError2(document, locator, error) {
3508
3789
  if (error instanceof Error && /No node with given id found|Could not find node with given id|Cannot find context/i.test(
3509
3790
  error.message
@@ -6533,6 +6814,12 @@ function resolveFallbackPageTarget(targets, tabMetadata) {
6533
6814
  }
6534
6815
 
6535
6816
  exports.AbpBrowserCoreEngine = AbpBrowserCoreEngine;
6817
+ exports.allocatePort = allocatePort;
6818
+ exports.buildAbpLaunchCommand = buildAbpLaunchCommand;
6536
6819
  exports.createAbpBrowserCoreEngine = createAbpBrowserCoreEngine;
6820
+ exports.launchAbpProcess = launchAbpProcess;
6821
+ exports.resolveDefaultAbpBrowserExecutablePath = resolveDefaultAbpBrowserExecutablePath;
6822
+ exports.resolveDefaultAbpExecutablePath = resolveDefaultAbpExecutablePath;
6823
+ exports.resolveDefaultAbpWrapperExecutablePath = resolveDefaultAbpWrapperExecutablePath;
6537
6824
  //# sourceMappingURL=index.cjs.map
6538
6825
  //# sourceMappingURL=index.cjs.map