@loadmill/executer 0.1.52 → 0.1.53

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.
@@ -1,73 +0,0 @@
1
- import {
2
- AssignmentExpression,
3
- ExpressionStatement,
4
- Identifier,
5
- Node,
6
- ObjectPattern,
7
- RestElement,
8
- SequenceExpression,
9
- VariableDeclaration,
10
- } from 'estree';
11
- import { isNonEmptyString } from '@loadmill/universal/dist/string-utils';
12
- import {
13
- ArrayPattern,
14
- Declarations,
15
- Expressions,
16
- FunctionDeclaration,
17
- Patterns,
18
- Statements,
19
- } from './types';
20
- import './types';
21
-
22
- export const isFunctionDeclaration = (node: Node) => {
23
- return isOfType(node, Declarations, Declarations.FunctionDeclaration) && hasId(node as FunctionDeclaration);
24
- };
25
- const hasId = (node: FunctionDeclaration) => !!node.id;
26
-
27
- export const isVariableDeclaration = (node: Node) => {
28
- return isOfType(node, Declarations, Declarations.VariableDeclaration) && hasDeclarations(node as VariableDeclaration);
29
- };
30
- const hasDeclarations = (node: VariableDeclaration) => Array.isArray(node.declarations);
31
-
32
- export const isExpressionStatement = (node: Node) => {
33
- return isOfType(node, Statements, Statements.ExpressionStatement) && hasExpression(node as ExpressionStatement);
34
- };
35
- const hasExpression = (node: ExpressionStatement) => !!node.expression;
36
-
37
- export const isAssignmentExpression = (node: Node) => {
38
- return isOfType(node, Expressions, Expressions.AssignmentExpression) && hasLeft(node as AssignmentExpression);
39
- };
40
- const hasLeft = (node: AssignmentExpression) => !!node.left;
41
-
42
- export const isSequenceExpression = (node: Node) => {
43
- return isOfType(node, Expressions, Expressions.SequenceExpression) && hasExpressions(node as SequenceExpression);
44
- };
45
- const hasExpressions = (node: SequenceExpression) => Array.isArray(node.expressions);
46
-
47
- export const isIdentifier = (node: Node) => {
48
- return isOfType(node, Patterns, Patterns.Identifier) && hasName(node as Identifier);
49
- };
50
- const hasName = (id: Identifier) => isNonEmptyString(id.name);
51
-
52
- export const isArray = (node: Node) => {
53
- return isOfType(node, Patterns, Patterns.ArrayPattern) && hasElements(node as ArrayPattern);
54
- };
55
- const hasElements = (node: ArrayPattern) => Array.isArray(node.elements);
56
-
57
- export const isObject = (node: Node) => {
58
- return isOfType(node, Patterns, Patterns.ObjectPattern) && hasProperties(node as ObjectPattern);
59
- };
60
- const hasProperties = (node: ObjectPattern) => Array.isArray(node.properties);
61
-
62
- export const isRestElement = (node: Node) => {
63
- return isOfType(node, Patterns, Patterns.RestElement) && hasArgument(node as RestElement);
64
- };
65
- const hasArgument = (rest: RestElement) => !!rest.argument;
66
-
67
- const isOfType = (
68
- node: Node,
69
- enumerator: any, // can't type an enum for some reason
70
- type: Expressions | Patterns | Statements | Declarations
71
- ) => {
72
- return node.type === enumerator[type];
73
- };
@@ -1,35 +0,0 @@
1
- import {
2
- ArrayPattern as ESTArrayPattern,
3
- FunctionDeclaration as ESTFunctionDeclaration,
4
- Identifier,
5
- Pattern,
6
- } from 'estree';
7
-
8
- export interface ArrayPattern extends ESTArrayPattern {
9
- elements: Array<Pattern>;
10
- }
11
-
12
- export interface FunctionDeclaration extends ESTFunctionDeclaration {
13
- id: Identifier;
14
- }
15
-
16
- export enum Declarations {
17
- FunctionDeclaration = 'FunctionDeclaration',
18
- VariableDeclaration = 'VariableDeclaration',
19
- }
20
-
21
- export enum Expressions {
22
- AssignmentExpression = 'AssignmentExpression',
23
- SequenceExpression = 'SequenceExpression',
24
- }
25
-
26
- export enum Patterns {
27
- ObjectPattern = 'ObjectPattern',
28
- ArrayPattern = 'ArrayPattern',
29
- RestElement = 'RestElement',
30
- Identifier = 'Identifier',
31
- }
32
-
33
- export enum Statements {
34
- ExpressionStatement = 'ExpressionStatement',
35
- }
@@ -1,24 +0,0 @@
1
- export const LINE_LIMIT = 1000;
2
- export const CONSOLE_LOG_LIMIT = 100;
3
-
4
- const isPassedTheLimit = (line) => line.length > LINE_LIMIT;
5
-
6
- export const myConsole = {
7
- stdout: [] as string[],
8
- log: (...args) => {
9
- if (myConsole.stdout.length >= CONSOLE_LOG_LIMIT) {
10
- return;
11
- }
12
- let line = '';
13
- for (const item of args) {
14
- line += typeof item === 'object' ? JSON.stringify(item, undefined, 2) : item;
15
- if (isPassedTheLimit(line)) {
16
- line = line.substring(0, LINE_LIMIT) + '\n...';
17
- break;
18
- }
19
- line += ' ';
20
- }
21
- !!line && myConsole.stdout.push(line);
22
- },
23
- setStdout: (newStdout) => myConsole.stdout = newStdout,
24
- };
@@ -1,8 +0,0 @@
1
- import { parse, Options } from 'acorn';
2
- import { ESTree, JSParser } from './js-parser';
3
-
4
- export class AcornJSParser implements JSParser {
5
- parse(script: string, options: Options = { ecmaVersion: 2020 }): ESTree {
6
- return parse(script, options) as ESTree;
7
- }
8
- }
@@ -1,22 +0,0 @@
1
- import { Node, Program } from 'estree';
2
- import { Parser } from './parser';
3
-
4
- interface JSParser extends Parser {
5
- parse(script: string, options?: JSParserOptions): ESTree;
6
- }
7
-
8
- type ExtendNode<T> = {
9
- [K in keyof T]: T[K] extends object ? ExtendNode<T[K]> : T[K];
10
- } & (T extends Node ? {
11
- start: number;
12
- end: number;
13
- } : {});
14
-
15
- type ESTree = ExtendNode<Program>;
16
- type JSParserOptions = { ecmaVersion: number | string }
17
-
18
- export {
19
- ESTree,
20
- JSParser,
21
- JSParserOptions,
22
- };
@@ -1,5 +0,0 @@
1
- export interface Parser {
2
- parse(script: string): AbstractSyntaxTree;
3
- }
4
-
5
- export type AbstractSyntaxTree = { [name: string]: any };
@@ -1,93 +0,0 @@
1
- import { strict as assert } from 'assert';
2
- import { SequenceExecutorParameters } from '@loadmill/core/dist/parameters';
3
- import { ParameterFunctionOperations } from '@loadmill/core/dist/parameters/parameter-functions/parameter-functions';
4
- import { ASTWalker } from './ast-walker';
5
- import { VM2VirtualMachine } from './virtual-machine/vm2-virtual-machine';
6
- import { myConsole } from './console-log';
7
-
8
- export class PostScriptRunner {
9
- public run = (
10
- staticContext: OuterStaticContext = { staticContext: { $: {}, __: {} } },
11
- dynamicContext: SequenceExecutorParameters = {},
12
- userCode: string = ''
13
- ): SequenceExecutorParameters => {
14
- const newVariables = new ASTWalker(userCode).detectDeclarations(Object.keys(staticContext));
15
- const wrappedUserCode = this.wrapUserCode(dynamicContext, userCode, newVariables);
16
- const stdout = [];
17
- myConsole.setStdout(stdout);
18
-
19
- const vm = new VM2VirtualMachine();
20
- vm.create({
21
- globalObject: { assert, ...dynamicContext, console: myConsole },
22
- staticObject: staticContext,
23
- });
24
-
25
- const modifiedDynamicContext = vm.run(wrappedUserCode);
26
- this.validateCircularDependency(modifiedDynamicContext);
27
- this.removeAssertModule(modifiedDynamicContext);
28
- return { result: modifiedDynamicContext, stdout: myConsole.stdout };
29
- };
30
-
31
- private wrapUserCode = (
32
- dynamicContext: SequenceExecutorParameters = {},
33
- userCode: string = '',
34
- newVariables: string[] = []
35
- ) => {
36
- const prefix =
37
- `
38
- Buffer = {};
39
- global = { assert: global.assert, console: global.console };
40
- const $ = staticContext.$;
41
- const __ = staticContext.__;
42
- `;
43
-
44
- const suffix =
45
- `
46
- exports = {
47
- ${[...Object.keys(dynamicContext), ...newVariables].join()}
48
- };
49
- `;
50
-
51
- return prefix + userCode + suffix;
52
- }
53
-
54
- validateCircularDependency = (obj: SequenceExecutorParameters) => {
55
- function circularError (propName: string) {
56
- return new TypeError(`Cyclic object value. Circular reference in property ${propName}`);
57
- }
58
-
59
- findCircular(obj);
60
-
61
- function findCircular(
62
- obj: SequenceExecutorParameters,
63
- _refs: WeakSet<SequenceExecutorParameters> = new WeakSet()
64
- ) {
65
- if (typeof obj === 'object' && obj !== null) {
66
- _refs.add(obj);
67
- for (const key in obj) {
68
- if (_refs.has(obj[key])) {
69
- throw circularError(key);
70
- }
71
- findCircular(obj[key], _refs);
72
- }
73
- _refs.delete(obj);
74
- }
75
- }
76
- };
77
-
78
- private removeAssertModule(context: SequenceExecutorParameters) {
79
- Object.keys(context).forEach(key => {
80
- if (context[key]?.assert?.name === 'strict') {
81
- context[key] = {};
82
- }
83
- });
84
- }
85
- }
86
- type OuterStaticContext = {
87
- staticContext: StaticContext;
88
- };
89
-
90
- type StaticContext = {
91
- $: SequenceExecutorParameters;
92
- __: ParameterFunctionOperations;
93
- };
@@ -1,15 +0,0 @@
1
- export interface VirtualMachine {
2
- engine: Engine;
3
- create(options: VirtualMachineOptions): Engine;
4
- run(code: string): object;
5
- }
6
-
7
- export interface VirtualMachineOptions {
8
- globalObject?: object;
9
- staticObject?: object;
10
- timeout?: number;
11
- memoryLimit?: number;
12
- async?: boolean;
13
- }
14
-
15
- export type Engine = any;
@@ -1,45 +0,0 @@
1
- import { VM } from 'vm2';
2
- import { SequenceExecutorParameters } from '../../../../core/dist/parameters';
3
-
4
- import { Engine, VirtualMachine, VirtualMachineOptions } from './virtual-machine';
5
-
6
- export class VM2VirtualMachine implements VirtualMachine {
7
- public engine: VM;
8
- private DEFAULT_TIMOUT_MS = Number(process.env.POSTSCRIPT_TIMEOUT_MS) || 1000;
9
-
10
- create(options: VirtualMachineOptions): Engine {
11
- const { timeout, globalObject, async, staticObject } = options;
12
- this.engine = new VM({
13
- timeout: timeout || this.DEFAULT_TIMOUT_MS,
14
- fixAsync: !async,
15
- eval: false,
16
- wasm: false,
17
- });
18
-
19
- this.makeStatic(staticObject);
20
-
21
- this.setGlobals(globalObject);
22
- }
23
-
24
- makeStatic(obj: object = {}) {
25
- this.deepFreeze(obj);
26
- }
27
-
28
- deepFreeze = (obj: object) => {
29
- for (const key of Object.keys(obj)) {
30
- if (typeof obj[key] === 'object' && obj[key] !== null) {
31
- this.engine.freeze(obj[key], key); // freeze me
32
- this.deepFreeze(obj[key]); // freeze my childrens
33
- }
34
- }
35
- return this.engine.freeze(obj); // freeze root
36
- };
37
-
38
- setGlobals = (globalObject: SequenceExecutorParameters = {}) => {
39
- this.engine.setGlobals(globalObject);
40
- }
41
-
42
- run(code: string): SequenceExecutorParameters {
43
- return this.engine.run(code);
44
- }
45
- }
@@ -1,127 +0,0 @@
1
- import { Failures } from './failures';
2
- import { MillInfo } from './mill-info';
3
- import { PerRequestResTime, PerRequestStats } from './request-stats';
4
-
5
- export type LoadResult = 'done' | 'aborted' | 'failed';
6
-
7
- /**
8
- * This report contains all the info that comes from Redis each time there is a status update.
9
- */
10
- export interface LoadStatusReport extends AggregatedStatsReport {
11
- endTime?: number;
12
- startTime?: number;
13
- result?: LoadResult;
14
- activeSessions?: number;
15
- }
16
-
17
- /**
18
- * This report contains all the aggregated stats computed from individual mill reports.
19
- */
20
- export interface AggregatedStatsReport extends BasicStatsReport {
21
- /**
22
- * The number of separate sessions / mills that sent at least one report.
23
- */
24
- reportedSessions?: number;
25
-
26
- append?: boolean;
27
- failures?: Failures;
28
- browserStatsMap?: StatsMap;
29
- locationStatsMap?: StatsMap;
30
- serverMetrics?: ServerMetrics;
31
- avgResTimeSequence?: SequenceObj;
32
- requestStats?: PerRequestResTime;
33
- failureReportsMap?: HashKeysToReportIds;
34
- }
35
-
36
- /**
37
- * A mapping that aggregates stats according to a certain feature, e.g. stats per browser version.
38
- */
39
- export interface StatsMap {
40
- [feature: string]: BasicStatsReport & {
41
- sessions: number;
42
- };
43
- }
44
-
45
- /**
46
- * A sequence of stats (currently only the average response time) reported within a certain
47
- * interval (between `firstTime` and `lastTime`).
48
- */
49
- export interface SequenceObj {
50
- append?: boolean;
51
- sequence: ClientMetrics;
52
- }
53
-
54
- export type ClientMetrics = ClientMetricsClump[];
55
-
56
- export type ClientMetricsClump = {
57
- s?: number;
58
- f?: number;
59
- avg: number;
60
- time: number;
61
- p50?: number;
62
- p95?: number;
63
- weight: number;
64
- concurrency: number;
65
- };
66
-
67
- export type ServerMetrics = ServerMetricsClump[];
68
-
69
- export type ServerMetricsClump = {
70
- _t: number;
71
-
72
- uc?: number;
73
- tc?: number;
74
-
75
- tm?: number;
76
- hm?: number;
77
- uhm?: number;
78
- };
79
-
80
- export type HashKeysToReportIds = { [hashKey: string]: string };
81
-
82
- /**
83
- * This is the report sent to Redis every time a mill report is received.
84
- */
85
- export type ToStoreStatsReport = StatsReportWithMillInfo & {
86
- timestamp: number;
87
- firstReport: boolean;
88
- };
89
-
90
- /**
91
- * This report includes a single mill stats report along with additional meta data.
92
- */
93
- export type StatsReportWithMillInfo = FromMillStatsReport &
94
- MillInfo & {
95
- lordId: string;
96
- };
97
-
98
- /**
99
- * The stats report sent by the mill.
100
- */
101
- export interface FromMillStatsReport extends BasicStatsReport {
102
- failures?: Failures;
103
- requestStats?: PerRequestStats;
104
- }
105
-
106
- /**
107
- * The basic stats we are usually interested in.
108
- */
109
- export interface BasicStatsReport {
110
- hits?: number;
111
- avgResTime?: number;
112
- failedIterations?: number;
113
- successfulIterations?: number;
114
- }
115
-
116
- export function isInformative({
117
- hits,
118
- avgResTime,
119
- failedIterations,
120
- successfulIterations,
121
- }: FromMillStatsReport) {
122
- return (
123
- (hits && hits > 0 && avgResTime != null && avgResTime >= 0) ||
124
- (failedIterations && failedIterations > 0) ||
125
- (successfulIterations && successfulIterations > 0)
126
- );
127
- }
@@ -1,63 +0,0 @@
1
- import { Failures } from './failures';
2
- import { PerRequestStats } from './request-stats';
3
- import { Parameters } from '@loadmill/core/dist/parameters';
4
- import {
5
- LoadmillHeaders,
6
- LoadmillRequest,
7
- PostFormData,
8
- RequestPostData,
9
- } from '@loadmill/core/dist/request';
10
-
11
- export function extendSequenceResult(
12
- result: RequestSequenceResult,
13
- requests: LoadmillRequest[]
14
- ): ExtendedSequenceResult {
15
- return {
16
- ...result,
17
- resolvedRequests: requests.map((request, index: number) => ({
18
- ...request,
19
- ...result.resolvedRequests[index],
20
- })),
21
- };
22
- }
23
-
24
- export interface ExtendedSequenceResult extends RequestSequenceResult {
25
- resolvedRequests: ExtendedRequest[];
26
- err?: string;
27
- }
28
-
29
- export interface RequestSequenceResult {
30
- failures: Failures;
31
- avgResTime: number;
32
- successfulHits: number;
33
- lastStartedIndex: number;
34
- requestStats: PerRequestStats;
35
- resolvedRequests: ResolvedRequest[];
36
- }
37
-
38
- export type ExtendedRequest = LoadmillRequest & ResolvedRequest;
39
-
40
- export interface ResolvedRequest {
41
- url?: string;
42
- headers?: LoadmillHeaders[];
43
- postData?: RequestPostData;
44
- postFormData?: PostFormData;
45
-
46
- response?: {
47
- type: string;
48
- text: string;
49
- status: number;
50
- statusText: string;
51
- headers: LoadmillHeaders[];
52
- };
53
-
54
- postParameters?: Parameters[];
55
- stdout?: string[];
56
- unexpectedError?: {
57
- stack: string;
58
- message: string;
59
- properties: string;
60
- };
61
-
62
- retried?: number;
63
- }
@@ -1,20 +0,0 @@
1
- export interface PerRequestStats {
2
- [index: number]: RequestStats;
3
- }
4
-
5
- export interface RequestStats {
6
- resTimes: number[];
7
- }
8
-
9
- export interface PerRequestResTime {
10
- [index: number]: { weight: number; avg: number };
11
- }
12
-
13
- export function setReqStats(
14
- reqStats: PerRequestStats,
15
- index,
16
- ...resTimes: number[]
17
- ) {
18
- const stats = reqStats[index] || (reqStats[index] = { resTimes: [] });
19
- stats.resTimes.push(...resTimes);
20
- }
package/src/res-keeper.ts DELETED
@@ -1,53 +0,0 @@
1
- import { ObjectMap } from '@loadmill/universal/dist/object-map';
2
- import * as promiseUtils from '@loadmill/universal/dist/promise-utils';
3
- import { getFailureKeys } from './failures';
4
- import { RequestSequenceResult } from './request-sequence-result';
5
-
6
- export class ResKeeper {
7
- knownKeysToResults: { [key: string]: string } = {};
8
- results = new ObjectMap<RequestSequenceResult>(this.generateResultKey);
9
-
10
- constructor(private generateResultKey?) {}
11
-
12
- getKnownKeys = () => Object.keys(this.knownKeysToResults);
13
-
14
- mapToResultIds = (failureKeys) => {
15
- const map: { [key: string]: string } = {};
16
- failureKeys.forEach((key) => (map[key] = this.knownKeysToResults[key]));
17
-
18
- return map;
19
- };
20
-
21
- keepIfNeeded = (result: RequestSequenceResult, keepFor?: number) => {
22
- const failureKeys = getFailureKeys(result.failures);
23
-
24
- if (failureKeys.find((key) => !this.knownKeysToResults[key])) {
25
- // We keep it:
26
- const id = this.results.add(result);
27
- failureKeys.forEach((key) => (this.knownKeysToResults[key] = id));
28
-
29
- if (keepFor) {
30
- promiseUtils.delay(keepFor).then(() => this.results.remove(id));
31
- }
32
- }
33
- };
34
-
35
- popForKeys = (failureKeys: string[], withIds = false) => {
36
- return failureKeys
37
- .map((key) => {
38
- const id = this.knownKeysToResults[key];
39
-
40
- if (id) {
41
- const res = this.results.get(id);
42
- this.results.remove(id);
43
-
44
- if (withIds && res) {
45
- res['id'] = id;
46
- }
47
-
48
- return res;
49
- }
50
- })
51
- .filter(Boolean);
52
- };
53
- }