@intuned/runtime-dev 1.0.6-cli.8.0.5 → 1.0.6-cli.8.0.6

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.
@@ -4,23 +4,32 @@
4
4
  var _commander = require("commander");
5
5
  var _chalk = _interopRequireDefault(require("chalk"));
6
6
  var _dotenv = _interopRequireDefault(require("dotenv"));
7
- var _utils = require("../init/utils");
8
- var _utils2 = require("./utils");
7
+ var _utils = require("./utils");
8
+ var _utils2 = require("../../common/cli/utils");
9
9
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
10
10
  _dotenv.default.config({
11
11
  path: `.env`
12
12
  });
13
13
  _commander.program.description("Deploy an Intuned project to be public").argument("[project-name]", "Name of the project to deploy").option("-w, --workspace-id <id>", "Your Intuned workspace ID").option("-k, --api-key <key>", "Your Intuned API key").action(async (projectName, options) => {
14
+ const _projectName = projectName || (await (0, _utils2.getSettingIntunedJSON)("projectName"));
14
15
  try {
15
- if (!projectName) {
16
+ if (!_projectName) {
16
17
  throw new Error("Project name is required");
17
18
  }
18
- const projectNameValidation = (0, _utils2.validateProjectName)(projectName);
19
+ const projectNameValidation = (0, _utils.validateProjectName)(projectName);
19
20
  if (!projectNameValidation.isValid) {
20
21
  throw new Error(projectNameValidation.errorMessage);
21
22
  }
22
- const auth = (0, _utils.getAuthCredentials)(options);
23
- const deployStarted = await (0, _utils2.deployProject)(projectName, auth);
23
+ const {
24
+ isValid,
25
+ errorMessage
26
+ } = await (0, _utils.validateIntunedProject)();
27
+ if (!isValid) {
28
+ const userErrorMesage = `Project to be deployed is not a valid Intuned project: ${errorMessage}, modify and check again`;
29
+ throw new Error(userErrorMesage);
30
+ }
31
+ const auth = await (0, _utils2.getAuthCredentials)(options);
32
+ const deployStarted = await (0, _utils.deployProject)(projectName, auth);
24
33
  if (!deployStarted) {
25
34
  throw new Error("Project not deployed");
26
35
  }
@@ -1,7 +1,11 @@
1
- import { AuthCredentials, FileSystemTree } from "../init/types";
1
+ import { AuthCredentials, FileSystemTree } from "../../common/cli/types";
2
2
  export declare function convertProjectToCodeTree(projectPath: string): Promise<FileSystemTree>;
3
3
  export declare function deployProject(projectName: string, auth: AuthCredentials): Promise<boolean>;
4
4
  export declare const validateProjectName: (projectName: string) => {
5
5
  isValid: boolean;
6
6
  errorMessage?: string;
7
7
  };
8
+ export declare const validateIntunedProject: () => Promise<{
9
+ isValid: boolean;
10
+ errorMessage?: string;
11
+ }>;
@@ -5,13 +5,14 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.convertProjectToCodeTree = convertProjectToCodeTree;
7
7
  exports.deployProject = deployProject;
8
- exports.validateProjectName = void 0;
9
- var fs = _interopRequireWildcard(require("fs"));
8
+ exports.validateProjectName = exports.validateIntunedProject = void 0;
9
+ var fs = _interopRequireWildcard(require("fs-extra"));
10
10
  var path = _interopRequireWildcard(require("path"));
11
11
  var _chalk = _interopRequireDefault(require("chalk"));
12
12
  var _minimatch = require("minimatch");
13
13
  var _zod = require("zod");
14
14
  var _projectExclusions = _interopRequireDefault(require("../common/projectExclusions"));
15
+ var _constants = require("../../common/cli/constants");
15
16
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
16
17
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
17
18
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
@@ -70,14 +71,12 @@ function listFilesNotIgnored(projectPath, ignorePatterns) {
70
71
  async function convertProjectToCodeTree(projectPath) {
71
72
  let ignorePatterns = [..._projectExclusions.default];
72
73
  let currentDir = path.normalize(projectPath);
73
- let gitignoreFound = false;
74
74
  while (currentDir !== path.parse(currentDir).root) {
75
75
  const gitignorePath = path.join(currentDir, ".gitignore");
76
76
  if (fs.existsSync(gitignorePath)) {
77
77
  const gitignoreContent = fs.readFileSync(gitignorePath, "utf-8").split("\n");
78
78
  ignorePatterns = [...ignorePatterns, ...gitignoreContent];
79
79
  console.log(`Found .gitignore file: ${_chalk.default.cyan.bold(gitignorePath)}`);
80
- gitignoreFound = true;
81
80
  break;
82
81
  } else {
83
82
  currentDir = path.dirname(currentDir);
@@ -157,7 +156,7 @@ async function deployProject(projectName, auth) {
157
156
  const codeTree = await convertProjectToCodeTree(projectPath);
158
157
  const deployProjectPayload = {
159
158
  name: projectName,
160
- codeTree: codeTree,
159
+ codeTree,
161
160
  isCli: true,
162
161
  language: "typescript"
163
162
  };
@@ -170,12 +169,22 @@ async function deployProject(projectName, auth) {
170
169
  throw new Error(`Error deploying project`);
171
170
  }
172
171
  const data = await response.json();
173
- if (response.status === 202) {
174
- return true;
175
- }
176
172
  if (!data) {
177
173
  throw new Error("Project not deployed");
178
174
  }
175
+ const startTime = Date.now();
176
+ const checkInterval = setInterval(async () => {
177
+ const status = await checkIntunedProjectDeployStatus(workspaceId, projectName, apiKey);
178
+ if (status === "completed" || status === "failed" || status === "not_found") {
179
+ clearInterval(checkInterval);
180
+ return status === "completed";
181
+ }
182
+ const elapsedTime = Date.now() - startTime;
183
+ if (elapsedTime > _constants.PROJECT_DEPLOY_TIMEOUT) {
184
+ clearInterval(checkInterval);
185
+ throw new Error(`Deployment timed out after ${Math.floor(_constants.PROJECT_DEPLOY_TIMEOUT / 1000 / 60)} minutes`);
186
+ }
187
+ }, 5000);
179
188
  return false;
180
189
  }
181
190
  const validateProjectName = projectName => {
@@ -193,4 +202,104 @@ const validateProjectName = projectName => {
193
202
  isValid: true
194
203
  };
195
204
  };
196
- exports.validateProjectName = validateProjectName;
205
+ exports.validateProjectName = validateProjectName;
206
+ const validateIntunedProject = async () => {
207
+ const currentDirectoryToDeploy = process.cwd();
208
+ const validationSteps = [{
209
+ name: "package.json",
210
+ check: async () => {
211
+ const packageJsonPath = path.join(currentDirectoryToDeploy, "package.json");
212
+ try {
213
+ var _packageJson$dependen;
214
+ await fs.exists(packageJsonPath);
215
+ const packageJson = JSON.parse(await fs.readFile(packageJsonPath, {
216
+ encoding: "utf-8"
217
+ }));
218
+ const userCodePlaywrightVersion = (_packageJson$dependen = packageJson.dependencies) === null || _packageJson$dependen === void 0 ? void 0 : _packageJson$dependen.playwright;
219
+ if (userCodePlaywrightVersion !== _constants.CURRENT_PLAYWRIGHT_VERSION) {
220
+ return {
221
+ isValid: false,
222
+ errorMessage: `Playwright version mismatch: expected ${_constants.CURRENT_PLAYWRIGHT_VERSION}, found ${userCodePlaywrightVersion || "none"}`
223
+ };
224
+ }
225
+ return {
226
+ isValid: true
227
+ };
228
+ } catch (error) {
229
+ return {
230
+ isValid: false,
231
+ errorMessage: "Package.json file not found or cannot be read"
232
+ };
233
+ }
234
+ }
235
+ }, {
236
+ name: "intuned.json",
237
+ check: async () => {
238
+ const intunedJsonPath = path.join(currentDirectoryToDeploy, "intuned.json");
239
+ try {
240
+ await fs.exists(intunedJsonPath);
241
+ return {
242
+ isValid: true
243
+ };
244
+ } catch (error) {
245
+ return {
246
+ isValid: false,
247
+ errorMessage: "Intuned.json file not found"
248
+ };
249
+ }
250
+ }
251
+ }, {
252
+ name: "api folder",
253
+ check: async () => {
254
+ const apiFolderPath = path.join(currentDirectoryToDeploy, "api");
255
+ try {
256
+ await fs.access(apiFolderPath);
257
+ return {
258
+ isValid: true
259
+ };
260
+ } catch (error) {
261
+ return {
262
+ isValid: false,
263
+ errorMessage: "API folder not found"
264
+ };
265
+ }
266
+ }
267
+ }];
268
+ for (const step of validationSteps) {
269
+ const result = await step.check();
270
+ if (!result.isValid) {
271
+ return result;
272
+ }
273
+ }
274
+ return {
275
+ isValid: true
276
+ };
277
+ };
278
+ exports.validateIntunedProject = validateIntunedProject;
279
+ const checkIntunedProjectDeployStatus = async (workspaceId, projectName, apiKey) => {
280
+ try {
281
+ const baseUrl = "https://rauf-2.intuned-team-local.com";
282
+ const url = `${baseUrl}/api/v1/workspace/${workspaceId}/projects/create/${projectName}/result`;
283
+ const headers = {
284
+ "x-api-key": apiKey,
285
+ "Content-Type": "application/json"
286
+ };
287
+ const response = await fetch(url, {
288
+ headers,
289
+ method: "GET"
290
+ });
291
+ if (response.status === 404) {
292
+ return "not_found";
293
+ }
294
+ if (!response.ok) {
295
+ throw new Error("Error fetching deployment status");
296
+ }
297
+ const data = await response.json();
298
+ if (data.status) {
299
+ return data.status.name;
300
+ }
301
+ return "failed";
302
+ } catch (e) {
303
+ throw new Error(`Error during deployment`);
304
+ }
305
+ };
@@ -1,4 +1,4 @@
1
- import { FileSystemTree } from "./types";
1
+ import { FileSystemTree } from "../../common/cli/types";
2
2
  export declare function mountFiles(cwd: string, tree: FileSystemTree): Promise<void>;
3
3
  export declare function checkEmptyDirectory(): Promise<boolean>;
4
4
  export declare function getAuthCredentials(options: any): {
@@ -9,7 +9,7 @@ exports.mountFiles = mountFiles;
9
9
  exports.scaffoldProject = scaffoldProject;
10
10
  exports.selectTemplate = selectTemplate;
11
11
  var fs = _interopRequireWildcard(require("fs-extra"));
12
- var _types = require("./types");
12
+ var _types = require("../../common/cli/types");
13
13
  var _chalk = _interopRequireDefault(require("chalk"));
14
14
  var _inquirer = _interopRequireDefault(require("inquirer"));
15
15
  var _path = _interopRequireDefault(require("path"));
@@ -0,0 +1,3 @@
1
+ export declare const CURRENT_PLAYWRIGHT_VERSION = "1.44.1";
2
+ export declare const ProjectDeploymentStatus: string[];
3
+ export declare const PROJECT_DEPLOY_TIMEOUT = 600000;
@@ -1 +1,9 @@
1
- "use strict";
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.ProjectDeploymentStatus = exports.PROJECT_DEPLOY_TIMEOUT = exports.CURRENT_PLAYWRIGHT_VERSION = void 0;
7
+ const CURRENT_PLAYWRIGHT_VERSION = exports.CURRENT_PLAYWRIGHT_VERSION = "1.44.1";
8
+ const ProjectDeploymentStatus = exports.ProjectDeploymentStatus = ["completed", "failed", "pending", "not_found"];
9
+ const PROJECT_DEPLOY_TIMEOUT = exports.PROJECT_DEPLOY_TIMEOUT = 600000;
@@ -1,5 +1,5 @@
1
- export declare function getAuthCredentials(options: any): {
1
+ export declare function getAuthCredentials(options: any): Promise<{
2
2
  workspaceId: any;
3
3
  apiKey: any;
4
- };
5
- export declare function getSettingIntunedJSON(key: string): null;
4
+ }>;
5
+ export declare function getSettingIntunedJSON(key: string): Promise<any>;
@@ -5,28 +5,15 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.getAuthCredentials = getAuthCredentials;
7
7
  exports.getSettingIntunedJSON = getSettingIntunedJSON;
8
- function getAuthCredentials(options) {
9
- const workspaceId = options.workspaceId || process.env.INTUNED_WORKSPACE_ID;
8
+ var _path = _interopRequireDefault(require("path"));
9
+ var fs = _interopRequireWildcard(require("fs-extra"));
10
+ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
11
+ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
12
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
13
+ async function getAuthCredentials(options) {
14
+ const workspaceId = options.workspaceId || (await getSettingIntunedJSON("workspaceId"));
10
15
  const apiKey = options.apiKey || process.env.INTUNED_API_KEY;
11
- const missingAuth = [];
12
- if (!workspaceId) {
13
- missingAuth.push({
14
- type: "input",
15
- name: "workspaceId",
16
- message: "Enter your Intuned workspace ID:",
17
- validate: input => input.trim() !== "" ? true : "Workspace ID is required"
18
- });
19
- }
20
- if (!apiKey) {
21
- missingAuth.push({
22
- type: "password",
23
- name: "apiKey",
24
- message: "Enter your Intuned API key:",
25
- mask: "*",
26
- validate: input => input.trim() !== "" ? true : "API key is required"
27
- });
28
- }
29
- if (missingAuth.length) {
16
+ if (!workspaceId || !workspaceId) {
30
17
  throw new Error("Authentication details are required. Please provide them via command line options or environment variables.");
31
18
  }
32
19
  return {
@@ -34,6 +21,11 @@ function getAuthCredentials(options) {
34
21
  apiKey
35
22
  };
36
23
  }
37
- function getSettingIntunedJSON(key) {
24
+ async function getSettingIntunedJSON(key) {
25
+ const intunedJsonPath = _path.default.join(process.cwd(), "intuned.json");
26
+ const intunedJson = await fs.readJSON(intunedJsonPath);
27
+ if (intunedJson && intunedJson[key]) {
28
+ return intunedJson[key];
29
+ }
38
30
  return null;
39
31
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intuned/runtime-dev",
3
- "version": "1.0.6-cli.8.0.5",
3
+ "version": "1.0.6-cli.8.0.6",
4
4
  "description": "Intuned runtime",
5
5
  "exports": {
6
6
  ".": "./dist/index.js",
@@ -1,52 +0,0 @@
1
- export declare const templateIds: string[];
2
- export type TemplateId = (typeof templateIds)[number];
3
- /**
4
- * A simple, tree-like structure to describe the contents of a folder to be mounted.
5
- *
6
- * @example
7
- * ```
8
- * const tree = {
9
- * myproject: {
10
- * directory: {
11
- * 'foo.js': {
12
- * file: {
13
- * contents: 'const x = 1;',
14
- * },
15
- * },
16
- * .envrc: {
17
- * file: {
18
- * contents: 'ENVIRONMENT=staging'
19
- * }
20
- * },
21
- * },
22
- * },
23
- * emptyFolder: {
24
- * directory: {}
25
- * },
26
- * };
27
- * ```
28
- */
29
- export interface FileSystemTree {
30
- [name: string]: DirectoryNode | FileNode;
31
- }
32
- /**
33
- * Represents a directory, see {@link FileSystemTree}.
34
- */
35
- export interface DirectoryNode {
36
- directory: FileSystemTree;
37
- }
38
- /**
39
- * Represents a file, see {@link FileSystemTree}.
40
- */
41
- export interface FileNode {
42
- file: {
43
- /**
44
- * The contents of the file, either as a UTF-8 string or as raw binary.
45
- */
46
- contents: string | Uint8Array;
47
- };
48
- }
49
- export interface AuthCredentials {
50
- workspaceId: string;
51
- apiKey: string;
52
- }
@@ -1,7 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.templateIds = void 0;
7
- const templateIds = exports.templateIds = ["default", "empty", "linkedin-recorder", "api-auth-sessions", "nested-scheduling", "ai-extractors", "npm-auth-sessions", "python-empty"];
package/r/Intuned.json DELETED
@@ -1,15 +0,0 @@
1
- {
2
- "apiAccess": {
3
- "enabled": true
4
- },
5
- "authSessions": {
6
- "enabled": false
7
- },
8
- "scale": {
9
- "machineCount": 1,
10
- "softLimit": 8,
11
- "hardLimit": 10,
12
- "memory": 2048,
13
- "cpus": 6
14
- }
15
- }
@@ -1,45 +0,0 @@
1
- import { BrowserContext, Page } from "playwright-core";
2
- import { extendPlaywrightPage } from "@intuned/sdk/playwright";
3
-
4
- interface Params {
5
- bookFullUrl: string;
6
- }
7
-
8
- export default async function handler(
9
- params: Params,
10
- _playwrightPage: Page,
11
- context: BrowserContext
12
- ) {
13
- const page = extendPlaywrightPage(_playwrightPage);
14
-
15
- // go to the url that is passed in the params
16
- await page.goto(params.bookFullUrl);
17
-
18
- // optimized extractor!
19
- // for more info checkout
20
- // https://docs.intunedhq.com/docs/data-extraction/web-data-extraction
21
- const result = await page.extractObjectOptimized({
22
- label: "book-deatils",
23
- entityName: "book_info",
24
- entitySchema: {
25
- type: "object",
26
- properties: {
27
- name: {
28
- type: "string",
29
- description: "book name",
30
- },
31
- upc: {
32
- type: "string",
33
- description: "upc of the book",
34
- },
35
- numberOfReviews: {
36
- type: "string",
37
- description: "Number of reviews on the book",
38
- },
39
- },
40
- required: ["name", "upc"],
41
- },
42
- });
43
-
44
- return result;
45
- }
@@ -1,35 +0,0 @@
1
- import { BrowserContext, Page } from "playwright-core";
2
- import { extendPlaywrightPage } from "@intuned/sdk/playwright";
3
- import { extendPayload } from "@intuned/sdk/runtime";
4
- import { extractBooks } from "../utils/books";
5
-
6
- interface Params {
7
- // Add your params here
8
- }
9
-
10
- export default async function handler(
11
- params: Params,
12
- _playwrightPage: Page,
13
- context: BrowserContext
14
- ) {
15
- // extends playwright page with Intuned helpers.
16
- const page = extendPlaywrightPage(_playwrightPage);
17
-
18
- await page.goto("https://books.toscrape.com/");
19
-
20
- // optimized extractor!
21
- // for more info checkout
22
- // https://docs.intunedhq.com/docs/data-extraction/web-data-extraction
23
-
24
- const result = await extractBooks(page);
25
- // for each book on the main page, schedule api to get details
26
- result.forEach((book) => {
27
- // Extend job payload so it runs API `book-details` with provided params
28
- extendPayload({
29
- api: "book-details",
30
- parameters: {
31
- bookFullUrl: `${page.url()}${book.bookUrl}`,
32
- },
33
- });
34
- });
35
- }
package/r/api/c.ts DELETED
File without changes
package/r/package.json DELETED
@@ -1,33 +0,0 @@
1
- {
2
- "name": "Nested-Scheduling",
3
- "version": "1.0.0",
4
- "description": "project uses nested scheduling feature",
5
- "tags": [
6
- "Scrapper"
7
- ],
8
- "main": "index.js",
9
- "scripts": {
10
- "dev:local": "intuned-api-run sample playwright -j '{}'",
11
- "dev": "intuned-api-run",
12
- "debug": "node --inspect-brk ./node_modules/.bin/intuned-api-run",
13
- "build": "intuned-build",
14
- "types-check": "intuned-ts-check",
15
- "pre-publish": "intuned-ts-check && intuned-build",
16
- "start": "node ./output/bundle_v2.js",
17
- "browser-save-state": "intuned-browser-save-state",
18
- "auth-session-check": "intuned-auth-session-check",
19
- "auth-session-create": "intuned-auth-session-create",
20
- "auth-session-refresh": "intuned-auth-session-refresh",
21
- "auth-session-load": "intuned-auth-session-load"
22
- },
23
- "author": "",
24
- "license": "ISC",
25
- "dependencies": {
26
- "@intuned/runtime": "1.0.5",
27
- "@intuned/sdk": "2.0.2",
28
- "@types/intuned__sdk": "npm:@intuned/sdk-types@2.0.2",
29
- "@types/node": "^20.10.3",
30
- "playwright": "1.44.1",
31
- "playwright-core": "1.44.1"
32
- }
33
- }
package/r/tsconfig.json DELETED
@@ -1,47 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "declaration": false,
4
- "experimentalDecorators": true,
5
- "sourceMap": true,
6
- "baseUrl": ".",
7
- "lib": ["dom", "dom.iterable", "esnext", "es2021"],
8
- "module": "commonjs",
9
- "target": "es2021",
10
- "allowJs": true,
11
- "skipLibCheck": true,
12
- "strict": true,
13
- "forceConsistentCasingInFileNames": true,
14
- "noEmit": false,
15
- "incremental": true,
16
- "esModuleInterop": true,
17
- "resolveJsonModule": true,
18
- "isolatedModules": true,
19
- "jsx": "preserve",
20
- "noImplicitAny": false,
21
- "composite": false,
22
- "inlineSources": false,
23
- "moduleResolution": "node",
24
- "noUnusedLocals": false,
25
- "noUnusedParameters": false,
26
- "preserveWatchOutput": true,
27
- "rootDir": "./",
28
- "outDir": "./dist",
29
- "paths": {
30
- "primitives/*": ["components/primitives/*"]
31
- }
32
- },
33
- "include": [
34
- "next-env.d.ts",
35
- "**/*.ts",
36
- "**/*.tsx",
37
- "node_modules/monaco-editor/**/*.d.ts",
38
- "node_modules/@monaco-editor/**/*.d.ts",
39
- "node_modules/@kusto/monaco-kusto/**/*.d.ts"
40
- ],
41
- "exclude": [
42
- "public",
43
- "dist",
44
- "packagerWorkerAssets/packagerTemplates",
45
- "httpRequests"
46
- ]
47
- }
package/r/utils/books.ts DELETED
@@ -1,32 +0,0 @@
1
- import { Page } from "playwright-core";
2
-
3
- interface BookItem {
4
- name: string;
5
- bookUrl: string;
6
- }
7
-
8
- export async function extractBooks(page: Page): Promise<BookItem[]> {
9
- await page.waitForSelector("article.product_pod");
10
-
11
- const books: BookItem[] = [];
12
-
13
- const bookElements = await page.getByRole("article").all();
14
-
15
- for (const bookElement of bookElements) {
16
- const linkElement = bookElement.getByRole("link").first();
17
-
18
- const name =
19
- (await linkElement.getAttribute("title")) ||
20
- (await linkElement.textContent()) ||
21
- "Unknown Title";
22
-
23
- const bookUrl = (await linkElement.getAttribute("href")) || "";
24
-
25
- books.push({
26
- name: name.trim(),
27
- bookUrl: bookUrl.trim(),
28
- });
29
- }
30
-
31
- return books;
32
- }