@playdrop/playdrop-cli 0.4.0 → 0.4.1
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/config/client-meta.json +7 -7
- package/dist/appUrls.d.ts +9 -0
- package/dist/appUrls.js +33 -0
- package/dist/browser.d.ts +5 -0
- package/dist/browser.js +45 -0
- package/dist/captureRuntime.d.ts +31 -0
- package/dist/captureRuntime.js +414 -0
- package/dist/commands/browse.js +1 -1
- package/dist/commands/capture.js +182 -479
- package/dist/commands/captureRemote.d.ts +2 -1
- package/dist/commands/captureRemote.js +144 -47
- package/dist/commands/comments.js +3 -3
- package/dist/commands/create.js +6 -6
- package/dist/commands/createRemixContent.js +1 -1
- package/dist/commands/creations.js +2 -2
- package/dist/commands/credits.js +2 -2
- package/dist/commands/detail.js +9 -4
- package/dist/commands/dev.js +2 -2
- package/dist/commands/feedback.js +26 -11
- package/dist/commands/generation.js +2 -2
- package/dist/commands/gettingStarted.js +1 -1
- package/dist/commands/init.js +1 -1
- package/dist/commands/login.d.ts +2 -4
- package/dist/commands/login.js +16 -51
- package/dist/commands/logout.js +1 -1
- package/dist/commands/notifications.js +3 -3
- package/dist/commands/play.d.ts +5 -0
- package/dist/commands/play.js +102 -0
- package/dist/commands/upload.js +10 -11
- package/dist/commands/versionsBrowse.js +11 -3
- package/dist/commands/whoami.js +3 -3
- package/dist/index.js +15 -3
- package/dist/messages.js +5 -5
- package/dist/playwright.d.ts +2 -1
- package/dist/playwright.js +18 -1
- package/dist/uploadLog.d.ts +2 -0
- package/dist/uploadLog.js +14 -0
- package/node_modules/@playdrop/ai-client/dist/index.d.ts.map +1 -1
- package/node_modules/@playdrop/ai-client/dist/index.js +136 -3
- package/node_modules/@playdrop/api-client/dist/client.d.ts +9 -1
- package/node_modules/@playdrop/api-client/dist/client.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/client.js +10 -1
- package/node_modules/@playdrop/api-client/dist/domains/auth.d.ts +3 -1
- package/node_modules/@playdrop/api-client/dist/domains/auth.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/auth.js +21 -0
- package/node_modules/@playdrop/api-client/dist/domains/free-credits.d.ts +27 -0
- package/node_modules/@playdrop/api-client/dist/domains/free-credits.d.ts.map +1 -0
- package/node_modules/@playdrop/api-client/dist/domains/free-credits.js +66 -0
- package/node_modules/@playdrop/api-client/dist/index.d.ts +10 -2
- package/node_modules/@playdrop/api-client/dist/index.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/index.js +31 -2
- package/node_modules/@playdrop/config/client-meta.json +7 -7
- package/node_modules/@playdrop/types/dist/api.d.ts +85 -1
- package/node_modules/@playdrop/types/dist/api.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/api.js +15 -0
- package/package.json +2 -2
package/dist/commands/capture.js
CHANGED
|
@@ -2,82 +2,16 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.capture = capture;
|
|
4
4
|
const node_fs_1 = require("node:fs");
|
|
5
|
-
const promises_1 = require("node:fs/promises");
|
|
6
5
|
const node_path_1 = require("node:path");
|
|
7
6
|
const types_1 = require("@playdrop/types");
|
|
8
|
-
const config_1 = require("../config");
|
|
9
|
-
const apiClient_1 = require("../apiClient");
|
|
10
7
|
const http_1 = require("../http");
|
|
11
|
-
const environment_1 = require("../environment");
|
|
12
8
|
const messages_1 = require("../messages");
|
|
13
9
|
const catalogue_utils_1 = require("../catalogue-utils");
|
|
14
10
|
const devShared_1 = require("./devShared");
|
|
15
11
|
const devServer_1 = require("./devServer");
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
if (value === undefined || Number.isNaN(value)) {
|
|
20
|
-
return 5;
|
|
21
|
-
}
|
|
22
|
-
if (!Number.isFinite(value) || value <= 0) {
|
|
23
|
-
throw new Error('Timeout must be a positive number of seconds.');
|
|
24
|
-
}
|
|
25
|
-
if (value > MAX_CAPTURE_TIMEOUT_SECONDS) {
|
|
26
|
-
throw new Error(`Timeout cannot exceed ${MAX_CAPTURE_TIMEOUT_SECONDS} seconds (10 minutes).`);
|
|
27
|
-
}
|
|
28
|
-
return value;
|
|
29
|
-
}
|
|
30
|
-
function formatAppTypeSlug(type) {
|
|
31
|
-
switch (type) {
|
|
32
|
-
case 'TOOL':
|
|
33
|
-
return 'tool';
|
|
34
|
-
case 'TEMPLATE':
|
|
35
|
-
return 'template';
|
|
36
|
-
case 'DEMO':
|
|
37
|
-
return 'demo';
|
|
38
|
-
case undefined:
|
|
39
|
-
return 'game';
|
|
40
|
-
default:
|
|
41
|
-
return 'game';
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
function formatConsoleValue(value) {
|
|
45
|
-
if (typeof value === 'string')
|
|
46
|
-
return value;
|
|
47
|
-
if (typeof value === 'number' || typeof value === 'boolean' || value === null) {
|
|
48
|
-
return String(value);
|
|
49
|
-
}
|
|
50
|
-
if (typeof value === 'undefined') {
|
|
51
|
-
return 'undefined';
|
|
52
|
-
}
|
|
53
|
-
if (typeof value === 'function') {
|
|
54
|
-
return '[function]';
|
|
55
|
-
}
|
|
56
|
-
if (typeof value === 'object') {
|
|
57
|
-
try {
|
|
58
|
-
return JSON.stringify(value);
|
|
59
|
-
}
|
|
60
|
-
catch {
|
|
61
|
-
return '[object]';
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
return String(value);
|
|
65
|
-
}
|
|
66
|
-
function serializePayload(payload) {
|
|
67
|
-
if (payload === undefined) {
|
|
68
|
-
return '';
|
|
69
|
-
}
|
|
70
|
-
if (typeof payload === 'string') {
|
|
71
|
-
return payload;
|
|
72
|
-
}
|
|
73
|
-
try {
|
|
74
|
-
return JSON.stringify(payload);
|
|
75
|
-
}
|
|
76
|
-
catch {
|
|
77
|
-
return String(payload);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
const LOG_LEVEL_VALUES = ['debug', 'info', 'warn', 'error'];
|
|
12
|
+
const commandContext_1 = require("../commandContext");
|
|
13
|
+
const appUrls_1 = require("../appUrls");
|
|
14
|
+
const captureRuntime_1 = require("../captureRuntime");
|
|
81
15
|
const MOBILE_USER_AGENT = 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1';
|
|
82
16
|
const SURFACE_PRESETS = {
|
|
83
17
|
desktop: {
|
|
@@ -111,42 +45,6 @@ const SURFACE_PRESETS = {
|
|
|
111
45
|
},
|
|
112
46
|
};
|
|
113
47
|
const SURFACE_ORDER = ['desktop', 'mobileLandscape', 'mobilePortrait'];
|
|
114
|
-
function resolveLogLevel(value) {
|
|
115
|
-
if (!value)
|
|
116
|
-
return 'info';
|
|
117
|
-
const normalized = value.trim().toLowerCase();
|
|
118
|
-
if (LOG_LEVEL_VALUES.includes(normalized)) {
|
|
119
|
-
return normalized;
|
|
120
|
-
}
|
|
121
|
-
throw new Error(`Unsupported log level "${value}". Choose one of: ${LOG_LEVEL_VALUES.join(', ')}`);
|
|
122
|
-
}
|
|
123
|
-
function severityOrder(level) {
|
|
124
|
-
switch (level) {
|
|
125
|
-
case 'debug':
|
|
126
|
-
return 0;
|
|
127
|
-
case 'info':
|
|
128
|
-
return 1;
|
|
129
|
-
case 'warn':
|
|
130
|
-
return 2;
|
|
131
|
-
case 'error':
|
|
132
|
-
return 3;
|
|
133
|
-
default:
|
|
134
|
-
return 1;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
function mapConsoleTypeToLevel(type) {
|
|
138
|
-
const normalized = type.toLowerCase();
|
|
139
|
-
if (normalized === 'debug')
|
|
140
|
-
return 'debug';
|
|
141
|
-
if (normalized === 'warning' || normalized === 'warn')
|
|
142
|
-
return 'warn';
|
|
143
|
-
if (normalized === 'error' || normalized === 'assert' || normalized === 'trace')
|
|
144
|
-
return 'error';
|
|
145
|
-
return 'info';
|
|
146
|
-
}
|
|
147
|
-
function shouldEmit(level, threshold) {
|
|
148
|
-
return severityOrder(level) >= severityOrder(threshold);
|
|
149
|
-
}
|
|
150
48
|
function normalizeSurfaceOverride(value) {
|
|
151
49
|
if (!value)
|
|
152
50
|
return null;
|
|
@@ -187,43 +85,38 @@ function formatSurfaceList(targets) {
|
|
|
187
85
|
}
|
|
188
86
|
return enabled.join(', ');
|
|
189
87
|
}
|
|
88
|
+
function resolveCaptureLoginOverride(options) {
|
|
89
|
+
const username = options.username?.trim() || '';
|
|
90
|
+
const password = options.password || '';
|
|
91
|
+
if ((username && !password) || (!username && password)) {
|
|
92
|
+
throw new Error('invalid_credentials_pair');
|
|
93
|
+
}
|
|
94
|
+
if (!username) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
return { username, password };
|
|
98
|
+
}
|
|
99
|
+
// eslint-disable-next-line max-lines-per-function
|
|
190
100
|
async function capture(targetArg, options = {}) {
|
|
191
|
-
const timeoutSeconds =
|
|
101
|
+
const timeoutSeconds = (0, captureRuntime_1.validateCaptureTimeout)(options.timeoutSeconds);
|
|
192
102
|
const timeoutMs = Math.round(timeoutSeconds * 1000);
|
|
193
103
|
let minimumLogLevel;
|
|
104
|
+
let loginOverride;
|
|
194
105
|
try {
|
|
195
|
-
minimumLogLevel =
|
|
106
|
+
minimumLogLevel = (0, captureRuntime_1.resolveCaptureLogLevel)(options.logLevel);
|
|
107
|
+
loginOverride = resolveCaptureLoginOverride(options);
|
|
196
108
|
}
|
|
197
109
|
catch (error) {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
: null;
|
|
205
|
-
const cfg = (0, config_1.loadConfig)();
|
|
206
|
-
if (!cfg.env) {
|
|
207
|
-
(0, messages_1.printConfigEnvironmentMissing)('project capture');
|
|
208
|
-
process.exitCode = 1;
|
|
209
|
-
return;
|
|
210
|
-
}
|
|
211
|
-
if (!cfg.token) {
|
|
212
|
-
(0, messages_1.printLoginRequired)('Capturing app logs', 'project capture');
|
|
213
|
-
process.exitCode = 1;
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
const envConfig = (0, environment_1.resolveEnvironmentConfig)(cfg.env);
|
|
217
|
-
if (!envConfig) {
|
|
218
|
-
const choices = (0, environment_1.formatEnvironmentList)();
|
|
219
|
-
(0, messages_1.printUnknownEnvironment)(cfg.env, choices, 'project capture');
|
|
110
|
+
if (error instanceof Error && error.message === 'invalid_credentials_pair') {
|
|
111
|
+
(0, messages_1.printErrorWithHelp)('Use --username and --password together.', [], { command: 'project capture' });
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
(0, messages_1.printErrorWithHelp)(error?.message || 'Invalid log level.', ['Valid values: debug, info, warn, error'], { command: 'project capture' });
|
|
115
|
+
}
|
|
220
116
|
process.exitCode = 1;
|
|
221
117
|
return;
|
|
222
118
|
}
|
|
223
|
-
|
|
224
|
-
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
|
225
|
-
}
|
|
226
|
-
const client = (0, apiClient_1.createCliApiClient)({ baseUrl: envConfig.apiBase, token: cfg.token });
|
|
119
|
+
const screenshotPath = options.screenshotPath ? (0, node_path_1.resolve)(process.cwd(), options.screenshotPath) : null;
|
|
227
120
|
const portToUse = 8888;
|
|
228
121
|
let resolvedTarget;
|
|
229
122
|
try {
|
|
@@ -293,11 +186,11 @@ async function capture(targetArg, options = {}) {
|
|
|
293
186
|
? ` (${surfaceContextOptions.viewport.width}x${surfaceContextOptions.viewport.height})`
|
|
294
187
|
: '';
|
|
295
188
|
console.log(`[capture] Surface: ${surfacePreset.label}${viewportLabel}`);
|
|
296
|
-
let appTypeSlug =
|
|
189
|
+
let appTypeSlug = (0, appUrls_1.getAppTypeSlug)(null);
|
|
297
190
|
try {
|
|
298
191
|
const match = (0, catalogue_utils_1.findAppDefinition)(filePath);
|
|
299
192
|
appName = match.name;
|
|
300
|
-
appTypeSlug =
|
|
193
|
+
appTypeSlug = (0, appUrls_1.getAppTypeSlug)(match.type);
|
|
301
194
|
}
|
|
302
195
|
catch (error) {
|
|
303
196
|
if (resolvedTarget.cataloguePath) {
|
|
@@ -309,374 +202,184 @@ async function capture(targetArg, options = {}) {
|
|
|
309
202
|
return;
|
|
310
203
|
}
|
|
311
204
|
}
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
currentUser =
|
|
316
|
-
currentUsername =
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
return;
|
|
321
|
-
}
|
|
322
|
-
if (error instanceof types_1.UnsupportedClientError) {
|
|
323
|
-
(0, http_1.handleUnsupportedError)(error, 'Authentication');
|
|
324
|
-
process.exitCode = 1;
|
|
325
|
-
return;
|
|
326
|
-
}
|
|
327
|
-
if (error instanceof types_1.ApiError) {
|
|
328
|
-
(0, messages_1.printErrorWithHelp)(`Could not fetch your account (status ${error.status}).`, [
|
|
329
|
-
'Run "playdrop login" to refresh your session and ensure the API is reachable.',
|
|
330
|
-
'Use "playdrop whoami" afterwards to confirm your status.',
|
|
331
|
-
], { command: 'project capture' });
|
|
332
|
-
process.exitCode = 1;
|
|
333
|
-
return;
|
|
334
|
-
}
|
|
335
|
-
if ((0, devShared_1.isNetworkError)(error)) {
|
|
336
|
-
(0, messages_1.printNetworkIssue)('Could not reach the Playdrop API to resolve your account.', 'project capture');
|
|
337
|
-
process.exitCode = 1;
|
|
338
|
-
return;
|
|
339
|
-
}
|
|
340
|
-
if (error instanceof Error && error.message === 'DEV_USERNAME_EMPTY') {
|
|
341
|
-
(0, messages_1.printErrorWithHelp)('Could not determine your Playdrop creator username.', [
|
|
342
|
-
'Retry "playdrop project capture" in a moment.',
|
|
343
|
-
'If the problem persists, contact the Playdrop team.'
|
|
344
|
-
], { command: 'project capture' });
|
|
345
|
-
process.exitCode = 1;
|
|
346
|
-
return;
|
|
347
|
-
}
|
|
348
|
-
throw error;
|
|
349
|
-
}
|
|
350
|
-
try {
|
|
351
|
-
await (0, devShared_1.assertAppRegistered)(client, currentUsername, appName);
|
|
352
|
-
}
|
|
353
|
-
catch (error) {
|
|
354
|
-
if (error instanceof http_1.CLIUnsupportedClientError) {
|
|
355
|
-
return;
|
|
356
|
-
}
|
|
357
|
-
if (error instanceof types_1.UnsupportedClientError) {
|
|
358
|
-
(0, http_1.handleUnsupportedError)(error, 'Capture');
|
|
359
|
-
process.exitCode = 1;
|
|
360
|
-
return;
|
|
205
|
+
const projectInfo = (0, devShared_1.findProjectInfo)(filePath);
|
|
206
|
+
const devScriptAvailable = Boolean(projectInfo.projectDir && projectInfo.packageJson && typeof projectInfo.packageJson.scripts?.dev === 'string');
|
|
207
|
+
await (0, commandContext_1.withEnvironment)('project capture', 'Capturing app logs', async ({ client, env, envConfig, token }) => {
|
|
208
|
+
let currentUser = null;
|
|
209
|
+
let currentUsername = '';
|
|
210
|
+
try {
|
|
211
|
+
currentUser = await (0, devShared_1.fetchDevUser)(client);
|
|
212
|
+
currentUsername = currentUser.username.trim();
|
|
361
213
|
}
|
|
362
|
-
|
|
363
|
-
if (error
|
|
364
|
-
|
|
365
|
-
`Run "playdrop project create app ${appName}" to register the app before running capture.`,
|
|
366
|
-
'If you expected it to exist, ensure you are logged into the correct environment.'
|
|
367
|
-
], { command: 'project capture' });
|
|
214
|
+
catch (error) {
|
|
215
|
+
if (error instanceof http_1.CLIUnsupportedClientError) {
|
|
216
|
+
return;
|
|
368
217
|
}
|
|
369
|
-
|
|
370
|
-
(0,
|
|
371
|
-
|
|
372
|
-
|
|
218
|
+
if (error instanceof types_1.UnsupportedClientError) {
|
|
219
|
+
(0, http_1.handleUnsupportedError)(error, 'Authentication');
|
|
220
|
+
process.exitCode = 1;
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
if (error instanceof types_1.ApiError) {
|
|
224
|
+
(0, messages_1.printErrorWithHelp)(`Could not fetch your account (status ${error.status}).`, [
|
|
225
|
+
'Run "playdrop auth login" to refresh your session and ensure the API is reachable.',
|
|
226
|
+
'Use "playdrop auth whoami" afterwards to confirm your status.',
|
|
373
227
|
], { command: 'project capture' });
|
|
228
|
+
process.exitCode = 1;
|
|
229
|
+
return;
|
|
374
230
|
}
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
(0, messages_1.printNetworkIssue)('Could not reach the Playdrop API to verify the app registration.', 'project capture');
|
|
380
|
-
process.exitCode = 1;
|
|
381
|
-
return;
|
|
382
|
-
}
|
|
383
|
-
throw error;
|
|
384
|
-
}
|
|
385
|
-
const projectInfo = (0, devShared_1.findProjectInfo)(filePath);
|
|
386
|
-
const devScriptAvailable = Boolean(projectInfo.projectDir && projectInfo.packageJson && typeof projectInfo.packageJson.scripts?.dev === 'string');
|
|
387
|
-
const entryLabel = (0, node_path_1.relative)(process.cwd(), filePath) || filePath;
|
|
388
|
-
console.log(`[capture] Preparing ${entryLabel} for ${cfg.env} (${appTypeSlug}).`);
|
|
389
|
-
const serverAlreadyRunning = await (0, devServer_1.isDevServerAvailable)(appName, portToUse, 750);
|
|
390
|
-
const devServerStartedByCapture = !serverAlreadyRunning;
|
|
391
|
-
let serverHandle = null;
|
|
392
|
-
let signalHandler = null;
|
|
393
|
-
let cleanupRequested = false;
|
|
394
|
-
const cleanup = async () => {
|
|
395
|
-
if (cleanupRequested)
|
|
396
|
-
return;
|
|
397
|
-
cleanupRequested = true;
|
|
398
|
-
if (serverHandle) {
|
|
399
|
-
try {
|
|
400
|
-
await serverHandle.close();
|
|
231
|
+
if ((0, devShared_1.isNetworkError)(error)) {
|
|
232
|
+
(0, messages_1.printNetworkIssue)('Could not reach the Playdrop API to resolve your account.', 'project capture');
|
|
233
|
+
process.exitCode = 1;
|
|
234
|
+
return;
|
|
401
235
|
}
|
|
402
|
-
|
|
403
|
-
|
|
236
|
+
if (error instanceof Error && error.message === 'DEV_USERNAME_EMPTY') {
|
|
237
|
+
(0, messages_1.printErrorWithHelp)('Could not determine your Playdrop creator username.', [
|
|
238
|
+
'Retry "playdrop project capture" in a moment.',
|
|
239
|
+
'If the problem persists, contact the Playdrop team.',
|
|
240
|
+
], { command: 'project capture' });
|
|
241
|
+
process.exitCode = 1;
|
|
242
|
+
return;
|
|
404
243
|
}
|
|
244
|
+
throw error;
|
|
405
245
|
}
|
|
406
|
-
};
|
|
407
|
-
if (serverAlreadyRunning) {
|
|
408
|
-
console.log(`[capture] Reusing dev server at http://localhost:${portToUse}/apps/${appName}.html`);
|
|
409
|
-
}
|
|
410
|
-
else {
|
|
411
246
|
try {
|
|
412
|
-
|
|
413
|
-
appName,
|
|
414
|
-
htmlPath: filePath,
|
|
415
|
-
port: portToUse,
|
|
416
|
-
projectInfo,
|
|
417
|
-
});
|
|
418
|
-
signalHandler = () => {
|
|
419
|
-
void cleanup().finally(() => process.exit(130));
|
|
420
|
-
};
|
|
421
|
-
process.on('SIGINT', signalHandler);
|
|
422
|
-
process.on('SIGTERM', signalHandler);
|
|
247
|
+
await (0, devShared_1.assertAppRegistered)(client, currentUsername, appName);
|
|
423
248
|
}
|
|
424
249
|
catch (error) {
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
'Ensure the HTML file exists and is readable.',
|
|
428
|
-
], { command: 'project capture' });
|
|
429
|
-
process.exitCode = 1;
|
|
430
|
-
return;
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
if (!serverAlreadyRunning && projectInfo.projectDir && !devScriptAvailable && projectInfo.packageJsonPath) {
|
|
434
|
-
const projectLabel = (0, devShared_1.formatProjectLabel)(projectInfo);
|
|
435
|
-
if (projectLabel) {
|
|
436
|
-
console.log(`[capture] package.json detected at ${projectLabel}, but no "dev" script was found. Run your build/watch scripts manually if needed.`);
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
if (devServerStartedByCapture) {
|
|
440
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
441
|
-
}
|
|
442
|
-
const webBase = envConfig.webBase ?? 'https://www.playdrop.ai';
|
|
443
|
-
const frameUrl = `${webBase}/creators/${encodeURIComponent(currentUsername)}/apps/${appTypeSlug}/${encodeURIComponent(appName)}/dev`;
|
|
444
|
-
console.log(`[capture] Launching Playwright against ${frameUrl}`);
|
|
445
|
-
const errors = [];
|
|
446
|
-
const handleConsoleMessage = async (message) => {
|
|
447
|
-
const type = message.type();
|
|
448
|
-
const level = mapConsoleTypeToLevel(type);
|
|
449
|
-
const originPage = message.page();
|
|
450
|
-
const originLabel = originPage ? originPage.url() : 'worker';
|
|
451
|
-
const location = message.location();
|
|
452
|
-
const locationLabel = location && location.url
|
|
453
|
-
? `${location.url}:${location.lineNumber ?? 0}:${location.columnNumber ?? 0}`
|
|
454
|
-
: undefined;
|
|
455
|
-
const args = await Promise.all(message
|
|
456
|
-
.args()
|
|
457
|
-
.map(arg => arg.jsonValue().catch(() => arg.toString())));
|
|
458
|
-
const rendered = args.length > 0
|
|
459
|
-
? args.map(formatConsoleValue).join(' ')
|
|
460
|
-
: message.text();
|
|
461
|
-
const prefix = `[capture][console:${type}]`;
|
|
462
|
-
const components = [prefix, rendered].filter(Boolean);
|
|
463
|
-
const line = components.join(' ');
|
|
464
|
-
let effectiveLevel = level;
|
|
465
|
-
if (level === 'warn' && line.includes('GL Driver Message')) {
|
|
466
|
-
effectiveLevel = 'debug';
|
|
467
|
-
}
|
|
468
|
-
const allowed = shouldEmit(effectiveLevel, minimumLogLevel);
|
|
469
|
-
if (type === 'error' || type === 'assert' || type === 'trace') {
|
|
470
|
-
errors.push(line);
|
|
471
|
-
if (allowed) {
|
|
472
|
-
console.error(line);
|
|
250
|
+
if (error instanceof http_1.CLIUnsupportedClientError) {
|
|
251
|
+
return;
|
|
473
252
|
}
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
else if (effectiveLevel === 'debug') {
|
|
486
|
-
console.debug(line);
|
|
487
|
-
}
|
|
488
|
-
else {
|
|
489
|
-
console.log(line);
|
|
490
|
-
}
|
|
491
|
-
};
|
|
492
|
-
try {
|
|
493
|
-
await (0, playwright_1.withChromiumPage)(async ({ page }) => {
|
|
494
|
-
await page.addInitScript(({ token, user }) => {
|
|
495
|
-
try {
|
|
496
|
-
if (token) {
|
|
497
|
-
window.localStorage.setItem('playdrop.accessToken', token);
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
catch {
|
|
501
|
-
// ignore storage errors
|
|
502
|
-
}
|
|
503
|
-
try {
|
|
504
|
-
if (user) {
|
|
505
|
-
window.localStorage.setItem('playdrop.user', JSON.stringify(user));
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
catch {
|
|
509
|
-
// ignore storage errors
|
|
510
|
-
}
|
|
511
|
-
try {
|
|
512
|
-
window.sessionStorage.removeItem('playdrop.logoutReason');
|
|
513
|
-
}
|
|
514
|
-
catch {
|
|
515
|
-
// ignore storage errors
|
|
516
|
-
}
|
|
517
|
-
}, { token: cfg.token, user: currentUser });
|
|
518
|
-
await page.exposeBinding('__playdropCaptureLog', async ({ frame }, type, payload) => {
|
|
519
|
-
const normalized = typeof type === 'string' ? type.toLowerCase() : 'info';
|
|
520
|
-
const level = normalized === 'error' || normalized === 'fatal'
|
|
521
|
-
? 'error'
|
|
522
|
-
: normalized === 'warn' || normalized === 'warning'
|
|
523
|
-
? 'warn'
|
|
524
|
-
: normalized === 'debug'
|
|
525
|
-
? 'debug'
|
|
526
|
-
: 'info';
|
|
527
|
-
const frameUrl = frame?.url();
|
|
528
|
-
const serialized = serializePayload(payload);
|
|
529
|
-
const parts = ['[capture][custom]', normalized, serialized].filter(Boolean);
|
|
530
|
-
const line = parts.join(' ');
|
|
531
|
-
const allowed = shouldEmit(level, minimumLogLevel);
|
|
532
|
-
if (level === 'error') {
|
|
533
|
-
errors.push(line);
|
|
534
|
-
if (allowed) {
|
|
535
|
-
console.error(line);
|
|
536
|
-
}
|
|
537
|
-
return;
|
|
538
|
-
}
|
|
539
|
-
if (!allowed) {
|
|
540
|
-
return;
|
|
541
|
-
}
|
|
542
|
-
if (level === 'warn') {
|
|
543
|
-
console.warn(line);
|
|
544
|
-
}
|
|
545
|
-
else if (level === 'debug') {
|
|
546
|
-
console.debug(line);
|
|
253
|
+
if (error instanceof types_1.UnsupportedClientError) {
|
|
254
|
+
(0, http_1.handleUnsupportedError)(error, 'Capture');
|
|
255
|
+
process.exitCode = 1;
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
if (error instanceof types_1.ApiError) {
|
|
259
|
+
if (error.status === 404) {
|
|
260
|
+
(0, messages_1.printErrorWithHelp)(`App ${currentUsername}/${appName} is not registered on ${env}.`, [
|
|
261
|
+
`Run "playdrop project create app ${appName}" to register the app before running capture.`,
|
|
262
|
+
'If you expected it to exist, ensure you are logged into the correct environment.',
|
|
263
|
+
], { command: 'project capture' });
|
|
547
264
|
}
|
|
548
265
|
else {
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
window.addEventListener('unhandledrejection', event => {
|
|
554
|
-
try {
|
|
555
|
-
console.error('[capture][unhandledrejection]', event.reason);
|
|
556
|
-
}
|
|
557
|
-
catch {
|
|
558
|
-
console.error('[capture][unhandledrejection]');
|
|
559
|
-
}
|
|
560
|
-
});
|
|
561
|
-
const bridgeWindow = window;
|
|
562
|
-
bridgeWindow.playdrop = bridgeWindow.playdrop || {};
|
|
563
|
-
const bridge = (type, payload) => {
|
|
564
|
-
try {
|
|
565
|
-
const binding = bridgeWindow.__playdropCaptureLog;
|
|
566
|
-
if (typeof binding === 'function') {
|
|
567
|
-
binding(type, payload);
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
catch (error) {
|
|
571
|
-
console.error('[capture][bridge-error]', error);
|
|
572
|
-
}
|
|
573
|
-
};
|
|
574
|
-
Object.defineProperty(bridgeWindow.playdrop, 'capture', {
|
|
575
|
-
value: bridge,
|
|
576
|
-
configurable: true,
|
|
577
|
-
writable: true,
|
|
578
|
-
});
|
|
579
|
-
});
|
|
580
|
-
page.on('console', message => {
|
|
581
|
-
void handleConsoleMessage(message);
|
|
582
|
-
});
|
|
583
|
-
page.on('pageerror', error => {
|
|
584
|
-
const text = error?.message ?? String(error);
|
|
585
|
-
errors.push(text);
|
|
586
|
-
if (shouldEmit('error', minimumLogLevel)) {
|
|
587
|
-
console.error(`[capture][pageerror] ${text}`);
|
|
266
|
+
(0, messages_1.printErrorWithHelp)(`Failed to verify app registration (status ${error.status}).`, [
|
|
267
|
+
'Retry in a moment.',
|
|
268
|
+
'If the issue persists, contact the Playdrop team.',
|
|
269
|
+
], { command: 'project capture' });
|
|
588
270
|
}
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
const failure = request.failure();
|
|
592
|
-
const errorText = failure?.errorText ?? '';
|
|
593
|
-
const text = `${request.method()} ${request.url()} - ${errorText || 'failed'}`;
|
|
594
|
-
if (/ERR_ABORTED/i.test(errorText) || /ERR_HTTP2_PROTOCOL_ERROR/i.test(errorText)) {
|
|
595
|
-
if (shouldEmit('debug', minimumLogLevel)) {
|
|
596
|
-
console.debug(`[capture][requestcancelled] ${text}`);
|
|
597
|
-
}
|
|
598
|
-
return;
|
|
599
|
-
}
|
|
600
|
-
errors.push(text);
|
|
601
|
-
if (shouldEmit('warn', minimumLogLevel)) {
|
|
602
|
-
console.error(`[capture][requestfailed] ${text}`);
|
|
603
|
-
}
|
|
604
|
-
});
|
|
605
|
-
page.on('response', response => {
|
|
606
|
-
const status = response.status();
|
|
607
|
-
if (status >= 400) {
|
|
608
|
-
const text = `${status} ${response.statusText()} ${response.url()}`;
|
|
609
|
-
if (status === 404 && /\\?playdrop_channel=/.test(response.url())) {
|
|
610
|
-
if (shouldEmit('debug', minimumLogLevel)) {
|
|
611
|
-
console.debug(`[capture][response-reload] ${text}`);
|
|
612
|
-
}
|
|
613
|
-
return;
|
|
614
|
-
}
|
|
615
|
-
errors.push(text);
|
|
616
|
-
if (shouldEmit('warn', minimumLogLevel)) {
|
|
617
|
-
console.error(`[capture][response] ${text}`);
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
});
|
|
621
|
-
const response = await page.goto(frameUrl, { waitUntil: 'domcontentloaded', timeout: 30000 });
|
|
622
|
-
if (response && !response.ok()) {
|
|
623
|
-
const statusText = `${response.status()} ${response.statusText()}`;
|
|
624
|
-
console.warn(`[capture] Navigation succeeded with non-OK status: ${statusText}`);
|
|
271
|
+
process.exitCode = 1;
|
|
272
|
+
return;
|
|
625
273
|
}
|
|
626
|
-
|
|
627
|
-
|
|
274
|
+
if ((0, devShared_1.isNetworkError)(error)) {
|
|
275
|
+
(0, messages_1.printNetworkIssue)('Could not reach the Playdrop API to verify the app registration.', 'project capture');
|
|
276
|
+
process.exitCode = 1;
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
throw error;
|
|
280
|
+
}
|
|
281
|
+
const entryLabel = (0, node_path_1.relative)(process.cwd(), filePath) || filePath;
|
|
282
|
+
console.log(`[capture] Preparing ${entryLabel} for ${env} (${appTypeSlug}).`);
|
|
283
|
+
const serverAlreadyRunning = await (0, devServer_1.isDevServerAvailable)(appName, portToUse, 750);
|
|
284
|
+
const devServerStartedByCapture = !serverAlreadyRunning;
|
|
285
|
+
let serverHandle = null;
|
|
286
|
+
let signalHandler = null;
|
|
287
|
+
let cleanupRequested = false;
|
|
288
|
+
const cleanup = async () => {
|
|
289
|
+
if (cleanupRequested)
|
|
290
|
+
return;
|
|
291
|
+
cleanupRequested = true;
|
|
292
|
+
if (serverHandle) {
|
|
628
293
|
try {
|
|
629
|
-
|
|
630
|
-
if (targetDir && targetDir !== '.' && targetDir !== '/') {
|
|
631
|
-
await (0, promises_1.mkdir)(targetDir, { recursive: true });
|
|
632
|
-
}
|
|
633
|
-
await page.screenshot({ path: screenshotPath, fullPage: true });
|
|
634
|
-
const relativePath = (0, node_path_1.relative)(process.cwd(), screenshotPath) || screenshotPath;
|
|
635
|
-
console.log(`[capture] Saved screenshot to ${relativePath}`);
|
|
294
|
+
await serverHandle.close();
|
|
636
295
|
}
|
|
637
296
|
catch (error) {
|
|
638
|
-
|
|
639
|
-
errors.push(`screenshot_failed: ${reason}`);
|
|
640
|
-
console.error(`[capture] Failed to write screenshot: ${reason}`);
|
|
297
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
641
298
|
}
|
|
642
299
|
}
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
if (error instanceof http_1.CLIUnsupportedClientError) {
|
|
647
|
-
return;
|
|
300
|
+
};
|
|
301
|
+
if (serverAlreadyRunning) {
|
|
302
|
+
console.log(`[capture] Reusing dev server at http://localhost:${portToUse}/apps/${appName}.html`);
|
|
648
303
|
}
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
304
|
+
else {
|
|
305
|
+
try {
|
|
306
|
+
serverHandle = await (0, devServer_1.startDevServer)({
|
|
307
|
+
appName,
|
|
308
|
+
htmlPath: filePath,
|
|
309
|
+
port: portToUse,
|
|
310
|
+
projectInfo,
|
|
311
|
+
});
|
|
312
|
+
signalHandler = () => {
|
|
313
|
+
void cleanup().finally(() => process.exit(130));
|
|
314
|
+
};
|
|
315
|
+
process.on('SIGINT', signalHandler);
|
|
316
|
+
process.on('SIGTERM', signalHandler);
|
|
317
|
+
}
|
|
318
|
+
catch (error) {
|
|
319
|
+
(0, messages_1.printErrorWithHelp)(error?.message || `Failed to start dev server on port ${portToUse}.`, [
|
|
320
|
+
'Check if another process is already using the port.',
|
|
321
|
+
'Ensure the HTML file exists and is readable.',
|
|
322
|
+
], { command: 'project capture' });
|
|
323
|
+
process.exitCode = 1;
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
if (!serverAlreadyRunning && projectInfo.projectDir && !devScriptAvailable && projectInfo.packageJsonPath) {
|
|
328
|
+
const projectLabel = (0, devShared_1.formatProjectLabel)(projectInfo);
|
|
329
|
+
if (projectLabel) {
|
|
330
|
+
console.log(`[capture] package.json detected at ${projectLabel}, but no "dev" script was found. Run your build/watch scripts manually if needed.`);
|
|
331
|
+
}
|
|
653
332
|
}
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
const launchError = (0, playwright_1.createPlaywrightLaunchError)('playdrop project capture', error);
|
|
657
|
-
console.error(launchError.message);
|
|
333
|
+
if (devServerStartedByCapture) {
|
|
334
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
658
335
|
}
|
|
659
|
-
|
|
660
|
-
|
|
336
|
+
const webBase = envConfig.webBase ?? 'https://www.playdrop.ai';
|
|
337
|
+
const frameUrl = `${webBase}/creators/${encodeURIComponent(currentUsername)}/apps/${appTypeSlug}/${encodeURIComponent(appName)}/dev`;
|
|
338
|
+
console.log(`[capture] Launching Playwright against ${frameUrl}`);
|
|
339
|
+
try {
|
|
340
|
+
const result = await (0, captureRuntime_1.runCapture)({
|
|
341
|
+
targetUrl: frameUrl,
|
|
342
|
+
expectedUrl: frameUrl,
|
|
343
|
+
timeoutMs,
|
|
344
|
+
minimumLogLevel,
|
|
345
|
+
screenshotPath,
|
|
346
|
+
contextOptions: surfaceContextOptions,
|
|
347
|
+
token: loginOverride ? undefined : token,
|
|
348
|
+
user: loginOverride ? undefined : currentUser,
|
|
349
|
+
login: loginOverride,
|
|
350
|
+
enableCaptureBridge: true,
|
|
351
|
+
});
|
|
352
|
+
if (result.errorCount > 0) {
|
|
353
|
+
console.error(`[capture] Completed with ${result.errorCount} error(s) after ${timeoutSeconds} seconds.`);
|
|
354
|
+
process.exitCode = 1;
|
|
355
|
+
}
|
|
356
|
+
else {
|
|
357
|
+
console.log(`[capture] Completed without console errors after ${timeoutSeconds} seconds.`);
|
|
358
|
+
}
|
|
661
359
|
}
|
|
662
|
-
|
|
663
|
-
|
|
360
|
+
catch (error) {
|
|
361
|
+
if (error instanceof http_1.CLIUnsupportedClientError) {
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
if (error instanceof types_1.UnsupportedClientError) {
|
|
365
|
+
(0, http_1.handleUnsupportedError)(error, 'Capture');
|
|
366
|
+
process.exitCode = 1;
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
if (error instanceof Error && error.stack) {
|
|
370
|
+
console.error(error.stack);
|
|
371
|
+
}
|
|
372
|
+
else {
|
|
373
|
+
console.error(`[capture] ${error instanceof Error ? error.message : String(error)}`);
|
|
374
|
+
}
|
|
375
|
+
process.exitCode = 1;
|
|
664
376
|
}
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
process.off('SIGTERM', signalHandler);
|
|
377
|
+
finally {
|
|
378
|
+
if (signalHandler) {
|
|
379
|
+
process.off('SIGINT', signalHandler);
|
|
380
|
+
process.off('SIGTERM', signalHandler);
|
|
381
|
+
}
|
|
382
|
+
await cleanup();
|
|
672
383
|
}
|
|
673
|
-
|
|
674
|
-
}
|
|
675
|
-
if (errors.length > 0) {
|
|
676
|
-
console.error(`[capture] Completed with ${errors.length} error(s) after ${timeoutSeconds} seconds.`);
|
|
677
|
-
process.exitCode = 1;
|
|
678
|
-
}
|
|
679
|
-
else {
|
|
680
|
-
console.log(`[capture] Completed without console errors after ${timeoutSeconds} seconds.`);
|
|
681
|
-
}
|
|
384
|
+
});
|
|
682
385
|
}
|