Skip to content

Commit

Permalink
fix(client): fix logging dependency error
Browse files Browse the repository at this point in the history
  • Loading branch information
blakebyrnes committed Mar 31, 2021
1 parent 5d56597 commit 22900c4
Show file tree
Hide file tree
Showing 26 changed files with 184 additions and 142 deletions.
6 changes: 3 additions & 3 deletions client/connections/ConnectionToCore.ts
Expand Up @@ -181,15 +181,15 @@ export default abstract class ConnectionToCore extends TypedEventEmitter<{
return this.isDisconnecting === false && this.coreSessions.hasAvailability();
}

public async createSession(options: ICreateSessionOptions): Promise<CoreSession> {
public async createSession(options: ICreateSessionOptions): Promise<CoreSession | Error> {
try {
const sessionMeta = await this.commandQueue.run<ISessionMeta>('Session.create', options);
const session = new CoreSession({ ...sessionMeta, sessionName: options.sessionName }, this);
this.coreSessions.track(session);
return session;
} catch (error) {
if (error instanceof DisconnectedFromCoreError && this.isDisconnecting) return null;
throw error;
return error;
}
}

Expand Down Expand Up @@ -307,8 +307,8 @@ export default abstract class ConnectionToCore extends TypedEventEmitter<{
this.pendingRequestsById.delete(id);
this.rejectPendingRequest(entry, new DisconnectedFromCoreError(host));
}
this.coreSessions.stop(new DisconnectedFromCoreError(host));
this.commandQueue.stop(new DisconnectedFromCoreError(host));
this.coreSessions.stop(new DisconnectedFromCoreError(host));
}

private createPendingResult(): IResolvablePromiseWithId {
Expand Down
2 changes: 1 addition & 1 deletion client/connections/RemoteConnectionToCore.ts
Expand Up @@ -64,7 +64,7 @@ export default class RemoteConnectionToCore extends ConnectionToCore {
webSocket.once('close', this.internalDisconnect);
webSocket.once('error', this.internalDisconnect);
webSocket.on('message', message => {
const payload = TypeSerializer.parse(message.toString());
const payload = TypeSerializer.parse(message.toString(), 'REMOTE CORE');
this.onMessage(payload);
});
} catch (error) {
Expand Down
62 changes: 27 additions & 35 deletions client/lib/Agent.ts
Expand Up @@ -75,7 +75,7 @@ export default class Agent extends AwaitedEventTarget<{ close: void }> {
constructor(options: IAgentCreateOptions = {}) {
super(() => {
return {
target: getState(this).connection.coreSession,
target: getState(this).connection.getCoreSessionOrReject(),
};
});
initializeConstantsAndProperties(this, [], propertyKeys);
Expand Down Expand Up @@ -121,7 +121,7 @@ export default class Agent extends AwaitedEventTarget<{ close: void }> {
}

public get sessionId(): Promise<string> {
const { coreSession } = getState(this).connection;
const coreSession = getState(this).connection.getCoreSessionOrReject();
return coreSession.then(x => x.sessionId);
}

Expand All @@ -130,7 +130,7 @@ export default class Agent extends AwaitedEventTarget<{ close: void }> {
}

public get meta(): Promise<IAgentMeta> {
const { coreSession } = getState(this).connection;
const coreSession = getState(this).connection.getCoreSessionOrReject();
return coreSession.then(x => x.getAgentMeta());
}

Expand Down Expand Up @@ -198,7 +198,7 @@ export default class Agent extends AwaitedEventTarget<{ close: void }> {
);
}
} else {
const session = await connection.coreSession;
const session = await connection.getCoreSessionOrReject();
await session.configure(options);
}
}
Expand Down Expand Up @@ -314,35 +314,29 @@ export default class Agent extends AwaitedEventTarget<{ close: void }> {

/////// THENABLE ///////////////////////////////////////////////////////////////////////////////////////////////////

public then<TResult1 = Agent, TResult2 = never>(
public async then<TResult1 = Agent, TResult2 = never>(
onfulfilled?:
| ((value: Omit<Agent, 'then'>) => PromiseLike<TResult1> | TResult1)
| undefined
| null,
onrejected?: ((reason: any) => PromiseLike<TResult2> | TResult2) | undefined | null,
): Promise<TResult1 | TResult2> {
const session = getState(this).connection.coreSession;
return session
.then(() => {
this.then = null;
return this;
})
.then(onfulfilled, onrejected)
.catch(onrejected);
try {
this.then = null;
await getState(this).connection.getCoreSessionOrReject();
return onfulfilled(this);
} catch (err) {
return onrejected(err);
}
}
}

// This class will lazily connect to core on first access of the tab properties
class SessionConnection {
public hasConnected = false;

public get coreSession(): Promise<CoreSession> {
if (this._coreSession) return this.getCoreSessionOrReject();
return this.connectSession();
}

public get activeTab(): Tab {
this.connectSession();
this.getCoreSessionOrReject().catch(() => null);
return this._activeTab;
}

Expand Down Expand Up @@ -380,7 +374,7 @@ class SessionConnection {

public async close(): Promise<void> {
if (!this.hasConnected) return;
const sessionOrError = await this.getCoreSessionOrReject();
const sessionOrError = await this._coreSession;
if (sessionOrError instanceof CoreSession) {
await sessionOrError.close();
}
Expand All @@ -398,16 +392,11 @@ class SessionConnection {
this._tabs.push(tab);
}

private async getCoreSessionOrReject(): Promise<CoreSession> {
if (!this._coreSession) return undefined;
const sessionOrError = await this._coreSession;
if (sessionOrError instanceof CoreSession) return sessionOrError;
throw sessionOrError;
}

private connectSession(): Promise<CoreSession> {
public async getCoreSessionOrReject(): Promise<CoreSession> {
if (this.hasConnected) {
return this.getCoreSessionOrReject();
const coreSession = await this._coreSession;
if (coreSession instanceof CoreSession) return coreSession;
throw coreSession;
}
this.hasConnected = true;
const { showReplay, connectionToCore, ...options } = getState(this.agent).options;
Expand All @@ -417,23 +406,26 @@ class SessionConnection {
);
this._connection = connection;

this._coreSession = connection.createSession(options).catch(err => err);
this._coreSession = connection.createSession(options);

const defaultShowReplay = Boolean(JSON.parse(process.env.SA_SHOW_REPLAY ?? 'true'));

if (showReplay ?? defaultShowReplay) {
this._coreSession = this._coreSession.then(async x => {
if (x instanceof Error) return x;
await scriptInstance.launchReplay(x);
if (x instanceof CoreSession) await scriptInstance.launchReplay(x);
return x;
});
}

const session = this.getCoreSessionOrReject();
const session = this._coreSession.then(value => {
if (value instanceof CoreSession) return value;
throw value;
});

const coreTab = session.then(x => x.firstTab);
const coreTab = session.then(x => x.firstTab).catch(err => err);
this._activeTab = createTab(this.agent, coreTab);
this._tabs = [this._activeTab];
return session;

return await session;
}
}
38 changes: 19 additions & 19 deletions client/lib/CoreCommandQueue.ts
Expand Up @@ -32,10 +32,25 @@ export default class CoreCommandQueue {
if (this.connection.isDisconnecting) {
return Promise.resolve(null);
}
return this.internalQueue.run<T>(this.runRequest.bind(this, command, args)).catch(error => {
error.stack += `${this.sessionMarker}`;
throw error;
});
return this.internalQueue
.run<T>(async () => {
const response = await this.connection.sendRequest({
meta: this.meta,
command,
args,
});

let data: T = null;
if (response) {
this.lastCommandId = response.commandId;
data = response.data;
}
return data;
})
.catch(error => {
error.stack += `${this.sessionMarker}`;
throw error;
});
}

public stop(cancelError: CanceledPromiseError): void {
Expand All @@ -45,19 +60,4 @@ export default class CoreCommandQueue {
public createSharedQueue(meta: ISessionMeta & { sessionName: string }): CoreCommandQueue {
return new CoreCommandQueue(meta, this.connection, this.internalQueue);
}

private async runRequest<T>(command: string, args: any[]): Promise<T> {
const response = await this.connection.sendRequest({
meta: this.meta,
command,
args,
});

let data: T = null;
if (response) {
this.lastCommandId = response.commandId;
data = response.data;
}
return data;
}
}
9 changes: 6 additions & 3 deletions client/lib/CoreSession.ts
Expand Up @@ -88,9 +88,12 @@ export default class CoreSession implements IJsPathEventTarget {
}

public async close(): Promise<void> {
await this.commandQueue.run('Session.close');
process.nextTick(() => this.connection.closeSession(this));
loggerSessionIdNames.delete(this.sessionId);
try {
await this.commandQueue.run('Session.close');
} finally {
process.nextTick(() => this.connection.closeSession(this));
loggerSessionIdNames.delete(this.sessionId);
}
}

public async addEventListener(
Expand Down
4 changes: 3 additions & 1 deletion client/lib/Handler.ts
Expand Up @@ -189,7 +189,7 @@ export default class Handler {
if (this.isClosing) return;
this.isClosing = true;
// eslint-disable-next-line promise/no-promise-in-callback
await Promise.all(this.connections.map(x => x.disconnect(error)));
await Promise.all(this.connections.map(x => x.disconnect(error).catch(() => null)));
}

private getAvailableConnections(): ConnectionToCore[] {
Expand All @@ -204,6 +204,8 @@ export default class Handler {
}

private registerUnhandledExceptionHandlers(): void {
if (process.env.NODE_ENV === 'test') return;

process.on('uncaughtExceptionMonitor', this.close.bind(this));
process.on('unhandledRejection', this.logUnhandledError.bind(this));
}
Expand Down
2 changes: 1 addition & 1 deletion commons/Logger.ts
Expand Up @@ -154,7 +154,7 @@ class LogEvents {
}
}

export { LogEvents, loggerSessionIdNames, hasBeenLoggedSymbol };
export { Log, LogEvents, loggerSessionIdNames, hasBeenLoggedSymbol };

export function injectLogger(builder: (module: NodeModule) => ILogBuilder): void {
logCreator = builder;
Expand Down
4 changes: 2 additions & 2 deletions commons/ShutdownHandler.ts
@@ -1,9 +1,9 @@
import Log from './Logger';
import logger from './Logger';
import { CanceledPromiseError } from './interfaces/IPendingWaitEvent';

type ShutdownSignal = NodeJS.Signals | 'exit';

const { log } = Log(module);
const { log } = logger(module);

export default class ShutdownHandler {
public static exitOnSignal = true;
Expand Down
7 changes: 5 additions & 2 deletions commons/TypeSerializer.ts
Expand Up @@ -2,7 +2,7 @@ export default class TypeSerializer {
public static errorTypes = new Map<string, { new (message?: string): Error }>();
private static isNodejs = typeof process !== 'undefined' && process.release.name === 'node';

public static parse(stringified: string): any {
public static parse(stringified: string, stackMarker = 'SERIALIZER'): any {
return JSON.parse(stringified, (key, entry) => {
if (!entry || !entry.type) return entry;

Expand Down Expand Up @@ -41,10 +41,13 @@ export default class TypeSerializer {
}
}

const startStack = new Error('').stack.split(/\r?\n/).slice(1).join('\n');
const e = new Constructor(message);
e.name = name;
if (stack) e.stack = stack;
Object.assign(e, data);
if (stack) {
e.stack = `${stack}\n${`------${stackMarker}`.padEnd(50, '-')}\n${startStack}`;
}
return e;
}

Expand Down
4 changes: 2 additions & 2 deletions commons/test/TypeSerializer.test.ts
Expand Up @@ -2,9 +2,9 @@ import Puppet from '@secret-agent/puppet';
import Chrome83 from '@secret-agent/emulate-chrome-83';
import TypeSerializer, { stringifiedTypeSerializerClass } from '../TypeSerializer';
import { CanceledPromiseError } from '../interfaces/IPendingWaitEvent';
import Log from '../Logger';
import logger from '../Logger';

const { log } = Log(module);
const { log } = logger(module);

let testObject: any;
beforeAll(() => {
Expand Down
17 changes: 8 additions & 9 deletions core/index.ts
Expand Up @@ -151,13 +151,12 @@ export default class Core {
});
});

process.on('uncaughtExceptionMonitor', async (error: Error) => {
await Core.logUnhandledError(error, true);
if (process.env.NODE_ENV !== 'test') {
if (process.env.NODE_ENV !== 'test') {
process.on('uncaughtExceptionMonitor', async (error: Error) => {
await Core.logUnhandledError(error, true);
await Core.shutdown();
}
});

process.on('unhandledRejection', async (error: Error) => {
await Core.logUnhandledError(error, false);
});
});
process.on('unhandledRejection', async (error: Error) => {
await Core.logUnhandledError(error, false);
});
}
4 changes: 3 additions & 1 deletion core/lib/FrameEnvironment.ts
Expand Up @@ -403,7 +403,9 @@ b) Use the UserProfile feature to set cookies for 1 or more domains before they'
return this.runFn(fnName, serializedFn, retries - 1);
}

const result = unparsedResult ? TypeSerializer.parse(unparsedResult as string) : unparsedResult;
const result = unparsedResult
? TypeSerializer.parse(unparsedResult as string, 'BROWSER')
: unparsedResult;
if (result?.error) {
this.logger.error(fnName, { result });
throw new InjectedScriptError(result.error, result.pathState);
Expand Down
2 changes: 1 addition & 1 deletion core/lib/GlobalPool.ts
Expand Up @@ -80,7 +80,7 @@ export default class GlobalPool {
const closePromises: Promise<any>[] = [];
while (this.puppets.length) {
const puppetBrowser = this.puppets.shift();
closePromises.push(puppetBrowser.close());
closePromises.push(puppetBrowser.close().catch(err => err));
}
if (this.mitmServer) {
closePromises.push(this.mitmServer.close());
Expand Down
7 changes: 3 additions & 4 deletions core/package.json
Expand Up @@ -7,8 +7,7 @@
"import": "./index.mjs",
"require": "./index.cjs"
},
"./start": "./start.js",
"./server": "./server/index.js"
"./start": "./start.js"
},
"dependencies": {
"@secret-agent/commons": "1.4.1-alpha.0",
Expand All @@ -22,7 +21,7 @@
"@secret-agent/puppet": "1.4.1-alpha.0",
"@secret-agent/puppet-interfaces": "1.4.1-alpha.0",
"awaited-dom": "^1.1.10",
"better-sqlite3": "^7.1.2",
"better-sqlite3": "^7.1.4",
"moment": "^2.24.1",
"uuid": "^8.3.2",
"ws": "^7.4.2"
Expand All @@ -33,7 +32,7 @@
"@secret-agent/emulate-safari-13": "1.4.1-alpha.0",
"@secret-agent/puppet-chrome": "1.4.1-alpha.0",
"@secret-agent/testing": "1.4.1-alpha.0",
"@types/better-sqlite3": "^5.4.0",
"@types/better-sqlite3": "^5.4.1",
"@types/json-socket": "^0.1.17",
"http-proxy-agent": "^4.0.1",
"vue": "^2.6.12"
Expand Down

0 comments on commit 22900c4

Please sign in to comment.