@devicecloud.dev/dcd 5.0.0-beta.0 → 5.0.0-beta.1
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 +35 -0
- package/dist/commands/artifacts.d.ts +28 -28
- package/dist/commands/artifacts.js +20 -23
- package/dist/commands/cloud.d.ts +57 -57
- package/dist/commands/cloud.js +173 -186
- package/dist/commands/list.d.ts +22 -22
- package/dist/commands/list.js +36 -38
- package/dist/commands/live.js +134 -127
- package/dist/commands/login.d.ts +11 -11
- package/dist/commands/login.js +46 -44
- package/dist/commands/logout.js +16 -18
- package/dist/commands/status.d.ts +11 -11
- package/dist/commands/status.js +45 -43
- package/dist/commands/switch-org.d.ts +7 -7
- package/dist/commands/switch-org.js +19 -21
- package/dist/commands/upgrade.js +29 -31
- package/dist/commands/upload.d.ts +10 -10
- package/dist/commands/upload.js +42 -43
- package/dist/commands/whoami.js +17 -20
- package/dist/config/environments.js +6 -12
- package/dist/config/flags/api.flags.js +1 -4
- package/dist/config/flags/binary.flags.js +1 -4
- package/dist/config/flags/device.flags.js +6 -9
- package/dist/config/flags/environment.flags.js +1 -4
- package/dist/config/flags/execution.flags.js +1 -4
- package/dist/config/flags/github.flags.js +1 -4
- package/dist/config/flags/output.flags.js +1 -4
- package/dist/constants.js +15 -18
- package/dist/gateways/api-gateway.d.ts +31 -6
- package/dist/gateways/api-gateway.js +70 -16
- package/dist/gateways/cli-auth-gateway.d.ts +1 -1
- package/dist/gateways/cli-auth-gateway.js +3 -6
- package/dist/gateways/realtime-gateway.d.ts +32 -0
- package/dist/gateways/realtime-gateway.js +103 -0
- package/dist/gateways/supabase-gateway.d.ts +1 -1
- package/dist/gateways/supabase-gateway.js +10 -14
- package/dist/index.js +41 -38
- package/dist/mcp/context.d.ts +33 -0
- package/dist/mcp/context.js +33 -0
- package/dist/mcp/helpers.d.ts +16 -0
- package/dist/mcp/helpers.js +34 -0
- package/dist/mcp/index.d.ts +2 -0
- package/dist/mcp/index.js +24 -0
- package/dist/mcp/server.d.ts +7 -0
- package/dist/mcp/server.js +27 -0
- package/dist/mcp/tools/download-artifacts.d.ts +11 -0
- package/dist/mcp/tools/download-artifacts.js +84 -0
- package/dist/mcp/tools/get-status.d.ts +7 -0
- package/dist/mcp/tools/get-status.js +39 -0
- package/dist/mcp/tools/list-devices.d.ts +7 -0
- package/dist/mcp/tools/list-devices.js +27 -0
- package/dist/mcp/tools/list-runs.d.ts +3 -0
- package/dist/mcp/tools/list-runs.js +60 -0
- package/dist/mcp/tools/run-cloud-test.d.ts +14 -0
- package/dist/mcp/tools/run-cloud-test.js +233 -0
- package/dist/methods.d.ts +32 -1
- package/dist/methods.js +125 -66
- package/dist/services/device-validation.service.d.ts +1 -1
- package/dist/services/device-validation.service.js +1 -5
- package/dist/services/execution-plan.service.js +14 -17
- package/dist/services/execution-plan.utils.js +15 -23
- package/dist/services/flow-paths.d.ts +17 -0
- package/dist/services/flow-paths.js +52 -0
- package/dist/services/metadata-extractor.service.js +22 -25
- package/dist/services/moropo.service.js +18 -20
- package/dist/services/report-download.service.d.ts +1 -1
- package/dist/services/report-download.service.js +5 -9
- package/dist/services/results-polling.service.d.ts +18 -3
- package/dist/services/results-polling.service.js +195 -108
- package/dist/services/telemetry.service.d.ts +10 -1
- package/dist/services/telemetry.service.js +40 -18
- package/dist/services/test-submission.service.d.ts +21 -4
- package/dist/services/test-submission.service.js +51 -34
- package/dist/services/version.service.d.ts +1 -1
- package/dist/services/version.service.js +1 -5
- package/dist/types/domain/auth.types.d.ts +8 -0
- package/dist/types/domain/auth.types.js +1 -2
- package/dist/types/domain/device.types.js +8 -11
- package/dist/types/domain/live.types.js +1 -2
- package/dist/types/generated/schema.types.js +1 -2
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.js +2 -18
- package/dist/types.js +1 -2
- package/dist/utils/auth.d.ts +1 -1
- package/dist/utils/auth.js +27 -28
- package/dist/utils/ci.d.ts +12 -0
- package/dist/utils/ci.js +39 -0
- package/dist/utils/cli.js +18 -27
- package/dist/utils/compatibility.d.ts +1 -1
- package/dist/utils/compatibility.js +5 -7
- package/dist/utils/config-store.js +33 -43
- package/dist/utils/connectivity.js +1 -4
- package/dist/utils/expo.js +15 -21
- package/dist/utils/orgs.js +8 -12
- package/dist/utils/paths.js +2 -5
- package/dist/utils/progress.js +2 -5
- package/dist/utils/styling.d.ts +35 -37
- package/dist/utils/styling.js +52 -86
- package/dist/utils/ui.d.ts +41 -0
- package/dist/utils/ui.js +95 -0
- package/package.json +27 -24
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Longest whole-segment directory prefix shared by every flow + referenced
|
|
3
|
+
* file path. Segment comparison (not `startsWith`) so sibling dirs like
|
|
4
|
+
* `flows`/`flows-extra` can't merge, and the file segment itself is never
|
|
5
|
+
* consumed. Returns '' when the paths share no root at all (or none are given).
|
|
6
|
+
*/
|
|
7
|
+
export declare function computeCommonRoot(testFileNames: string[], referencedFiles: string[]): string;
|
|
8
|
+
export interface FlowMetadataEntry {
|
|
9
|
+
flowName: string;
|
|
10
|
+
tags: string[];
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Build the portable-relative-path → {flowName, tags} map that results are
|
|
14
|
+
* keyed by. Flow name comes from the YAML `name` field, falling back to the
|
|
15
|
+
* filename without extension; tags are normalized to a string array.
|
|
16
|
+
*/
|
|
17
|
+
export declare function buildTestMetadataMap(flowMetadata: Record<string, Record<string, unknown> | null>, commonRoot: string): Record<string, FlowMetadataEntry>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure helpers for turning absolute flow paths into the server-side relative
|
|
3
|
+
* keys that submission, the polling layer, and JSON output all share.
|
|
4
|
+
*
|
|
5
|
+
* Extracted from the cloud command so the MCP `dcd_run_cloud_test` tool builds
|
|
6
|
+
* byte-identical paths without duplicating the logic.
|
|
7
|
+
*/
|
|
8
|
+
import * as path from 'node:path';
|
|
9
|
+
import { toPortableRelativePath } from '../utils/paths.js';
|
|
10
|
+
/**
|
|
11
|
+
* Longest whole-segment directory prefix shared by every flow + referenced
|
|
12
|
+
* file path. Segment comparison (not `startsWith`) so sibling dirs like
|
|
13
|
+
* `flows`/`flows-extra` can't merge, and the file segment itself is never
|
|
14
|
+
* consumed. Returns '' when the paths share no root at all (or none are given).
|
|
15
|
+
*/
|
|
16
|
+
export function computeCommonRoot(testFileNames, referencedFiles) {
|
|
17
|
+
const pathsShortestToLongest = [...testFileNames, ...referencedFiles].sort((a, b) => a.split(path.sep).length - b.split(path.sep).length);
|
|
18
|
+
if (pathsShortestToLongest.length === 0)
|
|
19
|
+
return '';
|
|
20
|
+
const splitPaths = pathsShortestToLongest.map((p) => p.split(path.sep));
|
|
21
|
+
const shortestSegments = splitPaths[0];
|
|
22
|
+
let matchedSegments = 0;
|
|
23
|
+
for (let i = 0; i < shortestSegments.length - 1; i++) {
|
|
24
|
+
if (splitPaths.every((segments) => segments[i] === shortestSegments[i])) {
|
|
25
|
+
matchedSegments = i + 1;
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return shortestSegments.slice(0, matchedSegments).join(path.sep);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Build the portable-relative-path → {flowName, tags} map that results are
|
|
35
|
+
* keyed by. Flow name comes from the YAML `name` field, falling back to the
|
|
36
|
+
* filename without extension; tags are normalized to a string array.
|
|
37
|
+
*/
|
|
38
|
+
export function buildTestMetadataMap(flowMetadata, commonRoot) {
|
|
39
|
+
const map = {};
|
|
40
|
+
for (const [absolutePath, meta] of Object.entries(flowMetadata)) {
|
|
41
|
+
const normalizedPath = toPortableRelativePath(absolutePath, commonRoot);
|
|
42
|
+
const flowName = meta?.name || path.parse(absolutePath).name;
|
|
43
|
+
const rawTags = meta?.tags;
|
|
44
|
+
const tags = Array.isArray(rawTags)
|
|
45
|
+
? rawTags.map(String)
|
|
46
|
+
: rawTags
|
|
47
|
+
? [String(rawTags)]
|
|
48
|
+
: [];
|
|
49
|
+
map[normalizedPath] = { flowName, tags };
|
|
50
|
+
}
|
|
51
|
+
return map;
|
|
52
|
+
}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
import bplistParser from 'bplist-parser';
|
|
2
|
+
import nodeApk from 'node-apk';
|
|
3
|
+
import { readFile, rm } from 'node:fs/promises';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
import StreamZip from 'node-stream-zip';
|
|
6
|
+
import { parse } from 'plist';
|
|
7
|
+
// node-apk and bplist-parser are CJS with no `exports` map; Node's named-export
|
|
8
|
+
// detection for CJS (cjs-module-lexer) is version-dependent, so destructure off
|
|
9
|
+
// the default import instead — that interop is guaranteed on every Node version.
|
|
10
|
+
const { Apk } = nodeApk;
|
|
11
|
+
const { parseBuffer } = bplistParser;
|
|
10
12
|
/**
|
|
11
13
|
* Parses an Info.plist buffer (XML, UTF-8 BOM'd XML, or binary bplist).
|
|
12
14
|
* Shared by the .app and .zip extractors.
|
|
@@ -16,10 +18,10 @@ function parseInfoPlist(buffer) {
|
|
|
16
18
|
const bufferType = buffer[0];
|
|
17
19
|
// 60 = '<' (XML plist), 239 = UTF-8 BOM, 98 = 'b' (binary "bplist")
|
|
18
20
|
if (bufferType === 60 || bufferType === 239) {
|
|
19
|
-
data =
|
|
21
|
+
data = parse(buffer.toString());
|
|
20
22
|
}
|
|
21
23
|
else if (bufferType === 98) {
|
|
22
|
-
data =
|
|
24
|
+
data = parseBuffer(buffer)[0];
|
|
23
25
|
}
|
|
24
26
|
else {
|
|
25
27
|
throw new Error('Unknown plist buffer type.');
|
|
@@ -29,12 +31,12 @@ function parseInfoPlist(buffer) {
|
|
|
29
31
|
/**
|
|
30
32
|
* Extracts metadata from Android APK files
|
|
31
33
|
*/
|
|
32
|
-
class AndroidMetadataExtractor {
|
|
34
|
+
export class AndroidMetadataExtractor {
|
|
33
35
|
canHandle(filePath) {
|
|
34
36
|
return filePath.endsWith('.apk');
|
|
35
37
|
}
|
|
36
38
|
async extract(filePath) {
|
|
37
|
-
const apk = new
|
|
39
|
+
const apk = new Apk(filePath);
|
|
38
40
|
try {
|
|
39
41
|
const manifest = await apk.getManifestInfo();
|
|
40
42
|
return { appId: manifest.package, platform: 'android' };
|
|
@@ -44,27 +46,25 @@ class AndroidMetadataExtractor {
|
|
|
44
46
|
}
|
|
45
47
|
}
|
|
46
48
|
}
|
|
47
|
-
exports.AndroidMetadataExtractor = AndroidMetadataExtractor;
|
|
48
49
|
/**
|
|
49
50
|
* Extracts metadata from iOS .app directories
|
|
50
51
|
*/
|
|
51
|
-
class IosAppMetadataExtractor {
|
|
52
|
+
export class IosAppMetadataExtractor {
|
|
52
53
|
canHandle(filePath) {
|
|
53
54
|
return filePath.endsWith('.app');
|
|
54
55
|
}
|
|
55
56
|
async extract(filePath) {
|
|
56
57
|
const infoPlistPath = path.normalize(path.join(filePath, 'Info.plist'));
|
|
57
|
-
const buffer = await
|
|
58
|
+
const buffer = await readFile(infoPlistPath);
|
|
58
59
|
const data = parseInfoPlist(buffer);
|
|
59
60
|
const appId = data.CFBundleIdentifier;
|
|
60
61
|
return { appId, platform: 'ios' };
|
|
61
62
|
}
|
|
62
63
|
}
|
|
63
|
-
exports.IosAppMetadataExtractor = IosAppMetadataExtractor;
|
|
64
64
|
/**
|
|
65
65
|
* Extracts metadata from iOS .zip files containing .app bundles
|
|
66
66
|
*/
|
|
67
|
-
class IosZipMetadataExtractor {
|
|
67
|
+
export class IosZipMetadataExtractor {
|
|
68
68
|
canHandle(filePath) {
|
|
69
69
|
return filePath.endsWith('.zip');
|
|
70
70
|
}
|
|
@@ -106,18 +106,17 @@ class IosZipMetadataExtractor {
|
|
|
106
106
|
});
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
|
-
exports.IosZipMetadataExtractor = IosZipMetadataExtractor;
|
|
110
109
|
/**
|
|
111
110
|
* Extracts metadata from Expo iOS .tar.gz archives by extracting the
|
|
112
111
|
* archive to a temp directory, finding the .app bundle inside, then
|
|
113
112
|
* delegating to IosAppMetadataExtractor.
|
|
114
113
|
*/
|
|
115
|
-
class ExpoTarGzMetadataExtractor {
|
|
114
|
+
export class ExpoTarGzMetadataExtractor {
|
|
116
115
|
canHandle(filePath) {
|
|
117
116
|
return filePath.endsWith('.tar.gz');
|
|
118
117
|
}
|
|
119
118
|
async extract(filePath) {
|
|
120
|
-
const { extractTarGz, findAppBundle } = await
|
|
119
|
+
const { extractTarGz, findAppBundle } = await import('../utils/expo.js');
|
|
121
120
|
const extractDir = await extractTarGz(filePath, false);
|
|
122
121
|
try {
|
|
123
122
|
const appPath = await findAppBundle(extractDir);
|
|
@@ -125,15 +124,14 @@ class ExpoTarGzMetadataExtractor {
|
|
|
125
124
|
return await iosExtractor.extract(appPath);
|
|
126
125
|
}
|
|
127
126
|
finally {
|
|
128
|
-
await
|
|
127
|
+
await rm(extractDir, { recursive: true, force: true }).catch(() => { });
|
|
129
128
|
}
|
|
130
129
|
}
|
|
131
130
|
}
|
|
132
|
-
exports.ExpoTarGzMetadataExtractor = ExpoTarGzMetadataExtractor;
|
|
133
131
|
/**
|
|
134
132
|
* Service for extracting app metadata from various file formats
|
|
135
133
|
*/
|
|
136
|
-
class MetadataExtractorService {
|
|
134
|
+
export class MetadataExtractorService {
|
|
137
135
|
extractors = [
|
|
138
136
|
new AndroidMetadataExtractor(),
|
|
139
137
|
new ExpoTarGzMetadataExtractor(),
|
|
@@ -159,4 +157,3 @@ class MetadataExtractorService {
|
|
|
159
157
|
}
|
|
160
158
|
}
|
|
161
159
|
}
|
|
162
|
-
exports.MetadataExtractorService = MetadataExtractorService;
|
|
@@ -1,17 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const node_stream_1 = require("node:stream");
|
|
9
|
-
const promises_1 = require("node:stream/promises");
|
|
10
|
-
const StreamZip = require("node-stream-zip");
|
|
1
|
+
import { ux } from '../utils/progress.js';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import * as os from 'node:os';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
import { Readable } from 'node:stream';
|
|
6
|
+
import { pipeline } from 'node:stream/promises';
|
|
7
|
+
import StreamZip from 'node-stream-zip';
|
|
11
8
|
/**
|
|
12
9
|
* Service for downloading and extracting Moropo tests from the Moropo API
|
|
13
10
|
*/
|
|
14
|
-
class MoropoService {
|
|
11
|
+
export class MoropoService {
|
|
15
12
|
MOROPO_API_URL = 'https://api.moropo.com/tests';
|
|
16
13
|
/**
|
|
17
14
|
* Download and extract Moropo tests from the API
|
|
@@ -25,7 +22,7 @@ class MoropoService {
|
|
|
25
22
|
let moropoDir;
|
|
26
23
|
try {
|
|
27
24
|
if (!quiet && !json) {
|
|
28
|
-
|
|
25
|
+
ux.action.start('Downloading Moropo tests', 'Initializing', {
|
|
29
26
|
stdout: true,
|
|
30
27
|
});
|
|
31
28
|
}
|
|
@@ -52,7 +49,7 @@ class MoropoService {
|
|
|
52
49
|
// Extract zip file
|
|
53
50
|
await this.extractZipFile(zipPath, moropoDir);
|
|
54
51
|
if (!quiet && !json) {
|
|
55
|
-
|
|
52
|
+
ux.action.stop('completed');
|
|
56
53
|
}
|
|
57
54
|
this.logDebug(debug, logger, '[DEBUG] Successfully extracted Moropo tests');
|
|
58
55
|
// Create config.yaml file
|
|
@@ -62,14 +59,16 @@ class MoropoService {
|
|
|
62
59
|
}
|
|
63
60
|
catch (error) {
|
|
64
61
|
if (!quiet && !json) {
|
|
65
|
-
|
|
62
|
+
ux.action.stop('failed');
|
|
66
63
|
}
|
|
67
64
|
// Remove the temp directory (and any partially-written zip inside it)
|
|
68
65
|
if (moropoDir) {
|
|
69
66
|
fs.rmSync(moropoDir, { recursive: true, force: true });
|
|
70
67
|
}
|
|
71
68
|
this.logDebug(debug, logger, `[DEBUG] Error downloading/extracting Moropo tests: ${error}`);
|
|
72
|
-
throw new Error(`Failed to download/extract Moropo tests: ${error}
|
|
69
|
+
throw new Error(`Failed to download/extract Moropo tests: ${error}`, {
|
|
70
|
+
cause: error,
|
|
71
|
+
});
|
|
73
72
|
}
|
|
74
73
|
}
|
|
75
74
|
createConfigFile(moropoDir) {
|
|
@@ -84,19 +83,19 @@ class MoropoService {
|
|
|
84
83
|
if (!response.body) {
|
|
85
84
|
throw new Error('Failed to get response reader');
|
|
86
85
|
}
|
|
87
|
-
const source =
|
|
86
|
+
const source = Readable.fromWeb(response.body);
|
|
88
87
|
if (!quiet && !json && totalSize) {
|
|
89
88
|
// Progress tap — pipeline below still owns the flow/backpressure
|
|
90
89
|
source.on('data', (chunk) => {
|
|
91
90
|
downloadedSize += chunk.length;
|
|
92
91
|
const progress = Math.round((downloadedSize / totalSize) * 100);
|
|
93
|
-
|
|
92
|
+
ux.action.status = `Downloading: ${progress}%`;
|
|
94
93
|
});
|
|
95
94
|
}
|
|
96
95
|
// pipeline (unlike a bare 'finish' wait) propagates errors from both
|
|
97
96
|
// streams, so disk-full or a stalled download rejects instead of
|
|
98
97
|
// crashing or hanging.
|
|
99
|
-
await
|
|
98
|
+
await pipeline(source, fs.createWriteStream(zipPath));
|
|
100
99
|
}
|
|
101
100
|
async extractZipFile(zipPath, extractPath) {
|
|
102
101
|
// eslint-disable-next-line new-cap
|
|
@@ -112,8 +111,7 @@ class MoropoService {
|
|
|
112
111
|
}
|
|
113
112
|
showProgress(quiet, json, message) {
|
|
114
113
|
if (!quiet && !json) {
|
|
115
|
-
|
|
114
|
+
ux.action.status = message;
|
|
116
115
|
}
|
|
117
116
|
}
|
|
118
117
|
}
|
|
119
|
-
exports.MoropoService = MoropoService;
|
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
exports.ReportDownloadService = void 0;
|
|
4
|
-
const path = require("node:path");
|
|
5
|
-
const api_gateway_1 = require("../gateways/api-gateway");
|
|
1
|
+
import * as path from 'node:path';
|
|
2
|
+
import { ApiGateway } from '../gateways/api-gateway.js';
|
|
6
3
|
/**
|
|
7
4
|
* Service for downloading test artifacts and reports
|
|
8
5
|
*/
|
|
9
|
-
class ReportDownloadService {
|
|
6
|
+
export class ReportDownloadService {
|
|
10
7
|
/**
|
|
11
8
|
* Download test artifacts as a zip file
|
|
12
9
|
* @param options Download configuration
|
|
@@ -18,7 +15,7 @@ class ReportDownloadService {
|
|
|
18
15
|
if (debug && logger) {
|
|
19
16
|
logger(`[DEBUG] Downloading artifacts: ${downloadType}`);
|
|
20
17
|
}
|
|
21
|
-
await
|
|
18
|
+
await ApiGateway.downloadArtifactsZip(apiUrl, auth, uploadId, downloadType, artifactsPath);
|
|
22
19
|
if (logger) {
|
|
23
20
|
logger('\n');
|
|
24
21
|
logger(`Test artifacts have been downloaded to ${artifactsPath}`);
|
|
@@ -84,7 +81,7 @@ class ReportDownloadService {
|
|
|
84
81
|
if (debug && logger) {
|
|
85
82
|
logger(`[DEBUG] Downloading ${type.toUpperCase()} report`);
|
|
86
83
|
}
|
|
87
|
-
await
|
|
84
|
+
await ApiGateway.downloadReportGeneric(apiUrl, auth, uploadId, type, filePath);
|
|
88
85
|
if (logger) {
|
|
89
86
|
logger(`${type.toUpperCase()} test report has been downloaded to ${filePath}`);
|
|
90
87
|
}
|
|
@@ -122,4 +119,3 @@ class ReportDownloadService {
|
|
|
122
119
|
}
|
|
123
120
|
}
|
|
124
121
|
}
|
|
125
|
-
exports.ReportDownloadService = ReportDownloadService;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AuthContext } from '../types/domain/auth.types';
|
|
1
|
+
import type { AuthContext } from '../types/domain/auth.types.js';
|
|
2
2
|
/**
|
|
3
3
|
* Custom error for run failures that includes the polling result
|
|
4
4
|
*/
|
|
@@ -46,7 +46,9 @@ export interface PollingResult {
|
|
|
46
46
|
*/
|
|
47
47
|
export declare class ResultsPollingService {
|
|
48
48
|
private readonly MAX_SEQUENTIAL_FAILURES;
|
|
49
|
-
private readonly
|
|
49
|
+
private readonly BEARER_POLL_INTERVAL_MS;
|
|
50
|
+
private readonly APIKEY_POLL_INTERVAL_MS;
|
|
51
|
+
private readonly ERROR_BACKOFF_BASE_MS;
|
|
50
52
|
private readonly MAX_ERROR_BACKOFF_MS;
|
|
51
53
|
/**
|
|
52
54
|
* Poll for test results until all tests complete
|
|
@@ -91,5 +93,18 @@ export declare class ResultsPollingService {
|
|
|
91
93
|
* @returns Promise that resolves after the delay
|
|
92
94
|
*/
|
|
93
95
|
private sleep;
|
|
94
|
-
|
|
96
|
+
/**
|
|
97
|
+
* Build the body of the live status display (the per-test table, or just the
|
|
98
|
+
* one-line summary in quiet mode). The footer (countdown + realtime state) is
|
|
99
|
+
* appended separately by {@link buildStatusFooter} so it can re-render on a
|
|
100
|
+
* timer without re-fetching.
|
|
101
|
+
*/
|
|
102
|
+
private buildStatusBody;
|
|
103
|
+
/**
|
|
104
|
+
* Build the live footer shown under the status display: whether realtime
|
|
105
|
+
* updates are connected (for logged-in users) and how long until the next
|
|
106
|
+
* backstop poll. While a fetch is in flight (`nextPollAt` is null) the
|
|
107
|
+
* countdown reads "refreshing…".
|
|
108
|
+
*/
|
|
109
|
+
private buildStatusFooter;
|
|
95
110
|
}
|