@opensteer/engine-abp 0.7.0 → 0.8.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/index.d.cts CHANGED
@@ -1,3 +1,5 @@
1
+ import { ChildProcess } from 'node:child_process';
2
+
1
3
  declare const brandSymbol: unique symbol;
2
4
  type Brand<Value, Name extends string> = Value & {
3
5
  readonly [brandSymbol]: Name;
@@ -829,6 +831,40 @@ interface ComputerUseBridge {
829
831
  execute(input: ComputerUseBridgeInput): Promise<ComputerUseBridgeOutput>;
830
832
  }
831
833
 
834
+ type MatchOperator = "exact" | "startsWith" | "contains";
835
+ interface AttributeMatchClause {
836
+ readonly kind: "attr";
837
+ readonly key: string;
838
+ readonly op?: MatchOperator;
839
+ readonly value?: string;
840
+ }
841
+ interface PositionMatchClause {
842
+ readonly kind: "position";
843
+ readonly axis: "nthOfType" | "nthChild";
844
+ }
845
+ type MatchClause = AttributeMatchClause | PositionMatchClause;
846
+ interface PathNodePosition {
847
+ readonly nthChild: number;
848
+ readonly nthOfType: number;
849
+ }
850
+ interface PathNode {
851
+ readonly tag: string;
852
+ readonly attrs: Readonly<Record<string, string>>;
853
+ readonly position: PathNodePosition;
854
+ readonly match: readonly MatchClause[];
855
+ }
856
+ interface ContextHop {
857
+ readonly kind: "iframe" | "shadow";
858
+ readonly host: readonly PathNode[];
859
+ }
860
+ interface ElementRouteBase {
861
+ readonly context: readonly ContextHop[];
862
+ readonly nodes: readonly PathNode[];
863
+ }
864
+ interface ReplayElementPath extends ElementRouteBase {
865
+ readonly resolution: "deterministic";
866
+ }
867
+
832
868
  declare const OPENSTEER_DOM_ACTION_BRIDGE_SYMBOL: unique symbol;
833
869
  type DomActionScrollAlignment = "start" | "center" | "end" | "nearest";
834
870
  interface DomActionTargetInspection {
@@ -863,6 +899,7 @@ interface DomPointerHitAssessment {
863
899
  readonly hitOwner?: NodeLocator;
864
900
  }
865
901
  interface DomActionBridge {
902
+ buildReplayPath(locator: NodeLocator): Promise<ReplayElementPath>;
866
903
  inspectActionTarget(locator: NodeLocator): Promise<DomActionTargetInspection>;
867
904
  canonicalizePointerTarget(locator: NodeLocator): Promise<NodeLocator>;
868
905
  classifyPointerHit(input: {
@@ -894,6 +931,16 @@ interface AbpBrowserCoreEngineOptions {
894
931
  readonly browser?: AdoptedAbpBrowser;
895
932
  readonly extraHTTPHeaders?: readonly HeaderEntry[];
896
933
  }
934
+ interface LaunchRequestOptions {
935
+ readonly port: number;
936
+ readonly userDataDir: string;
937
+ readonly sessionDir: string;
938
+ readonly abpExecutablePath?: string;
939
+ readonly browserExecutablePath?: string;
940
+ readonly headless: boolean;
941
+ readonly args: readonly string[];
942
+ readonly verbose: boolean;
943
+ }
897
944
 
898
945
  declare class AbpBrowserCoreEngine implements BrowserCoreEngine {
899
946
  readonly capabilities: BrowserCapabilities;
@@ -1140,4 +1187,20 @@ declare class AbpBrowserCoreEngine implements BrowserCoreEngine {
1140
1187
  }
1141
1188
  declare function createAbpBrowserCoreEngine(options?: AbpBrowserCoreEngineOptions): Promise<AbpBrowserCoreEngine>;
1142
1189
 
1143
- export { AbpBrowserCoreEngine, type AbpBrowserCoreEngineOptions, type AbpLaunchOptions, type AdoptedAbpBrowser, createAbpBrowserCoreEngine };
1190
+ interface LaunchedAbpProcess {
1191
+ readonly process: ChildProcess;
1192
+ readonly baseUrl: string;
1193
+ readonly remoteDebuggingUrl: string;
1194
+ }
1195
+ interface AbpLaunchCommand {
1196
+ readonly executablePath: string;
1197
+ readonly args: readonly string[];
1198
+ }
1199
+ declare function allocatePort(): Promise<number>;
1200
+ declare function resolveDefaultAbpBrowserExecutablePath(): string | undefined;
1201
+ declare function resolveDefaultAbpWrapperExecutablePath(): string | undefined;
1202
+ declare function resolveDefaultAbpExecutablePath(): string | undefined;
1203
+ declare function buildAbpLaunchCommand(options: LaunchRequestOptions): AbpLaunchCommand;
1204
+ declare function launchAbpProcess(options: LaunchRequestOptions): Promise<LaunchedAbpProcess>;
1205
+
1206
+ export { AbpBrowserCoreEngine, type AbpBrowserCoreEngineOptions, type AbpLaunchOptions, type AdoptedAbpBrowser, allocatePort, buildAbpLaunchCommand, createAbpBrowserCoreEngine, launchAbpProcess, resolveDefaultAbpBrowserExecutablePath, resolveDefaultAbpExecutablePath, resolveDefaultAbpWrapperExecutablePath };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { ChildProcess } from 'node:child_process';
2
+
1
3
  declare const brandSymbol: unique symbol;
2
4
  type Brand<Value, Name extends string> = Value & {
3
5
  readonly [brandSymbol]: Name;
@@ -829,6 +831,40 @@ interface ComputerUseBridge {
829
831
  execute(input: ComputerUseBridgeInput): Promise<ComputerUseBridgeOutput>;
830
832
  }
831
833
 
834
+ type MatchOperator = "exact" | "startsWith" | "contains";
835
+ interface AttributeMatchClause {
836
+ readonly kind: "attr";
837
+ readonly key: string;
838
+ readonly op?: MatchOperator;
839
+ readonly value?: string;
840
+ }
841
+ interface PositionMatchClause {
842
+ readonly kind: "position";
843
+ readonly axis: "nthOfType" | "nthChild";
844
+ }
845
+ type MatchClause = AttributeMatchClause | PositionMatchClause;
846
+ interface PathNodePosition {
847
+ readonly nthChild: number;
848
+ readonly nthOfType: number;
849
+ }
850
+ interface PathNode {
851
+ readonly tag: string;
852
+ readonly attrs: Readonly<Record<string, string>>;
853
+ readonly position: PathNodePosition;
854
+ readonly match: readonly MatchClause[];
855
+ }
856
+ interface ContextHop {
857
+ readonly kind: "iframe" | "shadow";
858
+ readonly host: readonly PathNode[];
859
+ }
860
+ interface ElementRouteBase {
861
+ readonly context: readonly ContextHop[];
862
+ readonly nodes: readonly PathNode[];
863
+ }
864
+ interface ReplayElementPath extends ElementRouteBase {
865
+ readonly resolution: "deterministic";
866
+ }
867
+
832
868
  declare const OPENSTEER_DOM_ACTION_BRIDGE_SYMBOL: unique symbol;
833
869
  type DomActionScrollAlignment = "start" | "center" | "end" | "nearest";
834
870
  interface DomActionTargetInspection {
@@ -863,6 +899,7 @@ interface DomPointerHitAssessment {
863
899
  readonly hitOwner?: NodeLocator;
864
900
  }
865
901
  interface DomActionBridge {
902
+ buildReplayPath(locator: NodeLocator): Promise<ReplayElementPath>;
866
903
  inspectActionTarget(locator: NodeLocator): Promise<DomActionTargetInspection>;
867
904
  canonicalizePointerTarget(locator: NodeLocator): Promise<NodeLocator>;
868
905
  classifyPointerHit(input: {
@@ -894,6 +931,16 @@ interface AbpBrowserCoreEngineOptions {
894
931
  readonly browser?: AdoptedAbpBrowser;
895
932
  readonly extraHTTPHeaders?: readonly HeaderEntry[];
896
933
  }
934
+ interface LaunchRequestOptions {
935
+ readonly port: number;
936
+ readonly userDataDir: string;
937
+ readonly sessionDir: string;
938
+ readonly abpExecutablePath?: string;
939
+ readonly browserExecutablePath?: string;
940
+ readonly headless: boolean;
941
+ readonly args: readonly string[];
942
+ readonly verbose: boolean;
943
+ }
897
944
 
898
945
  declare class AbpBrowserCoreEngine implements BrowserCoreEngine {
899
946
  readonly capabilities: BrowserCapabilities;
@@ -1140,4 +1187,20 @@ declare class AbpBrowserCoreEngine implements BrowserCoreEngine {
1140
1187
  }
1141
1188
  declare function createAbpBrowserCoreEngine(options?: AbpBrowserCoreEngineOptions): Promise<AbpBrowserCoreEngine>;
1142
1189
 
1143
- export { AbpBrowserCoreEngine, type AbpBrowserCoreEngineOptions, type AbpLaunchOptions, type AdoptedAbpBrowser, createAbpBrowserCoreEngine };
1190
+ interface LaunchedAbpProcess {
1191
+ readonly process: ChildProcess;
1192
+ readonly baseUrl: string;
1193
+ readonly remoteDebuggingUrl: string;
1194
+ }
1195
+ interface AbpLaunchCommand {
1196
+ readonly executablePath: string;
1197
+ readonly args: readonly string[];
1198
+ }
1199
+ declare function allocatePort(): Promise<number>;
1200
+ declare function resolveDefaultAbpBrowserExecutablePath(): string | undefined;
1201
+ declare function resolveDefaultAbpWrapperExecutablePath(): string | undefined;
1202
+ declare function resolveDefaultAbpExecutablePath(): string | undefined;
1203
+ declare function buildAbpLaunchCommand(options: LaunchRequestOptions): AbpLaunchCommand;
1204
+ declare function launchAbpProcess(options: LaunchRequestOptions): Promise<LaunchedAbpProcess>;
1205
+
1206
+ export { AbpBrowserCoreEngine, type AbpBrowserCoreEngineOptions, type AbpLaunchOptions, type AdoptedAbpBrowser, allocatePort, buildAbpLaunchCommand, createAbpBrowserCoreEngine, launchAbpProcess, resolveDefaultAbpBrowserExecutablePath, resolveDefaultAbpExecutablePath, resolveDefaultAbpWrapperExecutablePath };
package/dist/index.js CHANGED
@@ -1769,6 +1769,9 @@ function resolveDefaultAbpWrapperExecutablePath() {
1769
1769
  }
1770
1770
  return resolveExecutablePath([join(root, "dist", "bin", "abp.js")]);
1771
1771
  }
1772
+ function resolveDefaultAbpExecutablePath() {
1773
+ return resolveDefaultAbpBrowserExecutablePath() ?? resolveDefaultAbpWrapperExecutablePath();
1774
+ }
1772
1775
  function buildAbpLaunchCommand(options) {
1773
1776
  if (options.abpExecutablePath !== void 0 && options.browserExecutablePath !== void 0) {
1774
1777
  throw createBrowserCoreError(
@@ -3164,8 +3167,257 @@ var CLASSIFY_POINTER_HIT_DECLARATION = String.raw`function(hitNode, point) {
3164
3167
  ambiguous,
3165
3168
  };
3166
3169
  }`;
3170
+ var LIVE_REPLAY_PATH_MATCH_ATTRIBUTE_PRIORITY = {
3171
+ stablePrimaryExact: 150,
3172
+ stablePrimaryPrefix: 130,
3173
+ attrExact: 100,
3174
+ attrPrefix: 80,
3175
+ tagOnly: 10
3176
+ };
3177
+ var LIVE_REPLAY_PATH_STABLE_PRIMARY_ATTR_KEYS = [
3178
+ "data-testid",
3179
+ "data-test",
3180
+ "data-qa",
3181
+ "data-cy",
3182
+ "name",
3183
+ "role",
3184
+ "type",
3185
+ "aria-label",
3186
+ "title",
3187
+ "placeholder"
3188
+ ];
3189
+ var LIVE_REPLAY_PATH_DEFERRED_MATCH_ATTR_KEYS = [
3190
+ "href",
3191
+ "src",
3192
+ "srcset",
3193
+ "imagesrcset",
3194
+ "ping",
3195
+ "value",
3196
+ "for",
3197
+ "aria-controls",
3198
+ "aria-labelledby",
3199
+ "aria-describedby"
3200
+ ];
3201
+ var LIVE_REPLAY_PATH_POLICY = {
3202
+ matchAttributePriority: LIVE_REPLAY_PATH_MATCH_ATTRIBUTE_PRIORITY,
3203
+ stablePrimaryAttrKeys: LIVE_REPLAY_PATH_STABLE_PRIMARY_ATTR_KEYS,
3204
+ deferredMatchAttrKeys: LIVE_REPLAY_PATH_DEFERRED_MATCH_ATTR_KEYS
3205
+ };
3206
+ var BUILD_LIVE_REPLAY_PATH_DECLARATION = String.raw`function(policy, source) {
3207
+ const buildReplayPath = (0, eval)(source);
3208
+ return buildReplayPath(this, policy);
3209
+ }`;
3210
+ var BUILD_LIVE_REPLAY_PATH_SOURCE = String.raw`(target, policy) => {
3211
+ const MAX_ATTRIBUTE_VALUE_LENGTH = 300;
3212
+
3213
+ function isValidAttrKey(key) {
3214
+ const trimmed = String(key || "").trim();
3215
+ if (!trimmed) return false;
3216
+ if (/[\s"'<>/]/.test(trimmed)) return false;
3217
+ return /^[A-Za-z_][A-Za-z0-9_:\-.]*$/.test(trimmed);
3218
+ }
3219
+
3220
+ function isMediaTag(tag) {
3221
+ return new Set(["img", "video", "source", "iframe"]).has(String(tag || "").toLowerCase());
3222
+ }
3223
+
3224
+ function shouldKeepAttr(tag, key, value) {
3225
+ const normalized = String(key || "").trim().toLowerCase();
3226
+ if (!normalized || !String(value || "").trim()) return false;
3227
+ if (!isValidAttrKey(key)) return false;
3228
+ if (normalized === "c") return false;
3229
+ if (/^on[a-z]/i.test(normalized)) return false;
3230
+ if (new Set(["style", "nonce", "integrity", "crossorigin", "referrerpolicy", "autocomplete"]).has(normalized)) {
3231
+ return false;
3232
+ }
3233
+ if (normalized.startsWith("data-os-") || normalized.startsWith("data-opensteer-")) {
3234
+ return false;
3235
+ }
3236
+ if (
3237
+ isMediaTag(tag) &&
3238
+ new Set([
3239
+ "data-src",
3240
+ "data-lazy-src",
3241
+ "data-original",
3242
+ "data-lazy",
3243
+ "data-image",
3244
+ "data-url",
3245
+ "data-srcset",
3246
+ "data-lazy-srcset",
3247
+ "data-was-processed",
3248
+ ]).has(normalized)
3249
+ ) {
3250
+ return false;
3251
+ }
3252
+ return true;
3253
+ }
3254
+
3255
+ function collectAttrs(node) {
3256
+ const tag = node.tagName.toLowerCase();
3257
+ const attrs = {};
3258
+ for (const attr of Array.from(node.attributes)) {
3259
+ if (!shouldKeepAttr(tag, attr.name, attr.value)) {
3260
+ continue;
3261
+ }
3262
+ const value = String(attr.value || "");
3263
+ if (!value.trim()) continue;
3264
+ if (value.length > MAX_ATTRIBUTE_VALUE_LENGTH) continue;
3265
+ attrs[attr.name] = value;
3266
+ }
3267
+ return attrs;
3268
+ }
3269
+
3270
+ function getSiblings(node, root) {
3271
+ if (node.parentElement) return Array.from(node.parentElement.children);
3272
+ return Array.from(root.children || []);
3273
+ }
3274
+
3275
+ function cssEscape(value) {
3276
+ return String(value).replace(/\\/g, "\\\\").replace(/"/g, '\\"');
3277
+ }
3278
+
3279
+ function stablePrimaryKey(attrs) {
3280
+ for (const key of policy.stablePrimaryAttrKeys || []) {
3281
+ if (attrs[key]) return key;
3282
+ }
3283
+ return null;
3284
+ }
3285
+
3286
+ function countMatches(root, tag, attrKey, attrValue, mode) {
3287
+ const scope = root instanceof ShadowRoot ? root : root.ownerDocument;
3288
+ if (!scope || typeof scope.querySelectorAll !== "function") return 0;
3289
+ const escapedTag = String(tag || "*");
3290
+ let selector = escapedTag;
3291
+ if (attrKey && attrValue) {
3292
+ const operator = mode === "prefix" ? "^=" : "=";
3293
+ selector += "[" + attrKey + operator + "\\"" + cssEscape(attrValue) + "\\"]";
3294
+ }
3295
+ try {
3296
+ return scope.querySelectorAll(selector).length;
3297
+ } catch {
3298
+ return 0;
3299
+ }
3300
+ }
3301
+
3302
+ function chooseAttribute(tag, attrs, root) {
3303
+ const stableKey = stablePrimaryKey(attrs);
3304
+ if (stableKey) {
3305
+ const exactCount = countMatches(root, tag, stableKey, attrs[stableKey], "exact");
3306
+ if (exactCount === 1) {
3307
+ return {
3308
+ key: stableKey,
3309
+ value: attrs[stableKey],
3310
+ match: "exact",
3311
+ };
3312
+ }
3313
+ }
3314
+
3315
+ const entries = Object.entries(attrs)
3316
+ .filter(([key]) => !policy.deferredMatchAttrKeys.includes(key))
3317
+ .concat(Object.entries(attrs).filter(([key]) => policy.deferredMatchAttrKeys.includes(key)));
3318
+
3319
+ for (const [key, value] of entries) {
3320
+ const exactCount = countMatches(root, tag, key, value, "exact");
3321
+ if (exactCount === 1) {
3322
+ return { key, value, match: "exact" };
3323
+ }
3324
+ if (value.length >= 4) {
3325
+ const prefixLength = Math.min(Math.max(4, Math.floor(value.length / 2)), value.length);
3326
+ const prefix = value.slice(0, prefixLength);
3327
+ const prefixCount = countMatches(root, tag, key, prefix, "prefix");
3328
+ if (prefixCount === 1) {
3329
+ return { key, value: prefix, match: "prefix" };
3330
+ }
3331
+ }
3332
+ }
3333
+
3334
+ return null;
3335
+ }
3336
+
3337
+ function buildChain(node) {
3338
+ const nodes = [];
3339
+ let current = node;
3340
+ while (current && current instanceof Element) {
3341
+ nodes.unshift(current);
3342
+ current = current.parentElement;
3343
+ }
3344
+ return nodes;
3345
+ }
3346
+
3347
+ function finalizePath(chain, root) {
3348
+ const result = [];
3349
+ for (const node of chain) {
3350
+ const tag = node.tagName.toLowerCase();
3351
+ const attrs = collectAttrs(node);
3352
+ const attribute = chooseAttribute(tag, attrs, root);
3353
+ if (attribute) {
3354
+ result.push({
3355
+ tag,
3356
+ attributes: [
3357
+ {
3358
+ name: attribute.key,
3359
+ value: attribute.value,
3360
+ match: attribute.match,
3361
+ },
3362
+ ],
3363
+ });
3364
+ continue;
3365
+ }
3366
+
3367
+ const siblings = getSiblings(node, root).filter(
3368
+ (candidate) => candidate.tagName.toLowerCase() === tag,
3369
+ );
3370
+ const index = siblings.indexOf(node);
3371
+ result.push({
3372
+ tag,
3373
+ index: siblings.length <= 1 || index < 0 ? undefined : index,
3374
+ });
3375
+ }
3376
+
3377
+ return {
3378
+ nodes: result,
3379
+ };
3380
+ }
3381
+
3382
+ if (!(target instanceof Element)) return null;
3383
+
3384
+ const context = [];
3385
+ let currentRoot = target.getRootNode() instanceof ShadowRoot ? target.getRootNode() : document;
3386
+ const targetChain = buildChain(target);
3387
+ const finalizedTarget = finalizePath(targetChain, currentRoot);
3388
+ if (!finalizedTarget) return null;
3389
+
3390
+ while (currentRoot instanceof ShadowRoot) {
3391
+ const host = currentRoot.host;
3392
+ const hostRoot = host.getRootNode() instanceof ShadowRoot ? host.getRootNode() : document;
3393
+ const hostChain = buildChain(host);
3394
+ const finalizedHost = finalizePath(hostChain, hostRoot);
3395
+ if (!finalizedHost) return null;
3396
+ context.unshift({
3397
+ kind: "shadow",
3398
+ host: finalizedHost.nodes,
3399
+ });
3400
+ currentRoot = hostRoot;
3401
+ }
3402
+
3403
+ return {
3404
+ resolution: "deterministic",
3405
+ context,
3406
+ nodes: finalizedTarget.nodes,
3407
+ };
3408
+ }`;
3167
3409
  function createAbpDomActionBridge(context) {
3168
3410
  return {
3411
+ async buildReplayPath(locator) {
3412
+ const { controller, document, backendNodeId } = await prepareLiveNodeContext(context, locator);
3413
+ return withTemporaryExecutionResume(context, controller, async () => {
3414
+ const raw = await callNodeValueFunction(controller, document, locator, backendNodeId, {
3415
+ functionDeclaration: BUILD_LIVE_REPLAY_PATH_DECLARATION,
3416
+ arguments: [{ value: LIVE_REPLAY_PATH_POLICY }, { value: BUILD_LIVE_REPLAY_PATH_SOURCE }]
3417
+ });
3418
+ return requireReplayPath(raw, locator);
3419
+ });
3420
+ },
3169
3421
  async inspectActionTarget(locator) {
3170
3422
  const { controller, document, backendNodeId } = await prepareLiveNodeContext(
3171
3423
  context,
@@ -3403,6 +3655,24 @@ async function callNodeFunctionWithNodeArgument(controller, document, locator, b
3403
3655
  await releaseObject(controller, objectId);
3404
3656
  }
3405
3657
  }
3658
+ async function callNodeValueFunction(controller, document, locator, backendNodeId, input) {
3659
+ let objectId;
3660
+ try {
3661
+ objectId = await resolveNodeObjectId(controller, document, locator, backendNodeId);
3662
+ const evaluated = await controller.cdp.send("Runtime.callFunctionOn", {
3663
+ objectId,
3664
+ functionDeclaration: input.functionDeclaration,
3665
+ ...input.arguments === void 0 ? {} : { arguments: [...input.arguments] },
3666
+ returnByValue: true,
3667
+ awaitPromise: true
3668
+ });
3669
+ return evaluated.result?.value;
3670
+ } catch (error) {
3671
+ rethrowNodeLookupError2(document, locator, error);
3672
+ } finally {
3673
+ await releaseObject(controller, objectId);
3674
+ }
3675
+ }
3406
3676
  async function resolveNodeObjectId(controller, document, locator, backendNodeId) {
3407
3677
  try {
3408
3678
  const resolved = await controller.cdp.send("DOM.resolveNode", {
@@ -3497,6 +3767,14 @@ function normalizePointerHitAssessment(value, canonicalTarget) {
3497
3767
  canonicalTarget
3498
3768
  };
3499
3769
  }
3770
+ function requireReplayPath(value, locator) {
3771
+ if (!value || typeof value !== "object" || Array.isArray(value) || value.resolution !== "deterministic") {
3772
+ throw new Error(
3773
+ `live DOM replay path builder returned an invalid result for ${locator.nodeRef}`
3774
+ );
3775
+ }
3776
+ return value;
3777
+ }
3500
3778
  function rethrowNodeLookupError2(document, locator, error) {
3501
3779
  if (error instanceof Error && /No node with given id found|Could not find node with given id|Cannot find context/i.test(
3502
3780
  error.message
@@ -6525,6 +6803,6 @@ function resolveFallbackPageTarget(targets, tabMetadata) {
6525
6803
  return void 0;
6526
6804
  }
6527
6805
 
6528
- export { AbpBrowserCoreEngine, createAbpBrowserCoreEngine };
6806
+ export { AbpBrowserCoreEngine, allocatePort, buildAbpLaunchCommand, createAbpBrowserCoreEngine, launchAbpProcess, resolveDefaultAbpBrowserExecutablePath, resolveDefaultAbpExecutablePath, resolveDefaultAbpWrapperExecutablePath };
6529
6807
  //# sourceMappingURL=index.js.map
6530
6808
  //# sourceMappingURL=index.js.map