Skip to content

Commit f531d63

Browse files
committed
add basic console commands
1 parent ada5c99 commit f531d63

9 files changed

Lines changed: 172 additions & 2 deletions

File tree

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@nothing-special/kaiware-lib",
3-
"version": "0.7.0",
3+
"version": "0.8.0",
44
"type": "module",
55
"author": {
66
"name": "Garrett Downs",
@@ -13,6 +13,7 @@
1313
},
1414
"scripts": {
1515
"build": "rimraf build && rollup -c --bundleConfigAsCjs",
16+
"build:watch": "rimraf build && rollup -c --bundleConfigAsCjs --watch",
1617
"format": "prettier --check .",
1718
"format:fix": "prettier --check --write .",
1819
"lint": "eslint .",

src/enums/MessageType.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ export enum MessageType {
2626
ClearLogs = 'clear-logs',
2727
ClearLogsRes = 'clear-logs-res',
2828

29+
ExecuteConsoleCommand = 'execute-console-command',
30+
ExecuteConsoleCommandRes = 'execute-console-command-res',
31+
2932
// Misc
3033

3134
NewLog = 'new-log',

src/lib/connection.ts

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { z } from 'zod';
22
import { MessageType } from '../enums';
3-
import { Config, MessageWithId, rawMessageSchema } from '../types';
3+
import { Config, ConsoleCommandResPayload, MessageWithId, rawMessageSchema } from '../types';
4+
import { makeSerializable, parseConsoleCommand, stringifyObject } from '../utils';
45

56
export class Connection {
67
private config: Config;
@@ -61,6 +62,9 @@ export class Connection {
6162
case MessageType.GetStorage:
6263
this.handleGetStorage(message);
6364
break;
65+
case MessageType.ExecuteConsoleCommand:
66+
this.handleConsoleCommand(message);
67+
break;
6468
default:
6569
this.cleanConsole.log(`Unknown message type received: ${message.type}`);
6670
break;
@@ -198,6 +202,54 @@ export class Connection {
198202
data: { index: message.data.index, data: {} }
199203
});
200204
}
205+
206+
private async handleConsoleCommand(
207+
message: MessageWithId & { type: MessageType.ExecuteConsoleCommand }
208+
) {
209+
const consoleCommand = parseConsoleCommand(message.data.command);
210+
211+
let response: ConsoleCommandResPayload = {};
212+
let currentValue: unknown = window;
213+
try {
214+
for (const step of consoleCommand.steps) {
215+
switch (step.type) {
216+
case 'property':
217+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
218+
currentValue = (currentValue as any)[step.value];
219+
break;
220+
case 'function':
221+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
222+
currentValue = await (currentValue as any)[step.value](...step.params);
223+
break;
224+
case 'array':
225+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
226+
currentValue = (currentValue as any)[Number(step.value)];
227+
break;
228+
}
229+
}
230+
231+
let responseData;
232+
if (typeof currentValue === 'object' && !Array.isArray(currentValue)) {
233+
responseData = stringifyObject(currentValue ?? {});
234+
console.log('responseData', responseData);
235+
} else if (Array.isArray(currentValue)) {
236+
responseData = currentValue.map((val) => makeSerializable(val));
237+
}
238+
239+
response = {
240+
result: responseData
241+
};
242+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
243+
} catch (err: any) {
244+
response = { error: err?.message ?? 'Unknown error' };
245+
}
246+
247+
this.sendMessage({
248+
requestId: message.requestId,
249+
type: MessageType.ExecuteConsoleCommandRes,
250+
data: response
251+
});
252+
}
201253
}
202254

203255
function formatValidationError(issues: z.ZodIssue[]) {

src/types/schemas.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ export type ClearLogsResPayload = z.infer<typeof clearLogsResPayloadSchema>;
113113

114114
// NetworkRequest
115115
export const networkRequestSchema = z.object({
116+
id: z.number(),
116117
requestId: z.string(),
117118
url: z.string(),
118119
method: z.union([
@@ -133,6 +134,7 @@ export const networkRequestSchema = z.object({
133134
body: z.string().optional(),
134135
startTime: z.number(),
135136
endTime: z.number().optional(),
137+
duration: z.number().optional(),
136138
responseStatus: z.number().optional(),
137139
responseHeaders: z.array(z.object({ key: z.string(), value: z.string() })).optional(),
138140
responseBody: z.string().optional(),
@@ -166,13 +168,37 @@ export const networkRequestUpdateResPayload = z.object({
166168
body: z.string().optional(),
167169
startTime: z.number().optional(),
168170
endTime: z.number().optional(),
171+
duration: z.number().optional(),
169172
responseStatus: z.number().optional(),
170173
responseHeaders: z.array(z.object({ key: z.string(), value: z.string() })).optional(),
171174
responseBody: z.string().optional(),
172175
responseSize: z.number().optional()
173176
});
174177
export type NetworkRequestUpdateResPayload = z.infer<typeof networkRequestUpdateResPayload>;
175178

179+
// ConsoleCommandPayload
180+
export const consoleCommandPayloadSchema = z.object({
181+
command: z.string()
182+
});
183+
export type ConsoleCommandPayload = z.infer<typeof consoleCommandPayloadSchema>;
184+
185+
// ConsoleCommandResPayload
186+
export const consoleCommandResPayloadSchema = z.object({
187+
result: z.unknown().optional(),
188+
error: z.string().optional()
189+
});
190+
export type ConsoleCommandResPayload = z.infer<typeof consoleCommandResPayloadSchema>;
191+
192+
export type ConsoleCommand = {
193+
steps: CommandStep[];
194+
};
195+
196+
export type CommandStep = {
197+
type: 'property' | 'function' | 'array';
198+
value: string;
199+
params: unknown[];
200+
};
201+
176202
// Message
177203
export const messageSchema = z.discriminatedUnion('type', [
178204
z.object({
@@ -274,6 +300,16 @@ export const messageSchema = z.discriminatedUnion('type', [
274300
requestId: z.string(),
275301
type: z.literal(MessageType.NetworkRequestUpdate),
276302
data: networkRequestUpdateResPayload
303+
}),
304+
z.object({
305+
requestId: z.string(),
306+
type: z.literal(MessageType.ExecuteConsoleCommand),
307+
data: consoleCommandPayloadSchema
308+
}),
309+
z.object({
310+
requestId: z.string(),
311+
type: z.literal(MessageType.ExecuteConsoleCommandRes),
312+
data: consoleCommandResPayloadSchema
277313
})
278314
]);
279315
type OmitRequestId<T> = {

src/utils/getObjectKeys.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export function getObjectKeys(obj: object): string[] {
2+
const keys: string[] = [];
3+
for (const key in obj) {
4+
keys.push(key);
5+
}
6+
return keys;
7+
}

src/utils/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,6 @@
1+
export * from './getObjectKeys';
12
export * from './isJson';
3+
export * from './makeSerializable';
4+
export * from './parseConsoleCommand';
25
export * from './parseError';
6+
export * from './stringifyObject';

src/utils/makeSerializable.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { getObjectKeys } from './getObjectKeys';
2+
3+
export function makeSerializable(obj: object, spaces = 2): object {
4+
const newObj: { [key: string]: unknown } = {};
5+
6+
const keys = getObjectKeys(obj);
7+
keys.forEach((key) => {
8+
newObj[key] = (obj as { [key: string]: unknown })[key];
9+
});
10+
11+
return JSON.parse(JSON.stringify(newObj, null, spaces));
12+
}

src/utils/parseConsoleCommand.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { CommandStep, ConsoleCommand } from '../types';
2+
3+
export function parseConsoleCommand(command: string): ConsoleCommand {
4+
const steps = command.match(/(\w+\[\d+\])|(\w+\([^)]*\))|(\w+)/g);
5+
const result: CommandStep[] = [];
6+
7+
steps?.forEach((step) => {
8+
let matches;
9+
if ((matches = step.match(/^(\w+)\((.*)\)$/))) {
10+
result.push(createStep('function', matches as RegExpMatchArray));
11+
} else if ((matches = step.match(/^(\w+)\[(.*?)\]$/))) {
12+
result.push(createStep('array', matches as RegExpMatchArray));
13+
} else {
14+
result.push({
15+
type: 'property',
16+
value: step,
17+
params: []
18+
});
19+
}
20+
});
21+
22+
return {
23+
steps: result
24+
};
25+
}
26+
27+
function createStep(type: CommandStep['type'], matches: RegExpMatchArray): CommandStep {
28+
return {
29+
type,
30+
value: matches[1] as string,
31+
params: parseParams(matches[2] as string)
32+
};
33+
}
34+
35+
function parseParams(paramString: string): unknown[] {
36+
const matches = paramString.match(/(\d+)|('[^']*')|("[^"]*")|(\[[^\]]*\])|({[^}]*})/g);
37+
if (!matches) return [];
38+
39+
return matches.map((match) => {
40+
if (match.startsWith('[') || match.startsWith('{')) {
41+
return JSON.parse(match);
42+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
43+
} else if (!isNaN(match as any)) {
44+
return Number(match);
45+
} else {
46+
return match.slice(1, -1);
47+
}
48+
});
49+
}

src/utils/stringifyObject.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { getObjectKeys } from './getObjectKeys';
2+
3+
export function stringifyObject(obj: object, spaces = 2): string {
4+
const keys = getObjectKeys(obj);
5+
return JSON.stringify(obj, keys, spaces);
6+
}

0 commit comments

Comments
 (0)