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.
- package/dist/cjs/core/cache/cache-config.d.ts +5 -0
- package/dist/cjs/core/cache/cache-config.js +2 -0
- package/dist/cjs/core/cache/cache-entry-reference.d.ts +7 -0
- package/dist/cjs/core/cache/cache-entry-reference.js +20 -0
- package/dist/cjs/core/cache/cache-entry.d.ts +11 -0
- package/dist/cjs/core/cache/cache-entry.js +47 -0
- package/dist/cjs/core/cache/cache-interface.d.ts +11 -0
- package/dist/cjs/core/cache/cache-interface.js +2 -0
- package/dist/cjs/core/cache/cache-manager.d.ts +24 -0
- package/dist/cjs/core/cache/cache-manager.js +145 -0
- package/dist/cjs/core/cache/cahe-file.d.ts +19 -0
- package/dist/cjs/core/cache/cahe-file.js +128 -0
- package/dist/cjs/core/cache/dummy-cache-manager.d.ts +12 -0
- package/dist/cjs/core/cache/dummy-cache-manager.js +27 -0
- package/dist/cjs/core/cache/image-reference.d.ts +10 -0
- package/dist/cjs/core/cache/image-reference.js +40 -0
- package/dist/cjs/core/cache/index.d.ts +7 -0
- package/dist/cjs/core/cache/index.js +13 -0
- package/dist/cjs/core/model/annotation-result/boundary-box.js +1 -1
- package/dist/cjs/core/model/custom-element.d.ts +1 -0
- package/dist/cjs/core/model/custom-element.js +3 -0
- package/dist/cjs/core/ui-control-commands/action.d.ts +1 -0
- package/dist/cjs/core/ui-control-commands/action.js +10 -2
- package/dist/cjs/core/ui-control-commands/control-command.d.ts +1 -0
- package/dist/cjs/core/ui-control-commands/control-command.js +7 -0
- package/dist/cjs/execution/dsl.d.ts +12 -5
- package/dist/cjs/execution/dsl.js +30 -15
- package/dist/cjs/execution/execution-runtime.d.ts +1 -1
- package/dist/cjs/execution/execution-runtime.js +21 -17
- package/dist/cjs/execution/inference-client.d.ts +5 -3
- package/dist/cjs/execution/inference-client.js +22 -3
- package/dist/cjs/execution/ui-control-client-dependency-builder.js +6 -1
- package/dist/cjs/execution/ui-control-client.d.ts +2 -1
- package/dist/cjs/execution/ui-control-client.js +28 -11
- package/dist/cjs/execution/ui-controller-client-interface.d.ts +2 -0
- package/dist/cjs/utils/base_64_image/base-64-image.d.ts +2 -0
- package/dist/cjs/utils/base_64_image/base-64-image.js +27 -4
- package/dist/cjs/utils/http/http-client-got.d.ts +15 -0
- package/dist/cjs/utils/http/http-client-got.js +59 -11
- package/dist/esm/core/cache/cache-config.d.ts +5 -0
- package/dist/esm/core/cache/cache-config.js +1 -0
- package/dist/esm/core/cache/cache-entry-reference.d.ts +7 -0
- package/dist/esm/core/cache/cache-entry-reference.js +16 -0
- package/dist/esm/core/cache/cache-entry.d.ts +11 -0
- package/dist/esm/core/cache/cache-entry.js +43 -0
- package/dist/esm/core/cache/cache-interface.d.ts +11 -0
- package/dist/esm/core/cache/cache-interface.js +1 -0
- package/dist/esm/core/cache/cache-manager.d.ts +24 -0
- package/dist/esm/core/cache/cache-manager.js +141 -0
- package/dist/esm/core/cache/cahe-file.d.ts +19 -0
- package/dist/esm/core/cache/cahe-file.js +121 -0
- package/dist/esm/core/cache/dummy-cache-manager.d.ts +12 -0
- package/dist/esm/core/cache/dummy-cache-manager.js +23 -0
- package/dist/esm/core/cache/image-reference.d.ts +10 -0
- package/dist/esm/core/cache/image-reference.js +36 -0
- package/dist/esm/core/cache/index.d.ts +7 -0
- package/dist/esm/core/cache/index.js +5 -0
- package/dist/esm/core/model/annotation-result/boundary-box.js +1 -1
- package/dist/esm/core/model/custom-element.d.ts +1 -0
- package/dist/esm/core/model/custom-element.js +3 -0
- package/dist/esm/core/ui-control-commands/action.d.ts +1 -0
- package/dist/esm/core/ui-control-commands/action.js +10 -2
- package/dist/esm/core/ui-control-commands/control-command.d.ts +1 -0
- package/dist/esm/core/ui-control-commands/control-command.js +7 -0
- package/dist/esm/execution/dsl.d.ts +12 -5
- package/dist/esm/execution/dsl.js +30 -15
- package/dist/esm/execution/execution-runtime.d.ts +1 -1
- package/dist/esm/execution/execution-runtime.js +21 -17
- package/dist/esm/execution/inference-client.d.ts +5 -3
- package/dist/esm/execution/inference-client.js +22 -3
- package/dist/esm/execution/ui-control-client-dependency-builder.js +6 -1
- package/dist/esm/execution/ui-control-client.d.ts +2 -1
- package/dist/esm/execution/ui-control-client.js +28 -11
- package/dist/esm/execution/ui-controller-client-interface.d.ts +2 -0
- package/dist/esm/utils/base_64_image/base-64-image.d.ts +2 -0
- package/dist/esm/utils/base_64_image/base-64-image.js +27 -4
- package/dist/esm/utils/http/http-client-got.d.ts +15 -0
- package/dist/esm/utils/http/http-client-got.js +36 -8
- 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 =
|
|
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
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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
|
-
|
|
131
|
-
|
|
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 @@
|
|
|
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
|
*
|
|
@@ -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'),
|