@bloom-perf/yaml-pptr 2.0.1 → 3.0.0
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 +117 -14
- package/dist/core/errors.d.ts +26 -0
- package/dist/core/model.d.ts +1 -1
- package/dist/index.d.ts +23 -3
- package/dist/index.js +2 -1
- package/dist/puppet/interpreter.d.ts +9 -3
- package/dist/puppet/launcher.d.ts +9 -0
- package/dist/yaml/parser.d.ts +1 -1
- package/dist/yaml/resolver.d.ts +20 -8
- package/dist/yaml/validator.d.ts +172 -172
- package/package.json +21 -18
package/README.md
CHANGED
|
@@ -11,9 +11,11 @@
|
|
|
11
11
|
## Installation
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
npm install @bloom-perf/yaml-pptr
|
|
14
|
+
npm install @bloom-perf/yaml-pptr puppeteer
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
+
`puppeteer` is a peer dependency (`^25.0.0`): your project controls its version.
|
|
18
|
+
|
|
17
19
|
## Quick Start
|
|
18
20
|
|
|
19
21
|
```typescript
|
|
@@ -108,7 +110,71 @@ readYamlAndInterpret(yaml)
|
|
|
108
110
|
### 3. Run it
|
|
109
111
|
|
|
110
112
|
```bash
|
|
111
|
-
npx
|
|
113
|
+
npx tsx src/runner.ts
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## API Options
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
await readYamlAndInterpret(yaml, {
|
|
120
|
+
headless: true, // default: true
|
|
121
|
+
launchOptions: { // extra Puppeteer launch options, per browser
|
|
122
|
+
firefox: { executablePath: '/path/to/firefox' },
|
|
123
|
+
},
|
|
124
|
+
browsers: { chrome: myBrowser }, // reuse already launched browsers (not closed)
|
|
125
|
+
logger: myLogger,
|
|
126
|
+
onPage: async (page, ctx) => { /* page hook */ },
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
| Option | Default | Description |
|
|
131
|
+
| ------ | ------- | ----------- |
|
|
132
|
+
| `headless` | `true` | Run launched browsers headless |
|
|
133
|
+
| `launchOptions` | `{}` | Puppeteer `LaunchOptions` per browser; `args` are appended to the defaults |
|
|
134
|
+
| `browsers` | `{}` | Pre-launched browsers to use instead of launching new ones |
|
|
135
|
+
| `logger` | console | Custom logger (`debug`, `info`, `warn`, `error`) |
|
|
136
|
+
| `onPage` | - | Callback invoked on every new page with `{ scenarioName, workerIndex, iteration }` |
|
|
137
|
+
|
|
138
|
+
### Firefox
|
|
139
|
+
|
|
140
|
+
Puppeteer drives Firefox through WebDriver BiDi, which requires a recent Firefox. Install the version matching your Puppeteer release with:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
npx puppeteer browsers install firefox
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
To use another Firefox installed on the machine, pass its path with `launchOptions.firefox.executablePath`.
|
|
147
|
+
|
|
148
|
+
### Error handling
|
|
149
|
+
|
|
150
|
+
The configuration is fully checked before anything runs: YAML syntax, schema (an unknown key such as a typo is an error), and environment variables. If it is invalid, `readYamlAndInterpret` rejects with a `ConfigurationError` listing every problem with its location, and no browser page is opened:
|
|
151
|
+
|
|
152
|
+
```text
|
|
153
|
+
ConfigurationError: Invalid scenario configuration:
|
|
154
|
+
✖ Unrecognized key: "iteration"
|
|
155
|
+
→ at scenarios[0]
|
|
156
|
+
✖ Invalid input: expected int, received number
|
|
157
|
+
→ at scenarios[0].workers
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Then every scenario, worker and iteration always runs to completion: a failed iteration does not stop the next iterations nor the other workers, and its page is always closed. If anything failed, `readYamlAndInterpret` then rejects with an `AggregateError` whose `errors` contain:
|
|
161
|
+
|
|
162
|
+
- `WorkerError` — an iteration failed (`scenarioName`, `workerIndex`, `iteration`, original error in `cause`)
|
|
163
|
+
- `BrowserLaunchError` — a browser could not be launched (`browser`, original error in `cause`)
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
import { readYamlAndInterpret, ConfigurationError } from '@bloom-perf/yaml-pptr';
|
|
167
|
+
|
|
168
|
+
try {
|
|
169
|
+
await readYamlAndInterpret(yaml);
|
|
170
|
+
} catch (e) {
|
|
171
|
+
if (e instanceof ConfigurationError) {
|
|
172
|
+
console.error(e.message); // invalid YAML, unknown key, missing variable...
|
|
173
|
+
} else if (e instanceof AggregateError) {
|
|
174
|
+
for (const err of e.errors) console.error(err.message); // WorkerError / BrowserLaunchError
|
|
175
|
+
}
|
|
176
|
+
process.exit(1);
|
|
177
|
+
}
|
|
112
178
|
```
|
|
113
179
|
|
|
114
180
|
## YAML Reference
|
|
@@ -119,13 +185,15 @@ npx ts-node src/runner.ts
|
|
|
119
185
|
| -------- | ---- | ------- | ----------- |
|
|
120
186
|
| `name` | string | `Scenario N` | Scenario name |
|
|
121
187
|
| `browser` | `chrome` \| `firefox` | `chrome` | Browser to use |
|
|
122
|
-
| `run` | `PARALLEL` \| `SEQUENTIAL` | `PARALLEL` | Worker start strategy |
|
|
123
|
-
| `initialDelaySeconds` | number | `0` |
|
|
124
|
-
| `workers` |
|
|
125
|
-
| `iterations` |
|
|
188
|
+
| `run` | `PARALLEL` \| `SEQUENTIAL` | `PARALLEL` | Worker start strategy: `PARALLEL` starts all workers after `initialDelaySeconds`; `SEQUENTIAL` starts worker *n* after *n* × `initialDelaySeconds` |
|
|
189
|
+
| `initialDelaySeconds` | number | `0` | Start delay, applied once per worker before its first iteration (see `run`) |
|
|
190
|
+
| `workers` | integer ≥ 1 | `1` | Number of parallel workers |
|
|
191
|
+
| `iterations` | integer ≥ 1 | `1` | Repetitions per worker |
|
|
126
192
|
| `location` | string | - | Starting URL |
|
|
127
|
-
| `steps` | array | - | Simplified syntax |
|
|
128
|
-
| `actions` | array | - | Full syntax with options |
|
|
193
|
+
| `steps` | array | - | Simplified syntax (full actions are accepted too) |
|
|
194
|
+
| `actions` | array | - | Full syntax with options, run after `steps` |
|
|
195
|
+
|
|
196
|
+
All the scenarios of a file start at the same time, whatever their browser: each one is a population of users with its own `workers`, `iterations` and start strategy. Unknown keys are rejected (see [Error handling](#error-handling)).
|
|
129
197
|
|
|
130
198
|
### Steps (Simplified Syntax)
|
|
131
199
|
|
|
@@ -164,7 +232,17 @@ npx ts-node src/runner.ts
|
|
|
164
232
|
|
|
165
233
|
### Actions (Full Syntax)
|
|
166
234
|
|
|
167
|
-
Use `actions`
|
|
235
|
+
Use the full syntax when you need advanced options. Full actions can be listed in `actions` (run after `steps`), or written directly among `steps` to mix both syntaxes in the order you need:
|
|
236
|
+
|
|
237
|
+
```yaml
|
|
238
|
+
steps:
|
|
239
|
+
- click: "#open-menu"
|
|
240
|
+
- type: WAIT_FOR_SELECTOR
|
|
241
|
+
selector: ".menu"
|
|
242
|
+
options:
|
|
243
|
+
visible: true
|
|
244
|
+
- click: ".menu .logout"
|
|
245
|
+
```
|
|
168
246
|
|
|
169
247
|
#### GOTO
|
|
170
248
|
|
|
@@ -248,6 +326,8 @@ Use `actions` instead of `steps` when you need advanced options.
|
|
|
248
326
|
script: "document.title"
|
|
249
327
|
```
|
|
250
328
|
|
|
329
|
+
The script is never interpolated with environment variables (injecting values into code would be unsafe).
|
|
330
|
+
|
|
251
331
|
#### SET_VIEWPORT
|
|
252
332
|
|
|
253
333
|
```yaml
|
|
@@ -262,6 +342,8 @@ Use `actions` instead of `steps` when you need advanced options.
|
|
|
262
342
|
- type: PAUSE # Press Enter to continue
|
|
263
343
|
```
|
|
264
344
|
|
|
345
|
+
Meant for debugging in an interactive terminal. When there is none (CI, container, piped input), the PAUSE is ignored with a warning instead of blocking forever.
|
|
346
|
+
|
|
265
347
|
#### CLOSE
|
|
266
348
|
|
|
267
349
|
```yaml
|
|
@@ -270,19 +352,37 @@ Use `actions` instead of `steps` when you need advanced options.
|
|
|
270
352
|
|
|
271
353
|
## Environment Variables
|
|
272
354
|
|
|
273
|
-
|
|
355
|
+
Keep URLs and credentials out of the YAML with `${VAR_NAME}` placeholders. They are resolved before anything runs: a missing variable is a `ConfigurationError`.
|
|
274
356
|
|
|
275
357
|
```yaml
|
|
276
|
-
|
|
358
|
+
scenarios:
|
|
359
|
+
- name: Login
|
|
360
|
+
location: "${BASE_URL}/login"
|
|
361
|
+
steps:
|
|
362
|
+
- input:
|
|
363
|
+
selector: "#username"
|
|
364
|
+
text: "${APP_USER}"
|
|
365
|
+
- input:
|
|
366
|
+
selector: "#password"
|
|
367
|
+
text: "${APP_PASSWORD}"
|
|
368
|
+
- click: "#login-button"
|
|
277
369
|
```
|
|
278
370
|
|
|
279
371
|
```bash
|
|
280
|
-
BASE_URL=https://example.com npx
|
|
372
|
+
BASE_URL=https://example.com APP_USER=alice APP_PASSWORD=... npx tsx runner.ts
|
|
281
373
|
```
|
|
282
374
|
|
|
375
|
+
| Syntax | Where | Notes |
|
|
376
|
+
| ------ | ----- | ----- |
|
|
377
|
+
| `${VAR}` | Anywhere in `location`, `navigate`, GOTO `url`/`referer`, selectors, typed `text`, SCREENSHOT `path` | `$${` produces a literal `${` |
|
|
378
|
+
| `$VAR` | As the whole value of `location`, `navigate`, GOTO `url`, SCREENSHOT `path` | Elsewhere a `$` is kept as is (e.g. a password like `$ecret`) |
|
|
379
|
+
| `$VAR[workerIndex]` | As the whole value of `location`, `navigate` | One value per worker, see below |
|
|
380
|
+
|
|
381
|
+
`EVALUATE` scripts are never interpolated. Typed texts and resolved values are never logged.
|
|
382
|
+
|
|
283
383
|
### Indexed Variables
|
|
284
384
|
|
|
285
|
-
Assign different values to each worker using `$VAR[workerIndex]`:
|
|
385
|
+
Assign different values to each worker using `$VAR[workerIndex]` (comma-separated values). There must be at least one non-empty value per worker, otherwise it is a `ConfigurationError`:
|
|
286
386
|
|
|
287
387
|
```yaml
|
|
288
388
|
scenarios:
|
|
@@ -294,7 +394,7 @@ scenarios:
|
|
|
294
394
|
```
|
|
295
395
|
|
|
296
396
|
```bash
|
|
297
|
-
URLS="https://site1.com,https://site2.com,https://site3.com" npx
|
|
397
|
+
URLS="https://site1.com,https://site2.com,https://site3.com" npx tsx runner.ts
|
|
298
398
|
```
|
|
299
399
|
|
|
300
400
|
Worker 0 gets `site1.com`, worker 1 gets `site2.com`, etc.
|
|
@@ -324,6 +424,9 @@ npm test
|
|
|
324
424
|
# Run tests with coverage
|
|
325
425
|
npm run test:coverage
|
|
326
426
|
|
|
427
|
+
# Type check
|
|
428
|
+
npm run typecheck
|
|
429
|
+
|
|
327
430
|
# Build
|
|
328
431
|
npm run build
|
|
329
432
|
```
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Raised before anything runs when the configuration is invalid: YAML syntax,
|
|
3
|
+
* schema validation (unknown key, wrong type...) or environment variable
|
|
4
|
+
* resolution (missing variable, not enough per-worker values...).
|
|
5
|
+
*/
|
|
6
|
+
export declare class ConfigurationError extends Error {
|
|
7
|
+
constructor(message: string, options?: ErrorOptions);
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Raised when an iteration of a scenario worker fails. Carries the scenario
|
|
11
|
+
* name, the worker index and the iteration so the caller knows exactly where
|
|
12
|
+
* the failure happened.
|
|
13
|
+
*/
|
|
14
|
+
export declare class WorkerError extends Error {
|
|
15
|
+
readonly scenarioName: string;
|
|
16
|
+
readonly workerIndex: number;
|
|
17
|
+
readonly iteration: number;
|
|
18
|
+
constructor(scenarioName: string, workerIndex: number, iteration: number, cause: unknown);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Raised when a browser could not be launched (missing binary, bad options...).
|
|
22
|
+
*/
|
|
23
|
+
export declare class BrowserLaunchError extends Error {
|
|
24
|
+
readonly browser: string;
|
|
25
|
+
constructor(browser: string, cause: unknown);
|
|
26
|
+
}
|
package/dist/core/model.d.ts
CHANGED
|
@@ -103,7 +103,7 @@ export interface CloseAction extends BaseAction {
|
|
|
103
103
|
}
|
|
104
104
|
export interface EvaluateAction extends BaseAction {
|
|
105
105
|
actionType: ActionType.Evaluate;
|
|
106
|
-
script: string
|
|
106
|
+
script: string;
|
|
107
107
|
}
|
|
108
108
|
export interface SetViewportAction extends BaseAction {
|
|
109
109
|
actionType: ActionType.SetViewport;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,32 @@
|
|
|
1
1
|
import * as p from 'puppeteer';
|
|
2
|
-
import { SupportedBrowser, OnPageCallback } from './core/model';
|
|
3
|
-
import { YpLogger } from './core/logger';
|
|
4
|
-
export type { ScenarioContext, OnPageCallback } from './core/model';
|
|
2
|
+
import { SupportedBrowser, OnPageCallback } from './core/model.js';
|
|
3
|
+
import { YpLogger } from './core/logger.js';
|
|
4
|
+
export type { ScenarioContext, OnPageCallback } from './core/model.js';
|
|
5
|
+
export { WorkerError, BrowserLaunchError, ConfigurationError } from './core/errors.js';
|
|
5
6
|
export type ReadYamlOptions = {
|
|
7
|
+
/** Already launched browsers to use instead of launching new ones. They are not closed. */
|
|
6
8
|
browsers?: {
|
|
7
9
|
[key in SupportedBrowser]?: p.Browser;
|
|
8
10
|
};
|
|
9
11
|
logger?: YpLogger;
|
|
10
12
|
onPage?: OnPageCallback;
|
|
13
|
+
/** Run launched browsers in headless mode. Defaults to `true`. */
|
|
14
|
+
headless?: boolean;
|
|
15
|
+
/** Extra Puppeteer launch options per browser (e.g. `executablePath`). `args` are appended to the defaults. */
|
|
16
|
+
launchOptions?: {
|
|
17
|
+
[key in SupportedBrowser]?: p.LaunchOptions;
|
|
18
|
+
};
|
|
11
19
|
};
|
|
20
|
+
/**
|
|
21
|
+
* Parses and validates the YAML, resolves environment variables, then runs
|
|
22
|
+
* every scenario. All scenarios start at the same time (each one with its own
|
|
23
|
+
* workers and start strategy), whatever the browser they target.
|
|
24
|
+
*
|
|
25
|
+
* - An invalid configuration (YAML syntax, unknown key, missing environment
|
|
26
|
+
* variable...) rejects with a `ConfigurationError` before anything runs.
|
|
27
|
+
* - Otherwise all scenarios are always run to completion. If anything failed
|
|
28
|
+
* (browser launch or iteration), the promise rejects with an `AggregateError`
|
|
29
|
+
* whose `errors` are `BrowserLaunchError` / `WorkerError` instances, in
|
|
30
|
+
* scenario order.
|
|
31
|
+
*/
|
|
12
32
|
export declare const readYamlAndInterpret: (yamlContent: string, options?: ReadYamlOptions) => Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
"use strict";var L=Object.create;var w=Object.defineProperty;var B=Object.getOwnPropertyDescriptor;var j=Object.getOwnPropertyNames;var U=Object.getPrototypeOf,V=Object.prototype.hasOwnProperty;var _=(o,t)=>{for(var n in t)w(o,n,{get:t[n],enumerable:!0})},$=(o,t,n,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of j(t))!V.call(o,r)&&r!==n&&w(o,r,{get:()=>t[r],enumerable:!(i=B(t,r))||i.enumerable});return o};var z=(o,t,n)=>(n=o!=null?L(U(o)):{},$(t||!o||!o.__esModule?w(n,"default",{value:o,enumerable:!0}):n,o)),G=o=>$(w({},"__esModule",{value:!0}),o);var wt={};_(wt,{readYamlAndInterpret:()=>bt});module.exports=G(wt);var R=z(require("puppeteer"),1);var x=z(require("js-yaml"),1);var e=z(require("zod"),1),k=e.enum(["load","domcontentloaded","networkidle0","networkidle2"]),I=e.enum(["left","right","middle"]),N=e.enum(["chrome","firefox"]),M=e.object({timeout:e.number().optional(),waitUntil:e.union([k,k.array()]).optional(),referer:e.string().optional()}).optional(),D=e.object({type:e.literal("WAIT"),milliseconds:e.number()}),q=e.object({button:I.optional(),clickCount:e.number().optional(),delay:e.number().optional()}).optional(),H=e.object({delay:e.number().optional()}).optional(),K=e.object({visible:e.boolean().optional(),hidden:e.boolean().optional(),timeout:e.number().optional()}).optional(),J=e.object({type:e.enum(["png","jpeg"]).optional(),quality:e.number().optional(),fullPage:e.boolean().optional(),clip:e.object({x:e.number(),y:e.number(),width:e.number(),height:e.number()}).optional(),omitBackground:e.boolean().optional(),encoding:e.enum(["base64","binary"]).optional()}).optional(),Q=e.object({type:e.literal("GOTO"),url:e.string(),options:M}),Z=e.object({type:e.literal("CLICK"),selector:e.string(),options:q}),X=e.object({type:e.literal("TYPE"),selector:e.string(),text:e.string(),options:H}),Y=e.object({type:e.literal("WAIT_FOR_SELECTOR"),selector:e.string(),options:K}),tt=e.object({type:e.literal("WAIT_FOR_TIMEOUT"),timeout:e.number()}),et=e.object({type:e.literal("PAUSE")}),ot=e.object({type:e.literal("SCREENSHOT"),path:e.string(),options:J}),it=e.object({type:e.literal("EVALUATE"),script:e.union([e.string(),e.function()])}),nt=e.object({type:e.literal("SET_VIEWPORT"),width:e.number(),height:e.number()}),rt=e.object({type:e.literal("CLOSE")}),E=[Q,Z,X,Y,tt,et,ot,it,nt,rt,D];if(E.length===0)throw new Error("Le tableau des sch\xE9mas d'actions est vide.");var st=E,at=e.discriminatedUnion("type",st),ct=e.object({navigate:e.string()}),pt=e.literal("waitForever"),lt=e.object({wait:e.number()}),ut=e.object({click:e.string()}),gt=e.object({input:e.object({selector:e.string(),text:e.string()})}),mt=e.union([ct,pt,lt,ut,gt]),dt=e.object({name:e.string().optional(),browser:N.optional(),run:e.enum(["PARALLEL","SEQUENTIAL"]).optional(),initialDelaySeconds:e.number().nonnegative().optional(),workers:e.number().positive().optional(),iterations:e.number().positive().optional(),location:e.string().optional(),steps:e.array(mt).optional(),actions:e.array(at).optional()}),W=e.object({scenarios:e.array(dt)});var O=o=>{let t=x.load(o);return W.parse(t)};var A={debug(o){console.log(o)},info(o){console.log(o)},warn(o){console.log(o)},error(o){console.error(o)}};var v=class{envVarResolve;logger;constructor(t,n=A){this.envVarResolve=i=>{let r=i.startsWith("$")?i.substring(1):i;return t(r)},this.logger=n}resolve(t){return{scenarios:t.scenarios.map((n,i)=>this.resolveScenario(n,i+1))}}resolveScenario(t,n){this.logger.debug(`Resolving YAML scenario: ${JSON.stringify(t)}`);let i=[];t.location&&i.push(this.createNavigateAction(t.location)),t.steps&&i.push(...t.steps.map(s=>this.resolveStep(s))),t.actions&&i.push(...t.actions.map(s=>this.resolveAction(s)));let r=t.run==="SEQUENTIAL"?{delaySecondsBetweenWorkerInits:t.initialDelaySeconds||0}:{initialDelaySeconds:t.initialDelaySeconds||0};return{name:t.name||`Scenario ${n}`,browser:t.browser||"chrome",run:r,workers:t.workers||1,iterations:t.iterations||1,actions:i}}resolveStep(t){if(t==="waitForever")return this.logger.debug("Resolving step: waitForever"),{actionType:"WaitForever"};if("wait"in t)return this.logger.debug(`Resolving step: wait ${t.wait} seconds`),{actionType:"Wait",milliseconds:t.wait*1e3};if("navigate"in t)return this.logger.debug("Resolving step: navigate"),this.createNavigateAction(t.navigate);if("click"in t)return this.logger.debug(`Resolving step: click "${t.click}"`),{actionType:"CLICK",selector:t.click};if("input"in t)return this.logger.debug(`Resolving step: input into "${t.input.selector}"`),{actionType:"TYPE",selector:t.input.selector,text:t.input.text};throw new Error(`Unknown step format: ${JSON.stringify(t)}`)}resolveAction(t){switch(this.logger.debug(`Resolving action: ${JSON.stringify(t)}`),t.type){case"GOTO":return{actionType:"GOTO",url:this.resolveStringOrEnv(t.url),options:t.options};case"WAIT":return{actionType:"Wait",milliseconds:t.milliseconds*1e3};case"CLICK":return{actionType:"CLICK",selector:t.selector,options:t.options};case"TYPE":return{actionType:"TYPE",selector:t.selector,text:t.text,options:t.options};case"WAIT_FOR_SELECTOR":return{actionType:"WAIT_FOR_SELECTOR",selector:t.selector,options:t.options};case"WAIT_FOR_TIMEOUT":return{actionType:"WAIT_FOR_TIMEOUT",timeout:t.timeout};case"PAUSE":return{actionType:"PAUSE"};case"SCREENSHOT":return{actionType:"SCREENSHOT",path:this.resolveStringOrEnv(t.path),options:t.options};case"EVALUATE":return{actionType:"EVALUATE",script:t.script};case"SET_VIEWPORT":return{actionType:"SET_VIEWPORT",width:t.width,height:t.height};case"CLOSE":return{actionType:"CLOSE"};default:return t}}createNavigateAction(t){return{actionType:"Navigate",location:this.resolveUrlOrEnv(t)}}resolveUrlOrEnv(t){let n="[workerIndex]";if(t.startsWith("$")){if(t.endsWith(n)){this.logger.debug(`Resolving indexed environment variable: "${t}"`);let i=t.substring(1,t.length-n.length);return{workerIndex:this.envVarResolve(i).split(",").map(c=>c.trim())}}return this.logger.debug(`Resolving environment variable: "${t}"`),{url:this.envVarResolve(t.substring(1))}}return this.logger.debug(`Using direct URL: "${t}"`),{url:t}}resolveStringOrEnv(t){return t.startsWith("$")?(this.logger.debug(`Resolving environment variable: "${t}"`),this.envVarResolve(t.substring(1))):t}};function At(o,t){let n=t||"png";return o.endsWith(".png")||o.endsWith(".jpeg")||o.endsWith(".jpg")||o.endsWith(".webp")?o:`${o}.${n}`}var C=async(o,t,n=A,i)=>await yt(t.workers,t.name,async r=>{for(let s=0;s<t.iterations;s++){let c=t.run;"delaySecondsBetweenWorkerInits"in c?(n.info(`Scenario "${t.name}" (worker ${r}) > Choosing SEQUENTIAL start with ${c.delaySecondsBetweenWorkerInits} seconds between workers`),await new Promise(u=>setTimeout(u,c.delaySecondsBetweenWorkerInits*r*1e3))):(n.info(`Scenario "${t.name}" (worker ${r}) > Choosing PARALLEL start with ${c.initialDelaySeconds} seconds of initial delay`),await new Promise(u=>setTimeout(u,c.initialDelaySeconds*1e3)));let l=await o.newPage();i&&await i(l,{scenarioName:t.name,workerIndex:r,iteration:s}),await ht(t.name,t.actions,l,r,n),await l.close()}},n),yt=async(o,t,n,i=A)=>Promise.all(Array.from({length:o},async(r,s)=>{try{i.info(`Scenario "${t}" (worker ${s}) started.`),await n(s)}catch(c){i.error(`Scenario ${t} (worker ${s}) failed: ${c}`)}})),ht=async(o,t,n,i,r=A)=>{for(let s of t){let{actionType:c}=s;switch(c){case"Navigate":let l=s;"url"in l.location?(r.info(`Scenario "${o}" (worker ${i}) > Navigating to location "${l.location.url}"`),await n.goto(l.location.url)):(r.info(`Scenario "${o}" (worker ${i}) > Navigating to indexed location "${l.location.workerIndex[i]}"`),await n.goto(l.location.workerIndex[i]));break;case"Wait":let u=s;r.info(`Scenario "${o}" (worker ${i}) > Waiting ${u.milliseconds} milliseconds...`),await n.evaluate(y=>new Promise(T=>setTimeout(T,y)),u.milliseconds);break;case"WaitForever":for(r.info(`Scenario "${o}" (worker ${i}) > Waiting forever...`);;)await new Promise(y=>setTimeout(y,3600*1e3));break;case"GOTO":let d=s;r.info(`Scenario "${o}" (worker ${i}) > GOTO "${d.url}"`);let f={timeout:d.options?.timeout,waitUntil:d.options?.waitUntil,referer:d.options?.referer};await n.goto(d.url,f);break;case"CLICK":let a=s;r.info(`Scenario "${o}" (worker ${i}) > CLICK on "${a.selector}"`);let g={button:a.options?.button,clickCount:a.options?.clickCount,delay:a.options?.delay};await n.click(a.selector,g);break;case"TYPE":let S=s;r.info(`Scenario "${o}" (worker ${i}) > TYPE into "${S.selector}"`),await n.type(S.selector,S.text,S.options);break;case"WAIT_FOR_SELECTOR":let m=s;r.info(`Scenario "${o}" (worker ${i}) > WAIT_FOR_SELECTOR "${m.selector}"`),await n.waitForSelector(m.selector,m.options);break;case"WAIT_FOR_TIMEOUT":let h=s;r.info(`Scenario "${o}" (worker ${i}) > WAIT_FOR_TIMEOUT ${h.timeout} milliseconds`),await n.evaluate(y=>new Promise(T=>setTimeout(T,y)),h.timeout);break;case"PAUSE":r.info(`Scenario "${o}" (worker ${i}) > PAUSE, press Enter to continue...`),await new Promise(y=>{process.stdin.resume(),process.stdin.once("data",()=>{process.stdin.pause(),y()})});break;case"SCREENSHOT":let p=s;r.info(`Scenario "${o}" (worker ${i}) > SCREENSHOT "${p.path}"`);let P={path:At(p.path,p.options?.type),type:p.options?.type,quality:p.options?.quality,fullPage:p.options?.fullPage,clip:p.options?.clip,omitBackground:p.options?.omitBackground,encoding:p.options?.encoding};await n.screenshot(P);break;case"EVALUATE":let F=s;r.info(`Scenario "${o}" (worker ${i}) > EVALUATE script`),await n.evaluate(F.script);break;case"SET_VIEWPORT":let b=s;r.info(`Scenario "${o}" (worker ${i}) > SET_VIEWPORT width=${b.width}, height=${b.height}`),await n.setViewport({width:b.width,height:b.height});break;case"CLOSE":r.info(`Scenario "${o}" (worker ${i}) > CLOSE page`),await n.close();break;default:throw new Error(`Unhandled action type "${c}" while evaluating scenario "${o}" on worker ${i}`)}}};var bt=async(o,t={})=>{let{browsers:n={},logger:i=A,onPage:r}=t,s=O(o),l=new v(a=>{let g=process.env[a];if(i.debug(`Resolving env variable "${a}" with value ***`),g)return g;throw new Error(`Tried to resolve env variable ${a} but was empty.`)},i).resolve(s),u={};for(let a of l.scenarios){let g=a.browser||"chrome";u[g]||(u[g]=[]),u[g]?.push(a)}let d=["chrome","firefox"],f=Object.entries(u).map(async([a,g])=>{if(!g)return;if(!d.includes(a)){console.error(`Navigateur non support\xE9: ${a}. Les navigateurs support\xE9s sont: ${d.join(", ")}`);return}let S=["--no-sandbox","--disable-setuid-sandbox","--disable-dev-shm-usage","--disable-accelerated-2d-canvas","--disable-gpu","--ignore-certificate-errors"];a==="chrome"&&S.push("--start-maximized"),a==="firefox"&&S.push("--private");let m=n[a],h=!1;m||(m=await R.launch({headless:!1,channel:a,acceptInsecureCerts:!0,args:S}),h=!0);try{for(let p of g)await C(m,p,i,r)}catch(p){console.error(`Erreur lors de l'ex\xE9cution des sc\xE9narios pour ${a}:`,p)}finally{h&&m&&await m.close()}});await Promise.all(f)};0&&(module.exports={readYamlAndInterpret});
|
|
1
|
+
import*as B from"js-yaml";import*as F from"zod";import*as e from"zod";var b=e.number().int().nonnegative(),v=e.number().int().positive(),C=e.enum(["load","domcontentloaded","networkidle0","networkidle2"]),G=e.enum(["left","right","middle"]),N=e.enum(["chrome","firefox"]),M=e.strictObject({timeout:b.optional(),waitUntil:e.union([C,C.array()]).optional(),referer:e.string().optional()}).optional(),Z=e.strictObject({button:G.optional(),clickCount:v.optional(),delay:b.optional()}).optional(),D=e.strictObject({delay:b.optional()}).optional(),H=e.strictObject({visible:e.boolean().optional(),hidden:e.boolean().optional(),timeout:b.optional()}).optional(),K=e.strictObject({type:e.enum(["png","jpeg"]).optional(),quality:e.number().int().min(0).max(100).optional(),fullPage:e.boolean().optional(),clip:e.strictObject({x:e.number(),y:e.number(),width:e.number().positive(),height:e.number().positive()}).optional(),omitBackground:e.boolean().optional(),encoding:e.enum(["base64","binary"]).optional()}).optional(),q=e.strictObject({type:e.literal("GOTO"),url:e.string(),options:M}),Q=e.strictObject({type:e.literal("CLICK"),selector:e.string(),options:Z}),J=e.strictObject({type:e.literal("TYPE"),selector:e.string(),text:e.string(),options:D}),X=e.strictObject({type:e.literal("WAIT_FOR_SELECTOR"),selector:e.string(),options:H}),Y=e.strictObject({type:e.literal("WAIT_FOR_TIMEOUT"),timeout:b}),tt=e.strictObject({type:e.literal("WAIT"),milliseconds:b}),et=e.strictObject({type:e.literal("PAUSE")}),ot=e.strictObject({type:e.literal("SCREENSHOT"),path:e.string(),options:K}),rt=e.strictObject({type:e.literal("EVALUATE"),script:e.string()}),nt=e.strictObject({type:e.literal("SET_VIEWPORT"),width:v,height:v}),it=e.strictObject({type:e.literal("CLOSE")}),R=e.discriminatedUnion("type",[q,Q,J,X,Y,tt,et,ot,rt,nt,it]),st=e.strictObject({navigate:e.string()}),at=e.literal("waitForever"),ct=e.strictObject({wait:e.number().nonnegative()}),pt=e.strictObject({click:e.string()}),lt=e.strictObject({input:e.strictObject({selector:e.string(),text:e.string()})}),ut=e.union([st,at,ct,pt,lt,R]),gt=e.strictObject({name:e.string().optional(),browser:N.optional(),run:e.enum(["PARALLEL","SEQUENTIAL"]).optional(),initialDelaySeconds:e.number().nonnegative().optional(),workers:v.optional(),iterations:v.optional(),location:e.string().optional(),steps:e.array(ut).optional(),actions:e.array(R).optional()}),P=e.strictObject({scenarios:e.array(gt)});var L=r=>r instanceof Error?r.message:String(r),S=class extends Error{constructor(t,o){super(t,o),this.name="ConfigurationError"}},$=class extends Error{scenarioName;workerIndex;iteration;constructor(t,o,n,i){super(`Scenario "${t}" (worker ${o}, iteration ${n}) failed: ${L(i)}`,{cause:i}),this.name="WorkerError",this.scenarioName=t,this.workerIndex=o,this.iteration=n}},T=class extends Error{browser;constructor(t,o){super(`Failed to launch browser "${t}": ${L(o)}`,{cause:o}),this.name="BrowserLaunchError",this.browser=t}};var V=r=>{let t;try{t=B.load(r)}catch(n){throw new S(`Invalid YAML syntax: ${n instanceof Error?n.message:String(n)}`,{cause:n})}let o=P.safeParse(t);if(!o.success)throw new S(`Invalid scenario configuration:
|
|
2
|
+
${F.prettifyError(o.error)}`,{cause:o.error});return o.data};var A={debug(r){console.log(r)},info(r){console.log(r)},warn(r){console.log(r)},error(r){console.error(r)}};var dt=/^[A-Za-z_][A-Za-z0-9_]*$/,ht=/^\$([A-Za-z_][A-Za-z0-9_]*)$/,wt=/^\$([A-Za-z_][A-Za-z0-9_]*)\[workerIndex\]$/,St=/\$\$\{|\$\{([^}]*)\}/g,_='per-worker values ("$VAR[workerIndex]") are only supported as the whole value of location and navigate',O=class{envVarResolve;logger;constructor(t,o=A){this.envVarResolve=t,this.logger=o}resolve(t){return{scenarios:t.scenarios.map((o,n)=>this.resolveScenario(o,n+1))}}resolveScenario(t,o){let n={name:t.name||`Scenario ${o}`,workers:t.workers||1};this.logger.debug(`Resolving scenario "${n.name}" (${t.steps?.length??0} step(s), ${t.actions?.length??0} action(s))`);let i=[];try{t.location&&i.push(this.createNavigateAction(t.location,n));for(let s of t.steps??[])i.push(this.resolveStep(s,n));for(let s of t.actions??[])i.push(this.resolveAction(s))}catch(s){throw new S(`Scenario "${n.name}": ${s instanceof Error?s.message:String(s)}`,{cause:s})}let a=t.run==="SEQUENTIAL"?{delaySecondsBetweenWorkerInits:t.initialDelaySeconds||0}:{initialDelaySeconds:t.initialDelaySeconds||0};return{name:n.name,browser:t.browser||"chrome",run:a,workers:n.workers,iterations:t.iterations||1,actions:i}}resolveStep(t,o){return t==="waitForever"?{actionType:"WaitForever"}:"type"in t?this.resolveAction(t):"wait"in t?{actionType:"Wait",milliseconds:t.wait*1e3}:"navigate"in t?this.createNavigateAction(t.navigate,o):"click"in t?{actionType:"CLICK",selector:this.interpolate(t.click)}:{actionType:"TYPE",selector:this.interpolate(t.input.selector),text:this.interpolate(t.input.text)}}resolveAction(t){switch(this.logger.debug(`Resolving action: ${t.type}`),t.type){case"GOTO":return{actionType:"GOTO",url:this.resolveWholeValueOrInterpolate(t.url),options:t.options&&{...t.options,referer:t.options.referer&&this.interpolate(t.options.referer)}};case"WAIT":return{actionType:"Wait",milliseconds:t.milliseconds};case"CLICK":return{actionType:"CLICK",selector:this.interpolate(t.selector),options:t.options};case"TYPE":return{actionType:"TYPE",selector:this.interpolate(t.selector),text:this.interpolate(t.text),options:t.options};case"WAIT_FOR_SELECTOR":return{actionType:"WAIT_FOR_SELECTOR",selector:this.interpolate(t.selector),options:t.options};case"WAIT_FOR_TIMEOUT":return{actionType:"WAIT_FOR_TIMEOUT",timeout:t.timeout};case"PAUSE":return{actionType:"PAUSE"};case"SCREENSHOT":return{actionType:"SCREENSHOT",path:this.resolveWholeValueOrInterpolate(t.path),options:t.options};case"EVALUATE":return{actionType:"EVALUATE",script:t.script};case"SET_VIEWPORT":return{actionType:"SET_VIEWPORT",width:t.width,height:t.height};case"CLOSE":return{actionType:"CLOSE"};default:return t}}createNavigateAction(t,o){let n=wt.exec(t);if(n){let i=n[1],a=this.env(i).split(",").map(s=>s.trim());if(a.some(s=>s===""))throw new Error(`environment variable "${i}" (used as "${t}") contains an empty value`);if(a.length<o.workers)throw new Error(`environment variable "${i}" (used as "${t}") provides ${a.length} value(s) for ${o.workers} worker(s)`);return{actionType:"Navigate",location:{workerIndex:a}}}return{actionType:"Navigate",location:{url:this.resolveWholeValueOrInterpolate(t)}}}resolveWholeValueOrInterpolate(t){let o=ht.exec(t);if(o)return this.env(o[1]);if(/^\$[A-Za-z_]/.test(t))throw new Error(`unsupported environment variable reference "${t}": use "$VAR" for the whole value or "\${VAR}" inside a value`+(t.endsWith("[workerIndex]")?`; ${_}`:""));return this.interpolate(t)}interpolate(t){return t.replace(St,(o,n)=>{if(o==="$${")return"${";if(n===void 0||!dt.test(n))throw new Error(`invalid placeholder "${o}": expected \${VAR_NAME}`+(n?.endsWith("[workerIndex]")?`; ${_}`:""));return this.env(n)})}env(t){return this.logger.debug(`Resolving environment variable "${t}"`),this.envVarResolve(t)}};import{setTimeout as k}from"node:timers/promises";function yt(r,t){let o=t||"png";return r.endsWith(".png")||r.endsWith(".jpeg")||r.endsWith(".jpg")||r.endsWith(".webp")?r:`${r}.${o}`}var At=(r,t)=>"delaySecondsBetweenWorkerInits"in r?r.delaySecondsBetweenWorkerInits*t:r.initialDelaySeconds,j=async(r,t,o=A,n)=>{let i=[],a=async s=>{let p=At(t.run,s),h="delaySecondsBetweenWorkerInits"in t.run?"SEQUENTIAL":"PARALLEL";o.info(`Scenario "${t.name}" (worker ${s}) started (${h}, starting in ${p}s).`),await k(p*1e3);for(let u=0;u<t.iterations;u++)try{await ft(r,t,s,u,o,n)}catch(y){let m=new $(t.name,s,u,y);o.error(m.message),i.push(m)}};if(await Promise.all(Array.from({length:t.workers},(s,p)=>a(p))),i.length>0){i.sort((p,h)=>p.workerIndex-h.workerIndex||p.iteration-h.iteration);let s=t.workers*t.iterations;throw new AggregateError(i,`Scenario "${t.name}": ${i.length}/${s} iteration(s) failed`)}},ft=async(r,t,o,n,i,a)=>{let s;try{s=await r.newPage(),a&&await a(s,{scenarioName:t.name,workerIndex:o,iteration:n}),await bt(t.name,t.actions,s,o,i)}finally{s&&!s.isClosed()&&await s.close().catch(p=>i.warn(`Scenario "${t.name}" (worker ${o}, iteration ${n}) > failed to close page: ${p}`))}},bt=async(r,t,o,n,i=A)=>{for(let a of t){let{actionType:s}=a;switch(s){case"Navigate":let p=a;"url"in p.location?(i.info(`Scenario "${r}" (worker ${n}) > Navigating to location "${p.location.url}"`),await o.goto(p.location.url)):(i.info(`Scenario "${r}" (worker ${n}) > Navigating to indexed location "${p.location.workerIndex[n]}"`),await o.goto(p.location.workerIndex[n]));break;case"Wait":let h=a;i.info(`Scenario "${r}" (worker ${n}) > Waiting ${h.milliseconds} milliseconds...`),await k(h.milliseconds);break;case"WaitForever":for(i.info(`Scenario "${r}" (worker ${n}) > Waiting forever...`);;)await k(3600*1e3);break;case"GOTO":let u=a;i.info(`Scenario "${r}" (worker ${n}) > GOTO "${u.url}"`);let y={timeout:u.options?.timeout,waitUntil:u.options?.waitUntil,referer:u.options?.referer};await o.goto(u.url,y);break;case"CLICK":let m=a;i.info(`Scenario "${r}" (worker ${n}) > CLICK on "${m.selector}"`);let W={button:m.options?.button,clickCount:m.options?.clickCount,delay:m.options?.delay};await o.click(m.selector,W);break;case"TYPE":let w=a;i.info(`Scenario "${r}" (worker ${n}) > TYPE into "${w.selector}"`),await o.type(w.selector,w.text,w.options);break;case"WAIT_FOR_SELECTOR":let c=a;i.info(`Scenario "${r}" (worker ${n}) > WAIT_FOR_SELECTOR "${c.selector}"`),await o.waitForSelector(c.selector,c.options);break;case"WAIT_FOR_TIMEOUT":let g=a;i.info(`Scenario "${r}" (worker ${n}) > WAIT_FOR_TIMEOUT ${g.timeout} milliseconds`),await k(g.timeout);break;case"PAUSE":if(!process.stdin.isTTY){i.warn(`Scenario "${r}" (worker ${n}) > PAUSE ignored: no interactive terminal`);break}i.info(`Scenario "${r}" (worker ${n}) > PAUSE, press Enter to continue...`),await new Promise(f=>{process.stdin.resume(),process.stdin.once("data",()=>{process.stdin.pause(),f()})});break;case"SCREENSHOT":let l=a;i.info(`Scenario "${r}" (worker ${n}) > SCREENSHOT "${l.path}"`);let z={path:yt(l.path,l.options?.type),type:l.options?.type,quality:l.options?.quality,fullPage:l.options?.fullPage,clip:l.options?.clip,omitBackground:l.options?.omitBackground,encoding:l.options?.encoding};await o.screenshot(z);break;case"EVALUATE":let E=a;i.info(`Scenario "${r}" (worker ${n}) > EVALUATE script`),await o.evaluate(E.script);break;case"SET_VIEWPORT":let d=a;i.info(`Scenario "${r}" (worker ${n}) > SET_VIEWPORT width=${d.width}, height=${d.height}`),await o.setViewport({width:d.width,height:d.height});break;case"CLOSE":i.info(`Scenario "${r}" (worker ${n}) > CLOSE page`),await o.close();break;default:throw new Error(`Unhandled action type "${s}" while evaluating scenario "${r}" on worker ${n}`)}}};import*as I from"puppeteer";var vt=(r,t)=>{if(r==="firefox")return[];let o=["--no-sandbox","--disable-setuid-sandbox","--disable-dev-shm-usage","--disable-accelerated-2d-canvas","--disable-gpu","--ignore-certificate-errors"];return t||o.push("--start-maximized"),o},$t=(r,t,o={})=>({browser:r,headless:t,acceptInsecureCerts:!0,...o,args:[...vt(r,t),...o.args??[]]}),U=(r,t,o)=>I.launch($t(r,t,o));var he=async(r,t={})=>{let{browsers:o={},logger:n=A,onPage:i,headless:a=!0,launchOptions:s={}}=t,p=V(r),u=new O(c=>{let g=process.env[c];if(g)return g;throw new Error(`environment variable "${c}" is not set (or empty)`)},n).resolve(p),y=new Map;for(let c of u.scenarios){let g=y.get(c.browser)??[];g.push(c),y.set(c.browser,g)}let m=async(c,g)=>{let l=o[c],z=!l;if(!l)try{n.info(`Launching ${c} (headless: ${a})`),l=await U(c,a,s[c])}catch(d){let f=new T(c,d);return n.error(f.message),[f]}let E=l;try{return(await Promise.all(g.map(async f=>{try{return await j(E,f,n,i),[]}catch(x){return x instanceof AggregateError?x.errors:[x]}}))).flat()}finally{z&&await E.close().catch(d=>n.warn(`Failed to close ${c}: ${d}`))}},w=(await Promise.all([...y].map(([c,g])=>m(c,g)))).flat();if(w.length>0)throw new AggregateError(w,`${w.length} error(s) occurred while running scenarios`)};export{T as BrowserLaunchError,S as ConfigurationError,$ as WorkerError,he as readYamlAndInterpret};
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import * as p from 'puppeteer';
|
|
2
|
-
import { YpLogger } from '../core/logger';
|
|
3
|
-
import { OnPageCallback, Scenario } from '../core/model';
|
|
4
|
-
|
|
2
|
+
import { YpLogger } from '../core/logger.js';
|
|
3
|
+
import { OnPageCallback, Scenario } from '../core/model.js';
|
|
4
|
+
/**
|
|
5
|
+
* Runs every worker of the scenario, each one running all its iterations.
|
|
6
|
+
* A failing iteration interrupts neither the next iterations of the worker
|
|
7
|
+
* nor the other workers. Once everything is done, the promise rejects with an
|
|
8
|
+
* `AggregateError` of `WorkerError` (one per failed iteration) if anything failed.
|
|
9
|
+
*/
|
|
10
|
+
export declare const evalScenario: (browser: p.Browser, scenario: Scenario, logger?: YpLogger, onPage?: OnPageCallback) => Promise<void>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as p from 'puppeteer';
|
|
2
|
+
import { SupportedBrowser } from '../core/model.js';
|
|
3
|
+
/**
|
|
4
|
+
* Builds Puppeteer launch options for the given browser. The browser is
|
|
5
|
+
* selected with `browser` (not `channel`, which only accepts Chrome release
|
|
6
|
+
* channels). User options override defaults; user `args` are appended.
|
|
7
|
+
*/
|
|
8
|
+
export declare const buildLaunchOptions: (browser: SupportedBrowser, headless: boolean, extra?: p.LaunchOptions) => p.LaunchOptions;
|
|
9
|
+
export declare const launchBrowser: (browser: SupportedBrowser, headless: boolean, extra?: p.LaunchOptions) => Promise<p.Browser>;
|
package/dist/yaml/parser.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { RootYaml } from './validator';
|
|
1
|
+
import { RootYaml } from './validator.js';
|
|
2
2
|
export declare const parseYaml: (yamlContent: string) => RootYaml;
|
package/dist/yaml/resolver.d.ts
CHANGED
|
@@ -1,10 +1,19 @@
|
|
|
1
|
-
import { YpLogger } from '../core/logger';
|
|
2
|
-
import { Scenario } from '../core/model';
|
|
3
|
-
import { RootYaml } from './validator';
|
|
4
|
-
type EnvVarResolve = (
|
|
1
|
+
import { YpLogger } from '../core/logger.js';
|
|
2
|
+
import { Scenario } from '../core/model.js';
|
|
3
|
+
import { RootYaml } from './validator.js';
|
|
4
|
+
type EnvVarResolve = (name: string) => string;
|
|
5
|
+
/**
|
|
6
|
+
* Transforms the validated YAML into the domain model and resolves
|
|
7
|
+
* environment variables:
|
|
8
|
+
* - `${VAR}` anywhere in URLs, selectors, typed texts and screenshot paths;
|
|
9
|
+
* - `$VAR` as the whole value of location, navigate, GOTO url and SCREENSHOT path;
|
|
10
|
+
* - `$VAR[workerIndex]` as the whole value of location and navigate.
|
|
11
|
+
*
|
|
12
|
+
* Resolved values are never logged: they may contain credentials.
|
|
13
|
+
*/
|
|
5
14
|
export declare class Resolver {
|
|
6
|
-
private envVarResolve;
|
|
7
|
-
private logger;
|
|
15
|
+
private readonly envVarResolve;
|
|
16
|
+
private readonly logger;
|
|
8
17
|
constructor(envVarResolve: EnvVarResolve, logger?: YpLogger);
|
|
9
18
|
resolve(yaml: RootYaml): {
|
|
10
19
|
scenarios: Scenario[];
|
|
@@ -13,7 +22,10 @@ export declare class Resolver {
|
|
|
13
22
|
private resolveStep;
|
|
14
23
|
private resolveAction;
|
|
15
24
|
private createNavigateAction;
|
|
16
|
-
|
|
17
|
-
private
|
|
25
|
+
/** `$VAR` as the whole value, or `${VAR}` placeholders anywhere. */
|
|
26
|
+
private resolveWholeValueOrInterpolate;
|
|
27
|
+
/** Replaces `${VAR}` placeholders; `$${` produces a literal `${`. */
|
|
28
|
+
private interpolate;
|
|
29
|
+
private env;
|
|
18
30
|
}
|
|
19
31
|
export {};
|
package/dist/yaml/validator.d.ts
CHANGED
|
@@ -1,64 +1,64 @@
|
|
|
1
1
|
import * as z from 'zod';
|
|
2
2
|
declare const actionSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
3
|
-
type: z.ZodLiteral<"WAIT">;
|
|
4
|
-
milliseconds: z.ZodNumber;
|
|
5
|
-
}, z.core.$strip> | z.ZodObject<{
|
|
6
3
|
type: z.ZodLiteral<"GOTO">;
|
|
7
4
|
url: z.ZodString;
|
|
8
5
|
options: z.ZodOptional<z.ZodObject<{
|
|
9
6
|
timeout: z.ZodOptional<z.ZodNumber>;
|
|
10
7
|
waitUntil: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
|
|
11
|
-
load: "load";
|
|
12
8
|
domcontentloaded: "domcontentloaded";
|
|
9
|
+
load: "load";
|
|
13
10
|
networkidle0: "networkidle0";
|
|
14
11
|
networkidle2: "networkidle2";
|
|
15
12
|
}>, z.ZodArray<z.ZodEnum<{
|
|
16
|
-
load: "load";
|
|
17
13
|
domcontentloaded: "domcontentloaded";
|
|
14
|
+
load: "load";
|
|
18
15
|
networkidle0: "networkidle0";
|
|
19
16
|
networkidle2: "networkidle2";
|
|
20
17
|
}>>]>>;
|
|
21
18
|
referer: z.ZodOptional<z.ZodString>;
|
|
22
|
-
}, z.core.$
|
|
23
|
-
}, z.core.$
|
|
19
|
+
}, z.core.$strict>>;
|
|
20
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
24
21
|
type: z.ZodLiteral<"CLICK">;
|
|
25
22
|
selector: z.ZodString;
|
|
26
23
|
options: z.ZodOptional<z.ZodObject<{
|
|
27
24
|
button: z.ZodOptional<z.ZodEnum<{
|
|
28
25
|
left: "left";
|
|
29
|
-
right: "right";
|
|
30
26
|
middle: "middle";
|
|
27
|
+
right: "right";
|
|
31
28
|
}>>;
|
|
32
29
|
clickCount: z.ZodOptional<z.ZodNumber>;
|
|
33
30
|
delay: z.ZodOptional<z.ZodNumber>;
|
|
34
|
-
}, z.core.$
|
|
35
|
-
}, z.core.$
|
|
31
|
+
}, z.core.$strict>>;
|
|
32
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
36
33
|
type: z.ZodLiteral<"TYPE">;
|
|
37
34
|
selector: z.ZodString;
|
|
38
35
|
text: z.ZodString;
|
|
39
36
|
options: z.ZodOptional<z.ZodObject<{
|
|
40
37
|
delay: z.ZodOptional<z.ZodNumber>;
|
|
41
|
-
}, z.core.$
|
|
42
|
-
}, z.core.$
|
|
38
|
+
}, z.core.$strict>>;
|
|
39
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
43
40
|
type: z.ZodLiteral<"WAIT_FOR_SELECTOR">;
|
|
44
41
|
selector: z.ZodString;
|
|
45
42
|
options: z.ZodOptional<z.ZodObject<{
|
|
46
43
|
visible: z.ZodOptional<z.ZodBoolean>;
|
|
47
44
|
hidden: z.ZodOptional<z.ZodBoolean>;
|
|
48
45
|
timeout: z.ZodOptional<z.ZodNumber>;
|
|
49
|
-
}, z.core.$
|
|
50
|
-
}, z.core.$
|
|
46
|
+
}, z.core.$strict>>;
|
|
47
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
51
48
|
type: z.ZodLiteral<"WAIT_FOR_TIMEOUT">;
|
|
52
49
|
timeout: z.ZodNumber;
|
|
53
|
-
}, z.core.$
|
|
50
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
51
|
+
type: z.ZodLiteral<"WAIT">;
|
|
52
|
+
milliseconds: z.ZodNumber;
|
|
53
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
54
54
|
type: z.ZodLiteral<"PAUSE">;
|
|
55
|
-
}, z.core.$
|
|
55
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
56
56
|
type: z.ZodLiteral<"SCREENSHOT">;
|
|
57
57
|
path: z.ZodString;
|
|
58
58
|
options: z.ZodOptional<z.ZodObject<{
|
|
59
59
|
type: z.ZodOptional<z.ZodEnum<{
|
|
60
|
-
png: "png";
|
|
61
60
|
jpeg: "jpeg";
|
|
61
|
+
png: "png";
|
|
62
62
|
}>>;
|
|
63
63
|
quality: z.ZodOptional<z.ZodNumber>;
|
|
64
64
|
fullPage: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -67,82 +67,95 @@ declare const actionSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
67
67
|
y: z.ZodNumber;
|
|
68
68
|
width: z.ZodNumber;
|
|
69
69
|
height: z.ZodNumber;
|
|
70
|
-
}, z.core.$
|
|
70
|
+
}, z.core.$strict>>;
|
|
71
71
|
omitBackground: z.ZodOptional<z.ZodBoolean>;
|
|
72
72
|
encoding: z.ZodOptional<z.ZodEnum<{
|
|
73
73
|
base64: "base64";
|
|
74
74
|
binary: "binary";
|
|
75
75
|
}>>;
|
|
76
|
-
}, z.core.$
|
|
77
|
-
}, z.core.$
|
|
76
|
+
}, z.core.$strict>>;
|
|
77
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
78
78
|
type: z.ZodLiteral<"EVALUATE">;
|
|
79
|
-
script: z.
|
|
80
|
-
}, z.core.$
|
|
79
|
+
script: z.ZodString;
|
|
80
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
81
81
|
type: z.ZodLiteral<"SET_VIEWPORT">;
|
|
82
82
|
width: z.ZodNumber;
|
|
83
83
|
height: z.ZodNumber;
|
|
84
|
-
}, z.core.$
|
|
84
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
85
85
|
type: z.ZodLiteral<"CLOSE">;
|
|
86
|
-
}, z.core.$
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
86
|
+
}, z.core.$strict>], "type">;
|
|
87
|
+
export type ActionYaml = z.infer<typeof actionSchema>;
|
|
88
|
+
declare const stepSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
89
|
+
navigate: z.ZodString;
|
|
90
|
+
}, z.core.$strict>, z.ZodLiteral<"waitForever">, z.ZodObject<{
|
|
91
|
+
wait: z.ZodNumber;
|
|
92
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
93
|
+
click: z.ZodString;
|
|
94
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
95
|
+
input: z.ZodObject<{
|
|
96
|
+
selector: z.ZodString;
|
|
97
|
+
text: z.ZodString;
|
|
98
|
+
}, z.core.$strict>;
|
|
99
|
+
}, z.core.$strict>, z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
90
100
|
type: z.ZodLiteral<"GOTO">;
|
|
91
101
|
url: z.ZodString;
|
|
92
102
|
options: z.ZodOptional<z.ZodObject<{
|
|
93
103
|
timeout: z.ZodOptional<z.ZodNumber>;
|
|
94
104
|
waitUntil: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
|
|
95
|
-
load: "load";
|
|
96
105
|
domcontentloaded: "domcontentloaded";
|
|
106
|
+
load: "load";
|
|
97
107
|
networkidle0: "networkidle0";
|
|
98
108
|
networkidle2: "networkidle2";
|
|
99
109
|
}>, z.ZodArray<z.ZodEnum<{
|
|
100
|
-
load: "load";
|
|
101
110
|
domcontentloaded: "domcontentloaded";
|
|
111
|
+
load: "load";
|
|
102
112
|
networkidle0: "networkidle0";
|
|
103
113
|
networkidle2: "networkidle2";
|
|
104
114
|
}>>]>>;
|
|
105
115
|
referer: z.ZodOptional<z.ZodString>;
|
|
106
|
-
}, z.core.$
|
|
107
|
-
}, z.core.$
|
|
116
|
+
}, z.core.$strict>>;
|
|
117
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
108
118
|
type: z.ZodLiteral<"CLICK">;
|
|
109
119
|
selector: z.ZodString;
|
|
110
120
|
options: z.ZodOptional<z.ZodObject<{
|
|
111
121
|
button: z.ZodOptional<z.ZodEnum<{
|
|
112
122
|
left: "left";
|
|
113
|
-
right: "right";
|
|
114
123
|
middle: "middle";
|
|
124
|
+
right: "right";
|
|
115
125
|
}>>;
|
|
116
126
|
clickCount: z.ZodOptional<z.ZodNumber>;
|
|
117
127
|
delay: z.ZodOptional<z.ZodNumber>;
|
|
118
|
-
}, z.core.$
|
|
119
|
-
}, z.core.$
|
|
128
|
+
}, z.core.$strict>>;
|
|
129
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
120
130
|
type: z.ZodLiteral<"TYPE">;
|
|
121
131
|
selector: z.ZodString;
|
|
122
132
|
text: z.ZodString;
|
|
123
133
|
options: z.ZodOptional<z.ZodObject<{
|
|
124
134
|
delay: z.ZodOptional<z.ZodNumber>;
|
|
125
|
-
}, z.core.$
|
|
126
|
-
}, z.core.$
|
|
135
|
+
}, z.core.$strict>>;
|
|
136
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
127
137
|
type: z.ZodLiteral<"WAIT_FOR_SELECTOR">;
|
|
128
138
|
selector: z.ZodString;
|
|
129
139
|
options: z.ZodOptional<z.ZodObject<{
|
|
130
140
|
visible: z.ZodOptional<z.ZodBoolean>;
|
|
131
141
|
hidden: z.ZodOptional<z.ZodBoolean>;
|
|
132
142
|
timeout: z.ZodOptional<z.ZodNumber>;
|
|
133
|
-
}, z.core.$
|
|
134
|
-
}, z.core.$
|
|
143
|
+
}, z.core.$strict>>;
|
|
144
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
135
145
|
type: z.ZodLiteral<"WAIT_FOR_TIMEOUT">;
|
|
136
146
|
timeout: z.ZodNumber;
|
|
137
|
-
}, z.core.$
|
|
147
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
148
|
+
type: z.ZodLiteral<"WAIT">;
|
|
149
|
+
milliseconds: z.ZodNumber;
|
|
150
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
138
151
|
type: z.ZodLiteral<"PAUSE">;
|
|
139
|
-
}, z.core.$
|
|
152
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
140
153
|
type: z.ZodLiteral<"SCREENSHOT">;
|
|
141
154
|
path: z.ZodString;
|
|
142
155
|
options: z.ZodOptional<z.ZodObject<{
|
|
143
156
|
type: z.ZodOptional<z.ZodEnum<{
|
|
144
|
-
png: "png";
|
|
145
157
|
jpeg: "jpeg";
|
|
158
|
+
png: "png";
|
|
146
159
|
}>>;
|
|
147
160
|
quality: z.ZodOptional<z.ZodNumber>;
|
|
148
161
|
fullPage: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -151,36 +164,23 @@ declare const actionSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
151
164
|
y: z.ZodNumber;
|
|
152
165
|
width: z.ZodNumber;
|
|
153
166
|
height: z.ZodNumber;
|
|
154
|
-
}, z.core.$
|
|
167
|
+
}, z.core.$strict>>;
|
|
155
168
|
omitBackground: z.ZodOptional<z.ZodBoolean>;
|
|
156
169
|
encoding: z.ZodOptional<z.ZodEnum<{
|
|
157
170
|
base64: "base64";
|
|
158
171
|
binary: "binary";
|
|
159
172
|
}>>;
|
|
160
|
-
}, z.core.$
|
|
161
|
-
}, z.core.$
|
|
173
|
+
}, z.core.$strict>>;
|
|
174
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
162
175
|
type: z.ZodLiteral<"EVALUATE">;
|
|
163
|
-
script: z.
|
|
164
|
-
}, z.core.$
|
|
176
|
+
script: z.ZodString;
|
|
177
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
165
178
|
type: z.ZodLiteral<"SET_VIEWPORT">;
|
|
166
179
|
width: z.ZodNumber;
|
|
167
180
|
height: z.ZodNumber;
|
|
168
|
-
}, z.core.$
|
|
181
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
169
182
|
type: z.ZodLiteral<"CLOSE">;
|
|
170
|
-
}, z.core.$
|
|
171
|
-
export type ActionYaml = z.infer<typeof actionSchema>;
|
|
172
|
-
declare const stepSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
173
|
-
navigate: z.ZodString;
|
|
174
|
-
}, z.core.$strip>, z.ZodLiteral<"waitForever">, z.ZodObject<{
|
|
175
|
-
wait: z.ZodNumber;
|
|
176
|
-
}, z.core.$strip>, z.ZodObject<{
|
|
177
|
-
click: z.ZodString;
|
|
178
|
-
}, z.core.$strip>, z.ZodObject<{
|
|
179
|
-
input: z.ZodObject<{
|
|
180
|
-
selector: z.ZodString;
|
|
181
|
-
text: z.ZodString;
|
|
182
|
-
}, z.core.$strip>;
|
|
183
|
-
}, z.core.$strip>]>;
|
|
183
|
+
}, z.core.$strict>], "type">]>;
|
|
184
184
|
export type StepYaml = z.infer<typeof stepSchema>;
|
|
185
185
|
declare const scenarioSchema: z.ZodObject<{
|
|
186
186
|
name: z.ZodOptional<z.ZodString>;
|
|
@@ -198,76 +198,75 @@ declare const scenarioSchema: z.ZodObject<{
|
|
|
198
198
|
location: z.ZodOptional<z.ZodString>;
|
|
199
199
|
steps: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
|
|
200
200
|
navigate: z.ZodString;
|
|
201
|
-
}, z.core.$
|
|
201
|
+
}, z.core.$strict>, z.ZodLiteral<"waitForever">, z.ZodObject<{
|
|
202
202
|
wait: z.ZodNumber;
|
|
203
|
-
}, z.core.$
|
|
203
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
204
204
|
click: z.ZodString;
|
|
205
|
-
}, z.core.$
|
|
205
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
206
206
|
input: z.ZodObject<{
|
|
207
207
|
selector: z.ZodString;
|
|
208
208
|
text: z.ZodString;
|
|
209
|
-
}, z.core.$
|
|
210
|
-
}, z.core.$
|
|
211
|
-
actions: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
212
|
-
type: z.ZodLiteral<"WAIT">;
|
|
213
|
-
milliseconds: z.ZodNumber;
|
|
214
|
-
}, z.core.$strip> | z.ZodObject<{
|
|
209
|
+
}, z.core.$strict>;
|
|
210
|
+
}, z.core.$strict>, z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
215
211
|
type: z.ZodLiteral<"GOTO">;
|
|
216
212
|
url: z.ZodString;
|
|
217
213
|
options: z.ZodOptional<z.ZodObject<{
|
|
218
214
|
timeout: z.ZodOptional<z.ZodNumber>;
|
|
219
215
|
waitUntil: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
|
|
220
|
-
load: "load";
|
|
221
216
|
domcontentloaded: "domcontentloaded";
|
|
217
|
+
load: "load";
|
|
222
218
|
networkidle0: "networkidle0";
|
|
223
219
|
networkidle2: "networkidle2";
|
|
224
220
|
}>, z.ZodArray<z.ZodEnum<{
|
|
225
|
-
load: "load";
|
|
226
221
|
domcontentloaded: "domcontentloaded";
|
|
222
|
+
load: "load";
|
|
227
223
|
networkidle0: "networkidle0";
|
|
228
224
|
networkidle2: "networkidle2";
|
|
229
225
|
}>>]>>;
|
|
230
226
|
referer: z.ZodOptional<z.ZodString>;
|
|
231
|
-
}, z.core.$
|
|
232
|
-
}, z.core.$
|
|
227
|
+
}, z.core.$strict>>;
|
|
228
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
233
229
|
type: z.ZodLiteral<"CLICK">;
|
|
234
230
|
selector: z.ZodString;
|
|
235
231
|
options: z.ZodOptional<z.ZodObject<{
|
|
236
232
|
button: z.ZodOptional<z.ZodEnum<{
|
|
237
233
|
left: "left";
|
|
238
|
-
right: "right";
|
|
239
234
|
middle: "middle";
|
|
235
|
+
right: "right";
|
|
240
236
|
}>>;
|
|
241
237
|
clickCount: z.ZodOptional<z.ZodNumber>;
|
|
242
238
|
delay: z.ZodOptional<z.ZodNumber>;
|
|
243
|
-
}, z.core.$
|
|
244
|
-
}, z.core.$
|
|
239
|
+
}, z.core.$strict>>;
|
|
240
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
245
241
|
type: z.ZodLiteral<"TYPE">;
|
|
246
242
|
selector: z.ZodString;
|
|
247
243
|
text: z.ZodString;
|
|
248
244
|
options: z.ZodOptional<z.ZodObject<{
|
|
249
245
|
delay: z.ZodOptional<z.ZodNumber>;
|
|
250
|
-
}, z.core.$
|
|
251
|
-
}, z.core.$
|
|
246
|
+
}, z.core.$strict>>;
|
|
247
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
252
248
|
type: z.ZodLiteral<"WAIT_FOR_SELECTOR">;
|
|
253
249
|
selector: z.ZodString;
|
|
254
250
|
options: z.ZodOptional<z.ZodObject<{
|
|
255
251
|
visible: z.ZodOptional<z.ZodBoolean>;
|
|
256
252
|
hidden: z.ZodOptional<z.ZodBoolean>;
|
|
257
253
|
timeout: z.ZodOptional<z.ZodNumber>;
|
|
258
|
-
}, z.core.$
|
|
259
|
-
}, z.core.$
|
|
254
|
+
}, z.core.$strict>>;
|
|
255
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
260
256
|
type: z.ZodLiteral<"WAIT_FOR_TIMEOUT">;
|
|
261
257
|
timeout: z.ZodNumber;
|
|
262
|
-
}, z.core.$
|
|
258
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
259
|
+
type: z.ZodLiteral<"WAIT">;
|
|
260
|
+
milliseconds: z.ZodNumber;
|
|
261
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
263
262
|
type: z.ZodLiteral<"PAUSE">;
|
|
264
|
-
}, z.core.$
|
|
263
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
265
264
|
type: z.ZodLiteral<"SCREENSHOT">;
|
|
266
265
|
path: z.ZodString;
|
|
267
266
|
options: z.ZodOptional<z.ZodObject<{
|
|
268
267
|
type: z.ZodOptional<z.ZodEnum<{
|
|
269
|
-
png: "png";
|
|
270
268
|
jpeg: "jpeg";
|
|
269
|
+
png: "png";
|
|
271
270
|
}>>;
|
|
272
271
|
quality: z.ZodOptional<z.ZodNumber>;
|
|
273
272
|
fullPage: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -276,82 +275,83 @@ declare const scenarioSchema: z.ZodObject<{
|
|
|
276
275
|
y: z.ZodNumber;
|
|
277
276
|
width: z.ZodNumber;
|
|
278
277
|
height: z.ZodNumber;
|
|
279
|
-
}, z.core.$
|
|
278
|
+
}, z.core.$strict>>;
|
|
280
279
|
omitBackground: z.ZodOptional<z.ZodBoolean>;
|
|
281
280
|
encoding: z.ZodOptional<z.ZodEnum<{
|
|
282
281
|
base64: "base64";
|
|
283
282
|
binary: "binary";
|
|
284
283
|
}>>;
|
|
285
|
-
}, z.core.$
|
|
286
|
-
}, z.core.$
|
|
284
|
+
}, z.core.$strict>>;
|
|
285
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
287
286
|
type: z.ZodLiteral<"EVALUATE">;
|
|
288
|
-
script: z.
|
|
289
|
-
}, z.core.$
|
|
287
|
+
script: z.ZodString;
|
|
288
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
290
289
|
type: z.ZodLiteral<"SET_VIEWPORT">;
|
|
291
290
|
width: z.ZodNumber;
|
|
292
291
|
height: z.ZodNumber;
|
|
293
|
-
}, z.core.$
|
|
292
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
294
293
|
type: z.ZodLiteral<"CLOSE">;
|
|
295
|
-
}, z.core.$
|
|
296
|
-
|
|
297
|
-
milliseconds: z.ZodNumber;
|
|
298
|
-
}, z.core.$strip> | z.ZodObject<{
|
|
294
|
+
}, z.core.$strict>], "type">]>>>;
|
|
295
|
+
actions: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
299
296
|
type: z.ZodLiteral<"GOTO">;
|
|
300
297
|
url: z.ZodString;
|
|
301
298
|
options: z.ZodOptional<z.ZodObject<{
|
|
302
299
|
timeout: z.ZodOptional<z.ZodNumber>;
|
|
303
300
|
waitUntil: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
|
|
304
|
-
load: "load";
|
|
305
301
|
domcontentloaded: "domcontentloaded";
|
|
302
|
+
load: "load";
|
|
306
303
|
networkidle0: "networkidle0";
|
|
307
304
|
networkidle2: "networkidle2";
|
|
308
305
|
}>, z.ZodArray<z.ZodEnum<{
|
|
309
|
-
load: "load";
|
|
310
306
|
domcontentloaded: "domcontentloaded";
|
|
307
|
+
load: "load";
|
|
311
308
|
networkidle0: "networkidle0";
|
|
312
309
|
networkidle2: "networkidle2";
|
|
313
310
|
}>>]>>;
|
|
314
311
|
referer: z.ZodOptional<z.ZodString>;
|
|
315
|
-
}, z.core.$
|
|
316
|
-
}, z.core.$
|
|
312
|
+
}, z.core.$strict>>;
|
|
313
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
317
314
|
type: z.ZodLiteral<"CLICK">;
|
|
318
315
|
selector: z.ZodString;
|
|
319
316
|
options: z.ZodOptional<z.ZodObject<{
|
|
320
317
|
button: z.ZodOptional<z.ZodEnum<{
|
|
321
318
|
left: "left";
|
|
322
|
-
right: "right";
|
|
323
319
|
middle: "middle";
|
|
320
|
+
right: "right";
|
|
324
321
|
}>>;
|
|
325
322
|
clickCount: z.ZodOptional<z.ZodNumber>;
|
|
326
323
|
delay: z.ZodOptional<z.ZodNumber>;
|
|
327
|
-
}, z.core.$
|
|
328
|
-
}, z.core.$
|
|
324
|
+
}, z.core.$strict>>;
|
|
325
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
329
326
|
type: z.ZodLiteral<"TYPE">;
|
|
330
327
|
selector: z.ZodString;
|
|
331
328
|
text: z.ZodString;
|
|
332
329
|
options: z.ZodOptional<z.ZodObject<{
|
|
333
330
|
delay: z.ZodOptional<z.ZodNumber>;
|
|
334
|
-
}, z.core.$
|
|
335
|
-
}, z.core.$
|
|
331
|
+
}, z.core.$strict>>;
|
|
332
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
336
333
|
type: z.ZodLiteral<"WAIT_FOR_SELECTOR">;
|
|
337
334
|
selector: z.ZodString;
|
|
338
335
|
options: z.ZodOptional<z.ZodObject<{
|
|
339
336
|
visible: z.ZodOptional<z.ZodBoolean>;
|
|
340
337
|
hidden: z.ZodOptional<z.ZodBoolean>;
|
|
341
338
|
timeout: z.ZodOptional<z.ZodNumber>;
|
|
342
|
-
}, z.core.$
|
|
343
|
-
}, z.core.$
|
|
339
|
+
}, z.core.$strict>>;
|
|
340
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
344
341
|
type: z.ZodLiteral<"WAIT_FOR_TIMEOUT">;
|
|
345
342
|
timeout: z.ZodNumber;
|
|
346
|
-
}, z.core.$
|
|
343
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
344
|
+
type: z.ZodLiteral<"WAIT">;
|
|
345
|
+
milliseconds: z.ZodNumber;
|
|
346
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
347
347
|
type: z.ZodLiteral<"PAUSE">;
|
|
348
|
-
}, z.core.$
|
|
348
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
349
349
|
type: z.ZodLiteral<"SCREENSHOT">;
|
|
350
350
|
path: z.ZodString;
|
|
351
351
|
options: z.ZodOptional<z.ZodObject<{
|
|
352
352
|
type: z.ZodOptional<z.ZodEnum<{
|
|
353
|
-
png: "png";
|
|
354
353
|
jpeg: "jpeg";
|
|
354
|
+
png: "png";
|
|
355
355
|
}>>;
|
|
356
356
|
quality: z.ZodOptional<z.ZodNumber>;
|
|
357
357
|
fullPage: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -360,24 +360,24 @@ declare const scenarioSchema: z.ZodObject<{
|
|
|
360
360
|
y: z.ZodNumber;
|
|
361
361
|
width: z.ZodNumber;
|
|
362
362
|
height: z.ZodNumber;
|
|
363
|
-
}, z.core.$
|
|
363
|
+
}, z.core.$strict>>;
|
|
364
364
|
omitBackground: z.ZodOptional<z.ZodBoolean>;
|
|
365
365
|
encoding: z.ZodOptional<z.ZodEnum<{
|
|
366
366
|
base64: "base64";
|
|
367
367
|
binary: "binary";
|
|
368
368
|
}>>;
|
|
369
|
-
}, z.core.$
|
|
370
|
-
}, z.core.$
|
|
369
|
+
}, z.core.$strict>>;
|
|
370
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
371
371
|
type: z.ZodLiteral<"EVALUATE">;
|
|
372
|
-
script: z.
|
|
373
|
-
}, z.core.$
|
|
372
|
+
script: z.ZodString;
|
|
373
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
374
374
|
type: z.ZodLiteral<"SET_VIEWPORT">;
|
|
375
375
|
width: z.ZodNumber;
|
|
376
376
|
height: z.ZodNumber;
|
|
377
|
-
}, z.core.$
|
|
377
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
378
378
|
type: z.ZodLiteral<"CLOSE">;
|
|
379
|
-
}, z.core.$
|
|
380
|
-
}, z.core.$
|
|
379
|
+
}, z.core.$strict>], "type">>>;
|
|
380
|
+
}, z.core.$strict>;
|
|
381
381
|
export type ScenarioYaml = z.infer<typeof scenarioSchema>;
|
|
382
382
|
export declare const rootZod: z.ZodObject<{
|
|
383
383
|
scenarios: z.ZodArray<z.ZodObject<{
|
|
@@ -396,76 +396,75 @@ export declare const rootZod: z.ZodObject<{
|
|
|
396
396
|
location: z.ZodOptional<z.ZodString>;
|
|
397
397
|
steps: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
|
|
398
398
|
navigate: z.ZodString;
|
|
399
|
-
}, z.core.$
|
|
399
|
+
}, z.core.$strict>, z.ZodLiteral<"waitForever">, z.ZodObject<{
|
|
400
400
|
wait: z.ZodNumber;
|
|
401
|
-
}, z.core.$
|
|
401
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
402
402
|
click: z.ZodString;
|
|
403
|
-
}, z.core.$
|
|
403
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
404
404
|
input: z.ZodObject<{
|
|
405
405
|
selector: z.ZodString;
|
|
406
406
|
text: z.ZodString;
|
|
407
|
-
}, z.core.$
|
|
408
|
-
}, z.core.$
|
|
409
|
-
actions: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
410
|
-
type: z.ZodLiteral<"WAIT">;
|
|
411
|
-
milliseconds: z.ZodNumber;
|
|
412
|
-
}, z.core.$strip> | z.ZodObject<{
|
|
407
|
+
}, z.core.$strict>;
|
|
408
|
+
}, z.core.$strict>, z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
413
409
|
type: z.ZodLiteral<"GOTO">;
|
|
414
410
|
url: z.ZodString;
|
|
415
411
|
options: z.ZodOptional<z.ZodObject<{
|
|
416
412
|
timeout: z.ZodOptional<z.ZodNumber>;
|
|
417
413
|
waitUntil: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
|
|
418
|
-
load: "load";
|
|
419
414
|
domcontentloaded: "domcontentloaded";
|
|
415
|
+
load: "load";
|
|
420
416
|
networkidle0: "networkidle0";
|
|
421
417
|
networkidle2: "networkidle2";
|
|
422
418
|
}>, z.ZodArray<z.ZodEnum<{
|
|
423
|
-
load: "load";
|
|
424
419
|
domcontentloaded: "domcontentloaded";
|
|
420
|
+
load: "load";
|
|
425
421
|
networkidle0: "networkidle0";
|
|
426
422
|
networkidle2: "networkidle2";
|
|
427
423
|
}>>]>>;
|
|
428
424
|
referer: z.ZodOptional<z.ZodString>;
|
|
429
|
-
}, z.core.$
|
|
430
|
-
}, z.core.$
|
|
425
|
+
}, z.core.$strict>>;
|
|
426
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
431
427
|
type: z.ZodLiteral<"CLICK">;
|
|
432
428
|
selector: z.ZodString;
|
|
433
429
|
options: z.ZodOptional<z.ZodObject<{
|
|
434
430
|
button: z.ZodOptional<z.ZodEnum<{
|
|
435
431
|
left: "left";
|
|
436
|
-
right: "right";
|
|
437
432
|
middle: "middle";
|
|
433
|
+
right: "right";
|
|
438
434
|
}>>;
|
|
439
435
|
clickCount: z.ZodOptional<z.ZodNumber>;
|
|
440
436
|
delay: z.ZodOptional<z.ZodNumber>;
|
|
441
|
-
}, z.core.$
|
|
442
|
-
}, z.core.$
|
|
437
|
+
}, z.core.$strict>>;
|
|
438
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
443
439
|
type: z.ZodLiteral<"TYPE">;
|
|
444
440
|
selector: z.ZodString;
|
|
445
441
|
text: z.ZodString;
|
|
446
442
|
options: z.ZodOptional<z.ZodObject<{
|
|
447
443
|
delay: z.ZodOptional<z.ZodNumber>;
|
|
448
|
-
}, z.core.$
|
|
449
|
-
}, z.core.$
|
|
444
|
+
}, z.core.$strict>>;
|
|
445
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
450
446
|
type: z.ZodLiteral<"WAIT_FOR_SELECTOR">;
|
|
451
447
|
selector: z.ZodString;
|
|
452
448
|
options: z.ZodOptional<z.ZodObject<{
|
|
453
449
|
visible: z.ZodOptional<z.ZodBoolean>;
|
|
454
450
|
hidden: z.ZodOptional<z.ZodBoolean>;
|
|
455
451
|
timeout: z.ZodOptional<z.ZodNumber>;
|
|
456
|
-
}, z.core.$
|
|
457
|
-
}, z.core.$
|
|
452
|
+
}, z.core.$strict>>;
|
|
453
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
458
454
|
type: z.ZodLiteral<"WAIT_FOR_TIMEOUT">;
|
|
459
455
|
timeout: z.ZodNumber;
|
|
460
|
-
}, z.core.$
|
|
456
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
457
|
+
type: z.ZodLiteral<"WAIT">;
|
|
458
|
+
milliseconds: z.ZodNumber;
|
|
459
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
461
460
|
type: z.ZodLiteral<"PAUSE">;
|
|
462
|
-
}, z.core.$
|
|
461
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
463
462
|
type: z.ZodLiteral<"SCREENSHOT">;
|
|
464
463
|
path: z.ZodString;
|
|
465
464
|
options: z.ZodOptional<z.ZodObject<{
|
|
466
465
|
type: z.ZodOptional<z.ZodEnum<{
|
|
467
|
-
png: "png";
|
|
468
466
|
jpeg: "jpeg";
|
|
467
|
+
png: "png";
|
|
469
468
|
}>>;
|
|
470
469
|
quality: z.ZodOptional<z.ZodNumber>;
|
|
471
470
|
fullPage: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -474,82 +473,83 @@ export declare const rootZod: z.ZodObject<{
|
|
|
474
473
|
y: z.ZodNumber;
|
|
475
474
|
width: z.ZodNumber;
|
|
476
475
|
height: z.ZodNumber;
|
|
477
|
-
}, z.core.$
|
|
476
|
+
}, z.core.$strict>>;
|
|
478
477
|
omitBackground: z.ZodOptional<z.ZodBoolean>;
|
|
479
478
|
encoding: z.ZodOptional<z.ZodEnum<{
|
|
480
479
|
base64: "base64";
|
|
481
480
|
binary: "binary";
|
|
482
481
|
}>>;
|
|
483
|
-
}, z.core.$
|
|
484
|
-
}, z.core.$
|
|
482
|
+
}, z.core.$strict>>;
|
|
483
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
485
484
|
type: z.ZodLiteral<"EVALUATE">;
|
|
486
|
-
script: z.
|
|
487
|
-
}, z.core.$
|
|
485
|
+
script: z.ZodString;
|
|
486
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
488
487
|
type: z.ZodLiteral<"SET_VIEWPORT">;
|
|
489
488
|
width: z.ZodNumber;
|
|
490
489
|
height: z.ZodNumber;
|
|
491
|
-
}, z.core.$
|
|
490
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
492
491
|
type: z.ZodLiteral<"CLOSE">;
|
|
493
|
-
}, z.core.$
|
|
494
|
-
|
|
495
|
-
milliseconds: z.ZodNumber;
|
|
496
|
-
}, z.core.$strip> | z.ZodObject<{
|
|
492
|
+
}, z.core.$strict>], "type">]>>>;
|
|
493
|
+
actions: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
497
494
|
type: z.ZodLiteral<"GOTO">;
|
|
498
495
|
url: z.ZodString;
|
|
499
496
|
options: z.ZodOptional<z.ZodObject<{
|
|
500
497
|
timeout: z.ZodOptional<z.ZodNumber>;
|
|
501
498
|
waitUntil: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
|
|
502
|
-
load: "load";
|
|
503
499
|
domcontentloaded: "domcontentloaded";
|
|
500
|
+
load: "load";
|
|
504
501
|
networkidle0: "networkidle0";
|
|
505
502
|
networkidle2: "networkidle2";
|
|
506
503
|
}>, z.ZodArray<z.ZodEnum<{
|
|
507
|
-
load: "load";
|
|
508
504
|
domcontentloaded: "domcontentloaded";
|
|
505
|
+
load: "load";
|
|
509
506
|
networkidle0: "networkidle0";
|
|
510
507
|
networkidle2: "networkidle2";
|
|
511
508
|
}>>]>>;
|
|
512
509
|
referer: z.ZodOptional<z.ZodString>;
|
|
513
|
-
}, z.core.$
|
|
514
|
-
}, z.core.$
|
|
510
|
+
}, z.core.$strict>>;
|
|
511
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
515
512
|
type: z.ZodLiteral<"CLICK">;
|
|
516
513
|
selector: z.ZodString;
|
|
517
514
|
options: z.ZodOptional<z.ZodObject<{
|
|
518
515
|
button: z.ZodOptional<z.ZodEnum<{
|
|
519
516
|
left: "left";
|
|
520
|
-
right: "right";
|
|
521
517
|
middle: "middle";
|
|
518
|
+
right: "right";
|
|
522
519
|
}>>;
|
|
523
520
|
clickCount: z.ZodOptional<z.ZodNumber>;
|
|
524
521
|
delay: z.ZodOptional<z.ZodNumber>;
|
|
525
|
-
}, z.core.$
|
|
526
|
-
}, z.core.$
|
|
522
|
+
}, z.core.$strict>>;
|
|
523
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
527
524
|
type: z.ZodLiteral<"TYPE">;
|
|
528
525
|
selector: z.ZodString;
|
|
529
526
|
text: z.ZodString;
|
|
530
527
|
options: z.ZodOptional<z.ZodObject<{
|
|
531
528
|
delay: z.ZodOptional<z.ZodNumber>;
|
|
532
|
-
}, z.core.$
|
|
533
|
-
}, z.core.$
|
|
529
|
+
}, z.core.$strict>>;
|
|
530
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
534
531
|
type: z.ZodLiteral<"WAIT_FOR_SELECTOR">;
|
|
535
532
|
selector: z.ZodString;
|
|
536
533
|
options: z.ZodOptional<z.ZodObject<{
|
|
537
534
|
visible: z.ZodOptional<z.ZodBoolean>;
|
|
538
535
|
hidden: z.ZodOptional<z.ZodBoolean>;
|
|
539
536
|
timeout: z.ZodOptional<z.ZodNumber>;
|
|
540
|
-
}, z.core.$
|
|
541
|
-
}, z.core.$
|
|
537
|
+
}, z.core.$strict>>;
|
|
538
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
542
539
|
type: z.ZodLiteral<"WAIT_FOR_TIMEOUT">;
|
|
543
540
|
timeout: z.ZodNumber;
|
|
544
|
-
}, z.core.$
|
|
541
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
542
|
+
type: z.ZodLiteral<"WAIT">;
|
|
543
|
+
milliseconds: z.ZodNumber;
|
|
544
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
545
545
|
type: z.ZodLiteral<"PAUSE">;
|
|
546
|
-
}, z.core.$
|
|
546
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
547
547
|
type: z.ZodLiteral<"SCREENSHOT">;
|
|
548
548
|
path: z.ZodString;
|
|
549
549
|
options: z.ZodOptional<z.ZodObject<{
|
|
550
550
|
type: z.ZodOptional<z.ZodEnum<{
|
|
551
|
-
png: "png";
|
|
552
551
|
jpeg: "jpeg";
|
|
552
|
+
png: "png";
|
|
553
553
|
}>>;
|
|
554
554
|
quality: z.ZodOptional<z.ZodNumber>;
|
|
555
555
|
fullPage: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -558,24 +558,24 @@ export declare const rootZod: z.ZodObject<{
|
|
|
558
558
|
y: z.ZodNumber;
|
|
559
559
|
width: z.ZodNumber;
|
|
560
560
|
height: z.ZodNumber;
|
|
561
|
-
}, z.core.$
|
|
561
|
+
}, z.core.$strict>>;
|
|
562
562
|
omitBackground: z.ZodOptional<z.ZodBoolean>;
|
|
563
563
|
encoding: z.ZodOptional<z.ZodEnum<{
|
|
564
564
|
base64: "base64";
|
|
565
565
|
binary: "binary";
|
|
566
566
|
}>>;
|
|
567
|
-
}, z.core.$
|
|
568
|
-
}, z.core.$
|
|
567
|
+
}, z.core.$strict>>;
|
|
568
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
569
569
|
type: z.ZodLiteral<"EVALUATE">;
|
|
570
|
-
script: z.
|
|
571
|
-
}, z.core.$
|
|
570
|
+
script: z.ZodString;
|
|
571
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
572
572
|
type: z.ZodLiteral<"SET_VIEWPORT">;
|
|
573
573
|
width: z.ZodNumber;
|
|
574
574
|
height: z.ZodNumber;
|
|
575
|
-
}, z.core.$
|
|
575
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
576
576
|
type: z.ZodLiteral<"CLOSE">;
|
|
577
|
-
}, z.core.$
|
|
578
|
-
}, z.core.$
|
|
579
|
-
}, z.core.$
|
|
577
|
+
}, z.core.$strict>], "type">>>;
|
|
578
|
+
}, z.core.$strict>>;
|
|
579
|
+
}, z.core.$strict>;
|
|
580
580
|
export type RootYaml = z.infer<typeof rootZod>;
|
|
581
581
|
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bloom-perf/yaml-pptr",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "A library to generate puppeteer browser controls through a YAML api.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -8,32 +8,35 @@
|
|
|
8
8
|
"files": [
|
|
9
9
|
"dist"
|
|
10
10
|
],
|
|
11
|
+
"engines": {
|
|
12
|
+
"node": ">=22.12.0"
|
|
13
|
+
},
|
|
11
14
|
"devDependencies": {
|
|
12
|
-
"@testdeck/mocha": "0.3.3",
|
|
13
15
|
"@types/chai": "5.2.3",
|
|
14
|
-
"@types/js-yaml": "4.0.9",
|
|
15
16
|
"@types/mocha": "10.0.10",
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"esbuild": "0.
|
|
20
|
-
"esbuild-node-externals": "
|
|
21
|
-
"mocha": "
|
|
22
|
-
"prettier": "3.
|
|
23
|
-
"puppeteer": "
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"tsx": "4.21.0",
|
|
27
|
-
"typescript": "5.9.3"
|
|
17
|
+
"@types/node": "22.20.5",
|
|
18
|
+
"c8": "12.0.0",
|
|
19
|
+
"chai": "6.3.0",
|
|
20
|
+
"esbuild": "0.28.2",
|
|
21
|
+
"esbuild-node-externals": "2.0.0",
|
|
22
|
+
"mocha": "12.0.3",
|
|
23
|
+
"prettier": "3.9.10",
|
|
24
|
+
"puppeteer": "25.17.0",
|
|
25
|
+
"tsx": "4.23.15",
|
|
26
|
+
"typescript": "7.0.2"
|
|
28
27
|
},
|
|
29
28
|
"dependencies": {
|
|
30
|
-
"js-yaml": "^
|
|
29
|
+
"js-yaml": "^5.0.0",
|
|
31
30
|
"zod": "^4.0.0"
|
|
32
31
|
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"puppeteer": "^25.0.0"
|
|
34
|
+
},
|
|
33
35
|
"scripts": {
|
|
34
36
|
"build": "rm -rf dist && node esbuild.js && tsc -p tsconfig.build.json",
|
|
35
|
-
"
|
|
36
|
-
"test
|
|
37
|
+
"typecheck": "tsc -p tsconfig.json",
|
|
38
|
+
"test": "mocha",
|
|
39
|
+
"test:coverage": "c8 mocha",
|
|
37
40
|
"example:login": "tsx examples/login-demo/run.ts",
|
|
38
41
|
"example:load": "tsx examples/load-test/run.ts"
|
|
39
42
|
},
|