@mediar-ai/terminator 0.23.0 → 0.23.2

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/package.json CHANGED
@@ -40,11 +40,11 @@
40
40
  }
41
41
  },
42
42
  "optionalDependencies": {
43
- "@mediar-ai/terminator-darwin-arm64": "0.23.0",
44
- "@mediar-ai/terminator-darwin-x64": "0.23.0",
45
- "@mediar-ai/terminator-linux-x64-gnu": "0.23.0",
46
- "@mediar-ai/terminator-win32-arm64-msvc": "0.23.0",
47
- "@mediar-ai/terminator-win32-x64-msvc": "0.23.0"
43
+ "@mediar-ai/terminator-darwin-arm64": "0.23.2",
44
+ "@mediar-ai/terminator-darwin-x64": "0.23.2",
45
+ "@mediar-ai/terminator-linux-x64-gnu": "0.23.2",
46
+ "@mediar-ai/terminator-win32-arm64-msvc": "0.23.2",
47
+ "@mediar-ai/terminator-win32-x64-msvc": "0.23.2"
48
48
  },
49
49
  "repository": {
50
50
  "type": "git",
@@ -62,5 +62,5 @@
62
62
  "test-hook": "powershell.exe -ExecutionPolicy Bypass -File \"../../.git/hooks/pre-push.ps1\""
63
63
  },
64
64
  "types": "wrapper.d.ts",
65
- "version": "0.23.0"
65
+ "version": "0.23.2"
66
66
  }
@@ -1,12 +1,12 @@
1
- const assert = require('assert');
2
- const path = require('path');
3
- const fs = require('fs');
1
+ const assert = require("assert");
2
+ const path = require("path");
3
+ const fs = require("fs");
4
4
 
5
- const { Desktop } = require('../wrapper.js');
5
+ const { Desktop } = require("../wrapper.js");
6
6
 
7
7
  async function testFunctionInput() {
8
8
  let capturedScript = null;
9
- const expected = { greeting: 'hello', answer: 42 };
9
+ const expected = { greeting: "hello", answer: 42 };
10
10
  const fake = {
11
11
  _originalExecuteBrowserScript: async (script) => {
12
12
  capturedScript = script;
@@ -17,68 +17,119 @@ async function testFunctionInput() {
17
17
  const result = await Desktop.prototype.executeBrowserScript.call(
18
18
  fake,
19
19
  ({ greeting, answer }) => ({ greeting, answer }),
20
- { greeting: 'hello', answer: 42 }
20
+ { greeting: "hello", answer: 42 },
21
21
  );
22
22
 
23
23
  assert.deepStrictEqual(result, expected);
24
24
  assert.ok(
25
25
  capturedScript.includes('"greeting":"hello"') &&
26
26
  capturedScript.includes('"answer":42'),
27
- 'env payload should be embedded in generated script'
27
+ "env payload should be embedded in generated script",
28
28
  );
29
29
  assert.ok(
30
- capturedScript.trim().startsWith('(async function()'),
31
- 'generated script should be wrapped in async IIFE'
30
+ capturedScript.trim().startsWith("(async function()"),
31
+ "generated script should be wrapped in async IIFE",
32
32
  );
33
33
  }
34
34
 
35
35
  async function testStringInput() {
36
36
  const fake = {
37
- _originalExecuteBrowserScript: async () => 'raw-result',
37
+ _originalExecuteBrowserScript: async () => "raw-result",
38
38
  };
39
39
  const script = '(() => "ignored")()';
40
- const result = await Desktop.prototype.executeBrowserScript.call(fake, script);
41
- assert.strictEqual(result, 'raw-result');
40
+ const result = await Desktop.prototype.executeBrowserScript.call(
41
+ fake,
42
+ script,
43
+ );
44
+ assert.strictEqual(result, "raw-result");
42
45
  }
43
46
 
44
47
  async function testFileInput() {
45
- const fixturePath = path.join(__dirname, 'fixtures', 'sample-browser-script.js');
46
- const expectedScript = fs.readFileSync(fixturePath, 'utf8');
48
+ const fixturePath = path.join(
49
+ __dirname,
50
+ "fixtures",
51
+ "sample-browser-script.js",
52
+ );
53
+ const expectedScript = fs.readFileSync(fixturePath, "utf8");
47
54
  let receivedScript = null;
48
55
  const fake = {
49
56
  _originalExecuteBrowserScript: async (script) => {
50
57
  receivedScript = script;
51
- return 'file-result';
58
+ return "file-result";
52
59
  },
53
60
  };
54
61
 
62
+ // Test without env - should not inject
55
63
  const result = await Desktop.prototype.executeBrowserScript.call(fake, {
56
64
  file: fixturePath,
57
- env: { unused: true },
58
65
  });
59
66
 
60
- assert.strictEqual(result, 'file-result');
67
+ assert.strictEqual(result, "file-result");
61
68
  assert.strictEqual(receivedScript, expectedScript);
62
69
  }
63
70
 
71
+ async function testFileInputWithEnvInjection() {
72
+ const fixturePath = path.join(__dirname, "fixtures", "script-with-env.js");
73
+ const columnPositions = [10, 20, 30];
74
+ let receivedScript = null;
75
+ const fake = {
76
+ _originalExecuteBrowserScript: async (script) => {
77
+ receivedScript = script;
78
+ // Simulate executing the script with injected env
79
+ const column_positions = columnPositions;
80
+ const result = eval(script);
81
+ return JSON.stringify(result);
82
+ },
83
+ };
84
+
85
+ const result = await Desktop.prototype.executeBrowserScript.call(fake, {
86
+ file: fixturePath,
87
+ env: { column_positions: columnPositions },
88
+ });
89
+
90
+ // Result comes back as JSON string for file-based scripts
91
+ const parsed = JSON.parse(result);
92
+ assert.deepStrictEqual(parsed, {
93
+ positions: columnPositions,
94
+ count: 3,
95
+ });
96
+
97
+ // Verify the env was injected into the script as an env object
98
+ assert.ok(
99
+ receivedScript.includes("const env = "),
100
+ "env object should be injected into script",
101
+ );
102
+ assert.ok(
103
+ receivedScript.includes('"column_positions"'),
104
+ "column_positions key should be in env object",
105
+ );
106
+ assert.ok(
107
+ receivedScript.includes(JSON.stringify(columnPositions)),
108
+ "column_positions value should be embedded",
109
+ );
110
+ }
111
+
64
112
  async function run() {
65
113
  try {
66
114
  await testFunctionInput();
67
- console.log('✅ executeBrowserScript handles function input');
115
+ console.log("✅ executeBrowserScript handles function input");
68
116
 
69
117
  await testStringInput();
70
- console.log('✅ executeBrowserScript preserves string behavior');
118
+ console.log("✅ executeBrowserScript preserves string behavior");
71
119
 
72
120
  await testFileInput();
73
- console.log('✅ executeBrowserScript loads scripts from files');
121
+ console.log("✅ executeBrowserScript loads scripts from files");
74
122
 
75
- console.log('🎉 All executeBrowserScript wrapper tests passed');
123
+ await testFileInputWithEnvInjection();
124
+ console.log(
125
+ "✅ executeBrowserScript injects env variables into file scripts",
126
+ );
127
+
128
+ console.log("🎉 All executeBrowserScript wrapper tests passed");
76
129
  } catch (err) {
77
- console.error('❌ executeBrowserScript wrapper test failed:', err);
130
+ console.error("❌ executeBrowserScript wrapper test failed:", err);
78
131
  process.exitCode = 1;
79
132
  }
80
133
  }
81
134
 
82
135
  run();
83
-
84
-
@@ -0,0 +1,16 @@
1
+ // Browser script that uses injected env object
2
+ (() => {
3
+ // env object should be auto-injected by wrapper
4
+ if (typeof env === "undefined") {
5
+ throw new Error("env not injected");
6
+ }
7
+
8
+ if (!env.column_positions) {
9
+ throw new Error("column_positions not in env");
10
+ }
11
+
12
+ return {
13
+ positions: env.column_positions,
14
+ count: env.column_positions.length,
15
+ };
16
+ })();
package/wrapper.js CHANGED
@@ -152,25 +152,35 @@ function mapNativeError(error) {
152
152
  if (!error.message) return error;
153
153
  const message = error.message;
154
154
  if (message.startsWith("ELEMENT_NOT_FOUND:")) {
155
- return new ElementNotFoundError(message.replace("ELEMENT_NOT_FOUND:", "").trim());
155
+ return new ElementNotFoundError(
156
+ message.replace("ELEMENT_NOT_FOUND:", "").trim()
157
+ );
156
158
  }
157
159
  if (message.startsWith("OPERATION_TIMED_OUT:")) {
158
160
  return new TimeoutError(message.replace("OPERATION_TIMED_OUT:", "").trim());
159
161
  }
160
162
  if (message.startsWith("PERMISSION_DENIED:")) {
161
- return new PermissionDeniedError(message.replace("PERMISSION_DENIED:", "").trim());
163
+ return new PermissionDeniedError(
164
+ message.replace("PERMISSION_DENIED:", "").trim()
165
+ );
162
166
  }
163
167
  if (message.startsWith("PLATFORM_ERROR:")) {
164
168
  return new PlatformError(message.replace("PLATFORM_ERROR:", "").trim());
165
169
  }
166
170
  if (message.startsWith("UNSUPPORTED_OPERATION:")) {
167
- return new UnsupportedOperationError(message.replace("UNSUPPORTED_OPERATION:", "").trim());
171
+ return new UnsupportedOperationError(
172
+ message.replace("UNSUPPORTED_OPERATION:", "").trim()
173
+ );
168
174
  }
169
175
  if (message.startsWith("UNSUPPORTED_PLATFORM:")) {
170
- return new UnsupportedPlatformError(message.replace("UNSUPPORTED_PLATFORM:", "").trim());
176
+ return new UnsupportedPlatformError(
177
+ message.replace("UNSUPPORTED_PLATFORM:", "").trim()
178
+ );
171
179
  }
172
180
  if (message.startsWith("INVALID_ARGUMENT:")) {
173
- return new InvalidArgumentError(message.replace("INVALID_ARGUMENT:", "").trim());
181
+ return new InvalidArgumentError(
182
+ message.replace("INVALID_ARGUMENT:", "").trim()
183
+ );
174
184
  }
175
185
  if (message.startsWith("INTERNAL_ERROR:")) {
176
186
  return new InternalError(message.replace("INTERNAL_ERROR:", "").trim());
@@ -180,6 +190,7 @@ function mapNativeError(error) {
180
190
  async function enhancedExecuteBrowserScript(scriptOrFunction, envOrOptions) {
181
191
  let script;
182
192
  let env = {};
193
+ let shouldInjectEnv = false;
183
194
  if (typeof scriptOrFunction === "string") {
184
195
  if (scriptOrFunction.endsWith(".ts") || scriptOrFunction.endsWith(".js")) {
185
196
  const filePath = path.resolve(scriptOrFunction);
@@ -197,11 +208,15 @@ async function enhancedExecuteBrowserScript(scriptOrFunction, envOrOptions) {
197
208
  });
198
209
  fileContent = result.code;
199
210
  } catch (e) {
200
- console.warn("esbuild not found - using TypeScript file as-is:", e.message);
211
+ console.warn(
212
+ "esbuild not found - using TypeScript file as-is:",
213
+ e.message
214
+ );
201
215
  }
202
216
  }
203
217
  script = fileContent;
204
218
  env = envOrOptions || {};
219
+ shouldInjectEnv = true;
205
220
  } else {
206
221
  script = scriptOrFunction;
207
222
  }
@@ -240,13 +255,24 @@ async function enhancedExecuteBrowserScript(scriptOrFunction, envOrOptions) {
240
255
  });
241
256
  fileContent = result.code;
242
257
  } catch (e) {
243
- console.warn("esbuild not found - using TypeScript file as-is:", e.message);
258
+ console.warn(
259
+ "esbuild not found - using TypeScript file as-is:",
260
+ e.message
261
+ );
244
262
  }
245
263
  }
246
264
  script = fileContent;
247
265
  env = options.env || {};
266
+ shouldInjectEnv = true;
248
267
  } else {
249
- throw new Error("Invalid argument to executeBrowserScript: expected string, function, or {file, env} object");
268
+ throw new Error(
269
+ "Invalid argument to executeBrowserScript: expected string, function, or {file, env} object"
270
+ );
271
+ }
272
+ if (shouldInjectEnv && env && Object.keys(env).length > 0) {
273
+ const envObject = `const env = ${JSON.stringify(env)};`;
274
+ script = `${envObject}
275
+ ${script}`;
250
276
  }
251
277
  const resultStr = await this._originalExecuteBrowserScript(script);
252
278
  if (typeof scriptOrFunction === "function") {
package/wrapper.ts CHANGED
@@ -1,27 +1,35 @@
1
- import * as native from './index.js';
2
- import * as util from 'util';
3
- import * as fs from 'fs';
4
- import * as path from 'path';
1
+ import * as native from "./index.js";
2
+ import * as util from "util";
3
+ import * as fs from "fs";
4
+ import * as path from "path";
5
5
 
6
6
  // Type definitions for native classes
7
- type NativeClass = typeof native.Desktop | typeof native.Element | typeof native.Locator | typeof native.Selector;
8
-
9
- function patchInspector(Klass: any, methodName = 'toString', forcePlainObject = false): void {
10
- if (!Klass || typeof Klass !== 'function') {
11
- console.log('inspect not a function')
7
+ type NativeClass =
8
+ | typeof native.Desktop
9
+ | typeof native.Element
10
+ | typeof native.Locator
11
+ | typeof native.Selector;
12
+
13
+ function patchInspector(
14
+ Klass: any,
15
+ methodName = "toString",
16
+ forcePlainObject = false,
17
+ ): void {
18
+ if (!Klass || typeof Klass !== "function") {
19
+ console.log("inspect not a function");
12
20
  return;
13
21
  }
14
22
  const proto = Klass.prototype;
15
23
  const original = proto[util.inspect.custom];
16
- proto[util.inspect.custom] = function(...args: any[]) {
17
- if (typeof this[methodName] === 'function') {
24
+ proto[util.inspect.custom] = function (...args: any[]) {
25
+ if (typeof this[methodName] === "function") {
18
26
  const result = this[methodName](...args);
19
- if (forcePlainObject && result && typeof result === 'object') {
27
+ if (forcePlainObject && result && typeof result === "object") {
20
28
  return { ...result };
21
29
  }
22
30
  return result;
23
31
  }
24
- if (typeof original === 'function') {
32
+ if (typeof original === "function") {
25
33
  return original.apply(this, args);
26
34
  }
27
35
  return { ...this };
@@ -29,12 +37,12 @@ function patchInspector(Klass: any, methodName = 'toString', forcePlainObject =
29
37
  }
30
38
 
31
39
  function wrapNativeFunction<T extends Function>(fn: T): T {
32
- if (typeof fn !== 'function') return fn;
33
- return function(this: any, ...args: any[]) {
40
+ if (typeof fn !== "function") return fn;
41
+ return function (this: any, ...args: any[]) {
34
42
  try {
35
43
  const result = fn.apply(this, args);
36
44
  if (result instanceof Promise) {
37
- return result.catch(error => {
45
+ return result.catch((error) => {
38
46
  throw mapNativeError(error);
39
47
  });
40
48
  }
@@ -48,15 +56,18 @@ function wrapNativeFunction<T extends Function>(fn: T): T {
48
56
  function wrapClassMethods<T extends NativeClass>(Class: T): T {
49
57
  const prototype = Class.prototype;
50
58
  const methods = Object.getOwnPropertyNames(prototype);
51
- methods.forEach(method => {
52
- if (method !== 'constructor' && typeof prototype[method] === 'function') {
59
+ methods.forEach((method) => {
60
+ if (method !== "constructor" && typeof prototype[method] === "function") {
53
61
  prototype[method] = wrapNativeFunction(prototype[method]);
54
62
  }
55
63
  });
56
64
  return Class;
57
65
  }
58
66
 
59
- function wrapClass<T extends NativeClass>(Class: T, ...inspectOptions: any[]): T {
67
+ function wrapClass<T extends NativeClass>(
68
+ Class: T,
69
+ ...inspectOptions: any[]
70
+ ): T {
60
71
  const Wrapped = wrapClassMethods(Class);
61
72
  patchInspector(Wrapped, ...(inspectOptions || []));
62
73
  return Wrapped;
@@ -64,91 +75,101 @@ function wrapClass<T extends NativeClass>(Class: T, ...inspectOptions: any[]): T
64
75
 
65
76
  // Custom error classes
66
77
  export class ElementNotFoundError extends Error {
67
- constructor(message: string) {
68
- super(message);
69
- this.name = 'ElementNotFoundError';
70
- }
78
+ constructor(message: string) {
79
+ super(message);
80
+ this.name = "ElementNotFoundError";
81
+ }
71
82
  }
72
83
 
73
84
  export class TimeoutError extends Error {
74
- constructor(message: string) {
75
- super(message);
76
- this.name = 'TimeoutError';
77
- }
85
+ constructor(message: string) {
86
+ super(message);
87
+ this.name = "TimeoutError";
88
+ }
78
89
  }
79
90
 
80
91
  export class PermissionDeniedError extends Error {
81
- constructor(message: string) {
82
- super(message);
83
- this.name = 'PermissionDeniedError';
84
- }
92
+ constructor(message: string) {
93
+ super(message);
94
+ this.name = "PermissionDeniedError";
95
+ }
85
96
  }
86
97
 
87
98
  export class PlatformError extends Error {
88
- constructor(message: string) {
89
- super(message);
90
- this.name = 'PlatformError';
91
- }
99
+ constructor(message: string) {
100
+ super(message);
101
+ this.name = "PlatformError";
102
+ }
92
103
  }
93
104
 
94
105
  export class UnsupportedOperationError extends Error {
95
- constructor(message: string) {
96
- super(message);
97
- this.name = 'UnsupportedOperationError';
98
- }
106
+ constructor(message: string) {
107
+ super(message);
108
+ this.name = "UnsupportedOperationError";
109
+ }
99
110
  }
100
111
 
101
112
  export class UnsupportedPlatformError extends Error {
102
- constructor(message: string) {
103
- super(message);
104
- this.name = 'UnsupportedPlatformError';
105
- }
113
+ constructor(message: string) {
114
+ super(message);
115
+ this.name = "UnsupportedPlatformError";
116
+ }
106
117
  }
107
118
 
108
119
  export class InvalidArgumentError extends Error {
109
- constructor(message: string) {
110
- super(message);
111
- this.name = 'InvalidArgumentError';
112
- }
120
+ constructor(message: string) {
121
+ super(message);
122
+ this.name = "InvalidArgumentError";
123
+ }
113
124
  }
114
125
 
115
126
  export class InternalError extends Error {
116
- constructor(message: string) {
117
- super(message);
118
- this.name = 'InternalError';
119
- }
127
+ constructor(message: string) {
128
+ super(message);
129
+ this.name = "InternalError";
130
+ }
120
131
  }
121
132
 
122
133
  // Error mapping function
123
134
  function mapNativeError(error: any): Error {
124
- if (!error.message) return error;
135
+ if (!error.message) return error;
125
136
 
126
- const message = error.message;
127
- if (message.startsWith('ELEMENT_NOT_FOUND:')) {
128
- return new ElementNotFoundError(message.replace('ELEMENT_NOT_FOUND:', '').trim());
129
- }
130
- if (message.startsWith('OPERATION_TIMED_OUT:')) {
131
- return new TimeoutError(message.replace('OPERATION_TIMED_OUT:', '').trim());
132
- }
133
- if (message.startsWith('PERMISSION_DENIED:')) {
134
- return new PermissionDeniedError(message.replace('PERMISSION_DENIED:', '').trim());
135
- }
136
- if (message.startsWith('PLATFORM_ERROR:')) {
137
- return new PlatformError(message.replace('PLATFORM_ERROR:', '').trim());
138
- }
139
- if (message.startsWith('UNSUPPORTED_OPERATION:')) {
140
- return new UnsupportedOperationError(message.replace('UNSUPPORTED_OPERATION:', '').trim());
141
- }
142
- if (message.startsWith('UNSUPPORTED_PLATFORM:')) {
143
- return new UnsupportedPlatformError(message.replace('UNSUPPORTED_PLATFORM:', '').trim());
144
- }
145
- if (message.startsWith('INVALID_ARGUMENT:')) {
146
- return new InvalidArgumentError(message.replace('INVALID_ARGUMENT:', '').trim());
147
- }
148
- if (message.startsWith('INTERNAL_ERROR:')) {
149
- return new InternalError(message.replace('INTERNAL_ERROR:', '').trim());
150
- }
151
- return error;
137
+ const message = error.message;
138
+ if (message.startsWith("ELEMENT_NOT_FOUND:")) {
139
+ return new ElementNotFoundError(
140
+ message.replace("ELEMENT_NOT_FOUND:", "").trim(),
141
+ );
142
+ }
143
+ if (message.startsWith("OPERATION_TIMED_OUT:")) {
144
+ return new TimeoutError(message.replace("OPERATION_TIMED_OUT:", "").trim());
145
+ }
146
+ if (message.startsWith("PERMISSION_DENIED:")) {
147
+ return new PermissionDeniedError(
148
+ message.replace("PERMISSION_DENIED:", "").trim(),
149
+ );
150
+ }
151
+ if (message.startsWith("PLATFORM_ERROR:")) {
152
+ return new PlatformError(message.replace("PLATFORM_ERROR:", "").trim());
153
+ }
154
+ if (message.startsWith("UNSUPPORTED_OPERATION:")) {
155
+ return new UnsupportedOperationError(
156
+ message.replace("UNSUPPORTED_OPERATION:", "").trim(),
157
+ );
158
+ }
159
+ if (message.startsWith("UNSUPPORTED_PLATFORM:")) {
160
+ return new UnsupportedPlatformError(
161
+ message.replace("UNSUPPORTED_PLATFORM:", "").trim(),
162
+ );
163
+ }
164
+ if (message.startsWith("INVALID_ARGUMENT:")) {
165
+ return new InvalidArgumentError(
166
+ message.replace("INVALID_ARGUMENT:", "").trim(),
167
+ );
168
+ }
169
+ if (message.startsWith("INTERNAL_ERROR:")) {
170
+ return new InternalError(message.replace("INTERNAL_ERROR:", "").trim());
171
+ }
172
+ return error;
152
173
  }
153
174
 
154
175
  // Types for executeBrowserScript arguments
@@ -164,46 +185,51 @@ type BrowserScriptInput = string | BrowserScriptFunction | BrowserScriptOptions;
164
185
  async function enhancedExecuteBrowserScript(
165
186
  this: any,
166
187
  scriptOrFunction: BrowserScriptInput,
167
- envOrOptions?: any
188
+ envOrOptions?: any,
168
189
  ): Promise<any> {
169
190
  let script: string;
170
191
  let env: any = {};
192
+ let shouldInjectEnv = false; // Only inject env for file-based and string scripts, not functions
171
193
 
172
194
  // Handle different input types
173
- if (typeof scriptOrFunction === 'string') {
195
+ if (typeof scriptOrFunction === "string") {
174
196
  // Check if it's a file path
175
- if (scriptOrFunction.endsWith('.ts') || scriptOrFunction.endsWith('.js')) {
197
+ if (scriptOrFunction.endsWith(".ts") || scriptOrFunction.endsWith(".js")) {
176
198
  // File path - read and compile
177
199
  const filePath = path.resolve(scriptOrFunction);
178
200
  if (!fs.existsSync(filePath)) {
179
201
  throw new Error(`Browser script file not found: ${filePath}`);
180
202
  }
181
203
 
182
- let fileContent = fs.readFileSync(filePath, 'utf-8');
204
+ let fileContent = fs.readFileSync(filePath, "utf-8");
183
205
 
184
206
  // If TypeScript, compile it
185
- if (filePath.endsWith('.ts')) {
207
+ if (filePath.endsWith(".ts")) {
186
208
  try {
187
- const esbuild = require('esbuild');
209
+ const esbuild = require("esbuild");
188
210
  const result = await esbuild.transform(fileContent, {
189
- loader: 'ts',
190
- target: 'es2020',
191
- format: 'iife'
211
+ loader: "ts",
212
+ target: "es2020",
213
+ format: "iife",
192
214
  });
193
215
  fileContent = result.code;
194
216
  } catch (e: any) {
195
217
  // If esbuild not available, try to use as-is (may work for simple TS)
196
- console.warn('esbuild not found - using TypeScript file as-is:', e.message);
218
+ console.warn(
219
+ "esbuild not found - using TypeScript file as-is:",
220
+ e.message,
221
+ );
197
222
  }
198
223
  }
199
224
 
200
225
  script = fileContent;
201
226
  env = envOrOptions || {};
227
+ shouldInjectEnv = true; // Inject env for file paths passed as strings
202
228
  } else {
203
229
  // Plain string script - use as-is (backward compatible)
204
230
  script = scriptOrFunction;
205
231
  }
206
- } else if (typeof scriptOrFunction === 'function') {
232
+ } else if (typeof scriptOrFunction === "function") {
207
233
  // Function - convert to IIFE with proper wrapping
208
234
  const funcString = scriptOrFunction.toString();
209
235
  env = envOrOptions || {};
@@ -224,7 +250,10 @@ async function enhancedExecuteBrowserScript(
224
250
  return null;
225
251
  })()
226
252
  `;
227
- } else if (typeof scriptOrFunction === 'object' && (scriptOrFunction as BrowserScriptOptions).file) {
253
+ } else if (
254
+ typeof scriptOrFunction === "object" &&
255
+ (scriptOrFunction as BrowserScriptOptions).file
256
+ ) {
228
257
  // Object with file property
229
258
  const options = scriptOrFunction as BrowserScriptOptions;
230
259
  const filePath = path.resolve(options.file);
@@ -232,34 +261,47 @@ async function enhancedExecuteBrowserScript(
232
261
  throw new Error(`Browser script file not found: ${filePath}`);
233
262
  }
234
263
 
235
- let fileContent = fs.readFileSync(filePath, 'utf-8');
264
+ let fileContent = fs.readFileSync(filePath, "utf-8");
236
265
 
237
266
  // If TypeScript, compile it
238
- if (filePath.endsWith('.ts')) {
267
+ if (filePath.endsWith(".ts")) {
239
268
  try {
240
- const esbuild = require('esbuild');
269
+ const esbuild = require("esbuild");
241
270
  const result = await esbuild.transform(fileContent, {
242
- loader: 'ts',
243
- target: 'es2020',
244
- format: 'iife'
271
+ loader: "ts",
272
+ target: "es2020",
273
+ format: "iife",
245
274
  });
246
275
  fileContent = result.code;
247
276
  } catch (e: any) {
248
- console.warn('esbuild not found - using TypeScript file as-is:', e.message);
277
+ console.warn(
278
+ "esbuild not found - using TypeScript file as-is:",
279
+ e.message,
280
+ );
249
281
  }
250
282
  }
251
283
 
252
284
  script = fileContent;
253
285
  env = options.env || {};
286
+ shouldInjectEnv = true; // Inject env for file option objects
254
287
  } else {
255
- throw new Error('Invalid argument to executeBrowserScript: expected string, function, or {file, env} object');
288
+ throw new Error(
289
+ "Invalid argument to executeBrowserScript: expected string, function, or {file, env} object",
290
+ );
291
+ }
292
+
293
+ // If env variables are provided and we should inject them (file-based scripts only)
294
+ if (shouldInjectEnv && env && Object.keys(env).length > 0) {
295
+ // Inject as an env object that scripts can access
296
+ const envObject = `const env = ${JSON.stringify(env)};`;
297
+ script = `${envObject}\n${script}`;
256
298
  }
257
299
 
258
300
  // Call the original native method
259
301
  const resultStr = await this._originalExecuteBrowserScript(script);
260
302
 
261
303
  // If function was passed, try to parse JSON result
262
- if (typeof scriptOrFunction === 'function') {
304
+ if (typeof scriptOrFunction === "function") {
263
305
  try {
264
306
  return JSON.parse(resultStr);
265
307
  } catch (e) {
@@ -280,15 +322,28 @@ export const Selector = wrapClass(native.Selector);
280
322
 
281
323
  // Patch executeBrowserScript on Desktop and Element
282
324
  if (Desktop.prototype.executeBrowserScript) {
283
- (Desktop.prototype as any)._originalExecuteBrowserScript = Desktop.prototype.executeBrowserScript;
325
+ (Desktop.prototype as any)._originalExecuteBrowserScript =
326
+ Desktop.prototype.executeBrowserScript;
284
327
  Desktop.prototype.executeBrowserScript = enhancedExecuteBrowserScript;
285
328
  }
286
329
 
287
330
  if (Element.prototype.executeBrowserScript) {
288
- (Element.prototype as any)._originalExecuteBrowserScript = Element.prototype.executeBrowserScript;
331
+ (Element.prototype as any)._originalExecuteBrowserScript =
332
+ Element.prototype.executeBrowserScript;
289
333
  Element.prototype.executeBrowserScript = enhancedExecuteBrowserScript;
290
334
  }
291
335
 
292
336
  // Re-export native types
293
- export type { ValidationResult, Bounds, Coordinates, ClickResult, CommandOutput, Monitor, MonitorScreenshotPair, ScreenshotResult, UIElementAttributes, UINode } from './index.js';
294
- export { PropertyLoadingMode, TextPosition } from './index.js';
337
+ export type {
338
+ ValidationResult,
339
+ Bounds,
340
+ Coordinates,
341
+ ClickResult,
342
+ CommandOutput,
343
+ Monitor,
344
+ MonitorScreenshotPair,
345
+ ScreenshotResult,
346
+ UIElementAttributes,
347
+ UINode,
348
+ } from "./index.js";
349
+ export { PropertyLoadingMode, TextPosition } from "./index.js";