@intuned/browser-dev 0.1.4-dev.0 → 0.1.5-dev.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/README.md CHANGED
@@ -50,7 +50,6 @@ The Intuned Browser SDK provides a comprehensive set of tools for browser automa
50
50
 
51
51
  - **Schema Validation** - Validate data structures with `validateDataUsingSchema()`
52
52
  - **Empty Value Filtering** - Filter empty values with `filterEmptyValues()`
53
- - **Custom Type Registry** - Define custom validators with `CustomTypeRegistry`
54
53
 
55
54
  ### ⚡ Optimized Extractors
56
55
 
@@ -535,7 +535,7 @@ export type SUPPORTED_MODELS =
535
535
  * @param {Object} input - Input object containing the page to check
536
536
  * @param {Page} input.page - The Playwright page to check
537
537
  * @param {number} [input.timeoutInMs=10000] - Screenshot timeout in milliseconds. Defaults to 10000
538
- * @param {SUPPORTED_MODELS} [input.model="gpt-4o-2024-08-06"] - AI model to use for the check. See [SUPPORTED_MODELS](../type-aliases/SUPPORTED_MODELS) for all supported models. Defaults to "gpt-4o-2024-08-06"
538
+ * @param {SUPPORTED_MODELS} [input.model="gpt-5-mini-2025-08-07"] - AI model to use for the check. See [SUPPORTED_MODELS](../type-aliases/SUPPORTED_MODELS) for all supported models. Defaults to "gpt-5-mini-2025-08-07"
539
539
  * @param {string} [input.apiKey] - Optional API key for the AI service (if provided, will not be billed to your account)
540
540
  * @returns {Promise<boolean>} Promise resolving to true if page is loaded, false if still loading
541
541
  * @example
@@ -535,7 +535,7 @@ export type SUPPORTED_MODELS =
535
535
  * @param {Object} input - Input object containing the page to check
536
536
  * @param {Page} input.page - The Playwright page to check
537
537
  * @param {number} [input.timeoutInMs=10000] - Screenshot timeout in milliseconds. Defaults to 10000
538
- * @param {SUPPORTED_MODELS} [input.model="gpt-4o-2024-08-06"] - AI model to use for the check. See [SUPPORTED_MODELS](../type-aliases/SUPPORTED_MODELS) for all supported models. Defaults to "gpt-4o-2024-08-06"
538
+ * @param {SUPPORTED_MODELS} [input.model="gpt-5-mini-2025-08-07"] - AI model to use for the check. See [SUPPORTED_MODELS](../type-aliases/SUPPORTED_MODELS) for all supported models. Defaults to "gpt-5-mini-2025-08-07"
539
539
  * @param {string} [input.apiKey] - Optional API key for the AI service (if provided, will not be billed to your account)
540
540
  * @returns {Promise<boolean>} Promise resolving to true if page is loaded, false if still loading
541
541
  * @example
@@ -20,7 +20,7 @@ const isPageLoaded = async options => {
20
20
  });
21
21
  const gateway = _utils.GatewayFactory.createAIGateway({
22
22
  apiKey: options === null || options === void 0 ? void 0 : options.apiKey,
23
- model: (options === null || options === void 0 ? void 0 : options.model) ?? "gpt-4o-2024-08-06"
23
+ model: (options === null || options === void 0 ? void 0 : options.model) ?? "gpt-5-mini-2025-08-07"
24
24
  });
25
25
  const gatewayModel = await gateway.getModel();
26
26
  const base64Image = Buffer.from(screenshotBytes).toString("base64");
@@ -30,11 +30,22 @@ const isPageLoaded = async options => {
30
30
  role: "system",
31
31
  content: `You are a helpful assistant that determines if a webpage finished loading. If the page finished loading, start your answer with 'True'. If the page is loading, start your answer with 'False'. If you are not sure, start your answer with 'Dont know'. In a new line, add a reason to your response.
32
32
 
33
- Some good cues for determining if a page is loading:
33
+ ## Some good cues for determining if a page is loading (return 'False'):
34
34
  - Loading spinner
35
35
  - Page is blank
36
36
  - Some content looks like it's missing
37
- - Not on splash screen`
37
+ - Not on splash screen
38
+
39
+ ## Special cases for LOADED pages (return 'True')
40
+ - CAPTCHA challenges are considered loaded, because it is indeed loaded and waiting for the user to solve the captcha.
41
+ - Bot detection screens (e.g., "Checking your browser", Cloudflare verification) are considered loaded, because it is indeed loaded and waiting for the user to solve the captcha.
42
+ - Static error pages (404 Not Found, 403 Forbidden, 500 Internal Server Error, etc.)
43
+ - Login/authentication screens
44
+ - Complete forms ready for user input
45
+ - Fully rendered articles, product pages, or dashboards
46
+ - Cookie consent banners or popups over complete content
47
+ - Payment or checkout pages with all fields visible
48
+ `
38
49
  }, {
39
50
  role: "user",
40
51
  content: [{
@@ -175,7 +175,7 @@ const ERROR_PAGE = `
175
175
  </body>
176
176
  </html>
177
177
  `;
178
- (0, _extendedTest.describe)("isPageLoaded Tests", () => {
178
+ _extendedTest.describe.skip("isPageLoaded Tests", () => {
179
179
  let browser;
180
180
  let page;
181
181
  (0, _extendedTest.beforeAll)(async () => {
@@ -263,11 +263,11 @@ const ERROR_PAGE = `
263
263
  });
264
264
  });
265
265
  (0, _extendedTest.describe)("Different model types", () => {
266
- (0, _extendedTest.test)("should work with claude-3-5-sonnet-20240620", async () => {
266
+ (0, _extendedTest.test)("should work with claude-3-7-sonnet-latest", async () => {
267
267
  await page.setContent(FULLY_LOADED_PAGE);
268
268
  const result = await (0, _isPageLoaded.isPageLoaded)({
269
269
  page,
270
- model: "claude-3-5-sonnet-20240620",
270
+ model: "claude-3-7-sonnet-latest",
271
271
  apiKey: process.env.ANTHROPIC_API_KEY,
272
272
  timeoutInMs: 10000
273
273
  });
@@ -62,6 +62,7 @@ const downloadFile = async input => {
62
62
  downloadPromise = pageToDownloadFrom.waitForEvent("download", {
63
63
  timeout: (timeoutInMs ?? 5000) + 1000
64
64
  });
65
+ await page.evaluate("(() => {window.waitForPrintDialog = new Promise(f => window.print = f);})()");
65
66
  if (isUrlTrigger(trigger)) {
66
67
  absoluteUrl = await getAbsoluteUrl(page, trigger);
67
68
  if (!isValidURL(absoluteUrl)) {
@@ -92,8 +93,44 @@ const downloadFile = async input => {
92
93
  }
93
94
  } else if (isCallableTrigger(trigger)) {
94
95
  action = await trigger(page);
96
+ try {
97
+ await pageToDownloadFrom.waitForFunction("window.waitForPrintDialog", undefined, {
98
+ timeout: 1000
99
+ });
100
+ const pdf = await pageToDownloadFrom.pdf({
101
+ format: "A4"
102
+ });
103
+ const pdfBase64 = pdf.toString("base64");
104
+ await pageToDownloadFrom.evaluate(base64 => {
105
+ const dataUrl = `data:application/pdf;base64,${base64}`;
106
+ const link = document.createElement("a");
107
+ link.href = dataUrl;
108
+ link.download = "print.pdf";
109
+ document.body.appendChild(link);
110
+ link.click();
111
+ document.body.removeChild(link);
112
+ }, pdfBase64);
113
+ } catch (error) {}
95
114
  } else if (isLocatorTrigger(trigger)) {
96
115
  action = await trigger.click();
116
+ try {
117
+ await pageToDownloadFrom.waitForFunction("window.waitForPrintDialog", undefined, {
118
+ timeout: 1000
119
+ });
120
+ const pdf = await pageToDownloadFrom.pdf({
121
+ format: "A4"
122
+ });
123
+ const pdfBase64 = pdf.toString("base64");
124
+ await pageToDownloadFrom.evaluate(base64 => {
125
+ const dataUrl = `data:application/pdf;base64,${base64}`;
126
+ const link = document.createElement("a");
127
+ link.href = dataUrl;
128
+ link.download = "print.pdf";
129
+ document.body.appendChild(link);
130
+ link.click();
131
+ document.body.removeChild(link);
132
+ }, pdfBase64);
133
+ } catch (error) {}
97
134
  }
98
135
  const download = await downloadPromise;
99
136
  if ((0, _utils.isGenerateCodeMode)()) {
@@ -204,7 +204,7 @@ export declare function filterEmptyValues<T>(input: { data: T }): T;
204
204
  * @param {string} [input.waitForLoadState="load"] - When to consider navigation succeeded. Options: "load", "domcontentloaded", "networkidle", "commit". Defaults to "load"
205
205
  * @param {boolean} [input.throwOnTimeout=true] - Whether to throw an error if navigation times out. When false, the function returns without throwing, allowing continued execution. Defaults to true.
206
206
  * @param {boolean} [input.waitForLoadingStateUsingAi=false] - When true, uses AI vision to verify the page is fully loaded by checking for loading spinners, blank content, or incomplete states. Retries up to 4 times with 5-second delays. Defaults to false
207
- * @param {SUPPORTED_MODELS} [input.model="gpt-4o-2024-08-06"] - AI model to use for loading verification. See [SUPPORTED_MODELS](../type-aliases/SUPPORTED_MODELS) for all supported models. Defaults to "gpt-4o-2024-08-06"
207
+ * @param {SUPPORTED_MODELS} [input.model="gpt-5-mini-2025-08-07"] - AI model to use for loading verification. See [SUPPORTED_MODELS](../type-aliases/SUPPORTED_MODELS) for all supported models. Defaults to "gpt-5-mini-2025-08-07"
208
208
  * @param {string} [input.apiKey] - Optional API key for the AI service (if provided, will not be billed to your account)
209
209
  * @returns {Promise<void>} Promise that resolves when navigation completes successfully. If the operation fails and `throwOnTimeout` is false, resolves without error
210
210
  *
@@ -540,7 +540,7 @@ export declare function uploadFileToS3(input: {
540
540
  * @param {Object} input - The input object containing data and schema
541
541
  * @param {Record<string, any> | Record<string, any>[]} input.data - The data to validate. Can be a single data object or an array of data objects
542
542
  * @param {Record<string, any>} input.schema - JSON schema object defining validation rules
543
- * @returns {Promise<void>} Promise that resolves if validation passes, throws ValidationError if it fails
543
+ * @returns {void} Returns nothing if validation passes, throws ValidationError if it fails
544
544
  *
545
545
  * @example
546
546
  * ```typescript Basic User Data Validation
@@ -562,7 +562,7 @@ export declare function uploadFileToS3(input: {
562
562
  * }
563
563
  * };
564
564
  *
565
- * await validateDataUsingSchema({ data: userData, schema: userSchema });
565
+ * validateDataUsingSchema({ data: userData, schema: userSchema });
566
566
  * // Validation passes, no error thrown
567
567
  * }
568
568
  * ```
@@ -585,7 +585,7 @@ export declare function uploadFileToS3(input: {
585
585
  * }
586
586
  * };
587
587
  *
588
- * await validateDataUsingSchema({ data: userData, schema: userSchema });
588
+ * validateDataUsingSchema({ data: userData, schema: userSchema });
589
589
  * // Validation fails, throws ValidationError
590
590
  * }
591
591
  * ```
@@ -593,7 +593,7 @@ export declare function uploadFileToS3(input: {
593
593
  export declare function validateDataUsingSchema(input: {
594
594
  data: Record<string, any>[] | Record<string, any>;
595
595
  schema: Record<string, any>;
596
- }): Promise<void>;
596
+ }): void;
597
597
 
598
598
  /**
599
599
  * Executes a callback function and waits for network requests to settle before returning.
@@ -839,6 +839,9 @@ export interface Attachment {
839
839
  /** The name/key of the file in the S3 bucket */
840
840
  fileName: string;
841
841
 
842
+ /** The S3 object key/path */
843
+ key: string;
844
+
842
845
  /** The S3 bucket name where the file is stored */
843
846
  bucket: string;
844
847
 
@@ -846,13 +849,13 @@ export interface Attachment {
846
849
  region: string;
847
850
 
848
851
  /** Optional custom S3 endpoint URL. Defaults to undefined for standard AWS S3 */
849
- endpoint?: string;
852
+ endpoint?: string | null;
850
853
 
851
854
  /** A human-readable filename suggestion for downloads or display */
852
855
  suggestedFileName: string;
853
856
 
854
857
  /** The file type of the file */
855
- fileType?: AttachmentType;
858
+ fileType?: AttachmentType | null;
856
859
 
857
860
  /**
858
861
  * Returns a JSON-serializable record representation of the file.
@@ -20,7 +20,7 @@ const goToUrl = async input => {
20
20
  throwOnTimeout = true,
21
21
  waitForLoadingStateUsingAi = false,
22
22
  retries = 3,
23
- model = "gpt-4o-2024-08-06",
23
+ model = "gpt-5-mini-2025-08-07",
24
24
  apiKey,
25
25
  waitForLoadState = "load"
26
26
  } = input || {};
@@ -204,7 +204,7 @@ export declare function filterEmptyValues<T>(input: { data: T }): T;
204
204
  * @param {string} [input.waitForLoadState="load"] - When to consider navigation succeeded. Options: "load", "domcontentloaded", "networkidle", "commit". Defaults to "load"
205
205
  * @param {boolean} [input.throwOnTimeout=true] - Whether to throw an error if navigation times out. When false, the function returns without throwing, allowing continued execution. Defaults to true.
206
206
  * @param {boolean} [input.waitForLoadingStateUsingAi=false] - When true, uses AI vision to verify the page is fully loaded by checking for loading spinners, blank content, or incomplete states. Retries up to 4 times with 5-second delays. Defaults to false
207
- * @param {SUPPORTED_MODELS} [input.model="gpt-4o-2024-08-06"] - AI model to use for loading verification. See [SUPPORTED_MODELS](../type-aliases/SUPPORTED_MODELS) for all supported models. Defaults to "gpt-4o-2024-08-06"
207
+ * @param {SUPPORTED_MODELS} [input.model="gpt-5-mini-2025-08-07"] - AI model to use for loading verification. See [SUPPORTED_MODELS](../type-aliases/SUPPORTED_MODELS) for all supported models. Defaults to "gpt-5-mini-2025-08-07"
208
208
  * @param {string} [input.apiKey] - Optional API key for the AI service (if provided, will not be billed to your account)
209
209
  * @returns {Promise<void>} Promise that resolves when navigation completes successfully. If the operation fails and `throwOnTimeout` is false, resolves without error
210
210
  *
@@ -540,7 +540,7 @@ export declare function uploadFileToS3(input: {
540
540
  * @param {Object} input - The input object containing data and schema
541
541
  * @param {Record<string, any> | Record<string, any>[]} input.data - The data to validate. Can be a single data object or an array of data objects
542
542
  * @param {Record<string, any>} input.schema - JSON schema object defining validation rules
543
- * @returns {Promise<void>} Promise that resolves if validation passes, throws ValidationError if it fails
543
+ * @returns {void} Returns nothing if validation passes, throws ValidationError if it fails
544
544
  *
545
545
  * @example
546
546
  * ```typescript Basic User Data Validation
@@ -562,7 +562,7 @@ export declare function uploadFileToS3(input: {
562
562
  * }
563
563
  * };
564
564
  *
565
- * await validateDataUsingSchema({ data: userData, schema: userSchema });
565
+ * validateDataUsingSchema({ data: userData, schema: userSchema });
566
566
  * // Validation passes, no error thrown
567
567
  * }
568
568
  * ```
@@ -585,7 +585,7 @@ export declare function uploadFileToS3(input: {
585
585
  * }
586
586
  * };
587
587
  *
588
- * await validateDataUsingSchema({ data: userData, schema: userSchema });
588
+ * validateDataUsingSchema({ data: userData, schema: userSchema });
589
589
  * // Validation fails, throws ValidationError
590
590
  * }
591
591
  * ```
@@ -593,7 +593,7 @@ export declare function uploadFileToS3(input: {
593
593
  export declare function validateDataUsingSchema(input: {
594
594
  data: Record<string, any>[] | Record<string, any>;
595
595
  schema: Record<string, any>;
596
- }): Promise<void>;
596
+ }): void;
597
597
 
598
598
  /**
599
599
  * Executes a callback function and waits for network requests to settle before returning.
@@ -839,6 +839,9 @@ export interface Attachment {
839
839
  /** The name/key of the file in the S3 bucket */
840
840
  fileName: string;
841
841
 
842
+ /** The S3 object key/path */
843
+ key: string;
844
+
842
845
  /** The S3 bucket name where the file is stored */
843
846
  bucket: string;
844
847
 
@@ -846,13 +849,13 @@ export interface Attachment {
846
849
  region: string;
847
850
 
848
851
  /** Optional custom S3 endpoint URL. Defaults to undefined for standard AWS S3 */
849
- endpoint?: string;
852
+ endpoint?: string | null;
850
853
 
851
854
  /** A human-readable filename suggestion for downloads or display */
852
855
  suggestedFileName: string;
853
856
 
854
857
  /** The file type of the file */
855
- fileType?: AttachmentType;
858
+ fileType?: AttachmentType | null;
856
859
 
857
860
  /**
858
861
  * Returns a JSON-serializable record representation of the file.
@@ -3,24 +3,6 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- Object.defineProperty(exports, "AttachmentValidator", {
7
- enumerable: true,
8
- get: function () {
9
- return _types.AttachmentValidator;
10
- }
11
- });
12
- Object.defineProperty(exports, "CustomTypeRegistry", {
13
- enumerable: true,
14
- get: function () {
15
- return _types.CustomTypeRegistry;
16
- }
17
- });
18
- Object.defineProperty(exports, "CustomTypeValidator", {
19
- enumerable: true,
20
- get: function () {
21
- return _types.CustomTypeValidator;
22
- }
23
- });
24
6
  Object.defineProperty(exports, "clickUntilExhausted", {
25
7
  enumerable: true,
26
8
  get: function () {
@@ -109,7 +91,6 @@ var _gotoUrl = require("./gotoUrl");
109
91
  var _uploadFileToS = require("./uploadFileToS3");
110
92
  var _withNetworkSettledWait = require("./withNetworkSettledWait");
111
93
  var _validateDataUsingSchema = require("./validateDataUsingSchema");
112
- var _types = require("./types");
113
94
  var _downloadFile = require("./downloadFile");
114
95
  var _filterEmptyValues = require("./filterEmptyValues");
115
96
  var _processDate = require("./processDate");
@@ -37,6 +37,16 @@ const content = `
37
37
  <a href="https://intuned-docs-public-images.s3.us-west-2.amazonaws.com/large_pdf.pdf">Download Text File</a>
38
38
  </div>
39
39
 
40
+ <div>
41
+ <h3>Print Dialog Download</h3>
42
+ <button id="print-button">Print Document</button>
43
+ <script>
44
+ document.getElementById('print-button').addEventListener('click', function() {
45
+ window.print();
46
+ });
47
+ </script>
48
+ </div>
49
+
40
50
  </body>
41
51
  </html>
42
52
  `;
@@ -56,14 +66,14 @@ async function createUserDirWithPreferences() {
56
66
  await (0, _promises.writeFile)(preferencesPath, JSON.stringify(preferences));
57
67
  return (0, _path.resolve)(userDir);
58
68
  }
59
- _extendedTest.describe.skip("TestNotInGeneration", () => {
69
+ (0, _extendedTest.describe)("TestNotInGeneration", () => {
60
70
  let context;
61
71
  let page;
62
72
  (0, _extendedTest.beforeAll)(async () => {
63
73
  const dir = await createUserDirWithPreferences();
64
74
  process.env.MODE = "";
65
75
  context = await _playwrightCore.chromium.launchPersistentContext(dir, {
66
- headless: true,
76
+ headless: false,
67
77
  args: ["--no-first-run", "--disable-sync", "--disable-translate", "--disable-features=TranslateUI", "--disable-features=NetworkService", "--lang=en", "--disable-blink-features=AutomationControlled"],
68
78
  acceptDownloads: true
69
79
  });
@@ -97,7 +107,7 @@ _extendedTest.describe.skip("TestNotInGeneration", () => {
97
107
  (0, _extendedTest.expect)(path).toBeDefined();
98
108
  (0, _extendedTest.expect)(String(path)).toContain("/var/folders/");
99
109
  });
100
- _extendedTest.test.skip("should upload image to s3", async () => {
110
+ (0, _extendedTest.test)("should upload image to s3", async () => {
101
111
  await page.setContent(content);
102
112
  const downloadedImage = await (0, _.downloadFile)({
103
113
  page,
@@ -120,7 +130,7 @@ _extendedTest.describe.skip("TestNotInGeneration", () => {
120
130
  (0, _extendedTest.expect)(uploadedFile.suggestedFileName).toBeDefined();
121
131
  (0, _extendedTest.expect)(uploadedFile.bucket).toBe(process.env.INTUNED_S3_BUCKET);
122
132
  });
123
- _extendedTest.test.skip("should upload file to s3", async () => {
133
+ (0, _extendedTest.test)("should upload file to s3", async () => {
124
134
  await page.setContent(content);
125
135
  const download = await (0, _.downloadFile)({
126
136
  page,
@@ -133,6 +143,18 @@ _extendedTest.describe.skip("TestNotInGeneration", () => {
133
143
  (0, _extendedTest.expect)(uploadedFile.suggestedFileName).toBeDefined();
134
144
  (0, _extendedTest.expect)(uploadedFile.bucket).toBe(process.env.INTUNED_S3_BUCKET);
135
145
  });
146
+ (0, _extendedTest.test)("should download from print dialog", async () => {
147
+ await page.setContent(content);
148
+ const printButton = page.locator("#print-button");
149
+ const download = await (0, _.downloadFile)({
150
+ page,
151
+ trigger: printButton
152
+ });
153
+ (0, _extendedTest.expect)(download).toBeDefined();
154
+ const downloadPath = await download.path();
155
+ (0, _extendedTest.expect)(downloadPath).toBeDefined();
156
+ (0, _extendedTest.expect)(download.suggestedFilename()).toContain(".pdf");
157
+ });
136
158
  });
137
159
  (0, _extendedTest.describe)("TestInGeneration", () => {
138
160
  let context;
@@ -141,7 +163,7 @@ _extendedTest.describe.skip("TestNotInGeneration", () => {
141
163
  process.env.MODE = "generate_code";
142
164
  const dir = await createUserDirWithPreferences();
143
165
  context = await _playwrightCore.chromium.launchPersistentContext(dir, {
144
- headless: true,
166
+ headless: false,
145
167
  userAgent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36",
146
168
  args: ["--no-first-run", "--disable-sync", "--disable-translate", "--disable-features=TranslateUI", "--disable-features=NetworkService", "--lang=en", "--disable-blink-features=AutomationControlled"],
147
169
  acceptDownloads: true
@@ -188,7 +210,7 @@ _extendedTest.describe.skip("TestNotInGeneration", () => {
188
210
  });
189
211
  (0, _extendedTest.expect)(downloadedImage.path).toBeDefined();
190
212
  });
191
- _extendedTest.test.skip("should upload file to s3", async () => {
213
+ (0, _extendedTest.test)("should upload file to s3", async () => {
192
214
  await page.setContent(content);
193
215
  const download = await (0, _.downloadFile)({
194
216
  page,
@@ -203,4 +225,17 @@ _extendedTest.describe.skip("TestNotInGeneration", () => {
203
225
  (0, _extendedTest.expect)(uploadedFile.bucket).toBe("testing_bucket");
204
226
  (0, _extendedTest.expect)(uploadedFile.region).toBe("testing_region");
205
227
  });
228
+ (0, _extendedTest.test)("should download from print dialog", async () => {
229
+ await page.setContent(content);
230
+ const printButton = page.locator("#print-button");
231
+ const download = await (0, _.downloadFile)({
232
+ page,
233
+ trigger: printButton
234
+ });
235
+ (0, _extendedTest.expect)(download).toBeDefined();
236
+ const cancelled = await download.failure();
237
+ if (cancelled) {
238
+ (0, _extendedTest.expect)(cancelled).toContain("canceled");
239
+ }
240
+ });
206
241
  });