@opentap/runner-client 4.2.0 → 4.3.0-beta.3.32232370336
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/BaseClient.js +70 -26
- package/dist/cjs/DTOs.d.ts +20 -2
- package/dist/cjs/DTOs.js +58 -1
- package/dist/cjs/RunnerClient.d.ts +6 -0
- package/dist/cjs/RunnerClient.js +10 -0
- package/dist/cjs/SessionClient.js +2 -0
- package/dist/mjs/BaseClient.js +68 -26
- package/dist/mjs/DTOs.d.ts +20 -2
- package/dist/mjs/DTOs.js +49 -0
- package/dist/mjs/RunnerClient.d.ts +6 -0
- package/dist/mjs/RunnerClient.js +10 -0
- package/dist/mjs/SessionClient.js +3 -1
- package/package.json +1 -1
package/dist/cjs/BaseClient.js
CHANGED
|
@@ -52,6 +52,28 @@ var nats_ws_1 = require("nats.ws");
|
|
|
52
52
|
var DTOs_1 = require("./DTOs");
|
|
53
53
|
var events_1 = require("events");
|
|
54
54
|
var uuid_1 = require("uuid");
|
|
55
|
+
// A browser extension like CrowdStrike may have already replaced globalThis.WebSocket before this module loads,
|
|
56
|
+
// so reading the global is unreliable. A freshly created same-origin iframe exposes the browser's
|
|
57
|
+
// pristine, un-patched WebSocket; NATS traffic is pinned to it to bypass any extension shim.
|
|
58
|
+
// The iframe is kept attached because a detached iframe's realm becomes unusable in some browsers.
|
|
59
|
+
function getNativeWebSocket() {
|
|
60
|
+
var _a, _b, _c;
|
|
61
|
+
var globalWebSocket = typeof globalThis !== 'undefined' ? globalThis.WebSocket : undefined;
|
|
62
|
+
var doc = globalThis.document;
|
|
63
|
+
if (!doc || typeof doc.createElement !== 'function') {
|
|
64
|
+
return globalWebSocket;
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
var iframe = doc.createElement('iframe');
|
|
68
|
+
iframe.style.display = 'none';
|
|
69
|
+
((_a = doc.body) !== null && _a !== void 0 ? _a : doc.documentElement).appendChild(iframe);
|
|
70
|
+
return (_c = (_b = iframe.contentWindow) === null || _b === void 0 ? void 0 : _b.WebSocket) !== null && _c !== void 0 ? _c : globalWebSocket;
|
|
71
|
+
}
|
|
72
|
+
catch (_d) {
|
|
73
|
+
return globalWebSocket;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
var OriginalWebSocket = getNativeWebSocket();
|
|
55
77
|
var DEFAULT_TIMEOUT = 40000; // default timeout of 40 seconds
|
|
56
78
|
var Events;
|
|
57
79
|
(function (Events) {
|
|
@@ -64,6 +86,15 @@ var BaseClient = /** @class */ (function () {
|
|
|
64
86
|
this.baseSubject = baseSubject;
|
|
65
87
|
this.connectionOptions = __assign({}, options);
|
|
66
88
|
this.connectionOptions.timeout = (options === null || options === void 0 ? void 0 : options.timeout) || DEFAULT_TIMEOUT;
|
|
89
|
+
// Force NATS to use the WebSocket captured at load time so an extension that later
|
|
90
|
+
// overrides globalThis.WebSocket cannot intercept NATS traffic. Respect a caller-provided factory.
|
|
91
|
+
// wsFactory is read by nats.ws's WsTransport at runtime but lives on WsConnectionOptions, which
|
|
92
|
+
// isn't re-exported, so it's absent from the public ConnectionOptions type and requires this cast.
|
|
93
|
+
if (OriginalWebSocket && !this.connectionOptions.wsFactory) {
|
|
94
|
+
this.connectionOptions.wsFactory = function (url) {
|
|
95
|
+
return Promise.resolve({ socket: new OriginalWebSocket(url), encrypted: url.indexOf('wss://') === 0 });
|
|
96
|
+
};
|
|
97
|
+
}
|
|
67
98
|
this.eventEmitter = new events_1.EventEmitter();
|
|
68
99
|
}
|
|
69
100
|
Object.defineProperty(BaseClient.prototype, "accessToken", {
|
|
@@ -157,7 +188,14 @@ var BaseClient = /** @class */ (function () {
|
|
|
157
188
|
};
|
|
158
189
|
subscription = this.connection.subscribe(replySubject);
|
|
159
190
|
responsePromise = new Promise(function (resolve, reject) {
|
|
191
|
+
// Chunk payloads stored at their sequence index so order is preserved
|
|
192
|
+
// regardless of the order in which messages arrive.
|
|
160
193
|
var messages = [];
|
|
194
|
+
var receivedCount = 0;
|
|
195
|
+
// Total number of chunks and the size of the final chunk, both known
|
|
196
|
+
// once the final (smaller) chunk arrives.
|
|
197
|
+
var expectedCount;
|
|
198
|
+
var finalChunkLength = 0;
|
|
161
199
|
subscription.callback = function (error, message) {
|
|
162
200
|
var _a, _b, _c, _d;
|
|
163
201
|
if (error) {
|
|
@@ -173,44 +211,50 @@ var BaseClient = /** @class */ (function () {
|
|
|
173
211
|
subscription.unsubscribe();
|
|
174
212
|
return resolve({ byteArray: message.data, isErrorResponse: ((_c = message.headers) === null || _c === void 0 ? void 0 : _c.get('OpenTapNatsError')) === 'true' });
|
|
175
213
|
}
|
|
176
|
-
//
|
|
177
|
-
messages
|
|
178
|
-
|
|
179
|
-
|
|
214
|
+
// Place each chunk at its sequence position (ChunkNumber is 1-based).
|
|
215
|
+
messages[parseInt(chunkNumber) - 1] = message.data;
|
|
216
|
+
receivedCount += 1;
|
|
217
|
+
// The final chunk is the only one smaller than chunkSize; its sequence
|
|
218
|
+
// number tells us how many chunks to expect and its length fixes the total size.
|
|
219
|
+
if (message.data.length < chunkSize) {
|
|
220
|
+
expectedCount = parseInt(chunkNumber);
|
|
221
|
+
finalChunkLength = message.data.length;
|
|
222
|
+
}
|
|
223
|
+
// Keep waiting until every expected chunk has been received.
|
|
224
|
+
if (expectedCount === undefined || receivedCount < expectedCount) {
|
|
180
225
|
return;
|
|
181
226
|
}
|
|
182
|
-
//
|
|
183
|
-
// If the final message was received, we can safely unsubscribe
|
|
227
|
+
// All expected chunks received; we can safely unsubscribe.
|
|
184
228
|
subscription.unsubscribe();
|
|
185
|
-
//
|
|
186
|
-
//
|
|
187
|
-
|
|
188
|
-
if (parseInt(chunkNumber) !== messages.length) {
|
|
189
|
-
return reject(Error("Expected {finalMessageNumber} chunks, but received ".concat(messages.length, ". ") +
|
|
190
|
-
"The connection may have been interrupted."));
|
|
191
|
-
}
|
|
192
|
-
// Concatenate the payloads
|
|
193
|
-
// When there are many chunks, doing a single allocation
|
|
194
|
-
// is significantly faster than concatenating arrays in sequence
|
|
195
|
-
var flattenedSize = messages.reduce(function (sum, array) { return sum + array.data.length; }, 0);
|
|
229
|
+
// Every chunk before the last is exactly chunkSize, so the total size is
|
|
230
|
+
// deterministic and each chunk's offset is simply its index * chunkSize.
|
|
231
|
+
var flattenedSize = (expectedCount - 1) * chunkSize + finalChunkLength;
|
|
196
232
|
var flattenedArray = new Uint8Array(flattenedSize);
|
|
197
|
-
var
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
233
|
+
for (var i = 0; i < expectedCount; i++) {
|
|
234
|
+
var chunk_1 = messages[i];
|
|
235
|
+
// A missing chunk means we dropped one, likely due to a network error,
|
|
236
|
+
// so the entire message is invalid.
|
|
237
|
+
if (!chunk_1) {
|
|
238
|
+
return reject(Error("Expected ".concat(expectedCount, " chunks, but chunk ").concat(i + 1, " is missing. ") +
|
|
239
|
+
"The connection may have been interrupted."));
|
|
201
240
|
}
|
|
202
|
-
|
|
241
|
+
flattenedArray.set(chunk_1, i * chunkSize);
|
|
242
|
+
}
|
|
203
243
|
return resolve({ byteArray: flattenedArray, isErrorResponse: ((_d = message.headers) === null || _d === void 0 ? void 0 : _d.get('OpenTapNatsError')) === 'true' });
|
|
204
244
|
};
|
|
205
245
|
}).then(function (_a) {
|
|
206
246
|
var byteArray = _a.byteArray, isErrorResponse = _a.isErrorResponse;
|
|
247
|
+
var jsonCodec = (0, nats_ws_1.JSONCodec)();
|
|
248
|
+
if (isErrorResponse) {
|
|
249
|
+
return Promise.reject(DTOs_1.ErrorResponse.fromJS(jsonCodec.decode(byteArray)));
|
|
250
|
+
}
|
|
207
251
|
if (byteArray.length === 0) {
|
|
208
252
|
return Promise.resolve(undefined);
|
|
209
253
|
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
return
|
|
254
|
+
if (options === null || options === void 0 ? void 0 : options.rawResponse) {
|
|
255
|
+
return Promise.resolve(byteArray);
|
|
256
|
+
}
|
|
257
|
+
return Promise.resolve(jsonCodec.decode(byteArray));
|
|
214
258
|
});
|
|
215
259
|
chunk = getChunk(0);
|
|
216
260
|
this.connection.publish(subject, chunk, opts);
|
package/dist/cjs/DTOs.d.ts
CHANGED
|
@@ -876,7 +876,8 @@ export declare enum SessionState {
|
|
|
876
876
|
Breaking = "Breaking",
|
|
877
877
|
Aborting = "Aborting",
|
|
878
878
|
WaitingForUserInput = "WaitingForUserInput",
|
|
879
|
-
Loading = "Loading"
|
|
879
|
+
Loading = "Loading",
|
|
880
|
+
Restarting = "Restarting"
|
|
880
881
|
}
|
|
881
882
|
export declare class TestPlan implements ITestPlan {
|
|
882
883
|
childTestSteps?: TestStep[] | undefined;
|
|
@@ -1207,7 +1208,9 @@ export declare enum SessionEventName {
|
|
|
1207
1208
|
TestStepChanged = "TestStepChanged",
|
|
1208
1209
|
Break = "Break",
|
|
1209
1210
|
UserInputRequested = "UserInputRequested",
|
|
1210
|
-
UserInputRequestCompleted = "UserInputRequestCompleted"
|
|
1211
|
+
UserInputRequestCompleted = "UserInputRequestCompleted",
|
|
1212
|
+
RestartInitiated = "RestartInitiated",
|
|
1213
|
+
RestartCompleted = "RestartCompleted"
|
|
1211
1214
|
}
|
|
1212
1215
|
export declare abstract class SessionEvent {
|
|
1213
1216
|
readonly eventName: SessionEventName;
|
|
@@ -1315,6 +1318,21 @@ export declare class UserInputRequestCompletedEvent extends SessionEvent {
|
|
|
1315
1318
|
static fromJS(data: any): UserInputRequestCompletedEvent;
|
|
1316
1319
|
toJSON(data?: any): any;
|
|
1317
1320
|
}
|
|
1321
|
+
export declare class RestartInitiatedEvent extends SessionEvent {
|
|
1322
|
+
oldProcessId?: number;
|
|
1323
|
+
constructor(data?: Partial<RestartInitiatedEvent>);
|
|
1324
|
+
init(_data?: any): void;
|
|
1325
|
+
static fromJS(data: any): RestartInitiatedEvent;
|
|
1326
|
+
toJSON(data?: any): any;
|
|
1327
|
+
}
|
|
1328
|
+
export declare class RestartCompletedEvent extends SessionEvent {
|
|
1329
|
+
success?: boolean;
|
|
1330
|
+
newProcessId?: number;
|
|
1331
|
+
constructor(data?: Partial<RestartCompletedEvent>);
|
|
1332
|
+
init(_data?: any): void;
|
|
1333
|
+
static fromJS(data: any): RestartCompletedEvent;
|
|
1334
|
+
toJSON(data?: any): any;
|
|
1335
|
+
}
|
|
1318
1336
|
export type ISessionEvent = SessionEvent;
|
|
1319
1337
|
export interface IOnTestPlanRun {
|
|
1320
1338
|
status?: string;
|
package/dist/cjs/DTOs.js
CHANGED
|
@@ -17,7 +17,7 @@ var __extends = (this && this.__extends) || (function () {
|
|
|
17
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
18
|
exports.SettingsTapPackage = exports.SaveTestPlanToRepositoryResponse = exports.GetTestPlanReferenceResponse = exports.RepositorySettingsPackageDefinition = exports.RepositoryPackageDefinition = exports.RepositoryPackageReference = exports.ProfileGroup = exports.ListItemType = exports.ComponentSettings = exports.FileDescriptor = exports.Resource = exports.PluginTypeSelectorControl = exports.PictureControl = exports.PasswordControl = exports.MultiSelectControl = exports.DropdownControl = exports.EnabledControl = exports.DirectoryPathControl = exports.FilePathControl = exports.ComboBoxControl = exports.TextBoxControl = exports.DataGridControl = exports.DataGridReferenceControl = exports.CheckBoxControl = exports.AvailableValue = exports.ButtonsControl = exports.ButtonControl = exports.ExternalParameter = exports.MetaData = exports.DisplayAttribute = exports.VisualStatus = exports.UnitAttribute = exports.Icon = exports.ColumnDisplayName = exports.LayoutMode = exports.Layout = exports.Setting = exports.ComponentSettingsListItem = exports.AnnotatedObject = exports.ComponentSettingsList = exports.DisplayMode = exports.ComponentSettingsIdentifier = exports.ComponentSettingsBase = exports.Links = exports.Session = exports.ImageResolveErrorResponse = exports.NoResponderError = exports.ErrorResponse = exports.PackageSpecifier = exports.Image = void 0;
|
|
19
19
|
exports.StartedEvent = exports.StartingEvent = exports.SessionTimeoutEvent = exports.TestPlanSettingsChangedEvent = exports.TypeCacheInvalidatedEvent = exports.SessionEvent = exports.SessionEventName = exports.MetricValue = exports.MetricsConfiguration = exports.SessionMetricInfo = exports.MetricsPollRate = exports.SessionMetricType = exports.SessionMetricKind = exports.ResultColumn = exports.Result = exports.LogEvent = exports.TestStepRun = exports.WatchDog = exports.Parameter = exports.BreakPoints = exports.InstalledFile = exports.Interaction = exports.CommonContext = exports.CommonSettings = exports.ValidationError = exports.TestStepValidationError = exports.TestStepCopy = exports.TestStepType = exports.TestStep = exports.TestPlan = exports.SessionState = exports.Verdict = exports.EditStatus = exports.RunStatus = exports.MissingLicenseResponse = exports.LogEntry = exports.LogList = exports.ApiException = exports.ImageCreationFailed = exports.ImageCreated = exports.ImageCreating = exports.ImageEventArgs = exports.SessionInactivityLimitHit = exports.SessionShutdownFailed = exports.SessionShutdown = exports.SessionShutdownInitiated = exports.SessionStartFailed = exports.SessionStarted = exports.SessionStartInitiated = exports.SessionEventArgs = void 0;
|
|
20
|
-
exports.RunnerStatus = exports.StreamInfoStatus = exports.RemoteConnection = exports.SaveDefaultSettings = exports.AssetUpdatedEvent = exports.MetadataUpdatedEvent = exports.RunnerDeletedEvent = exports.RunnerUpdatedEvent = exports.RunnerRegisteredEvent = exports.RunnerEvent = exports.TestPlanRunCompletedEvent = exports.TestPlanRunStartEvent = exports.TestPlanRunEvent = exports.OnTestStepRun = exports.OnTestPlanRun = exports.UserInputRequestCompletedEvent = exports.UserInputRequestedEvent = exports.BreakEvent = exports.TestStepChangedEvent = exports.SettingsChangedEvent = exports.HeartbeatEvent = exports.SessionStateChangedEvent = exports.TestPlanReferenceChangedEvent = exports.TestPlanChangedEvent = exports.StoppedEvent = exports.StoppingEvent = void 0;
|
|
20
|
+
exports.RunnerStatus = exports.StreamInfoStatus = exports.RemoteConnection = exports.SaveDefaultSettings = exports.AssetUpdatedEvent = exports.MetadataUpdatedEvent = exports.RunnerDeletedEvent = exports.RunnerUpdatedEvent = exports.RunnerRegisteredEvent = exports.RunnerEvent = exports.TestPlanRunCompletedEvent = exports.TestPlanRunStartEvent = exports.TestPlanRunEvent = exports.OnTestStepRun = exports.OnTestPlanRun = exports.RestartCompletedEvent = exports.RestartInitiatedEvent = exports.UserInputRequestCompletedEvent = exports.UserInputRequestedEvent = exports.BreakEvent = exports.TestStepChangedEvent = exports.SettingsChangedEvent = exports.HeartbeatEvent = exports.SessionStateChangedEvent = exports.TestPlanReferenceChangedEvent = exports.TestPlanChangedEvent = exports.StoppedEvent = exports.StoppingEvent = void 0;
|
|
21
21
|
var Image = /** @class */ (function () {
|
|
22
22
|
function Image(data) {
|
|
23
23
|
this.packages = [];
|
|
@@ -2444,6 +2444,7 @@ var SessionState;
|
|
|
2444
2444
|
SessionState["Aborting"] = "Aborting";
|
|
2445
2445
|
SessionState["WaitingForUserInput"] = "WaitingForUserInput";
|
|
2446
2446
|
SessionState["Loading"] = "Loading";
|
|
2447
|
+
SessionState["Restarting"] = "Restarting";
|
|
2447
2448
|
})(SessionState || (exports.SessionState = SessionState = {}));
|
|
2448
2449
|
var TestPlan = /** @class */ (function () {
|
|
2449
2450
|
function TestPlan(data) {
|
|
@@ -3326,6 +3327,8 @@ var SessionEventName;
|
|
|
3326
3327
|
SessionEventName["Break"] = "Break";
|
|
3327
3328
|
SessionEventName["UserInputRequested"] = "UserInputRequested";
|
|
3328
3329
|
SessionEventName["UserInputRequestCompleted"] = "UserInputRequestCompleted";
|
|
3330
|
+
SessionEventName["RestartInitiated"] = "RestartInitiated";
|
|
3331
|
+
SessionEventName["RestartCompleted"] = "RestartCompleted";
|
|
3329
3332
|
})(SessionEventName || (exports.SessionEventName = SessionEventName = {}));
|
|
3330
3333
|
var SessionEvent = /** @class */ (function () {
|
|
3331
3334
|
function SessionEvent(eventName) {
|
|
@@ -3659,6 +3662,60 @@ var UserInputRequestCompletedEvent = /** @class */ (function (_super) {
|
|
|
3659
3662
|
return UserInputRequestCompletedEvent;
|
|
3660
3663
|
}(SessionEvent));
|
|
3661
3664
|
exports.UserInputRequestCompletedEvent = UserInputRequestCompletedEvent;
|
|
3665
|
+
var RestartInitiatedEvent = /** @class */ (function (_super) {
|
|
3666
|
+
__extends(RestartInitiatedEvent, _super);
|
|
3667
|
+
function RestartInitiatedEvent(data) {
|
|
3668
|
+
var _this = _super.call(this, SessionEventName.RestartInitiated) || this;
|
|
3669
|
+
if (data) {
|
|
3670
|
+
Object.assign(_this, data);
|
|
3671
|
+
}
|
|
3672
|
+
return _this;
|
|
3673
|
+
}
|
|
3674
|
+
RestartInitiatedEvent.prototype.init = function (_data) {
|
|
3675
|
+
var data = ensureObject(_data);
|
|
3676
|
+
this.oldProcessId = data['OldProcessId'];
|
|
3677
|
+
};
|
|
3678
|
+
RestartInitiatedEvent.fromJS = function (data) {
|
|
3679
|
+
var result = new RestartInitiatedEvent();
|
|
3680
|
+
result.init(data);
|
|
3681
|
+
return result;
|
|
3682
|
+
};
|
|
3683
|
+
RestartInitiatedEvent.prototype.toJSON = function (data) {
|
|
3684
|
+
var json = ensureObject(data);
|
|
3685
|
+
json['OldProcessId'] = this.oldProcessId;
|
|
3686
|
+
return json;
|
|
3687
|
+
};
|
|
3688
|
+
return RestartInitiatedEvent;
|
|
3689
|
+
}(SessionEvent));
|
|
3690
|
+
exports.RestartInitiatedEvent = RestartInitiatedEvent;
|
|
3691
|
+
var RestartCompletedEvent = /** @class */ (function (_super) {
|
|
3692
|
+
__extends(RestartCompletedEvent, _super);
|
|
3693
|
+
function RestartCompletedEvent(data) {
|
|
3694
|
+
var _this = _super.call(this, SessionEventName.RestartCompleted) || this;
|
|
3695
|
+
if (data) {
|
|
3696
|
+
Object.assign(_this, data);
|
|
3697
|
+
}
|
|
3698
|
+
return _this;
|
|
3699
|
+
}
|
|
3700
|
+
RestartCompletedEvent.prototype.init = function (_data) {
|
|
3701
|
+
var data = ensureObject(_data);
|
|
3702
|
+
this.success = data['Success'];
|
|
3703
|
+
this.newProcessId = data['NewProcessId'];
|
|
3704
|
+
};
|
|
3705
|
+
RestartCompletedEvent.fromJS = function (data) {
|
|
3706
|
+
var result = new RestartCompletedEvent();
|
|
3707
|
+
result.init(data);
|
|
3708
|
+
return result;
|
|
3709
|
+
};
|
|
3710
|
+
RestartCompletedEvent.prototype.toJSON = function (data) {
|
|
3711
|
+
var json = ensureObject(data);
|
|
3712
|
+
json['Success'] = this.success;
|
|
3713
|
+
json['NewProcessId'] = this.newProcessId;
|
|
3714
|
+
return json;
|
|
3715
|
+
};
|
|
3716
|
+
return RestartCompletedEvent;
|
|
3717
|
+
}(SessionEvent));
|
|
3718
|
+
exports.RestartCompletedEvent = RestartCompletedEvent;
|
|
3662
3719
|
var OnTestPlanRun = /** @class */ (function () {
|
|
3663
3720
|
function OnTestPlanRun(data) {
|
|
3664
3721
|
if (data) {
|
|
@@ -58,6 +58,12 @@ export declare class RunnerClient extends BaseClient {
|
|
|
58
58
|
* @returns {{Promise<void>}}
|
|
59
59
|
*/
|
|
60
60
|
shutdownSession(sessionId: string, force?: boolean): Promise<void>;
|
|
61
|
+
/**
|
|
62
|
+
* Restart a session
|
|
63
|
+
* @param sessionId the ID of the session to restart
|
|
64
|
+
* @returns {{Promise<void>}}
|
|
65
|
+
*/
|
|
66
|
+
restartSession(sessionId: string): Promise<void>;
|
|
61
67
|
/**
|
|
62
68
|
* Start a session
|
|
63
69
|
* @returns {{Promise<Session>}}
|
package/dist/cjs/RunnerClient.js
CHANGED
|
@@ -232,6 +232,16 @@ var RunnerClient = /** @class */ (function (_super) {
|
|
|
232
232
|
.catch(this.error())
|
|
233
233
|
: Promise.reject('sessionId is not defined');
|
|
234
234
|
};
|
|
235
|
+
/**
|
|
236
|
+
* Restart a session
|
|
237
|
+
* @param sessionId the ID of the session to restart
|
|
238
|
+
* @returns {{Promise<void>}}
|
|
239
|
+
*/
|
|
240
|
+
RunnerClient.prototype.restartSession = function (sessionId) {
|
|
241
|
+
return (sessionId === null || sessionId === void 0 ? void 0 : sessionId.length) > 0
|
|
242
|
+
? this.request('RestartSession', { SessionId: sessionId }).then(this.success()).catch(this.error())
|
|
243
|
+
: Promise.reject('sessionId is not defined');
|
|
244
|
+
};
|
|
235
245
|
/**
|
|
236
246
|
* Start a session
|
|
237
247
|
* @returns {{Promise<Session>}}
|
|
@@ -847,6 +847,8 @@ var SessionClient = /** @class */ (function (_super) {
|
|
|
847
847
|
_a[DTOs_1.SessionEventName.Break] = function (payload) { return DTOs_1.BreakEvent.fromJS(payload); },
|
|
848
848
|
_a[DTOs_1.SessionEventName.UserInputRequested] = function (payload) { return DTOs_1.UserInputRequestedEvent.fromJS(payload); },
|
|
849
849
|
_a[DTOs_1.SessionEventName.UserInputRequestCompleted] = function (payload) { return DTOs_1.UserInputRequestCompletedEvent.fromJS(payload); },
|
|
850
|
+
_a[DTOs_1.SessionEventName.RestartInitiated] = function (payload) { return DTOs_1.RestartInitiatedEvent.fromJS(payload); },
|
|
851
|
+
_a[DTOs_1.SessionEventName.RestartCompleted] = function (payload) { return DTOs_1.RestartCompletedEvent.fromJS(payload); },
|
|
850
852
|
_a);
|
|
851
853
|
return SessionClient;
|
|
852
854
|
}(BaseClient_1.BaseClient));
|
package/dist/mjs/BaseClient.js
CHANGED
|
@@ -11,6 +11,28 @@ import { AckPolicy, Empty, ErrorCode, JSONCodec, StringCodec, connect, headers,
|
|
|
11
11
|
import { ComponentSettingsBase, ComponentSettingsIdentifier, ComponentSettingsListItem, DataGridControl, ErrorResponse, FileDescriptor, ListItemType, NoResponderError, ProfileGroup, } from './DTOs';
|
|
12
12
|
import { EventEmitter } from 'events';
|
|
13
13
|
import { v4 as uuidv4 } from 'uuid';
|
|
14
|
+
// A browser extension like CrowdStrike may have already replaced globalThis.WebSocket before this module loads,
|
|
15
|
+
// so reading the global is unreliable. A freshly created same-origin iframe exposes the browser's
|
|
16
|
+
// pristine, un-patched WebSocket; NATS traffic is pinned to it to bypass any extension shim.
|
|
17
|
+
// The iframe is kept attached because a detached iframe's realm becomes unusable in some browsers.
|
|
18
|
+
function getNativeWebSocket() {
|
|
19
|
+
var _a, _b, _c;
|
|
20
|
+
const globalWebSocket = typeof globalThis !== 'undefined' ? globalThis.WebSocket : undefined;
|
|
21
|
+
const doc = globalThis.document;
|
|
22
|
+
if (!doc || typeof doc.createElement !== 'function') {
|
|
23
|
+
return globalWebSocket;
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
const iframe = doc.createElement('iframe');
|
|
27
|
+
iframe.style.display = 'none';
|
|
28
|
+
((_a = doc.body) !== null && _a !== void 0 ? _a : doc.documentElement).appendChild(iframe);
|
|
29
|
+
return (_c = (_b = iframe.contentWindow) === null || _b === void 0 ? void 0 : _b.WebSocket) !== null && _c !== void 0 ? _c : globalWebSocket;
|
|
30
|
+
}
|
|
31
|
+
catch (_d) {
|
|
32
|
+
return globalWebSocket;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const OriginalWebSocket = getNativeWebSocket();
|
|
14
36
|
const DEFAULT_TIMEOUT = 40000; // default timeout of 40 seconds
|
|
15
37
|
var Events;
|
|
16
38
|
(function (Events) {
|
|
@@ -47,6 +69,13 @@ export class BaseClient {
|
|
|
47
69
|
this.baseSubject = baseSubject;
|
|
48
70
|
this.connectionOptions = Object.assign({}, options);
|
|
49
71
|
this.connectionOptions.timeout = (options === null || options === void 0 ? void 0 : options.timeout) || DEFAULT_TIMEOUT;
|
|
72
|
+
// Force NATS to use the WebSocket captured at load time so an extension that later
|
|
73
|
+
// overrides globalThis.WebSocket cannot intercept NATS traffic. Respect a caller-provided factory.
|
|
74
|
+
// wsFactory is read by nats.ws's WsTransport at runtime but lives on WsConnectionOptions, which
|
|
75
|
+
// isn't re-exported, so it's absent from the public ConnectionOptions type and requires this cast.
|
|
76
|
+
if (OriginalWebSocket && !this.connectionOptions.wsFactory) {
|
|
77
|
+
this.connectionOptions.wsFactory = (url) => Promise.resolve({ socket: new OriginalWebSocket(url), encrypted: url.indexOf('wss://') === 0 });
|
|
78
|
+
}
|
|
50
79
|
this.eventEmitter = new EventEmitter();
|
|
51
80
|
}
|
|
52
81
|
withTimeout(promise, timeout, subject) {
|
|
@@ -109,7 +138,14 @@ export class BaseClient {
|
|
|
109
138
|
// The response from the session will arrive in this promise after we have finished
|
|
110
139
|
// publishing chunks
|
|
111
140
|
const responsePromise = new Promise((resolve, reject) => {
|
|
141
|
+
// Chunk payloads stored at their sequence index so order is preserved
|
|
142
|
+
// regardless of the order in which messages arrive.
|
|
112
143
|
const messages = [];
|
|
144
|
+
let receivedCount = 0;
|
|
145
|
+
// Total number of chunks and the size of the final chunk, both known
|
|
146
|
+
// once the final (smaller) chunk arrives.
|
|
147
|
+
let expectedCount;
|
|
148
|
+
let finalChunkLength = 0;
|
|
113
149
|
subscription.callback = (error, message) => {
|
|
114
150
|
var _a, _b, _c, _d;
|
|
115
151
|
if (error) {
|
|
@@ -125,43 +161,49 @@ export class BaseClient {
|
|
|
125
161
|
subscription.unsubscribe();
|
|
126
162
|
return resolve({ byteArray: message.data, isErrorResponse: ((_c = message.headers) === null || _c === void 0 ? void 0 : _c.get('OpenTapNatsError')) === 'true' });
|
|
127
163
|
}
|
|
128
|
-
//
|
|
129
|
-
messages
|
|
130
|
-
|
|
131
|
-
|
|
164
|
+
// Place each chunk at its sequence position (ChunkNumber is 1-based).
|
|
165
|
+
messages[parseInt(chunkNumber) - 1] = message.data;
|
|
166
|
+
receivedCount += 1;
|
|
167
|
+
// The final chunk is the only one smaller than chunkSize; its sequence
|
|
168
|
+
// number tells us how many chunks to expect and its length fixes the total size.
|
|
169
|
+
if (message.data.length < chunkSize) {
|
|
170
|
+
expectedCount = parseInt(chunkNumber);
|
|
171
|
+
finalChunkLength = message.data.length;
|
|
172
|
+
}
|
|
173
|
+
// Keep waiting until every expected chunk has been received.
|
|
174
|
+
if (expectedCount === undefined || receivedCount < expectedCount) {
|
|
132
175
|
return;
|
|
133
176
|
}
|
|
134
|
-
//
|
|
135
|
-
// If the final message was received, we can safely unsubscribe
|
|
177
|
+
// All expected chunks received; we can safely unsubscribe.
|
|
136
178
|
subscription.unsubscribe();
|
|
137
|
-
//
|
|
138
|
-
//
|
|
139
|
-
|
|
140
|
-
if (parseInt(chunkNumber) !== messages.length) {
|
|
141
|
-
return reject(Error(`Expected {finalMessageNumber} chunks, but received ${messages.length}. ` +
|
|
142
|
-
`The connection may have been interrupted.`));
|
|
143
|
-
}
|
|
144
|
-
// Concatenate the payloads
|
|
145
|
-
// When there are many chunks, doing a single allocation
|
|
146
|
-
// is significantly faster than concatenating arrays in sequence
|
|
147
|
-
const flattenedSize = messages.reduce((sum, array) => sum + array.data.length, 0);
|
|
179
|
+
// Every chunk before the last is exactly chunkSize, so the total size is
|
|
180
|
+
// deterministic and each chunk's offset is simply its index * chunkSize.
|
|
181
|
+
const flattenedSize = (expectedCount - 1) * chunkSize + finalChunkLength;
|
|
148
182
|
const flattenedArray = new Uint8Array(flattenedSize);
|
|
149
|
-
let
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
183
|
+
for (let i = 0; i < expectedCount; i++) {
|
|
184
|
+
const chunk = messages[i];
|
|
185
|
+
// A missing chunk means we dropped one, likely due to a network error,
|
|
186
|
+
// so the entire message is invalid.
|
|
187
|
+
if (!chunk) {
|
|
188
|
+
return reject(Error(`Expected ${expectedCount} chunks, but chunk ${i + 1} is missing. ` +
|
|
189
|
+
`The connection may have been interrupted.`));
|
|
153
190
|
}
|
|
154
|
-
|
|
191
|
+
flattenedArray.set(chunk, i * chunkSize);
|
|
192
|
+
}
|
|
155
193
|
return resolve({ byteArray: flattenedArray, isErrorResponse: ((_d = message.headers) === null || _d === void 0 ? void 0 : _d.get('OpenTapNatsError')) === 'true' });
|
|
156
194
|
};
|
|
157
195
|
}).then(({ byteArray, isErrorResponse }) => {
|
|
196
|
+
const jsonCodec = JSONCodec();
|
|
197
|
+
if (isErrorResponse) {
|
|
198
|
+
return Promise.reject(ErrorResponse.fromJS(jsonCodec.decode(byteArray)));
|
|
199
|
+
}
|
|
158
200
|
if (byteArray.length === 0) {
|
|
159
201
|
return Promise.resolve(undefined);
|
|
160
202
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
return
|
|
203
|
+
if (options === null || options === void 0 ? void 0 : options.rawResponse) {
|
|
204
|
+
return Promise.resolve(byteArray);
|
|
205
|
+
}
|
|
206
|
+
return Promise.resolve(jsonCodec.decode(byteArray));
|
|
165
207
|
});
|
|
166
208
|
// 'Manually' publish the first chunk. This is to ensure that **at least** one chunk is published.
|
|
167
209
|
// This is a special case for empty payloads (which are common)
|
package/dist/mjs/DTOs.d.ts
CHANGED
|
@@ -876,7 +876,8 @@ export declare enum SessionState {
|
|
|
876
876
|
Breaking = "Breaking",
|
|
877
877
|
Aborting = "Aborting",
|
|
878
878
|
WaitingForUserInput = "WaitingForUserInput",
|
|
879
|
-
Loading = "Loading"
|
|
879
|
+
Loading = "Loading",
|
|
880
|
+
Restarting = "Restarting"
|
|
880
881
|
}
|
|
881
882
|
export declare class TestPlan implements ITestPlan {
|
|
882
883
|
childTestSteps?: TestStep[] | undefined;
|
|
@@ -1207,7 +1208,9 @@ export declare enum SessionEventName {
|
|
|
1207
1208
|
TestStepChanged = "TestStepChanged",
|
|
1208
1209
|
Break = "Break",
|
|
1209
1210
|
UserInputRequested = "UserInputRequested",
|
|
1210
|
-
UserInputRequestCompleted = "UserInputRequestCompleted"
|
|
1211
|
+
UserInputRequestCompleted = "UserInputRequestCompleted",
|
|
1212
|
+
RestartInitiated = "RestartInitiated",
|
|
1213
|
+
RestartCompleted = "RestartCompleted"
|
|
1211
1214
|
}
|
|
1212
1215
|
export declare abstract class SessionEvent {
|
|
1213
1216
|
readonly eventName: SessionEventName;
|
|
@@ -1315,6 +1318,21 @@ export declare class UserInputRequestCompletedEvent extends SessionEvent {
|
|
|
1315
1318
|
static fromJS(data: any): UserInputRequestCompletedEvent;
|
|
1316
1319
|
toJSON(data?: any): any;
|
|
1317
1320
|
}
|
|
1321
|
+
export declare class RestartInitiatedEvent extends SessionEvent {
|
|
1322
|
+
oldProcessId?: number;
|
|
1323
|
+
constructor(data?: Partial<RestartInitiatedEvent>);
|
|
1324
|
+
init(_data?: any): void;
|
|
1325
|
+
static fromJS(data: any): RestartInitiatedEvent;
|
|
1326
|
+
toJSON(data?: any): any;
|
|
1327
|
+
}
|
|
1328
|
+
export declare class RestartCompletedEvent extends SessionEvent {
|
|
1329
|
+
success?: boolean;
|
|
1330
|
+
newProcessId?: number;
|
|
1331
|
+
constructor(data?: Partial<RestartCompletedEvent>);
|
|
1332
|
+
init(_data?: any): void;
|
|
1333
|
+
static fromJS(data: any): RestartCompletedEvent;
|
|
1334
|
+
toJSON(data?: any): any;
|
|
1335
|
+
}
|
|
1318
1336
|
export type ISessionEvent = SessionEvent;
|
|
1319
1337
|
export interface IOnTestPlanRun {
|
|
1320
1338
|
status?: string;
|
package/dist/mjs/DTOs.js
CHANGED
|
@@ -2143,6 +2143,7 @@ export var SessionState;
|
|
|
2143
2143
|
SessionState["Aborting"] = "Aborting";
|
|
2144
2144
|
SessionState["WaitingForUserInput"] = "WaitingForUserInput";
|
|
2145
2145
|
SessionState["Loading"] = "Loading";
|
|
2146
|
+
SessionState["Restarting"] = "Restarting";
|
|
2146
2147
|
})(SessionState || (SessionState = {}));
|
|
2147
2148
|
export class TestPlan {
|
|
2148
2149
|
constructor(data) {
|
|
@@ -2925,6 +2926,8 @@ export var SessionEventName;
|
|
|
2925
2926
|
SessionEventName["Break"] = "Break";
|
|
2926
2927
|
SessionEventName["UserInputRequested"] = "UserInputRequested";
|
|
2927
2928
|
SessionEventName["UserInputRequestCompleted"] = "UserInputRequestCompleted";
|
|
2929
|
+
SessionEventName["RestartInitiated"] = "RestartInitiated";
|
|
2930
|
+
SessionEventName["RestartCompleted"] = "RestartCompleted";
|
|
2928
2931
|
})(SessionEventName || (SessionEventName = {}));
|
|
2929
2932
|
export class SessionEvent {
|
|
2930
2933
|
constructor(eventName) {
|
|
@@ -3200,6 +3203,52 @@ export class UserInputRequestCompletedEvent extends SessionEvent {
|
|
|
3200
3203
|
return json;
|
|
3201
3204
|
}
|
|
3202
3205
|
}
|
|
3206
|
+
export class RestartInitiatedEvent extends SessionEvent {
|
|
3207
|
+
constructor(data) {
|
|
3208
|
+
super(SessionEventName.RestartInitiated);
|
|
3209
|
+
if (data) {
|
|
3210
|
+
Object.assign(this, data);
|
|
3211
|
+
}
|
|
3212
|
+
}
|
|
3213
|
+
init(_data) {
|
|
3214
|
+
const data = ensureObject(_data);
|
|
3215
|
+
this.oldProcessId = data['OldProcessId'];
|
|
3216
|
+
}
|
|
3217
|
+
static fromJS(data) {
|
|
3218
|
+
const result = new RestartInitiatedEvent();
|
|
3219
|
+
result.init(data);
|
|
3220
|
+
return result;
|
|
3221
|
+
}
|
|
3222
|
+
toJSON(data) {
|
|
3223
|
+
const json = ensureObject(data);
|
|
3224
|
+
json['OldProcessId'] = this.oldProcessId;
|
|
3225
|
+
return json;
|
|
3226
|
+
}
|
|
3227
|
+
}
|
|
3228
|
+
export class RestartCompletedEvent extends SessionEvent {
|
|
3229
|
+
constructor(data) {
|
|
3230
|
+
super(SessionEventName.RestartCompleted);
|
|
3231
|
+
if (data) {
|
|
3232
|
+
Object.assign(this, data);
|
|
3233
|
+
}
|
|
3234
|
+
}
|
|
3235
|
+
init(_data) {
|
|
3236
|
+
const data = ensureObject(_data);
|
|
3237
|
+
this.success = data['Success'];
|
|
3238
|
+
this.newProcessId = data['NewProcessId'];
|
|
3239
|
+
}
|
|
3240
|
+
static fromJS(data) {
|
|
3241
|
+
const result = new RestartCompletedEvent();
|
|
3242
|
+
result.init(data);
|
|
3243
|
+
return result;
|
|
3244
|
+
}
|
|
3245
|
+
toJSON(data) {
|
|
3246
|
+
const json = ensureObject(data);
|
|
3247
|
+
json['Success'] = this.success;
|
|
3248
|
+
json['NewProcessId'] = this.newProcessId;
|
|
3249
|
+
return json;
|
|
3250
|
+
}
|
|
3251
|
+
}
|
|
3203
3252
|
export class OnTestPlanRun {
|
|
3204
3253
|
constructor(data) {
|
|
3205
3254
|
if (data) {
|
|
@@ -58,6 +58,12 @@ export declare class RunnerClient extends BaseClient {
|
|
|
58
58
|
* @returns {{Promise<void>}}
|
|
59
59
|
*/
|
|
60
60
|
shutdownSession(sessionId: string, force?: boolean): Promise<void>;
|
|
61
|
+
/**
|
|
62
|
+
* Restart a session
|
|
63
|
+
* @param sessionId the ID of the session to restart
|
|
64
|
+
* @returns {{Promise<void>}}
|
|
65
|
+
*/
|
|
66
|
+
restartSession(sessionId: string): Promise<void>;
|
|
61
67
|
/**
|
|
62
68
|
* Start a session
|
|
63
69
|
* @returns {{Promise<Session>}}
|
package/dist/mjs/RunnerClient.js
CHANGED
|
@@ -171,6 +171,16 @@ export class RunnerClient extends BaseClient {
|
|
|
171
171
|
.catch(this.error())
|
|
172
172
|
: Promise.reject('sessionId is not defined');
|
|
173
173
|
}
|
|
174
|
+
/**
|
|
175
|
+
* Restart a session
|
|
176
|
+
* @param sessionId the ID of the session to restart
|
|
177
|
+
* @returns {{Promise<void>}}
|
|
178
|
+
*/
|
|
179
|
+
restartSession(sessionId) {
|
|
180
|
+
return (sessionId === null || sessionId === void 0 ? void 0 : sessionId.length) > 0
|
|
181
|
+
? this.request('RestartSession', { SessionId: sessionId }).then(this.success()).catch(this.error())
|
|
182
|
+
: Promise.reject('sessionId is not defined');
|
|
183
|
+
}
|
|
174
184
|
/**
|
|
175
185
|
* Start a session
|
|
176
186
|
* @returns {{Promise<Session>}}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BreakEvent, BreakPoints, CommonContext, CommonSettings, DataGridControl, GetTestPlanReferenceResponse, HeartbeatEvent, Image, Interaction, ListItemType, LogList, MetricsConfiguration, OnTestPlanRun, OnTestStepRun, Result, RunStatus, SaveTestPlanToRepositoryResponse, SessionEventName, SessionStateChangedEvent, SessionTimeoutEvent, Setting, SettingsChangedEvent, StartingEvent, StartedEvent, StoppedEvent, StoppingEvent, TestPlan, TestPlanChangedEvent, TestPlanReferenceChangedEvent, TestPlanSettingsChangedEvent, TestStepChangedEvent, TestStepType, TestStepValidationError, TypeCacheInvalidatedEvent, UserInputRequestCompletedEvent, UserInputRequestedEvent, WatchDog, EditStatus, } from './DTOs';
|
|
1
|
+
import { BreakEvent, BreakPoints, CommonContext, CommonSettings, DataGridControl, GetTestPlanReferenceResponse, HeartbeatEvent, Image, Interaction, ListItemType, LogList, MetricsConfiguration, OnTestPlanRun, OnTestStepRun, Result, RunStatus, SaveTestPlanToRepositoryResponse, SessionEventName, SessionStateChangedEvent, SessionTimeoutEvent, Setting, SettingsChangedEvent, StartingEvent, StartedEvent, StoppedEvent, StoppingEvent, TestPlan, TestPlanChangedEvent, TestPlanReferenceChangedEvent, TestPlanSettingsChangedEvent, TestStepChangedEvent, TestStepType, TestStepValidationError, TypeCacheInvalidatedEvent, UserInputRequestCompletedEvent, UserInputRequestedEvent, RestartInitiatedEvent, RestartCompletedEvent, WatchDog, EditStatus, } from './DTOs';
|
|
2
2
|
import { BaseClient } from './BaseClient';
|
|
3
3
|
import { jsonCodec } from './encoders';
|
|
4
4
|
import { NIL } from 'uuid';
|
|
@@ -808,4 +808,6 @@ SessionClient.sessionEventFactories = {
|
|
|
808
808
|
[SessionEventName.Break]: payload => BreakEvent.fromJS(payload),
|
|
809
809
|
[SessionEventName.UserInputRequested]: payload => UserInputRequestedEvent.fromJS(payload),
|
|
810
810
|
[SessionEventName.UserInputRequestCompleted]: payload => UserInputRequestCompletedEvent.fromJS(payload),
|
|
811
|
+
[SessionEventName.RestartInitiated]: payload => RestartInitiatedEvent.fromJS(payload),
|
|
812
|
+
[SessionEventName.RestartCompleted]: payload => RestartCompletedEvent.fromJS(payload),
|
|
811
813
|
};
|