@showrun/core 0.1.10-rc.5 → 0.2.0-rc.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/dist/__tests__/playwrightJsExecutor.test.d.ts +2 -0
- package/dist/__tests__/playwrightJsExecutor.test.d.ts.map +1 -0
- package/dist/__tests__/playwrightJsExecutor.test.js +191 -0
- package/dist/dsl/playwrightJsExecutor.d.ts +56 -0
- package/dist/dsl/playwrightJsExecutor.d.ts.map +1 -0
- package/dist/dsl/playwrightJsExecutor.js +134 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/loader.d.ts +9 -1
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +39 -4
- package/dist/packUtils.d.ts +4 -0
- package/dist/packUtils.d.ts.map +1 -1
- package/dist/packUtils.js +7 -0
- package/dist/registry/client.d.ts.map +1 -1
- package/dist/registry/client.js +46 -13
- package/dist/runner.d.ts +25 -0
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +89 -38
- package/dist/types.d.ts +27 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/util/index.d.ts +66 -0
- package/dist/util/index.d.ts.map +1 -0
- package/dist/util/index.js +21 -0
- package/dist/util/turnstile.d.ts +105 -0
- package/dist/util/turnstile.d.ts.map +1 -0
- package/dist/util/turnstile.js +242 -0
- package/package.json +2 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playwrightJsExecutor.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/playwrightJsExecutor.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import { executePlaywrightJs, extractFunctionBody } from '../dsl/playwrightJsExecutor.js';
|
|
3
|
+
// Minimal mock page/context/frame for testing
|
|
4
|
+
function createMockScope(overrides) {
|
|
5
|
+
const mockPage = {
|
|
6
|
+
goto: vi.fn().mockResolvedValue(undefined),
|
|
7
|
+
title: vi.fn().mockResolvedValue('Test Title'),
|
|
8
|
+
url: vi.fn().mockReturnValue('https://example.com'),
|
|
9
|
+
locator: vi.fn().mockReturnValue({
|
|
10
|
+
evaluateAll: vi.fn().mockResolvedValue([]),
|
|
11
|
+
}),
|
|
12
|
+
screenshot: vi.fn().mockResolvedValue(Buffer.from('')),
|
|
13
|
+
mouse: { click: vi.fn().mockResolvedValue(undefined) },
|
|
14
|
+
waitForTimeout: vi.fn().mockResolvedValue(undefined),
|
|
15
|
+
};
|
|
16
|
+
return {
|
|
17
|
+
page: mockPage,
|
|
18
|
+
context: {},
|
|
19
|
+
frame: {},
|
|
20
|
+
inputs: { query: 'hello' },
|
|
21
|
+
secrets: { apiKey: 'secret123' },
|
|
22
|
+
showrun: {
|
|
23
|
+
network: {
|
|
24
|
+
list: vi.fn().mockResolvedValue([]),
|
|
25
|
+
find: vi.fn().mockResolvedValue(null),
|
|
26
|
+
get: vi.fn().mockResolvedValue(null),
|
|
27
|
+
replay: vi.fn().mockResolvedValue({ status: 200, contentType: 'application/json', body: '{}', bodySize: 2 }),
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
util: {
|
|
31
|
+
detectCloudflareTurnstile: vi.fn().mockResolvedValue({ found: false }),
|
|
32
|
+
solveCloudflareTurnstile: vi.fn().mockResolvedValue({ success: false, detected: false }),
|
|
33
|
+
},
|
|
34
|
+
...overrides,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
describe('extractFunctionBody', () => {
|
|
38
|
+
it('extracts body from async function export', () => {
|
|
39
|
+
const code = `module.exports = async function({ page }) {
|
|
40
|
+
await page.goto('https://example.com');
|
|
41
|
+
return { title: 'test' };
|
|
42
|
+
};`;
|
|
43
|
+
const body = extractFunctionBody(code);
|
|
44
|
+
expect(body).toContain("await page.goto('https://example.com')");
|
|
45
|
+
expect(body).toContain("return { title: 'test' }");
|
|
46
|
+
});
|
|
47
|
+
it('extracts body from async arrow export', () => {
|
|
48
|
+
const code = `module.exports = async ({ page }) => {
|
|
49
|
+
return { title: await page.title() };
|
|
50
|
+
};`;
|
|
51
|
+
const body = extractFunctionBody(code);
|
|
52
|
+
expect(body).toContain('return { title: await page.title() }');
|
|
53
|
+
});
|
|
54
|
+
it('throws on invalid format', () => {
|
|
55
|
+
expect(() => extractFunctionBody('const x = 1;')).toThrow('Could not parse flow.playwright.js');
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
describe('executePlaywrightJs', () => {
|
|
59
|
+
it('returns collectibles from user code', async () => {
|
|
60
|
+
const code = `module.exports = async function({ page, inputs }) {
|
|
61
|
+
return { result: inputs.query + '_processed' };
|
|
62
|
+
};`;
|
|
63
|
+
const scope = createMockScope();
|
|
64
|
+
const result = await executePlaywrightJs(code, scope);
|
|
65
|
+
expect(result.collectibles).toEqual({ result: 'hello_processed' });
|
|
66
|
+
expect(result.logs).toEqual([]);
|
|
67
|
+
});
|
|
68
|
+
it('can call page methods', async () => {
|
|
69
|
+
const code = `module.exports = async function({ page }) {
|
|
70
|
+
const t = await page.title();
|
|
71
|
+
return { title: t };
|
|
72
|
+
};`;
|
|
73
|
+
const scope = createMockScope();
|
|
74
|
+
const result = await executePlaywrightJs(code, scope);
|
|
75
|
+
expect(result.collectibles).toEqual({ title: 'Test Title' });
|
|
76
|
+
expect(scope.page.title).toHaveBeenCalled();
|
|
77
|
+
});
|
|
78
|
+
it('blocks dangerous globals', async () => {
|
|
79
|
+
const code = `module.exports = async function({ page }) {
|
|
80
|
+
return {
|
|
81
|
+
hasProcess: typeof process !== 'undefined',
|
|
82
|
+
hasRequire: typeof require !== 'undefined',
|
|
83
|
+
hasBuffer: typeof Buffer !== 'undefined',
|
|
84
|
+
hasGlobal: typeof global !== 'undefined',
|
|
85
|
+
hasGlobalThis: typeof globalThis !== 'undefined',
|
|
86
|
+
hasFetch: typeof fetch !== 'undefined',
|
|
87
|
+
hasEval: typeof eval !== 'undefined',
|
|
88
|
+
hasFunction: typeof Function !== 'undefined',
|
|
89
|
+
hasSetTimeout: typeof setTimeout !== 'undefined',
|
|
90
|
+
};
|
|
91
|
+
};`;
|
|
92
|
+
const scope = createMockScope();
|
|
93
|
+
const result = await executePlaywrightJs(code, scope);
|
|
94
|
+
expect(result.collectibles.hasProcess).toBe(false);
|
|
95
|
+
expect(result.collectibles.hasRequire).toBe(false);
|
|
96
|
+
expect(result.collectibles.hasBuffer).toBe(false);
|
|
97
|
+
expect(result.collectibles.hasGlobal).toBe(false);
|
|
98
|
+
expect(result.collectibles.hasGlobalThis).toBe(false);
|
|
99
|
+
expect(result.collectibles.hasFetch).toBe(false);
|
|
100
|
+
expect(result.collectibles.hasEval).toBe(false);
|
|
101
|
+
expect(result.collectibles.hasFunction).toBe(false);
|
|
102
|
+
expect(result.collectibles.hasSetTimeout).toBe(false);
|
|
103
|
+
});
|
|
104
|
+
it('freezes inputs (mutations do not propagate)', async () => {
|
|
105
|
+
const code = `module.exports = async function({ inputs }) {
|
|
106
|
+
inputs.query = 'modified';
|
|
107
|
+
return { value: inputs.query };
|
|
108
|
+
};`;
|
|
109
|
+
const scope = createMockScope();
|
|
110
|
+
const result = await executePlaywrightJs(code, scope);
|
|
111
|
+
// Frozen object silently ignores assignment in sloppy mode
|
|
112
|
+
expect(result.collectibles.value).toBe('hello');
|
|
113
|
+
// Original scope inputs are also unmodified
|
|
114
|
+
expect(scope.inputs.query).toBe('hello');
|
|
115
|
+
});
|
|
116
|
+
it('freezes secrets (mutations do not propagate)', async () => {
|
|
117
|
+
const code = `module.exports = async function({ secrets }) {
|
|
118
|
+
secrets.apiKey = 'hacked';
|
|
119
|
+
return { value: secrets.apiKey };
|
|
120
|
+
};`;
|
|
121
|
+
const scope = createMockScope();
|
|
122
|
+
const result = await executePlaywrightJs(code, scope);
|
|
123
|
+
expect(result.collectibles.value).toBe('secret123');
|
|
124
|
+
expect(scope.secrets.apiKey).toBe('secret123');
|
|
125
|
+
});
|
|
126
|
+
it('returns empty object when code returns undefined', async () => {
|
|
127
|
+
const code = `module.exports = async function({ page }) {
|
|
128
|
+
// no return
|
|
129
|
+
};`;
|
|
130
|
+
const scope = createMockScope();
|
|
131
|
+
const result = await executePlaywrightJs(code, scope);
|
|
132
|
+
expect(result.collectibles).toEqual({});
|
|
133
|
+
});
|
|
134
|
+
it('can access showrun.network.replay', async () => {
|
|
135
|
+
const code = `module.exports = async function({ showrun }) {
|
|
136
|
+
const res = await showrun.network.replay('req-1');
|
|
137
|
+
return { status: res.status };
|
|
138
|
+
};`;
|
|
139
|
+
const scope = createMockScope();
|
|
140
|
+
const result = await executePlaywrightJs(code, scope);
|
|
141
|
+
expect(result.collectibles).toEqual({ status: 200 });
|
|
142
|
+
expect(scope.showrun.network.replay).toHaveBeenCalledWith('req-1');
|
|
143
|
+
});
|
|
144
|
+
it('can access showrun.network.list', async () => {
|
|
145
|
+
const code = `module.exports = async function({ showrun }) {
|
|
146
|
+
const entries = await showrun.network.list();
|
|
147
|
+
return { count: entries.length };
|
|
148
|
+
};`;
|
|
149
|
+
const scope = createMockScope();
|
|
150
|
+
const result = await executePlaywrightJs(code, scope);
|
|
151
|
+
expect(result.collectibles).toEqual({ count: 0 });
|
|
152
|
+
expect(scope.showrun.network.list).toHaveBeenCalled();
|
|
153
|
+
});
|
|
154
|
+
it('can access showrun.network.find', async () => {
|
|
155
|
+
const code = `module.exports = async function({ showrun }) {
|
|
156
|
+
const entry = await showrun.network.find({ urlIncludes: '/api' });
|
|
157
|
+
return { found: entry !== null };
|
|
158
|
+
};`;
|
|
159
|
+
const scope = createMockScope();
|
|
160
|
+
const result = await executePlaywrightJs(code, scope);
|
|
161
|
+
expect(result.collectibles).toEqual({ found: false });
|
|
162
|
+
expect(scope.showrun.network.find).toHaveBeenCalledWith({ urlIncludes: '/api' });
|
|
163
|
+
});
|
|
164
|
+
it('captures console.log output', async () => {
|
|
165
|
+
const code = `module.exports = async function({ page }) {
|
|
166
|
+
console.log('hello', 'world');
|
|
167
|
+
console.warn('a warning');
|
|
168
|
+
console.error('an error');
|
|
169
|
+
console.info('info message');
|
|
170
|
+
return { done: true };
|
|
171
|
+
};`;
|
|
172
|
+
const scope = createMockScope();
|
|
173
|
+
const result = await executePlaywrightJs(code, scope);
|
|
174
|
+
expect(result.collectibles).toEqual({ done: true });
|
|
175
|
+
expect(result.logs).toEqual([
|
|
176
|
+
'hello world',
|
|
177
|
+
'[warn] a warning',
|
|
178
|
+
'[error] an error',
|
|
179
|
+
'info message',
|
|
180
|
+
]);
|
|
181
|
+
});
|
|
182
|
+
it('times out on long-running code', async () => {
|
|
183
|
+
const code = `module.exports = async function({ page }) {
|
|
184
|
+
await new Promise(() => {}); // never resolves
|
|
185
|
+
return {};
|
|
186
|
+
};`;
|
|
187
|
+
const scope = createMockScope();
|
|
188
|
+
await expect(executePlaywrightJs(code, scope, 100) // 100ms timeout
|
|
189
|
+
).rejects.toThrow('timed out');
|
|
190
|
+
});
|
|
191
|
+
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sandboxed executor for playwright-js task packs.
|
|
3
|
+
*
|
|
4
|
+
* Runs user-provided Playwright JavaScript in a best-effort sandbox:
|
|
5
|
+
* - Dangerous Node.js globals are shadowed via AsyncFunction parameters
|
|
6
|
+
* - inputs and secrets are passed as frozen copies (read-only)
|
|
7
|
+
* - page, context, frame have full Playwright access
|
|
8
|
+
*
|
|
9
|
+
* NOTE: This is NOT a hard security boundary. A determined attacker could
|
|
10
|
+
* escape via prototype chain tricks. Trust is managed at the registry level.
|
|
11
|
+
*/
|
|
12
|
+
import type { Page, BrowserContext, Frame } from 'playwright';
|
|
13
|
+
import type { NetworkCaptureApi } from '../networkCapture.js';
|
|
14
|
+
import { type PlaywrightJsUtil } from '../util/index.js';
|
|
15
|
+
/**
|
|
16
|
+
* Scope provided to user code.
|
|
17
|
+
*/
|
|
18
|
+
export interface PlaywrightJsScope {
|
|
19
|
+
page: Page;
|
|
20
|
+
context: BrowserContext;
|
|
21
|
+
frame: Frame;
|
|
22
|
+
inputs: Record<string, unknown>;
|
|
23
|
+
secrets: Record<string, string>;
|
|
24
|
+
showrun: {
|
|
25
|
+
network: {
|
|
26
|
+
list: NetworkCaptureApi['list'];
|
|
27
|
+
find: NetworkCaptureApi['find'];
|
|
28
|
+
get: NetworkCaptureApi['get'];
|
|
29
|
+
replay: NetworkCaptureApi['replay'];
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
util: PlaywrightJsUtil;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Result from executing a playwright-js flow.
|
|
36
|
+
*/
|
|
37
|
+
export interface PlaywrightJsResult {
|
|
38
|
+
collectibles: Record<string, unknown>;
|
|
39
|
+
logs: string[];
|
|
40
|
+
error?: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Extract the function body from a `module.exports = async function(...) { BODY }` pattern.
|
|
44
|
+
* Also supports `module.exports = async (...) => { BODY }` arrow functions.
|
|
45
|
+
*/
|
|
46
|
+
export declare function extractFunctionBody(code: string): string;
|
|
47
|
+
/**
|
|
48
|
+
* Execute a playwright-js flow in a sandboxed context.
|
|
49
|
+
*
|
|
50
|
+
* @param code Raw source of flow.playwright.js
|
|
51
|
+
* @param scope Playwright objects + inputs/secrets
|
|
52
|
+
* @param timeoutMs Maximum execution time (default 5 minutes)
|
|
53
|
+
* @returns The return value from the user function (collectibles object)
|
|
54
|
+
*/
|
|
55
|
+
export declare function executePlaywrightJs(code: string, scope: PlaywrightJsScope, timeoutMs?: number): Promise<PlaywrightJsResult>;
|
|
56
|
+
//# sourceMappingURL=playwrightJsExecutor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playwrightJsExecutor.d.ts","sourceRoot":"","sources":["../../src/dsl/playwrightJsExecutor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAC9D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAA0B,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAajF;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,IAAI,CAAC;IACX,OAAO,EAAE,cAAc,CAAC;IACxB,KAAK,EAAE,KAAK,CAAC;IACb,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,OAAO,EAAE;QACP,OAAO,EAAE;YACP,IAAI,EAAE,iBAAiB,CAAC,MAAM,CAAC,CAAC;YAChC,IAAI,EAAE,iBAAiB,CAAC,MAAM,CAAC,CAAC;YAChC,GAAG,EAAE,iBAAiB,CAAC,KAAK,CAAC,CAAC;YAC9B,MAAM,EAAE,iBAAiB,CAAC,QAAQ,CAAC,CAAC;SACrC,CAAC;KACH,CAAC;IACF,IAAI,EAAE,gBAAgB,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CA+BxD;AAMD;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,iBAAiB,EACxB,SAAS,SAAgB,GACxB,OAAO,CAAC,kBAAkB,CAAC,CA4E7B"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sandboxed executor for playwright-js task packs.
|
|
3
|
+
*
|
|
4
|
+
* Runs user-provided Playwright JavaScript in a best-effort sandbox:
|
|
5
|
+
* - Dangerous Node.js globals are shadowed via AsyncFunction parameters
|
|
6
|
+
* - inputs and secrets are passed as frozen copies (read-only)
|
|
7
|
+
* - page, context, frame have full Playwright access
|
|
8
|
+
*
|
|
9
|
+
* NOTE: This is NOT a hard security boundary. A determined attacker could
|
|
10
|
+
* escape via prototype chain tricks. Trust is managed at the registry level.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Globals shadowed inside user code (passed as undefined).
|
|
14
|
+
*/
|
|
15
|
+
const BLOCKED_GLOBALS = [
|
|
16
|
+
'process', 'require', 'module', 'exports',
|
|
17
|
+
'__dirname', '__filename', 'global', 'globalThis',
|
|
18
|
+
'Buffer', 'setTimeout', 'setInterval', 'setImmediate',
|
|
19
|
+
'clearTimeout', 'clearInterval', 'clearImmediate',
|
|
20
|
+
'fetch', 'XMLHttpRequest', 'eval', 'Function', 'Deno', 'Bun',
|
|
21
|
+
];
|
|
22
|
+
/**
|
|
23
|
+
* Extract the function body from a `module.exports = async function(...) { BODY }` pattern.
|
|
24
|
+
* Also supports `module.exports = async (...) => { BODY }` arrow functions.
|
|
25
|
+
*/
|
|
26
|
+
export function extractFunctionBody(code) {
|
|
27
|
+
// Match module.exports = async function(...) { BODY }
|
|
28
|
+
// or module.exports = async (...) => { BODY }
|
|
29
|
+
const patterns = [
|
|
30
|
+
// async function with destructuring or params
|
|
31
|
+
/module\.exports\s*=\s*async\s+function\s*\([^)]*\)\s*\{/,
|
|
32
|
+
// async arrow with destructuring or params
|
|
33
|
+
/module\.exports\s*=\s*async\s*\([^)]*\)\s*=>\s*\{/,
|
|
34
|
+
];
|
|
35
|
+
for (const pattern of patterns) {
|
|
36
|
+
const match = code.match(pattern);
|
|
37
|
+
if (match) {
|
|
38
|
+
const startIdx = match.index + match[0].length;
|
|
39
|
+
// Find matching closing brace
|
|
40
|
+
let depth = 1;
|
|
41
|
+
let i = startIdx;
|
|
42
|
+
while (i < code.length && depth > 0) {
|
|
43
|
+
if (code[i] === '{')
|
|
44
|
+
depth++;
|
|
45
|
+
else if (code[i] === '}')
|
|
46
|
+
depth--;
|
|
47
|
+
i++;
|
|
48
|
+
}
|
|
49
|
+
if (depth === 0) {
|
|
50
|
+
return code.slice(startIdx, i - 1).trim();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
throw new Error('Could not parse flow.playwright.js: expected `module.exports = async function({ page, context, frame, inputs, secrets }) { ... }` pattern.');
|
|
55
|
+
}
|
|
56
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
57
|
+
const AsyncFunction = Object.getPrototypeOf(async function () { }).constructor;
|
|
58
|
+
/**
|
|
59
|
+
* Execute a playwright-js flow in a sandboxed context.
|
|
60
|
+
*
|
|
61
|
+
* @param code Raw source of flow.playwright.js
|
|
62
|
+
* @param scope Playwright objects + inputs/secrets
|
|
63
|
+
* @param timeoutMs Maximum execution time (default 5 minutes)
|
|
64
|
+
* @returns The return value from the user function (collectibles object)
|
|
65
|
+
*/
|
|
66
|
+
export async function executePlaywrightJs(code, scope, timeoutMs = 5 * 60 * 1000) {
|
|
67
|
+
const functionBody = extractFunctionBody(code);
|
|
68
|
+
// Capture console.log output from user code
|
|
69
|
+
const logs = [];
|
|
70
|
+
const customConsole = {
|
|
71
|
+
log: (...args) => logs.push(args.map(String).join(' ')),
|
|
72
|
+
warn: (...args) => logs.push(`[warn] ${args.map(String).join(' ')}`),
|
|
73
|
+
error: (...args) => logs.push(`[error] ${args.map(String).join(' ')}`),
|
|
74
|
+
info: (...args) => logs.push(args.map(String).join(' ')),
|
|
75
|
+
};
|
|
76
|
+
// Build the sandboxed function:
|
|
77
|
+
// Blocked globals are function parameters, passed as undefined to shadow them.
|
|
78
|
+
// Scope variables (page, context, etc.) are also parameters.
|
|
79
|
+
const fn = new AsyncFunction(...BLOCKED_GLOBALS, 'page', 'context', 'frame', 'inputs', 'secrets', 'showrun', 'util', 'console', functionBody);
|
|
80
|
+
// Freeze inputs and secrets so user code cannot mutate them
|
|
81
|
+
const frozenInputs = Object.freeze({ ...scope.inputs });
|
|
82
|
+
const frozenSecrets = Object.freeze({ ...scope.secrets });
|
|
83
|
+
// Build args: undefined for each blocked global, then scope values
|
|
84
|
+
const blockedArgs = BLOCKED_GLOBALS.map(() => undefined);
|
|
85
|
+
const args = [
|
|
86
|
+
...blockedArgs,
|
|
87
|
+
scope.page,
|
|
88
|
+
scope.context,
|
|
89
|
+
scope.frame,
|
|
90
|
+
frozenInputs,
|
|
91
|
+
frozenSecrets,
|
|
92
|
+
scope.showrun,
|
|
93
|
+
scope.util,
|
|
94
|
+
customConsole,
|
|
95
|
+
];
|
|
96
|
+
// Execute with timeout, wrapped in try-catch to handle browser/page closure errors
|
|
97
|
+
let result;
|
|
98
|
+
try {
|
|
99
|
+
result = await Promise.race([
|
|
100
|
+
fn(...args),
|
|
101
|
+
new Promise((_, reject) => {
|
|
102
|
+
const timer = globalThis.setTimeout(() => {
|
|
103
|
+
reject(new Error(`playwright-js execution timed out after ${timeoutMs}ms`));
|
|
104
|
+
}, timeoutMs);
|
|
105
|
+
// Allow Node.js to exit even if timer is pending
|
|
106
|
+
if (typeof timer === 'object' && 'unref' in timer) {
|
|
107
|
+
timer.unref();
|
|
108
|
+
}
|
|
109
|
+
}),
|
|
110
|
+
]);
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
// Handle flow execution errors gracefully instead of crashing
|
|
114
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
115
|
+
logs.push(`[error] Flow execution failed: ${errorMessage}`);
|
|
116
|
+
return {
|
|
117
|
+
collectibles: {},
|
|
118
|
+
logs,
|
|
119
|
+
error: errorMessage,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
// Normalize result: if null/undefined, return empty object
|
|
123
|
+
let collectibles;
|
|
124
|
+
if (result == null) {
|
|
125
|
+
collectibles = {};
|
|
126
|
+
}
|
|
127
|
+
else if (typeof result !== 'object' || Array.isArray(result)) {
|
|
128
|
+
collectibles = { _result: result };
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
collectibles = result;
|
|
132
|
+
}
|
|
133
|
+
return { collectibles, logs };
|
|
134
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -24,4 +24,6 @@ export * from './dsl/validation.js';
|
|
|
24
24
|
export * from './dsl/templating.js';
|
|
25
25
|
export * from './dsl/target.js';
|
|
26
26
|
export * from './dsl/conditions.js';
|
|
27
|
+
export * from './dsl/playwrightJsExecutor.js';
|
|
28
|
+
export * from './util/index.js';
|
|
27
29
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC;AACpC,cAAc,aAAa,CAAC;AAC5B,cAAc,sBAAsB,CAAC;AACrC,cAAc,iBAAiB,CAAC;AAGhC,cAAc,qBAAqB,CAAC;AAGpC,cAAc,kBAAkB,CAAC;AAGjC,cAAc,sBAAsB,CAAC;AAGrC,cAAc,oBAAoB,CAAC;AAGnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC;AACpC,cAAc,aAAa,CAAC;AAC5B,cAAc,sBAAsB,CAAC;AACrC,cAAc,iBAAiB,CAAC;AAGhC,cAAc,qBAAqB,CAAC;AAGpC,cAAc,kBAAkB,CAAC;AAGjC,cAAc,sBAAsB,CAAC;AAGrC,cAAc,oBAAoB,CAAC;AAGnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC;AACpC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,iBAAiB,CAAC"}
|
package/dist/index.js
CHANGED
package/dist/loader.d.ts
CHANGED
|
@@ -19,9 +19,17 @@ export declare class TaskPackLoader {
|
|
|
19
19
|
*/
|
|
20
20
|
static loadManifest(packPath: string): TaskPackManifest;
|
|
21
21
|
/**
|
|
22
|
-
* Load task pack from directory
|
|
22
|
+
* Load task pack from directory
|
|
23
23
|
*/
|
|
24
24
|
static loadTaskPack(packPath: string): Promise<TaskPack>;
|
|
25
|
+
/**
|
|
26
|
+
* Load a json-dsl task pack
|
|
27
|
+
*/
|
|
28
|
+
private static loadJsonDslPack;
|
|
29
|
+
/**
|
|
30
|
+
* Load a playwright-js task pack
|
|
31
|
+
*/
|
|
32
|
+
private static loadPlaywrightJsPack;
|
|
25
33
|
/**
|
|
26
34
|
* Load secrets from .secrets.json file in pack directory
|
|
27
35
|
* Returns empty object if file doesn't exist
|
package/dist/loader.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,gBAAgB,EAAsC,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAInH;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,CAAC,CAAC;IACX,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED;;;;;;GAMG;AACH,qBAAa,cAAc;IACzB;;OAEG;IACH,MAAM,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB;
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,gBAAgB,EAAsC,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAInH;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,CAAC,CAAC;IACX,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED;;;;;;GAMG;AACH,qBAAa,cAAc;IACzB;;OAEG;IACH,MAAM,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB;IA6BvD;;OAEG;WACU,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAU9D;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,eAAe;IA0C9B;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,oBAAoB;IAyBnC;;;OAGG;IACH,MAAM,CAAC,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAwB5D;;OAEG;IACH,MAAM,CAAC,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,EAAE;CAQlE"}
|
package/dist/loader.js
CHANGED
|
@@ -29,17 +29,27 @@ export class TaskPackLoader {
|
|
|
29
29
|
if (!manifest.id || !manifest.name || !manifest.version) {
|
|
30
30
|
throw new Error('taskpack.json missing required fields: id, name, version');
|
|
31
31
|
}
|
|
32
|
-
//
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
// Validate kind
|
|
33
|
+
const validKinds = ['json-dsl', 'playwright-js'];
|
|
34
|
+
if (!validKinds.includes(manifest.kind)) {
|
|
35
|
+
throw new Error(`taskpack.json "kind" must be one of: ${validKinds.join(', ')}. Got: "${manifest.kind}"`);
|
|
35
36
|
}
|
|
36
37
|
return manifest;
|
|
37
38
|
}
|
|
38
39
|
/**
|
|
39
|
-
* Load task pack from directory
|
|
40
|
+
* Load task pack from directory
|
|
40
41
|
*/
|
|
41
42
|
static async loadTaskPack(packPath) {
|
|
42
43
|
const manifest = this.loadManifest(packPath);
|
|
44
|
+
if (manifest.kind === 'playwright-js') {
|
|
45
|
+
return this.loadPlaywrightJsPack(packPath, manifest);
|
|
46
|
+
}
|
|
47
|
+
return this.loadJsonDslPack(packPath, manifest);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Load a json-dsl task pack
|
|
51
|
+
*/
|
|
52
|
+
static loadJsonDslPack(packPath, manifest) {
|
|
43
53
|
const flowPath = join(packPath, 'flow.json');
|
|
44
54
|
if (!existsSync(flowPath)) {
|
|
45
55
|
throw new Error(`flow.json not found for json-dsl pack: ${flowPath}`);
|
|
@@ -72,6 +82,31 @@ export class TaskPackLoader {
|
|
|
72
82
|
...(snapshots ? { snapshots } : {}),
|
|
73
83
|
};
|
|
74
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* Load a playwright-js task pack
|
|
87
|
+
*/
|
|
88
|
+
static loadPlaywrightJsPack(packPath, manifest) {
|
|
89
|
+
const flowPath = join(packPath, 'flow.playwright.js');
|
|
90
|
+
if (!existsSync(flowPath)) {
|
|
91
|
+
throw new Error(`flow.playwright.js not found for playwright-js pack: ${flowPath}`);
|
|
92
|
+
}
|
|
93
|
+
const source = readFileSync(flowPath, 'utf-8');
|
|
94
|
+
return {
|
|
95
|
+
metadata: {
|
|
96
|
+
id: manifest.id,
|
|
97
|
+
name: manifest.name,
|
|
98
|
+
version: manifest.version,
|
|
99
|
+
description: manifest.description,
|
|
100
|
+
},
|
|
101
|
+
kind: 'playwright-js',
|
|
102
|
+
inputs: manifest.inputs || {},
|
|
103
|
+
collectibles: manifest.collectibles || [],
|
|
104
|
+
flow: [], // Not used for playwright-js packs
|
|
105
|
+
playwrightJsSource: source,
|
|
106
|
+
auth: manifest.auth,
|
|
107
|
+
browser: manifest.browser,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
75
110
|
/**
|
|
76
111
|
* Load secrets from .secrets.json file in pack directory
|
|
77
112
|
* Returns empty object if file doesn't exist
|
package/dist/packUtils.d.ts
CHANGED
|
@@ -33,4 +33,8 @@ export declare function writeFlowJson(packDir: string, flowData: {
|
|
|
33
33
|
collectibles?: CollectibleDefinition[];
|
|
34
34
|
flow: DslStep[];
|
|
35
35
|
}, skipValidation?: boolean): void;
|
|
36
|
+
/**
|
|
37
|
+
* Write flow.playwright.js
|
|
38
|
+
*/
|
|
39
|
+
export declare function writePlaywrightJs(packDir: string, source: string): void;
|
|
36
40
|
//# sourceMappingURL=packUtils.d.ts.map
|
package/dist/packUtils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"packUtils.d.ts","sourceRoot":"","sources":["../src/packUtils.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACvF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAG9C;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAGrD;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE/C;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAgBnE;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAO/E;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,CAAC,CAUnD;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,gBAAgB,GACzB,IAAI,CAIN;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE;IACR,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,YAAY,CAAC,EAAE,qBAAqB,EAAE,CAAC;IACvC,IAAI,EAAE,OAAO,EAAE,CAAC;CACjB,EACD,cAAc,UAAQ,GACrB,IAAI,CAoBN"}
|
|
1
|
+
{"version":3,"file":"packUtils.d.ts","sourceRoot":"","sources":["../src/packUtils.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACvF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAG9C;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAGrD;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE/C;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAgBnE;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAO/E;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,CAAC,CAUnD;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,gBAAgB,GACzB,IAAI,CAIN;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE;IACR,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,YAAY,CAAC,EAAE,qBAAqB,EAAE,CAAC;IACvC,IAAI,EAAE,OAAO,EAAE,CAAC;CACjB,EACD,cAAc,UAAQ,GACrB,IAAI,CAoBN;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAGvE"}
|
package/dist/packUtils.js
CHANGED
|
@@ -95,3 +95,10 @@ export function writeFlowJson(packDir, flowData, skipValidation = false) {
|
|
|
95
95
|
const content = JSON.stringify(flowData, null, 2) + '\n';
|
|
96
96
|
atomicWrite(flowPath, content);
|
|
97
97
|
}
|
|
98
|
+
/**
|
|
99
|
+
* Write flow.playwright.js
|
|
100
|
+
*/
|
|
101
|
+
export function writePlaywrightJs(packDir, source) {
|
|
102
|
+
const flowPath = join(packDir, 'flow.playwright.js');
|
|
103
|
+
atomicWrite(flowPath, source);
|
|
104
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/registry/client.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/registry/client.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH,OAAO,KAAK,EACV,eAAe,EACf,kBAAkB,EAClB,gBAAgB,EAGhB,WAAW,EACX,aAAa,EACb,aAAa,EACb,WAAW,EAEX,iBAAiB,EACjB,WAAW,EACX,YAAY,EAEb,MAAM,YAAY,CAAC;AAMpB,qBAAa,aAAc,SAAQ,KAAK;aAGpB,MAAM,EAAE,MAAM;aACd,IAAI,CAAC,EAAE,OAAO;gBAF9B,OAAO,EAAE,MAAM,EACC,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE,OAAO,YAAA;CAKjC;AAyCD,qBAAa,cAAe,YAAW,eAAe;IACpD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;gBAEzB,WAAW,CAAC,EAAE,MAAM;IAQ1B,gBAAgB,IAAI,OAAO,CAAC,kBAAkB,CAAC;IAS/C,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA+C9D,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAIvB,MAAM,IAAI,OAAO,CAAC,WAAW,CAAC;IAIpC,eAAe,IAAI,OAAO;IAOpB,WAAW,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;IA+G1D,WAAW,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC;IAWxE,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA4B1E,UAAU,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;YAcvC,OAAO;IAwCrB;;;OAGG;YACW,mBAAmB;YAkCnB,kBAAkB;CAcjC"}
|
package/dist/registry/client.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* automatically refreshes access tokens before they expire.
|
|
7
7
|
*/
|
|
8
8
|
import { join } from 'path';
|
|
9
|
+
import { readFileSync } from 'fs';
|
|
9
10
|
import { TaskPackLoader } from '../loader.js';
|
|
10
11
|
import { readJsonFile, ensureDir, writeTaskPackManifest, writeFlowJson } from '../packUtils.js';
|
|
11
12
|
import { loadTokens, saveTokens, clearTokens } from './tokenStore.js';
|
|
@@ -125,8 +126,22 @@ export class RegistryClient {
|
|
|
125
126
|
const warnings = [];
|
|
126
127
|
// Load local pack
|
|
127
128
|
const manifest = TaskPackLoader.loadManifest(packPath);
|
|
128
|
-
|
|
129
|
-
|
|
129
|
+
// Load flow data based on pack kind
|
|
130
|
+
let flowData;
|
|
131
|
+
let playwrightJsSource;
|
|
132
|
+
if (manifest.kind === 'playwright-js') {
|
|
133
|
+
// playwright-js packs use flow.playwright.js (raw source code)
|
|
134
|
+
// The registry expects 'playwrightJsSource' at top level, not inside 'flow'
|
|
135
|
+
const flowPath = join(packPath, 'flow.playwright.js');
|
|
136
|
+
playwrightJsSource = readFileSync(flowPath, 'utf-8');
|
|
137
|
+
// For playwright-js, no flow object is needed
|
|
138
|
+
flowData = undefined;
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
// json-dsl packs use flow.json
|
|
142
|
+
const flowPath = join(packPath, 'flow.json');
|
|
143
|
+
flowData = readJsonFile(flowPath);
|
|
144
|
+
}
|
|
130
145
|
const slug = userSlug || manifest.id;
|
|
131
146
|
// Try to get existing pack; create if 404.
|
|
132
147
|
// Publish uses the short slug — server infers the @username scope from the
|
|
@@ -149,19 +164,37 @@ export class RegistryClient {
|
|
|
149
164
|
throw err;
|
|
150
165
|
}
|
|
151
166
|
}
|
|
152
|
-
//
|
|
153
|
-
|
|
167
|
+
// Build the manifest payload for the registry
|
|
168
|
+
// For playwright-js packs, inputs and collectibles must be included in the manifest
|
|
169
|
+
// (the registry validates this)
|
|
170
|
+
const manifestPayload = {
|
|
171
|
+
id: manifest.id,
|
|
172
|
+
name: manifest.name,
|
|
173
|
+
version: manifest.version,
|
|
174
|
+
description: manifest.description,
|
|
175
|
+
kind: manifest.kind,
|
|
176
|
+
};
|
|
177
|
+
// Include inputs and collectibles for playwright-js packs (required by registry)
|
|
178
|
+
if (manifest.kind === 'playwright-js') {
|
|
179
|
+
manifestPayload.inputs = manifest.inputs || {};
|
|
180
|
+
manifestPayload.collectibles = manifest.collectibles || [];
|
|
181
|
+
}
|
|
182
|
+
// Build the version request body
|
|
183
|
+
// For playwright-js, playwrightJsSource goes at top level
|
|
184
|
+
// For json-dsl, flow data goes under 'flow' key
|
|
185
|
+
const versionBody = {
|
|
154
186
|
version: manifest.version,
|
|
155
|
-
manifest:
|
|
156
|
-
id: manifest.id,
|
|
157
|
-
name: manifest.name,
|
|
158
|
-
version: manifest.version,
|
|
159
|
-
description: manifest.description,
|
|
160
|
-
kind: manifest.kind,
|
|
161
|
-
},
|
|
162
|
-
flow: flowData,
|
|
187
|
+
manifest: manifestPayload,
|
|
163
188
|
changelog,
|
|
164
|
-
}
|
|
189
|
+
};
|
|
190
|
+
if (manifest.kind === 'playwright-js') {
|
|
191
|
+
versionBody.playwrightJsSource = playwrightJsSource;
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
versionBody.flow = flowData;
|
|
195
|
+
}
|
|
196
|
+
// Publish version
|
|
197
|
+
const versionData = await this.request('POST', `/api/packs/${encodeURIComponent(slug)}/versions`, versionBody, true);
|
|
165
198
|
// The API may return version as a string or as an object with a nested version field
|
|
166
199
|
const rawVersion = versionData.version;
|
|
167
200
|
const version = typeof rawVersion === 'string'
|
package/dist/runner.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { type Page } from 'playwright';
|
|
1
2
|
import type { TaskPack, RunResult } from './types.js';
|
|
2
3
|
import type { Logger } from './types.js';
|
|
4
|
+
import type { NetworkCaptureApi } from './networkCapture.js';
|
|
3
5
|
/**
|
|
4
6
|
* Options for running a task pack
|
|
5
7
|
*/
|
|
@@ -45,6 +47,29 @@ export interface RunTaskPackOptions {
|
|
|
45
47
|
* instead of launching a new one.
|
|
46
48
|
*/
|
|
47
49
|
cdpUrl?: string;
|
|
50
|
+
/**
|
|
51
|
+
* Executor for playwright-js packs.
|
|
52
|
+
* If not provided, playwright-js packs will throw an error.
|
|
53
|
+
*/
|
|
54
|
+
playwrightJsExecutor?: (source: string, scope: {
|
|
55
|
+
page: Page;
|
|
56
|
+
context: import('playwright').BrowserContext;
|
|
57
|
+
frame: import('playwright').Frame;
|
|
58
|
+
inputs: Record<string, unknown>;
|
|
59
|
+
secrets: Record<string, string>;
|
|
60
|
+
showrun: {
|
|
61
|
+
network: {
|
|
62
|
+
list: NetworkCaptureApi['list'];
|
|
63
|
+
find: NetworkCaptureApi['find'];
|
|
64
|
+
get: NetworkCaptureApi['get'];
|
|
65
|
+
replay: NetworkCaptureApi['replay'];
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
util: import('./util/index.js').PlaywrightJsUtil;
|
|
69
|
+
}, timeoutMs?: number) => Promise<{
|
|
70
|
+
collectibles: Record<string, unknown>;
|
|
71
|
+
logs: string[];
|
|
72
|
+
}>;
|
|
48
73
|
}
|
|
49
74
|
/**
|
|
50
75
|
* Result of running a task pack with paths
|