@playdrop/playdrop-cli 0.6.4 → 0.6.6
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 +8 -0
- package/dist/appUrls.js +27 -0
- package/dist/apps/index.d.ts +11 -0
- package/dist/apps/index.js +45 -1
- package/dist/apps/launchCheck.d.ts +30 -0
- package/dist/apps/launchCheck.js +272 -0
- package/dist/apps/registration.d.ts +12 -0
- package/dist/apps/registration.js +49 -0
- package/dist/apps/upload.d.ts +5 -0
- package/dist/apps/upload.js +17 -0
- package/dist/apps/validate.js +41 -3
- package/dist/captureRuntime.d.ts +13 -0
- package/dist/captureRuntime.js +99 -21
- package/dist/commands/capture.js +52 -39
- package/dist/commands/create.js +17 -3
- package/dist/commands/devServer.js +10 -2
- package/dist/commands/upload.js +16 -5
- package/dist/commands/validate.js +58 -1
- package/node_modules/@playdrop/api-client/dist/client.d.ts +3 -1
- package/node_modules/@playdrop/api-client/dist/client.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts +3 -1
- package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/apps.js +21 -0
- package/node_modules/@playdrop/config/client-meta.json +7 -7
- package/node_modules/@playdrop/config/dist/tsconfig.tsbuildinfo +1 -1
- package/node_modules/@playdrop/types/dist/version.d.ts +37 -1
- package/node_modules/@playdrop/types/dist/version.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/version.js +1 -0
- package/package.json +1 -1
- package/node_modules/@playdrop/boxel-core/dist/test/entity-utils.test.d.ts +0 -1
- package/node_modules/@playdrop/boxel-core/dist/test/entity-utils.test.js +0 -92
- package/node_modules/@playdrop/boxel-core/dist/test/entity-utils.test.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/dist/test/greedy-mesher.test.d.ts +0 -1
- package/node_modules/@playdrop/boxel-core/dist/test/greedy-mesher.test.js +0 -48
- package/node_modules/@playdrop/boxel-core/dist/test/greedy-mesher.test.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/dist/test/humanoid/humanoid-builders.test.d.ts +0 -1
- package/node_modules/@playdrop/boxel-core/dist/test/humanoid/humanoid-builders.test.js +0 -270
- package/node_modules/@playdrop/boxel-core/dist/test/humanoid/humanoid-builders.test.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/dist/test/index.test.d.ts +0 -1
- package/node_modules/@playdrop/boxel-core/dist/test/index.test.js +0 -48
- package/node_modules/@playdrop/boxel-core/dist/test/index.test.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/dist/test/layer-mode.test.d.ts +0 -1
- package/node_modules/@playdrop/boxel-core/dist/test/layer-mode.test.js +0 -67
- package/node_modules/@playdrop/boxel-core/dist/test/layer-mode.test.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/dist/test/materials.test.d.ts +0 -1
- package/node_modules/@playdrop/boxel-core/dist/test/materials.test.js +0 -55
- package/node_modules/@playdrop/boxel-core/dist/test/materials.test.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/dist/test/palette-tools.test.d.ts +0 -1
- package/node_modules/@playdrop/boxel-core/dist/test/palette-tools.test.js +0 -124
- package/node_modules/@playdrop/boxel-core/dist/test/palette-tools.test.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/dist/test/serialization.test.d.ts +0 -1
- package/node_modules/@playdrop/boxel-core/dist/test/serialization.test.js +0 -35
- package/node_modules/@playdrop/boxel-core/dist/test/serialization.test.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/dist/test/textures.test.d.ts +0 -1
- package/node_modules/@playdrop/boxel-core/dist/test/textures.test.js +0 -120
- package/node_modules/@playdrop/boxel-core/dist/test/textures.test.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/dist/test/types.test.d.ts +0 -1
- package/node_modules/@playdrop/boxel-core/dist/test/types.test.js +0 -32
- package/node_modules/@playdrop/boxel-core/dist/test/types.test.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/dist/test/upscale.test.d.ts +0 -1
- package/node_modules/@playdrop/boxel-core/dist/test/upscale.test.js +0 -100
- package/node_modules/@playdrop/boxel-core/dist/test/upscale.test.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/dist/test/validation.test.d.ts +0 -1
- package/node_modules/@playdrop/boxel-core/dist/test/validation.test.js +0 -61
- package/node_modules/@playdrop/boxel-core/dist/test/validation.test.js.map +0 -1
- package/node_modules/@playdrop/boxel-core/dist/test/voxels.test.d.ts +0 -1
- package/node_modules/@playdrop/boxel-core/dist/test/voxels.test.js +0 -51
- package/node_modules/@playdrop/boxel-core/dist/test/voxels.test.js.map +0 -1
- package/node_modules/@playdrop/config/dist/src/creator-docs.d.ts +0 -24
- package/node_modules/@playdrop/config/dist/src/creator-docs.d.ts.map +0 -1
- package/node_modules/@playdrop/config/dist/src/creator-docs.js +0 -253
- package/node_modules/@playdrop/config/dist/src/creator-faq.d.ts +0 -17
- package/node_modules/@playdrop/config/dist/src/creator-faq.d.ts.map +0 -1
- package/node_modules/@playdrop/config/dist/src/creator-faq.js +0 -141
- package/node_modules/@playdrop/config/dist/test/creator-docs.test.d.ts +0 -2
- package/node_modules/@playdrop/config/dist/test/creator-docs.test.d.ts.map +0 -1
- package/node_modules/@playdrop/config/dist/test/creator-docs.test.js +0 -36
package/config/client-meta.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.6.
|
|
3
|
-
"build":
|
|
2
|
+
"version": "0.6.6",
|
|
3
|
+
"build": 3,
|
|
4
4
|
"platforms": {
|
|
5
5
|
"ios": {
|
|
6
6
|
"minimumVersion": "16.0"
|
|
@@ -26,12 +26,12 @@
|
|
|
26
26
|
},
|
|
27
27
|
"clients": {
|
|
28
28
|
"web": {
|
|
29
|
-
"minimumVersion": "0.6.
|
|
30
|
-
"minimumBuild":
|
|
29
|
+
"minimumVersion": "0.6.6",
|
|
30
|
+
"minimumBuild": 3
|
|
31
31
|
},
|
|
32
32
|
"admin": {
|
|
33
|
-
"minimumVersion": "0.6.
|
|
34
|
-
"minimumBuild":
|
|
33
|
+
"minimumVersion": "0.6.6",
|
|
34
|
+
"minimumBuild": 3
|
|
35
35
|
},
|
|
36
36
|
"apple": {
|
|
37
37
|
"minimumVersion": "0.3.10",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"minimumBuild": 1
|
|
47
47
|
},
|
|
48
48
|
"cli": {
|
|
49
|
-
"minimumVersion": "0.6.
|
|
49
|
+
"minimumVersion": "0.6.6"
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
}
|
package/dist/appUrls.d.ts
CHANGED
|
@@ -7,3 +7,11 @@ export type AppUrlInput = {
|
|
|
7
7
|
export declare function normalizePlaydropWebBase(webBase?: string | null): string;
|
|
8
8
|
export declare function getAppTypeSlug(type: AppType | string | null | undefined): string;
|
|
9
9
|
export declare function buildPlatformPlayUrl(webBase: string | null | undefined, input: AppUrlInput): string;
|
|
10
|
+
export declare function buildPlatformDevUrl(webBase: string | null | undefined, input: AppUrlInput & {
|
|
11
|
+
devAuth?: 'prompt' | 'anonymous' | 'viewer' | 'player';
|
|
12
|
+
player?: string | null;
|
|
13
|
+
launchCheck?: boolean;
|
|
14
|
+
}): string;
|
|
15
|
+
export declare function buildUploadLaunchCheckUrl(webBase: string | null | undefined, input: AppUrlInput & {
|
|
16
|
+
sessionId: string;
|
|
17
|
+
}): string;
|
package/dist/appUrls.js
CHANGED
|
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.normalizePlaydropWebBase = normalizePlaydropWebBase;
|
|
4
4
|
exports.getAppTypeSlug = getAppTypeSlug;
|
|
5
5
|
exports.buildPlatformPlayUrl = buildPlatformPlayUrl;
|
|
6
|
+
exports.buildPlatformDevUrl = buildPlatformDevUrl;
|
|
7
|
+
exports.buildUploadLaunchCheckUrl = buildUploadLaunchCheckUrl;
|
|
6
8
|
const DEFAULT_WEB_BASE = 'https://www.playdrop.ai';
|
|
7
9
|
function normalizePlaydropWebBase(webBase) {
|
|
8
10
|
const base = typeof webBase === 'string' && webBase.trim().length > 0 ? webBase.trim() : DEFAULT_WEB_BASE;
|
|
@@ -31,3 +33,28 @@ function buildPlatformPlayUrl(webBase, input) {
|
|
|
31
33
|
const appName = encodeURIComponent(input.appName);
|
|
32
34
|
return `${base}/creators/${creator}/apps/${typeSlug}/${appName}/play`;
|
|
33
35
|
}
|
|
36
|
+
function buildPlatformDevUrl(webBase, input) {
|
|
37
|
+
const base = normalizePlaydropWebBase(webBase);
|
|
38
|
+
const creator = encodeURIComponent(input.creatorUsername);
|
|
39
|
+
const typeSlug = getAppTypeSlug(input.appType);
|
|
40
|
+
const appName = encodeURIComponent(input.appName);
|
|
41
|
+
const url = new URL(`${base}/creators/${creator}/apps/${typeSlug}/${appName}/dev`);
|
|
42
|
+
if (typeof input.devAuth === 'string' && input.devAuth.trim().length > 0) {
|
|
43
|
+
url.searchParams.set('devAuth', input.devAuth.trim());
|
|
44
|
+
}
|
|
45
|
+
if (typeof input.player === 'string' && input.player.trim().length > 0) {
|
|
46
|
+
url.searchParams.set('player', input.player.trim());
|
|
47
|
+
}
|
|
48
|
+
if (input.launchCheck) {
|
|
49
|
+
url.searchParams.set('launchCheck', '1');
|
|
50
|
+
}
|
|
51
|
+
return url.toString();
|
|
52
|
+
}
|
|
53
|
+
function buildUploadLaunchCheckUrl(webBase, input) {
|
|
54
|
+
const base = normalizePlaydropWebBase(webBase);
|
|
55
|
+
const creator = encodeURIComponent(input.creatorUsername);
|
|
56
|
+
const typeSlug = getAppTypeSlug(input.appType);
|
|
57
|
+
const appName = encodeURIComponent(input.appName);
|
|
58
|
+
const sessionId = encodeURIComponent(input.sessionId);
|
|
59
|
+
return `${base}/creators/${creator}/apps/${typeSlug}/${appName}/upload-check/${sessionId}`;
|
|
60
|
+
}
|
package/dist/apps/index.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { ApiClient } from '@playdrop/api-client';
|
|
2
|
+
import type { UserResponse } from '@playdrop/types';
|
|
2
3
|
import type { AppTask } from '../catalogue';
|
|
3
4
|
import { collectAppValidationWarnings, validateAppTask, runFormatScript } from './validate';
|
|
4
5
|
import { buildApp, type AppBuildArtifacts } from './build';
|
|
5
6
|
import { uploadApp, type AppUploadResult } from './upload';
|
|
7
|
+
import { formatHostedLaunchCheckFailure, runLocalHostedLaunchCheck, runUploadedHostedLaunchCheck, type HostedLaunchCheckResult } from './launchCheck';
|
|
6
8
|
export type AppPipelineResult = {
|
|
7
9
|
artifacts: AppBuildArtifacts | null;
|
|
8
10
|
upload: AppUploadResult;
|
|
@@ -13,6 +15,15 @@ export type AppPipelineOptions = {
|
|
|
13
15
|
skipReview?: boolean;
|
|
14
16
|
clearTags?: boolean;
|
|
15
17
|
creatorUsername?: string;
|
|
18
|
+
apiBase?: string;
|
|
19
|
+
webBase?: string | null;
|
|
20
|
+
token?: string | null;
|
|
21
|
+
user?: UserResponse | null;
|
|
22
|
+
runLocalLaunchCheck?: boolean;
|
|
23
|
+
runStagedUploadLaunchCheck?: boolean;
|
|
24
|
+
launchCheckTimeoutMs?: number;
|
|
25
|
+
ensureRegisteredAppShell?: boolean;
|
|
16
26
|
};
|
|
17
27
|
export declare function runAppPipeline(client: ApiClient, task: AppTask, options?: AppPipelineOptions): Promise<AppPipelineResult>;
|
|
18
28
|
export { collectAppValidationWarnings, validateAppTask, buildApp, uploadApp, runFormatScript };
|
|
29
|
+
export { formatHostedLaunchCheckFailure, runLocalHostedLaunchCheck, runUploadedHostedLaunchCheck, type HostedLaunchCheckResult, };
|
package/dist/apps/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.runFormatScript = exports.uploadApp = exports.buildApp = exports.validateAppTask = exports.collectAppValidationWarnings = void 0;
|
|
3
|
+
exports.runUploadedHostedLaunchCheck = exports.runLocalHostedLaunchCheck = exports.formatHostedLaunchCheckFailure = exports.runFormatScript = exports.uploadApp = exports.buildApp = exports.validateAppTask = exports.collectAppValidationWarnings = void 0;
|
|
4
4
|
exports.runAppPipeline = runAppPipeline;
|
|
5
5
|
const validate_1 = require("./validate");
|
|
6
6
|
Object.defineProperty(exports, "collectAppValidationWarnings", { enumerable: true, get: function () { return validate_1.collectAppValidationWarnings; } });
|
|
@@ -10,10 +10,50 @@ const build_1 = require("./build");
|
|
|
10
10
|
Object.defineProperty(exports, "buildApp", { enumerable: true, get: function () { return build_1.buildApp; } });
|
|
11
11
|
const upload_1 = require("./upload");
|
|
12
12
|
Object.defineProperty(exports, "uploadApp", { enumerable: true, get: function () { return upload_1.uploadApp; } });
|
|
13
|
+
const launchCheck_1 = require("./launchCheck");
|
|
14
|
+
Object.defineProperty(exports, "formatHostedLaunchCheckFailure", { enumerable: true, get: function () { return launchCheck_1.formatHostedLaunchCheckFailure; } });
|
|
15
|
+
Object.defineProperty(exports, "runLocalHostedLaunchCheck", { enumerable: true, get: function () { return launchCheck_1.runLocalHostedLaunchCheck; } });
|
|
16
|
+
Object.defineProperty(exports, "runUploadedHostedLaunchCheck", { enumerable: true, get: function () { return launchCheck_1.runUploadedHostedLaunchCheck; } });
|
|
17
|
+
const registration_1 = require("./registration");
|
|
13
18
|
async function runAppPipeline(client, task, options) {
|
|
14
19
|
await (0, validate_1.validateAppTask)(task);
|
|
15
20
|
// External apps don't need to be built - they're hosted elsewhere
|
|
16
21
|
const isExternal = task.hostingMode === 'EXTERNAL' || !!task.externalUrl;
|
|
22
|
+
const requiresRegisteredShell = !isExternal && task.authMode === 'REQUIRED';
|
|
23
|
+
if (requiresRegisteredShell && options?.ensureRegisteredAppShell) {
|
|
24
|
+
const creatorUsername = typeof options.creatorUsername === 'string' ? options.creatorUsername.trim() : '';
|
|
25
|
+
if (!creatorUsername) {
|
|
26
|
+
throw new Error(`Missing creator username for required hosted app registration of "${task.name}".`);
|
|
27
|
+
}
|
|
28
|
+
await (0, registration_1.ensureRegisteredHostedAppShell)({
|
|
29
|
+
client,
|
|
30
|
+
creatorUsername,
|
|
31
|
+
task,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
if (!isExternal && options?.runLocalLaunchCheck) {
|
|
35
|
+
const creatorUsername = typeof options.creatorUsername === 'string' ? options.creatorUsername.trim() : '';
|
|
36
|
+
if (!creatorUsername) {
|
|
37
|
+
throw new Error(`Missing creator username for local hosted launch-check of "${task.name}".`);
|
|
38
|
+
}
|
|
39
|
+
const apiBase = typeof options.apiBase === 'string' ? options.apiBase.trim() : '';
|
|
40
|
+
if (!apiBase) {
|
|
41
|
+
throw new Error(`Missing API base URL for local hosted launch-check of "${task.name}".`);
|
|
42
|
+
}
|
|
43
|
+
const launchCheck = await (0, launchCheck_1.runLocalHostedLaunchCheck)({
|
|
44
|
+
client,
|
|
45
|
+
apiBase,
|
|
46
|
+
webBase: options.webBase,
|
|
47
|
+
creatorUsername,
|
|
48
|
+
task,
|
|
49
|
+
timeoutMs: options.launchCheckTimeoutMs,
|
|
50
|
+
token: options.token ?? null,
|
|
51
|
+
currentUser: options.user ?? null,
|
|
52
|
+
});
|
|
53
|
+
if (launchCheck.status !== 'PASSED') {
|
|
54
|
+
throw new Error((0, launchCheck_1.formatHostedLaunchCheckFailure)(task.name, launchCheck, 'local'));
|
|
55
|
+
}
|
|
56
|
+
}
|
|
17
57
|
const artifacts = isExternal ? null : await (0, build_1.buildApp)(task);
|
|
18
58
|
const warnings = (0, validate_1.collectAppValidationWarnings)(task, isExternal ? 'source' : 'bundle');
|
|
19
59
|
const uploadOptions = {
|
|
@@ -21,6 +61,10 @@ async function runAppPipeline(client, task, options) {
|
|
|
21
61
|
skipReview: options?.skipReview,
|
|
22
62
|
clearTags: options?.clearTags,
|
|
23
63
|
creatorUsername: options?.creatorUsername,
|
|
64
|
+
token: options?.token,
|
|
65
|
+
user: options?.user ?? null,
|
|
66
|
+
runStagedUploadLaunchCheck: options?.runStagedUploadLaunchCheck,
|
|
67
|
+
launchCheckTimeoutMs: options?.launchCheckTimeoutMs,
|
|
24
68
|
};
|
|
25
69
|
const upload = await (0, upload_1.uploadApp)(client, task, artifacts, uploadOptions);
|
|
26
70
|
return { artifacts, upload, warnings };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ApiClient } from '@playdrop/api-client';
|
|
2
|
+
import type { UserResponse } from '@playdrop/types';
|
|
3
|
+
import type { AppTask } from '../catalogue';
|
|
4
|
+
export type HostedLaunchCheckResult = {
|
|
5
|
+
status: 'PASSED' | 'FAILED';
|
|
6
|
+
errorCode: string | null;
|
|
7
|
+
message: string | null;
|
|
8
|
+
checkedAt: string;
|
|
9
|
+
artifactFingerprint: string | null;
|
|
10
|
+
};
|
|
11
|
+
export declare function formatHostedLaunchCheckFailure(taskName: string, result: HostedLaunchCheckResult, scope?: 'local' | 'staged'): string;
|
|
12
|
+
export declare function runLocalHostedLaunchCheck(input: {
|
|
13
|
+
client: ApiClient;
|
|
14
|
+
apiBase: string;
|
|
15
|
+
webBase: string | null | undefined;
|
|
16
|
+
creatorUsername: string;
|
|
17
|
+
task: AppTask;
|
|
18
|
+
timeoutMs?: number;
|
|
19
|
+
token?: string | null;
|
|
20
|
+
currentUser?: UserResponse | null;
|
|
21
|
+
}): Promise<HostedLaunchCheckResult>;
|
|
22
|
+
export declare function runUploadedHostedLaunchCheck(input: {
|
|
23
|
+
client: ApiClient;
|
|
24
|
+
creatorUsername: string;
|
|
25
|
+
task: AppTask;
|
|
26
|
+
sessionId: string;
|
|
27
|
+
timeoutMs?: number;
|
|
28
|
+
token?: string | null;
|
|
29
|
+
currentUser?: UserResponse | null;
|
|
30
|
+
}): Promise<HostedLaunchCheckResult>;
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.formatHostedLaunchCheckFailure = formatHostedLaunchCheckFailure;
|
|
4
|
+
exports.runLocalHostedLaunchCheck = runLocalHostedLaunchCheck;
|
|
5
|
+
exports.runUploadedHostedLaunchCheck = runUploadedHostedLaunchCheck;
|
|
6
|
+
const appUrls_1 = require("../appUrls");
|
|
7
|
+
const captureRuntime_1 = require("../captureRuntime");
|
|
8
|
+
const devShared_1 = require("../commands/devShared");
|
|
9
|
+
const dev_1 = require("../commands/dev");
|
|
10
|
+
const devAuth_1 = require("../devAuth");
|
|
11
|
+
const devServer_1 = require("../commands/devServer");
|
|
12
|
+
const devRuntimeAssets_1 = require("../commands/devRuntimeAssets");
|
|
13
|
+
const registration_1 = require("./registration");
|
|
14
|
+
const DEFAULT_HOSTED_LAUNCH_TIMEOUT_MS = 15000;
|
|
15
|
+
const POST_READY_SETTLE_MS = 750;
|
|
16
|
+
function normalizeLaunchCheckMessage(message) {
|
|
17
|
+
const normalized = typeof message === 'string' ? message.trim() : '';
|
|
18
|
+
return normalized || 'Hosted app failed to launch.';
|
|
19
|
+
}
|
|
20
|
+
function buildPassedLaunchCheckResult(artifactFingerprint) {
|
|
21
|
+
return {
|
|
22
|
+
status: 'PASSED',
|
|
23
|
+
errorCode: null,
|
|
24
|
+
message: null,
|
|
25
|
+
checkedAt: new Date().toISOString(),
|
|
26
|
+
artifactFingerprint,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function buildFailedLaunchCheckResult(errorCode, message, artifactFingerprint) {
|
|
30
|
+
return {
|
|
31
|
+
status: 'FAILED',
|
|
32
|
+
errorCode,
|
|
33
|
+
message,
|
|
34
|
+
checkedAt: new Date().toISOString(),
|
|
35
|
+
artifactFingerprint,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function formatHostedLaunchCheckFailure(taskName, result, scope = 'local') {
|
|
39
|
+
const scopeLabel = scope === 'staged' ? 'staged upload preview' : 'local hosted preview';
|
|
40
|
+
const errorCode = result.errorCode?.trim() || 'startup_failure';
|
|
41
|
+
const message = normalizeLaunchCheckMessage(result.message);
|
|
42
|
+
return `hosted_launch_check_failed: ${taskName} failed the ${scopeLabel} launch-check (${errorCode}). ${message}`;
|
|
43
|
+
}
|
|
44
|
+
function normalizeLaunchCheckFailure(error, artifactFingerprint) {
|
|
45
|
+
const rawMessage = error instanceof Error ? error.message : String(error);
|
|
46
|
+
const message = normalizeLaunchCheckMessage(rawMessage);
|
|
47
|
+
if (/errorCode=missing_init\b/i.test(message) || /hosted_app_missing_client_init:/i.test(message)) {
|
|
48
|
+
return buildFailedLaunchCheckResult('missing_init', message, artifactFingerprint);
|
|
49
|
+
}
|
|
50
|
+
if (/errorCode=missing_ready\b/i.test(message) || /hosted_app_missing_ready:/i.test(message)) {
|
|
51
|
+
return buildFailedLaunchCheckResult('missing_ready', message, artifactFingerprint);
|
|
52
|
+
}
|
|
53
|
+
if (/errorCode=sdk_mismatch\b/i.test(message) || /hosted_app_sdk_version_mismatch:/i.test(message)) {
|
|
54
|
+
return buildFailedLaunchCheckResult('sdk_mismatch', message, artifactFingerprint);
|
|
55
|
+
}
|
|
56
|
+
return buildFailedLaunchCheckResult('startup_failure', message, artifactFingerprint);
|
|
57
|
+
}
|
|
58
|
+
function shouldRethrowHostedLaunchCheckError(error) {
|
|
59
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
60
|
+
return /Failed to launch Playwright Chromium/i.test(message) || /Required setup:/i.test(message);
|
|
61
|
+
}
|
|
62
|
+
function buildRegistrationRequiredLaunchCheckFailure(taskName) {
|
|
63
|
+
return buildFailedLaunchCheckResult('app_registration_required_for_auth_validation', `Auth-required hosted app "${taskName}" must be registered on PlayDrop before viewer launch validation can run. Run "playdrop project create app ${taskName}" and try again.`, null);
|
|
64
|
+
}
|
|
65
|
+
function appendLaunchCheckParams(targetUrl, devAuth) {
|
|
66
|
+
const withAuth = (0, devAuth_1.applyHostedDevAuthSelectionToUrl)(targetUrl, {
|
|
67
|
+
devAuth,
|
|
68
|
+
player: null,
|
|
69
|
+
});
|
|
70
|
+
const nextUrl = new URL(withAuth);
|
|
71
|
+
nextUrl.searchParams.set('launchCheck', '1');
|
|
72
|
+
return nextUrl.toString();
|
|
73
|
+
}
|
|
74
|
+
async function runHostedLaunchCheckWithCapture(options) {
|
|
75
|
+
const expectedHostedLaunchState = options.expectedHostedLaunchState ?? 'ready';
|
|
76
|
+
try {
|
|
77
|
+
const result = await (0, captureRuntime_1.runCapture)({
|
|
78
|
+
targetUrl: options.targetUrl,
|
|
79
|
+
expectedUrl: options.targetUrl,
|
|
80
|
+
timeoutMs: options.timeoutMs ?? DEFAULT_HOSTED_LAUNCH_TIMEOUT_MS,
|
|
81
|
+
settleAfterReadyMs: POST_READY_SETTLE_MS,
|
|
82
|
+
minimumLogLevel: 'warn',
|
|
83
|
+
enableCaptureBridge: true,
|
|
84
|
+
requireHostedLaunchReady: true,
|
|
85
|
+
expectedHostedLaunchState,
|
|
86
|
+
token: options.token ?? null,
|
|
87
|
+
user: options.user ?? null,
|
|
88
|
+
savedSessionBootstrap: options.savedSessionBootstrap ?? false,
|
|
89
|
+
});
|
|
90
|
+
if (result.errorCount > 0) {
|
|
91
|
+
return buildFailedLaunchCheckResult('startup_failure', `Hosted app emitted ${result.errorCount} console, page, or network error(s) during startup.`, options.artifactFingerprint ?? null);
|
|
92
|
+
}
|
|
93
|
+
if (result.hostedLaunchState?.state !== expectedHostedLaunchState) {
|
|
94
|
+
return buildFailedLaunchCheckResult('startup_failure', expectedHostedLaunchState === 'login_required'
|
|
95
|
+
? 'Hosted app did not stop at the PlayDrop sign-in gate.'
|
|
96
|
+
: 'Hosted app never reported the ready state.', options.artifactFingerprint ?? null);
|
|
97
|
+
}
|
|
98
|
+
return buildPassedLaunchCheckResult(options.artifactFingerprint ?? null);
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
if (shouldRethrowHostedLaunchCheckError(error)) {
|
|
102
|
+
throw error;
|
|
103
|
+
}
|
|
104
|
+
return normalizeLaunchCheckFailure(error, options.artifactFingerprint ?? null);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
async function startOrReuseMountedDevRuntime(input) {
|
|
108
|
+
const appTypeSlug = (0, appUrls_1.getAppTypeSlug)(input.task.type ?? 'GAME');
|
|
109
|
+
const runtimeAssetManifest = input.task.hostingMode === 'EXTERNAL' || input.task.externalUrl
|
|
110
|
+
? (0, devRuntimeAssets_1.createEmptyDevRuntimeAssetManifest)()
|
|
111
|
+
: await (0, devRuntimeAssets_1.buildDevRuntimeAssetManifest)({
|
|
112
|
+
client: input.client,
|
|
113
|
+
apiBase: input.apiBase,
|
|
114
|
+
task: input.task,
|
|
115
|
+
creatorUsername: input.creatorUsername,
|
|
116
|
+
appBaseUrl: `http://127.0.0.1:${devServer_1.DEV_ROUTER_PORT}/apps/dev/${encodeURIComponent(input.creatorUsername)}/${encodeURIComponent(appTypeSlug)}/${encodeURIComponent(input.task.name)}/`,
|
|
117
|
+
});
|
|
118
|
+
const serverAlreadyRunning = await (0, devServer_1.isDevServerAvailable)({
|
|
119
|
+
creatorUsername: input.creatorUsername,
|
|
120
|
+
appType: appTypeSlug,
|
|
121
|
+
appName: input.task.name,
|
|
122
|
+
port: devServer_1.DEV_ROUTER_PORT,
|
|
123
|
+
}, 750);
|
|
124
|
+
if (serverAlreadyRunning) {
|
|
125
|
+
await (0, devServer_1.updateMountedDevRuntimeAssetManifest)({
|
|
126
|
+
creatorUsername: input.creatorUsername,
|
|
127
|
+
appName: input.task.name,
|
|
128
|
+
runtimeAssetManifest,
|
|
129
|
+
port: devServer_1.DEV_ROUTER_PORT,
|
|
130
|
+
});
|
|
131
|
+
return { handle: null, runtimeAssetManifest };
|
|
132
|
+
}
|
|
133
|
+
try {
|
|
134
|
+
const handle = await (0, devServer_1.startDevServer)({
|
|
135
|
+
appName: input.task.name,
|
|
136
|
+
appType: appTypeSlug,
|
|
137
|
+
creatorUsername: input.creatorUsername,
|
|
138
|
+
htmlPath: input.task.filePath,
|
|
139
|
+
port: devServer_1.DEV_ROUTER_PORT,
|
|
140
|
+
projectInfo: (0, devShared_1.findProjectInfo)(input.task.filePath),
|
|
141
|
+
runtimeAssetManifest,
|
|
142
|
+
});
|
|
143
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
144
|
+
return { handle, runtimeAssetManifest };
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
const formatted = (0, dev_1.formatDevRuntimeAssetManifestFailure)(error);
|
|
148
|
+
const mountConflict = (0, devServer_1.parseMountConflictError)(error instanceof Error ? error.message : '');
|
|
149
|
+
if (mountConflict) {
|
|
150
|
+
throw new Error(`A different dev session already owns ${mountConflict.ref}. Active owner pid: ${mountConflict.ownerPid}.`);
|
|
151
|
+
}
|
|
152
|
+
throw new Error(formatted.message);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
async function runLocalHostedLaunchCheck(input) {
|
|
156
|
+
let mountedRuntime = null;
|
|
157
|
+
try {
|
|
158
|
+
mountedRuntime = await startOrReuseMountedDevRuntime({
|
|
159
|
+
client: input.client,
|
|
160
|
+
apiBase: input.apiBase,
|
|
161
|
+
creatorUsername: input.creatorUsername,
|
|
162
|
+
task: input.task,
|
|
163
|
+
});
|
|
164
|
+
const authMode = mountedRuntime.runtimeAssetManifest.response.localAppMetadata.authMode;
|
|
165
|
+
if (authMode === 'REQUIRED') {
|
|
166
|
+
const registeredApp = await (0, registration_1.fetchRegisteredAppShell)(input.client, input.creatorUsername, input.task.name);
|
|
167
|
+
if (!registeredApp?.id) {
|
|
168
|
+
return buildRegistrationRequiredLaunchCheckFailure(input.task.name);
|
|
169
|
+
}
|
|
170
|
+
const anonymousGateResult = await runHostedLaunchCheckWithCapture({
|
|
171
|
+
targetUrl: (0, appUrls_1.buildPlatformDevUrl)(input.webBase, {
|
|
172
|
+
creatorUsername: input.creatorUsername,
|
|
173
|
+
appName: input.task.name,
|
|
174
|
+
appType: input.task.type ?? 'GAME',
|
|
175
|
+
devAuth: 'anonymous',
|
|
176
|
+
launchCheck: true,
|
|
177
|
+
}),
|
|
178
|
+
timeoutMs: input.timeoutMs,
|
|
179
|
+
expectedHostedLaunchState: 'login_required',
|
|
180
|
+
});
|
|
181
|
+
if (anonymousGateResult.status !== 'PASSED') {
|
|
182
|
+
return anonymousGateResult;
|
|
183
|
+
}
|
|
184
|
+
return await runHostedLaunchCheckWithCapture({
|
|
185
|
+
targetUrl: (0, appUrls_1.buildPlatformDevUrl)(input.webBase, {
|
|
186
|
+
creatorUsername: input.creatorUsername,
|
|
187
|
+
appName: input.task.name,
|
|
188
|
+
appType: input.task.type ?? 'GAME',
|
|
189
|
+
devAuth: 'viewer',
|
|
190
|
+
launchCheck: true,
|
|
191
|
+
}),
|
|
192
|
+
timeoutMs: input.timeoutMs,
|
|
193
|
+
expectedHostedLaunchState: 'ready',
|
|
194
|
+
token: input.token ?? null,
|
|
195
|
+
user: input.currentUser ?? null,
|
|
196
|
+
savedSessionBootstrap: true,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
return await runHostedLaunchCheckWithCapture({
|
|
200
|
+
targetUrl: (0, appUrls_1.buildPlatformDevUrl)(input.webBase, {
|
|
201
|
+
creatorUsername: input.creatorUsername,
|
|
202
|
+
appName: input.task.name,
|
|
203
|
+
appType: input.task.type ?? 'GAME',
|
|
204
|
+
devAuth: 'anonymous',
|
|
205
|
+
launchCheck: true,
|
|
206
|
+
}),
|
|
207
|
+
timeoutMs: input.timeoutMs,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
finally {
|
|
211
|
+
if (mountedRuntime?.handle) {
|
|
212
|
+
await mountedRuntime.handle.close().catch(() => { });
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
async function runUploadedHostedLaunchCheck(input) {
|
|
217
|
+
const preview = await input.client.fetchAppUploadLaunchCheckPreview(input.creatorUsername, input.task.name, input.sessionId);
|
|
218
|
+
const authMode = preview.localAppMetadata.authMode;
|
|
219
|
+
let result;
|
|
220
|
+
if (authMode === 'REQUIRED') {
|
|
221
|
+
if (!preview.app?.id) {
|
|
222
|
+
result = buildRegistrationRequiredLaunchCheckFailure(input.task.name);
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
const anonymousGateResult = await runHostedLaunchCheckWithCapture({
|
|
226
|
+
targetUrl: appendLaunchCheckParams(preview.launchUrl, 'anonymous'),
|
|
227
|
+
artifactFingerprint: preview.session.manifestHash,
|
|
228
|
+
timeoutMs: input.timeoutMs,
|
|
229
|
+
expectedHostedLaunchState: 'login_required',
|
|
230
|
+
});
|
|
231
|
+
if (anonymousGateResult.status !== 'PASSED') {
|
|
232
|
+
result = anonymousGateResult;
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
result = await runHostedLaunchCheckWithCapture({
|
|
236
|
+
targetUrl: appendLaunchCheckParams(preview.launchUrl, 'viewer'),
|
|
237
|
+
artifactFingerprint: preview.session.manifestHash,
|
|
238
|
+
timeoutMs: input.timeoutMs,
|
|
239
|
+
expectedHostedLaunchState: 'ready',
|
|
240
|
+
token: input.token ?? null,
|
|
241
|
+
user: input.currentUser ?? null,
|
|
242
|
+
savedSessionBootstrap: true,
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
result = await runHostedLaunchCheckWithCapture({
|
|
249
|
+
targetUrl: appendLaunchCheckParams(preview.launchUrl, 'anonymous'),
|
|
250
|
+
artifactFingerprint: preview.session.manifestHash,
|
|
251
|
+
timeoutMs: input.timeoutMs,
|
|
252
|
+
expectedHostedLaunchState: 'ready',
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
const recorded = await input.client.recordAppUploadLaunchCheck(input.creatorUsername, input.task.name, input.sessionId, {
|
|
256
|
+
status: result.status,
|
|
257
|
+
artifactFingerprint: preview.session.manifestHash,
|
|
258
|
+
errorCode: result.errorCode,
|
|
259
|
+
message: result.message,
|
|
260
|
+
});
|
|
261
|
+
const recordedLaunchCheck = recorded.session.launchCheck;
|
|
262
|
+
if (recordedLaunchCheck.status !== 'PASSED' && recordedLaunchCheck.status !== 'FAILED') {
|
|
263
|
+
return buildFailedLaunchCheckResult('startup_failure', 'Upload launch-check was not recorded on the staged session.', preview.session.manifestHash);
|
|
264
|
+
}
|
|
265
|
+
return {
|
|
266
|
+
status: recordedLaunchCheck.status,
|
|
267
|
+
errorCode: recordedLaunchCheck.errorCode,
|
|
268
|
+
message: recordedLaunchCheck.message,
|
|
269
|
+
checkedAt: recordedLaunchCheck.checkedAt ?? new Date().toISOString(),
|
|
270
|
+
artifactFingerprint: recordedLaunchCheck.artifactFingerprint,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ApiClient } from '@playdrop/api-client';
|
|
2
|
+
import { type AppResponse } from '@playdrop/types';
|
|
3
|
+
import type { AppTask } from '../catalogue';
|
|
4
|
+
export declare function fetchRegisteredAppShell(client: ApiClient, creatorUsername: string, appName: string): Promise<AppResponse | null>;
|
|
5
|
+
export declare function ensureRegisteredHostedAppShell(input: {
|
|
6
|
+
client: ApiClient;
|
|
7
|
+
creatorUsername: string;
|
|
8
|
+
task: AppTask;
|
|
9
|
+
}): Promise<{
|
|
10
|
+
app: AppResponse;
|
|
11
|
+
created: boolean;
|
|
12
|
+
}>;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.fetchRegisteredAppShell = fetchRegisteredAppShell;
|
|
4
|
+
exports.ensureRegisteredHostedAppShell = ensureRegisteredHostedAppShell;
|
|
5
|
+
const types_1 = require("@playdrop/types");
|
|
6
|
+
function resolveRegistrationDisplayName(task) {
|
|
7
|
+
const displayName = typeof task.displayName === 'string' ? task.displayName.trim() : '';
|
|
8
|
+
return displayName || task.name;
|
|
9
|
+
}
|
|
10
|
+
async function fetchRegisteredAppShell(client, creatorUsername, appName) {
|
|
11
|
+
try {
|
|
12
|
+
const response = await client.fetchAppBySlug(creatorUsername, appName);
|
|
13
|
+
return response?.app ?? null;
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
if (error instanceof types_1.ApiError && error.status === 404) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
throw error;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
async function ensureRegisteredHostedAppShell(input) {
|
|
23
|
+
let app = await fetchRegisteredAppShell(input.client, input.creatorUsername, input.task.name);
|
|
24
|
+
let created = false;
|
|
25
|
+
const displayName = resolveRegistrationDisplayName(input.task);
|
|
26
|
+
if (!app) {
|
|
27
|
+
const createdResponse = await input.client.createApp(input.creatorUsername, {
|
|
28
|
+
name: input.task.name,
|
|
29
|
+
displayName,
|
|
30
|
+
});
|
|
31
|
+
app = createdResponse.app;
|
|
32
|
+
created = true;
|
|
33
|
+
}
|
|
34
|
+
if (!app) {
|
|
35
|
+
throw new Error(`App ${input.creatorUsername}/${input.task.name} could not be registered on PlayDrop.`);
|
|
36
|
+
}
|
|
37
|
+
const updateRequest = {};
|
|
38
|
+
if (app.displayName !== displayName) {
|
|
39
|
+
updateRequest.displayName = displayName;
|
|
40
|
+
}
|
|
41
|
+
if (input.task.type && app.type !== input.task.type) {
|
|
42
|
+
updateRequest.type = input.task.type;
|
|
43
|
+
}
|
|
44
|
+
if (Object.keys(updateRequest).length > 0) {
|
|
45
|
+
const updated = await input.client.updateApp(input.creatorUsername, input.task.name, updateRequest);
|
|
46
|
+
app = updated.app;
|
|
47
|
+
}
|
|
48
|
+
return { app, created };
|
|
49
|
+
}
|
package/dist/apps/upload.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ApiClient } from '@playdrop/api-client';
|
|
2
|
+
import type { UserResponse } from '@playdrop/types';
|
|
2
3
|
import type { AppTask } from '../catalogue';
|
|
3
4
|
import type { AppBuildArtifacts } from './build';
|
|
4
5
|
export type AppUploadResult = {
|
|
@@ -17,5 +18,9 @@ export type AppUploadOptions = {
|
|
|
17
18
|
skipReview?: boolean;
|
|
18
19
|
clearTags?: boolean;
|
|
19
20
|
creatorUsername?: string;
|
|
21
|
+
token?: string | null;
|
|
22
|
+
user?: UserResponse | null;
|
|
23
|
+
runStagedUploadLaunchCheck?: boolean;
|
|
24
|
+
launchCheckTimeoutMs?: number;
|
|
20
25
|
};
|
|
21
26
|
export declare function uploadApp(client: ApiClient, task: AppTask, artifacts: AppBuildArtifacts | null, options?: AppUploadOptions): Promise<AppUploadResult>;
|
package/dist/apps/upload.js
CHANGED
|
@@ -7,6 +7,7 @@ const config_1 = require("@playdrop/config");
|
|
|
7
7
|
const node_fs_1 = require("node:fs");
|
|
8
8
|
const node_path_1 = require("node:path");
|
|
9
9
|
const http_1 = require("../http");
|
|
10
|
+
const launchCheck_1 = require("./launchCheck");
|
|
10
11
|
const EXTENSION_TO_MIME = {
|
|
11
12
|
'.png': 'image/png',
|
|
12
13
|
'.jpg': 'image/jpeg',
|
|
@@ -355,6 +356,22 @@ async function uploadAppVersion(client, task, artifacts, options) {
|
|
|
355
356
|
})),
|
|
356
357
|
});
|
|
357
358
|
}
|
|
359
|
+
if (task.hostingMode !== 'EXTERNAL'
|
|
360
|
+
&& !task.externalUrl
|
|
361
|
+
&& options?.runStagedUploadLaunchCheck) {
|
|
362
|
+
const launchCheck = await (0, launchCheck_1.runUploadedHostedLaunchCheck)({
|
|
363
|
+
client,
|
|
364
|
+
creatorUsername,
|
|
365
|
+
task,
|
|
366
|
+
sessionId,
|
|
367
|
+
timeoutMs: options.launchCheckTimeoutMs,
|
|
368
|
+
token: options?.token ?? null,
|
|
369
|
+
currentUser: options?.user ?? null,
|
|
370
|
+
});
|
|
371
|
+
if (launchCheck.status !== 'PASSED') {
|
|
372
|
+
throw new Error((0, launchCheck_1.formatHostedLaunchCheckFailure)(task.name, launchCheck, 'staged'));
|
|
373
|
+
}
|
|
374
|
+
}
|
|
358
375
|
}
|
|
359
376
|
const finalized = initialized.status === 'finalized'
|
|
360
377
|
? initialized.finalized
|