askui 0.30.0 → 0.32.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.
Files changed (79) hide show
  1. package/dist/cjs/core/cache/cache-config.d.ts +5 -0
  2. package/dist/cjs/core/cache/cache-config.js +2 -0
  3. package/dist/cjs/core/cache/cache-entry-reference.d.ts +7 -0
  4. package/dist/cjs/core/cache/cache-entry-reference.js +20 -0
  5. package/dist/cjs/core/cache/cache-entry.d.ts +11 -0
  6. package/dist/cjs/core/cache/cache-entry.js +47 -0
  7. package/dist/cjs/core/cache/cache-interface.d.ts +11 -0
  8. package/dist/cjs/core/cache/cache-interface.js +2 -0
  9. package/dist/cjs/core/cache/cache-manager.d.ts +24 -0
  10. package/dist/cjs/core/cache/cache-manager.js +145 -0
  11. package/dist/cjs/core/cache/cahe-file.d.ts +19 -0
  12. package/dist/cjs/core/cache/cahe-file.js +128 -0
  13. package/dist/cjs/core/cache/dummy-cache-manager.d.ts +12 -0
  14. package/dist/cjs/core/cache/dummy-cache-manager.js +27 -0
  15. package/dist/cjs/core/cache/image-reference.d.ts +10 -0
  16. package/dist/cjs/core/cache/image-reference.js +40 -0
  17. package/dist/cjs/core/cache/index.d.ts +7 -0
  18. package/dist/cjs/core/cache/index.js +13 -0
  19. package/dist/cjs/core/model/annotation-result/boundary-box.js +1 -1
  20. package/dist/cjs/core/model/custom-element.d.ts +1 -0
  21. package/dist/cjs/core/model/custom-element.js +3 -0
  22. package/dist/cjs/core/ui-control-commands/action.d.ts +1 -0
  23. package/dist/cjs/core/ui-control-commands/action.js +10 -2
  24. package/dist/cjs/core/ui-control-commands/control-command.d.ts +1 -0
  25. package/dist/cjs/core/ui-control-commands/control-command.js +7 -0
  26. package/dist/cjs/execution/dsl.d.ts +12 -5
  27. package/dist/cjs/execution/dsl.js +30 -15
  28. package/dist/cjs/execution/execution-runtime.d.ts +1 -1
  29. package/dist/cjs/execution/execution-runtime.js +21 -17
  30. package/dist/cjs/execution/inference-client.d.ts +5 -3
  31. package/dist/cjs/execution/inference-client.js +22 -3
  32. package/dist/cjs/execution/ui-control-client-dependency-builder.js +6 -1
  33. package/dist/cjs/execution/ui-control-client.d.ts +2 -1
  34. package/dist/cjs/execution/ui-control-client.js +28 -11
  35. package/dist/cjs/execution/ui-controller-client-interface.d.ts +2 -0
  36. package/dist/cjs/utils/base_64_image/base-64-image.d.ts +2 -0
  37. package/dist/cjs/utils/base_64_image/base-64-image.js +27 -4
  38. package/dist/cjs/utils/http/http-client-got.d.ts +15 -0
  39. package/dist/cjs/utils/http/http-client-got.js +59 -11
  40. package/dist/esm/core/cache/cache-config.d.ts +5 -0
  41. package/dist/esm/core/cache/cache-config.js +1 -0
  42. package/dist/esm/core/cache/cache-entry-reference.d.ts +7 -0
  43. package/dist/esm/core/cache/cache-entry-reference.js +16 -0
  44. package/dist/esm/core/cache/cache-entry.d.ts +11 -0
  45. package/dist/esm/core/cache/cache-entry.js +43 -0
  46. package/dist/esm/core/cache/cache-interface.d.ts +11 -0
  47. package/dist/esm/core/cache/cache-interface.js +1 -0
  48. package/dist/esm/core/cache/cache-manager.d.ts +24 -0
  49. package/dist/esm/core/cache/cache-manager.js +141 -0
  50. package/dist/esm/core/cache/cahe-file.d.ts +19 -0
  51. package/dist/esm/core/cache/cahe-file.js +121 -0
  52. package/dist/esm/core/cache/dummy-cache-manager.d.ts +12 -0
  53. package/dist/esm/core/cache/dummy-cache-manager.js +23 -0
  54. package/dist/esm/core/cache/image-reference.d.ts +10 -0
  55. package/dist/esm/core/cache/image-reference.js +36 -0
  56. package/dist/esm/core/cache/index.d.ts +7 -0
  57. package/dist/esm/core/cache/index.js +5 -0
  58. package/dist/esm/core/model/annotation-result/boundary-box.js +1 -1
  59. package/dist/esm/core/model/custom-element.d.ts +1 -0
  60. package/dist/esm/core/model/custom-element.js +3 -0
  61. package/dist/esm/core/ui-control-commands/action.d.ts +1 -0
  62. package/dist/esm/core/ui-control-commands/action.js +10 -2
  63. package/dist/esm/core/ui-control-commands/control-command.d.ts +1 -0
  64. package/dist/esm/core/ui-control-commands/control-command.js +7 -0
  65. package/dist/esm/execution/dsl.d.ts +12 -5
  66. package/dist/esm/execution/dsl.js +30 -15
  67. package/dist/esm/execution/execution-runtime.d.ts +1 -1
  68. package/dist/esm/execution/execution-runtime.js +21 -17
  69. package/dist/esm/execution/inference-client.d.ts +5 -3
  70. package/dist/esm/execution/inference-client.js +22 -3
  71. package/dist/esm/execution/ui-control-client-dependency-builder.js +6 -1
  72. package/dist/esm/execution/ui-control-client.d.ts +2 -1
  73. package/dist/esm/execution/ui-control-client.js +28 -11
  74. package/dist/esm/execution/ui-controller-client-interface.d.ts +2 -0
  75. package/dist/esm/utils/base_64_image/base-64-image.d.ts +2 -0
  76. package/dist/esm/utils/base_64_image/base-64-image.js +27 -4
  77. package/dist/esm/utils/http/http-client-got.d.ts +15 -0
  78. package/dist/esm/utils/http/http-client-got.js +36 -8
  79. package/package.json +4 -2
@@ -1,4 +1,27 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
2
25
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
26
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
27
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -8,16 +31,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
31
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
32
  });
10
33
  };
11
- var __importDefault = (this && this.__importDefault) || function (mod) {
12
- return (mod && mod.__esModule) ? mod : { "default": mod };
13
- };
14
34
  Object.defineProperty(exports, "__esModule", { value: true });
15
35
  exports.HttpClientGot = void 0;
16
- const got_1 = __importDefault(require("got"));
36
+ const got_1 = __importStar(require("got"));
17
37
  const tough_cookie_1 = require("tough-cookie");
18
38
  const lib_1 = require("../../lib");
19
39
  const credentials_1 = require("./credentials");
20
40
  const custom_errors_1 = require("./custom-errors");
41
+ const unkown_http_client_error_1 = require("./custom-errors/unkown-http-client-error");
21
42
  function buildRetryLog(requestUrl, errorCode, statusCode, errorMessage, delayInMs, delayReason, attemptCount) {
22
43
  const failureReasons = [];
23
44
  if (statusCode !== undefined && statusCode >= 400) {
@@ -46,6 +67,16 @@ class HttpClientGot {
46
67
  const gotExtendOptions = this.buildGotExtendOptions(proxyAgents);
47
68
  this.askuiGot = got_1.default.extend(gotExtendOptions);
48
69
  }
70
+ /**
71
+ * Configures got with retry behavior for transient server errors.
72
+ *
73
+ * Got retries requests that fail with retryable status codes
74
+ * (500, 502, 503, 504, etc.) up to `limit` times using exponential backoff.
75
+ *
76
+ * POST requests are only retried if their URL is registered in `urlsToRetry`
77
+ * (see `shouldRetryOnError`), to avoid retrying non-idempotent calls
78
+ * to unknown endpoints.
79
+ */
49
80
  buildGotExtendOptions(proxyAgents) {
50
81
  const gotExtendOptions = {
51
82
  retry: {
@@ -93,6 +124,11 @@ class HttpClientGot {
93
124
  }
94
125
  return gotExtendOptions;
95
126
  }
127
+ /**
128
+ * Only retry POST requests if the URL is explicitly registered in `urlsToRetry`
129
+ * (populated by InferenceClient with inference endpoint URLs).
130
+ * Non-POST requests (GET, PUT, etc.) are always retried.
131
+ */
96
132
  shouldRetryOnError(error) {
97
133
  var _a;
98
134
  return (((_a = error.request) === null || _a === void 0 ? void 0 : _a.options.method) !== 'POST'
@@ -118,19 +154,31 @@ class HttpClientGot {
118
154
  }
119
155
  post(url, data) {
120
156
  return __awaiter(this, void 0, void 0, function* () {
157
+ // Note: We intentionally do NOT set `throwHttpErrors: false` here.
158
+ // Got must throw on non-2xx responses so that its built-in retry mechanism
159
+ // (configured in buildGotExtendOptions) can kick in for transient server errors
160
+ // (500, 502, 503, 504). After all retries are exhausted, got throws an HTTPError
161
+ // which we catch below and convert into our custom error hierarchy.
121
162
  const options = this.injectHeadersAndCookies(url, {
122
163
  json: data,
123
164
  responseType: 'json',
124
- throwHttpErrors: false,
125
165
  });
126
- const { body, statusCode, headers } = yield this.askuiGot.post(url, options);
127
- if (headers['deprecation'] !== undefined) {
128
- lib_1.logger.warn(headers['deprecation']);
166
+ try {
167
+ const { body, headers } = yield this.askuiGot.post(url, options);
168
+ if (headers['deprecation'] !== undefined) {
169
+ lib_1.logger.warn(headers['deprecation']);
170
+ }
171
+ return { body, headers };
129
172
  }
130
- if (statusCode !== 200) {
131
- throw (0, custom_errors_1.httpClientErrorHandler)(statusCode, JSON.stringify(body));
173
+ catch (error) {
174
+ // After got exhausts all retries, it throws HTTPError.
175
+ // Convert it to our custom error types (ServerHttpClientError,
176
+ // AuthenticationHttpClientError, etc.) for consistent error handling.
177
+ if (error instanceof got_1.HTTPError) {
178
+ throw (0, custom_errors_1.httpClientErrorHandler)(error.response.statusCode, error.response.body);
179
+ }
180
+ throw new unkown_http_client_error_1.UnkownHttpClientError(error instanceof Error ? error.message : String(error));
132
181
  }
133
- return { body, headers };
134
182
  });
135
183
  }
136
184
  get(url_1) {
@@ -0,0 +1,5 @@
1
+ export type ValidationType = 'PixelPerfect';
2
+ export interface CacheConfig {
3
+ cacheFilePath?: string;
4
+ validationType?: ValidationType;
5
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,7 @@
1
+ import { ImageReference } from './image-reference';
2
+ export declare class CacheEntryReference {
3
+ image?: ImageReference | undefined;
4
+ constructor(image?: ImageReference | undefined);
5
+ static fromJson(json: CacheEntryReference): CacheEntryReference;
6
+ toJson(): Record<string, unknown>;
7
+ }
@@ -0,0 +1,16 @@
1
+ import { ImageReference } from './image-reference';
2
+ export class CacheEntryReference {
3
+ constructor(image) {
4
+ this.image = image;
5
+ }
6
+ static fromJson(json) {
7
+ return new CacheEntryReference(json.image ? ImageReference.fromJson(json.image) : undefined);
8
+ }
9
+ toJson() {
10
+ const jsonObject = {};
11
+ if (this.image !== undefined) {
12
+ jsonObject['image'] = this.image.toJson();
13
+ }
14
+ return jsonObject;
15
+ }
16
+ }
@@ -0,0 +1,11 @@
1
+ import { ControlCommand } from '../ui-control-commands';
2
+ import { CacheEntryReference } from './cache-entry-reference';
3
+ export declare class CacheEntry {
4
+ alwaysValid: boolean;
5
+ controlCommand: ControlCommand;
6
+ reference?: CacheEntryReference | undefined;
7
+ createdAt: Date;
8
+ constructor(alwaysValid: boolean, controlCommand: ControlCommand, reference?: CacheEntryReference | undefined, createdAt?: Date);
9
+ static fromJson(json: CacheEntry): CacheEntry | undefined;
10
+ toJson(): Record<string, unknown>;
11
+ }
@@ -0,0 +1,43 @@
1
+ import { ControlCommand } from '../ui-control-commands';
2
+ import { CacheEntryReference } from './cache-entry-reference';
3
+ import { logger } from '../../lib';
4
+ export class CacheEntry {
5
+ constructor(alwaysValid, controlCommand, reference, createdAt = new Date()) {
6
+ this.alwaysValid = alwaysValid;
7
+ this.controlCommand = controlCommand;
8
+ this.reference = reference;
9
+ this.createdAt = createdAt;
10
+ }
11
+ static fromJson(json) {
12
+ try {
13
+ if (json === undefined) {
14
+ throw new Error('Cache entry is undefined');
15
+ }
16
+ if (json.controlCommand === undefined) {
17
+ throw new Error('the key "controlCommand" is required');
18
+ }
19
+ if (json.createdAt === undefined) {
20
+ throw new Error('the key "createdAt" is required');
21
+ }
22
+ if (json.alwaysValid === undefined) {
23
+ throw new Error('the key "alwaysValid" is required');
24
+ }
25
+ return new CacheEntry(json.alwaysValid, ControlCommand.fromJson(json.controlCommand), json.reference ? CacheEntryReference.fromJson(json.reference) : undefined, new Date(json.createdAt));
26
+ }
27
+ catch (error) {
28
+ logger.error(`Error deserializing cache entry: ${error}`);
29
+ return undefined;
30
+ }
31
+ }
32
+ toJson() {
33
+ const jsonObject = {
34
+ alwaysValid: this.alwaysValid,
35
+ controlCommand: this.controlCommand.toJson(),
36
+ createdAt: this.createdAt,
37
+ };
38
+ if (this.reference !== undefined) {
39
+ jsonObject['reference'] = this.reference.toJson();
40
+ }
41
+ return jsonObject;
42
+ }
43
+ }
@@ -0,0 +1,11 @@
1
+ import { CustomElement } from '../model/custom-element';
2
+ import { ControlCommand } from '../ui-control-commands';
3
+ import { CacheEntry } from './cache-entry';
4
+ export interface CacheInterface {
5
+ loadFromFile(): void;
6
+ saveToFile(): void;
7
+ add(key: string, value: CacheEntry): void;
8
+ addCacheEntryFromControlCommand(instruction: string, controlCommand: ControlCommand, customElements: CustomElement[], image?: string): Promise<void>;
9
+ isImageRequired(instruction: string, customElements: CustomElement[]): boolean | undefined;
10
+ getCachedControlCommand(instruction: string, customElements: CustomElement[], image?: string): Promise<ControlCommand | undefined>;
11
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,24 @@
1
+ import { ControlCommand } from '../ui-control-commands';
2
+ import { CacheInterface } from './cache-interface';
3
+ import { CacheEntry } from './cache-entry';
4
+ import { CacheConfig, ValidationType } from './cache-config';
5
+ import { CustomElement } from '../model/custom-element';
6
+ export declare class CacheManager implements CacheInterface {
7
+ private relevantData;
8
+ validationType: ValidationType;
9
+ private cacheFile;
10
+ private readonly referenceWidth;
11
+ private readonly referenceHeight;
12
+ private readonly defaultValidationType;
13
+ constructor(config: CacheConfig);
14
+ loadFromFile(): void;
15
+ saveToFile(): void;
16
+ add(key: string, value: CacheEntry): void;
17
+ addCacheEntryFromControlCommand(instruction: string, controlCommand: ControlCommand, customElements: CustomElement[], image?: string): Promise<void>;
18
+ isImageRequired(instruction: string, customElements: CustomElement[]): boolean | undefined;
19
+ getCachedControlCommand(instruction: string, customElements: CustomElement[], image?: string): Promise<ControlCommand | undefined>;
20
+ private encodeKey;
21
+ private getCacheEntriesByKey;
22
+ private getValidCacheEntry;
23
+ private validateAccordingToPixelPerfect;
24
+ }
@@ -0,0 +1,141 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { ControlCommandCode } from '../ui-control-commands';
11
+ import { InputEvent } from '../ui-control-commands/input-event';
12
+ import { Base64Image } from '../../utils/base_64_image/base-64-image';
13
+ import { CacheEntry } from './cache-entry';
14
+ import { ImageReference } from './image-reference';
15
+ import { CacheEntryReference } from './cache-entry-reference';
16
+ import { CacheFile } from './cahe-file';
17
+ export class CacheManager {
18
+ constructor(config) {
19
+ var _a;
20
+ this.relevantData = {};
21
+ this.referenceWidth = 32;
22
+ this.referenceHeight = 16;
23
+ this.defaultValidationType = 'PixelPerfect';
24
+ this.validationType = (_a = config.validationType) !== null && _a !== void 0 ? _a : this.defaultValidationType;
25
+ this.cacheFile = new CacheFile(config.cacheFilePath);
26
+ }
27
+ loadFromFile() {
28
+ this.cacheFile.loadFromFile();
29
+ this.relevantData = this.cacheFile.getDataForValidationType(this.validationType);
30
+ }
31
+ saveToFile() {
32
+ this.cacheFile.saveToFile(this.validationType, this.relevantData);
33
+ }
34
+ add(key, value) {
35
+ if (this.relevantData[key] === undefined) {
36
+ this.relevantData[key] = [];
37
+ }
38
+ this.relevantData[key].push(value);
39
+ }
40
+ addCacheEntryFromControlCommand(instruction, controlCommand, customElements, image) {
41
+ return __awaiter(this, void 0, void 0, function* () {
42
+ let imageReference;
43
+ if (image !== undefined) {
44
+ const moveAction = controlCommand.actions.find((action) => action.inputEvent === InputEvent.MOUSE_MOVE);
45
+ const x = moveAction === null || moveAction === void 0 ? void 0 : moveAction.position.x;
46
+ const y = moveAction === null || moveAction === void 0 ? void 0 : moveAction.position.y;
47
+ if (x !== undefined && y !== undefined) {
48
+ const xTopLeft = Math.max(0, (x - this.referenceWidth / 2));
49
+ const yTopLeft = Math.max(0, (y - this.referenceHeight / 2));
50
+ const base64Image = yield Base64Image.fromString(image);
51
+ const croppedImage = yield base64Image.cropRegion(xTopLeft, yTopLeft, this.referenceWidth, this.referenceHeight);
52
+ imageReference = new ImageReference(croppedImage.toString(true), this.referenceWidth, this.referenceHeight, xTopLeft, yTopLeft);
53
+ }
54
+ }
55
+ const cacheEntry = new CacheEntry(image === undefined, controlCommand, new CacheEntryReference(imageReference));
56
+ const cacheKey = this.encodeKey(instruction, customElements);
57
+ this.add(cacheKey, cacheEntry);
58
+ });
59
+ }
60
+ isImageRequired(instruction, customElements) {
61
+ const cacheKey = this.encodeKey(instruction, customElements);
62
+ const cacheEntries = this.getCacheEntriesByKey(cacheKey);
63
+ if (cacheEntries.length === 0) {
64
+ return undefined;
65
+ }
66
+ return cacheEntries.find((entry) => entry.alwaysValid === false) !== undefined;
67
+ }
68
+ getCachedControlCommand(instruction, customElements, image) {
69
+ return __awaiter(this, void 0, void 0, function* () {
70
+ const cacheKey = this.encodeKey(instruction, customElements);
71
+ const cacheEntry = yield this.getValidCacheEntry(cacheKey, image);
72
+ if (cacheEntry === undefined || cacheEntry.controlCommand.code !== ControlCommandCode.OK) {
73
+ return undefined;
74
+ }
75
+ return cacheEntry.controlCommand;
76
+ });
77
+ }
78
+ // eslint-disable-next-line class-methods-use-this
79
+ encodeKey(instruction, customElements) {
80
+ let key = instruction;
81
+ if (customElements.length > 0) {
82
+ key += ` with CustomElements:${customElements.map((element, index) => `${index}:${element.asString()}`).join(',')}`;
83
+ }
84
+ return key;
85
+ }
86
+ getCacheEntriesByKey(key) {
87
+ if (this.relevantData[key] === undefined) {
88
+ return [];
89
+ }
90
+ const entries = this.relevantData[key];
91
+ if (entries === undefined) {
92
+ return [];
93
+ }
94
+ return entries;
95
+ }
96
+ getValidCacheEntry(key, screenshot) {
97
+ return __awaiter(this, void 0, void 0, function* () {
98
+ const cacheEntries = this.getCacheEntriesByKey(key);
99
+ if (cacheEntries.length === 0) {
100
+ return undefined;
101
+ }
102
+ if (this.validationType === 'PixelPerfect') {
103
+ /* eslint-disable no-restricted-syntax, no-await-in-loop */
104
+ for (const cacheEntry of cacheEntries) {
105
+ if (cacheEntry.alwaysValid) {
106
+ return cacheEntry;
107
+ }
108
+ const isValid = yield this.validateAccordingToPixelPerfect(cacheEntry, screenshot);
109
+ if (isValid) {
110
+ /* eslint-enable no-restricted-syntax, no-await-in-loop */
111
+ return cacheEntry;
112
+ }
113
+ }
114
+ /* eslint-enable no-restricted-syntax, no-await-in-loop */
115
+ }
116
+ return undefined;
117
+ });
118
+ }
119
+ // eslint-disable-next-line class-methods-use-this
120
+ validateAccordingToPixelPerfect(cacheEntry, screenshot) {
121
+ return __awaiter(this, void 0, void 0, function* () {
122
+ var _a;
123
+ if (screenshot === undefined) {
124
+ return false;
125
+ }
126
+ if (((_a = cacheEntry.reference) === null || _a === void 0 ? void 0 : _a.image) === undefined) {
127
+ return false;
128
+ }
129
+ try {
130
+ const referenceImage = yield Base64Image.fromString(screenshot);
131
+ const croppedScreenshot = yield referenceImage.cropRegion(cacheEntry.reference.image.xTopLeft, cacheEntry.reference.image.yTopLeft, cacheEntry.reference.image.width, cacheEntry.reference.image.height);
132
+ const croppedReferenceImageString = croppedScreenshot.toString(true);
133
+ const isValid = croppedReferenceImageString === cacheEntry.reference.image.base64Image;
134
+ return isValid;
135
+ }
136
+ catch (error) {
137
+ return false;
138
+ }
139
+ });
140
+ }
141
+ }
@@ -0,0 +1,19 @@
1
+ import { CacheEntry } from './cache-entry';
2
+ import { ValidationType } from './cache-config';
3
+ export type CacheDataByInstruction = Record<string, CacheEntry[]>;
4
+ export type CacheDataByValidationType = Record<string, CacheDataByInstruction>;
5
+ export declare class CacheFile {
6
+ private createAt;
7
+ private version;
8
+ private type;
9
+ private data;
10
+ private filePath;
11
+ constructor(filePath?: string);
12
+ loadFromFileVersion1(json: CacheFile): void;
13
+ loadFromFile(): void;
14
+ getDataForValidationType(validationType: ValidationType): CacheDataByInstruction;
15
+ getData(): CacheDataByValidationType;
16
+ saveToFile(validationType: ValidationType, relevantData: CacheDataByInstruction): void;
17
+ private setRelevantData;
18
+ private asJsonObject;
19
+ }
@@ -0,0 +1,121 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { logger } from '../../lib';
4
+ import { CacheEntry } from './cache-entry';
5
+ export class CacheFile {
6
+ constructor(filePath) {
7
+ this.createAt = new Date();
8
+ this.version = 1;
9
+ this.type = 'AskUI-Cache';
10
+ this.data = {};
11
+ this.filePath = filePath;
12
+ }
13
+ loadFromFileVersion1(json) {
14
+ if (json.version !== 1) {
15
+ throw new Error(`Unsupported key 'version' in the cache file: '${json.version}'. Only version 1 is supported.`);
16
+ }
17
+ if (json.data === undefined) {
18
+ throw new Error("Key 'data' is required in the cache file.");
19
+ }
20
+ if (json.createAt === undefined) {
21
+ throw new Error("Key 'createAt' is required in the cache file.");
22
+ }
23
+ if (json.version === undefined) {
24
+ throw new Error("Key 'version' is required in the cache file.");
25
+ }
26
+ if (json.type !== 'AskUI-Cache') {
27
+ throw new Error(`Unsupported key 'type' in the cache file: '${json.type}'. Only 'AskUI-Cache' is supported.`);
28
+ }
29
+ this.createAt = new Date(json.createAt);
30
+ this.version = json.version;
31
+ this.type = json.type;
32
+ this.data = Object.keys(json.data).reduce((acc, validationType) => {
33
+ var _a, _b;
34
+ const nextAcc = Object.assign({}, acc);
35
+ const validationEntries = (_b = (_a = json.data) === null || _a === void 0 ? void 0 : _a[validationType]) !== null && _b !== void 0 ? _b : {};
36
+ nextAcc[validationType] = Object.keys(validationEntries).reduce((innerAcc, instruction) => {
37
+ var _a;
38
+ const nextInnerAcc = Object.assign({}, innerAcc);
39
+ const rawEntries = (_a = validationEntries[instruction]) !== null && _a !== void 0 ? _a : [];
40
+ nextInnerAcc[instruction] = rawEntries
41
+ .map((entry) => CacheEntry.fromJson(entry))
42
+ .filter((entry) => entry !== undefined);
43
+ return nextInnerAcc;
44
+ }, {});
45
+ return nextAcc;
46
+ }, {});
47
+ }
48
+ loadFromFile() {
49
+ if (this.filePath === undefined) {
50
+ logger.debug('The cache file path is not set. Skipping the cache load.');
51
+ return;
52
+ }
53
+ logger.debug(`Loading the cache from file '${this.filePath}'.`);
54
+ if (!fs.existsSync(this.filePath)) {
55
+ logger.warn(`The cache file does not exist: '${this.filePath}'. Skipping the cache load.`);
56
+ return;
57
+ }
58
+ const data = fs.readFileSync(this.filePath, 'utf8');
59
+ const json = JSON.parse(data);
60
+ if (json.version !== 1) {
61
+ throw new Error(`Unsupported key 'version' in the cache file: '${json.version}'. Only version 1 is supported.`);
62
+ }
63
+ this.loadFromFileVersion1(json);
64
+ }
65
+ getDataForValidationType(validationType) {
66
+ var _a;
67
+ return (_a = this.data[validationType]) !== null && _a !== void 0 ? _a : {};
68
+ }
69
+ getData() {
70
+ return this.data;
71
+ }
72
+ saveToFile(validationType, relevantData) {
73
+ if (this.filePath === undefined) {
74
+ logger.debug('The cache file path is not set. Skipping the cache save.');
75
+ return;
76
+ }
77
+ try {
78
+ logger.debug(`Saving the cache to file '${this.filePath}'.`);
79
+ this.setRelevantData(validationType, relevantData);
80
+ const jsonObject = this.asJsonObject();
81
+ const jsonString = JSON.stringify(jsonObject, null, 2);
82
+ // create the directory if it doesn't exist
83
+ if (!fs.existsSync(path.dirname(this.filePath))) {
84
+ fs.mkdirSync(path.dirname(this.filePath), { recursive: true });
85
+ }
86
+ fs.writeFileSync(this.filePath, jsonString, 'utf8');
87
+ logger.info(`The cache was saved successfully to '${this.filePath}'.`);
88
+ }
89
+ catch (error) {
90
+ logger.error(`An error occurred while saving the cache to file '${this.filePath}': '${error}'`);
91
+ throw error;
92
+ }
93
+ }
94
+ setRelevantData(validationType, relevantData) {
95
+ this.data[validationType] = relevantData;
96
+ }
97
+ asJsonObject() {
98
+ return {
99
+ createAt: this.createAt.toISOString(),
100
+ data: Object.keys(this.data).reduce((acc, validationType) => {
101
+ const validationData = this.data[validationType];
102
+ if (validationData === undefined) {
103
+ return acc;
104
+ }
105
+ const nextAcc = Object.assign({}, acc);
106
+ nextAcc[validationType] = Object.keys(validationData).reduce((innerAcc, instruction) => {
107
+ const entries = validationData[instruction];
108
+ if (entries !== undefined) {
109
+ const nextInnerAcc = Object.assign({}, innerAcc);
110
+ nextInnerAcc[instruction] = entries;
111
+ return nextInnerAcc;
112
+ }
113
+ return innerAcc;
114
+ }, {});
115
+ return nextAcc;
116
+ }, {}),
117
+ type: this.type,
118
+ version: this.version,
119
+ };
120
+ }
121
+ }
@@ -0,0 +1,12 @@
1
+ import { CacheInterface } from './cache-interface';
2
+ import { CacheEntry } from './cache-entry';
3
+ import { ControlCommand } from '../ui-control-commands';
4
+ import { CustomElement } from '../model/custom-element';
5
+ export declare class DummyCacheManager implements CacheInterface {
6
+ add(_key: string, _value: CacheEntry): void;
7
+ addCacheEntryFromControlCommand(_instruction: string, _controlCommand: ControlCommand, _customElements: CustomElement[], _image?: string): Promise<void>;
8
+ isImageRequired(_instruction: string, _customElements: CustomElement[]): boolean | undefined;
9
+ getCachedControlCommand(_instruction: string, _customElements: CustomElement[], _image?: string): Promise<ControlCommand | undefined>;
10
+ loadFromFile(): void;
11
+ saveToFile(): void;
12
+ }
@@ -0,0 +1,23 @@
1
+ export class DummyCacheManager {
2
+ // eslint-disable-next-line class-methods-use-this
3
+ add(_key, _value) {
4
+ }
5
+ // eslint-disable-next-line class-methods-use-this
6
+ addCacheEntryFromControlCommand(_instruction, _controlCommand, _customElements, _image) {
7
+ return Promise.resolve();
8
+ }
9
+ // eslint-disable-next-line class-methods-use-this
10
+ isImageRequired(_instruction, _customElements) {
11
+ return undefined;
12
+ }
13
+ // eslint-disable-next-line class-methods-use-this
14
+ getCachedControlCommand(_instruction, _customElements, _image) {
15
+ return Promise.resolve(undefined);
16
+ }
17
+ // eslint-disable-next-line class-methods-use-this
18
+ loadFromFile() {
19
+ }
20
+ // eslint-disable-next-line class-methods-use-this
21
+ saveToFile() {
22
+ }
23
+ }
@@ -0,0 +1,10 @@
1
+ export declare class ImageReference {
2
+ base64Image: string;
3
+ width: number;
4
+ height: number;
5
+ xTopLeft: number;
6
+ yTopLeft: number;
7
+ constructor(base64Image: string, width: number, height: number, xTopLeft: number, yTopLeft: number);
8
+ static fromJson(json: ImageReference): ImageReference;
9
+ toJson(): Record<string, unknown>;
10
+ }
@@ -0,0 +1,36 @@
1
+ export class ImageReference {
2
+ constructor(base64Image, width, height, xTopLeft, yTopLeft) {
3
+ this.base64Image = base64Image;
4
+ this.width = width;
5
+ this.height = height;
6
+ this.xTopLeft = xTopLeft;
7
+ this.yTopLeft = yTopLeft;
8
+ }
9
+ static fromJson(json) {
10
+ if (json.base64Image === undefined) {
11
+ throw new Error('the key "base64Image" is required');
12
+ }
13
+ if (json.width === undefined) {
14
+ throw new Error('the key "width" is required');
15
+ }
16
+ if (json.height === undefined) {
17
+ throw new Error('the key "height" is required');
18
+ }
19
+ if (json.xTopLeft === undefined) {
20
+ throw new Error('the key "xTopLeft" is required');
21
+ }
22
+ if (json.yTopLeft === undefined) {
23
+ throw new Error('the key "yTopLeft" is required');
24
+ }
25
+ return new ImageReference(json.base64Image, json.width, json.height, json.xTopLeft, json.yTopLeft);
26
+ }
27
+ toJson() {
28
+ return {
29
+ base64Image: this.base64Image,
30
+ height: this.height,
31
+ width: this.width,
32
+ xTopLeft: this.xTopLeft,
33
+ yTopLeft: this.yTopLeft,
34
+ };
35
+ }
36
+ }
@@ -0,0 +1,7 @@
1
+ export { ImageReference } from './image-reference';
2
+ export { CacheEntryReference } from './cache-entry-reference';
3
+ export { CacheEntry } from './cache-entry';
4
+ export { CacheConfig, ValidationType } from './cache-config';
5
+ export { CacheInterface } from './cache-interface';
6
+ export { DummyCacheManager } from './dummy-cache-manager';
7
+ export { CacheManager } from './cache-manager';
@@ -0,0 +1,5 @@
1
+ export { ImageReference } from './image-reference';
2
+ export { CacheEntryReference } from './cache-entry-reference';
3
+ export { CacheEntry } from './cache-entry';
4
+ export { DummyCacheManager } from './dummy-cache-manager';
5
+ export { CacheManager } from './cache-manager';
@@ -17,7 +17,7 @@ export class BoundingBox {
17
17
  this.ymax = ymax;
18
18
  }
19
19
  static fromJson(boundinBox, resizeRatio = 1) {
20
- return new BoundingBox(boundinBox.xmin * resizeRatio, boundinBox.ymin * resizeRatio, boundinBox.xmax * resizeRatio, boundinBox.ymax * resizeRatio);
20
+ return new BoundingBox(Math.round(boundinBox.xmin * resizeRatio), Math.round(boundinBox.ymin * resizeRatio), Math.round(boundinBox.xmax * resizeRatio), Math.round(boundinBox.ymax * resizeRatio));
21
21
  }
22
22
  /**
23
23
  *
@@ -19,4 +19,5 @@ export declare class CustomElement implements CustomElementJson {
19
19
  static fromJsonWithImagePathOrImage(ceJson: CustomElementJson): Promise<CustomElement>;
20
20
  static fromJson(ceJson: CustomElementJson): CustomElement;
21
21
  validate(): void;
22
+ asString(): string;
22
23
  }
@@ -43,6 +43,9 @@ export class CustomElement {
43
43
  throw new ValidationError(e.errors.join(', '));
44
44
  }
45
45
  }
46
+ asString() {
47
+ return `name:${this.name}-customImage:${this.customImage}-threshold:${this.threshold}-stopThreshold:${this.stopThreshold}-rotationDegreePerStep:${this.rotationDegreePerStep}-imageCompareFormat:${this.imageCompareFormat}-mask:${this.mask}`;
48
+ }
46
49
  }
47
50
  CustomElement.schema = object({
48
51
  mask: array().optional().min(3, 'mask must contain at least 3 points'),