@playdrop/playdrop-cli 0.16.5 → 0.16.12
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/bin/playdrop +1 -2
- package/config/client-meta.json +4 -4
- package/dist/apiClient.d.ts +2 -2
- package/dist/apiClient.js +16 -20
- package/dist/appUrls.d.ts +2 -2
- package/dist/appUrls.js +31 -33
- package/dist/apps/build.d.ts +2 -2
- package/dist/apps/build.js +83 -83
- package/dist/apps/index.d.ts +7 -7
- package/dist/apps/index.js +8 -8
- package/dist/apps/loadCheck.d.ts +28 -16
- package/dist/apps/loadCheck.js +411 -279
- package/dist/apps/registration.d.ts +3 -3
- package/dist/apps/registration.js +1 -1
- package/dist/apps/staticHtml.d.ts +3 -3
- package/dist/apps/staticHtml.js +90 -91
- package/dist/apps/surfaceProfiles.d.ts +2 -2
- package/dist/apps/surfaceProfiles.js +6 -6
- package/dist/apps/upload.d.ts +5 -5
- package/dist/apps/upload.js +75 -81
- package/dist/apps/validate.d.ts +2 -2
- package/dist/apps/validate.js +18 -22
- package/dist/assetSpecs.d.ts +2 -2
- package/dist/assetSpecs.js +35 -35
- package/dist/assets/model-artifacts.d.ts +1 -1
- package/dist/assets/model-artifacts.js +81 -77
- package/dist/assets/model-worker.d.ts +1 -1
- package/dist/assets/model-worker.js +6 -6
- package/dist/browser.js +9 -9
- package/dist/catalogue-utils.d.ts +1 -1
- package/dist/catalogue-utils.js +26 -26
- package/dist/catalogue.d.ts +21 -16
- package/dist/catalogue.js +509 -413
- package/dist/clientInfo.d.ts +1 -1
- package/dist/clientInfo.js +28 -28
- package/dist/commandContext.d.ts +5 -5
- package/dist/commandContext.js +19 -23
- package/dist/commands/accounts.js +6 -6
- package/dist/commands/ads.js +16 -16
- package/dist/commands/agents.d.ts +2 -2
- package/dist/commands/agents.js +131 -127
- package/dist/commands/boosts.js +41 -31
- package/dist/commands/browse.d.ts +2 -2
- package/dist/commands/browse.js +168 -128
- package/dist/commands/build.js +9 -9
- package/dist/commands/captureListing.d.ts +8 -8
- package/dist/commands/captureListing.js +292 -332
- package/dist/commands/changelog.js +1 -1
- package/dist/commands/chat.js +51 -53
- package/dist/commands/check.d.ts +1 -1
- package/dist/commands/check.js +44 -44
- package/dist/commands/clients.d.ts +3 -3
- package/dist/commands/clients.js +67 -64
- package/dist/commands/comments.js +36 -29
- package/dist/commands/create.js +305 -312
- package/dist/commands/createRemixContent.d.ts +1 -1
- package/dist/commands/createRemixContent.js +65 -60
- package/dist/commands/creations.js +205 -135
- package/dist/commands/credits.js +37 -27
- package/dist/commands/detail.js +40 -34
- package/dist/commands/dev.js +103 -126
- package/dist/commands/devBrowser.js +74 -65
- package/dist/commands/devRuntimeAssets.d.ts +4 -4
- package/dist/commands/devRuntimeAssets.js +38 -40
- package/dist/commands/devServer.d.ts +4 -4
- package/dist/commands/devServer.js +183 -174
- package/dist/commands/devShared.d.ts +3 -3
- package/dist/commands/devShared.js +31 -31
- package/dist/commands/doctor.d.ts +5 -5
- package/dist/commands/doctor.js +63 -57
- package/dist/commands/documentation.js +42 -32
- package/dist/commands/feedback.js +59 -42
- package/dist/commands/format.js +9 -9
- package/dist/commands/generation.d.ts +2 -2
- package/dist/commands/generation.js +344 -326
- package/dist/commands/init.d.ts +4 -4
- package/dist/commands/init.js +64 -61
- package/dist/commands/login.d.ts +2 -2
- package/dist/commands/login.js +42 -62
- package/dist/commands/logout.js +4 -4
- package/dist/commands/marketing.d.ts +8 -8
- package/dist/commands/marketing.js +246 -297
- package/dist/commands/notes.js +19 -19
- package/dist/commands/notifications.js +34 -22
- package/dist/commands/npmProcess.js +3 -3
- package/dist/commands/ownedRuntimeImageValidation.d.ts +1 -1
- package/dist/commands/ownedRuntimeImageValidation.js +19 -17
- package/dist/commands/play.js +12 -21
- package/dist/commands/publicPages.js +8 -4
- package/dist/commands/review.js +123 -110
- package/dist/commands/search.js +107 -88
- package/dist/commands/tags.js +10 -12
- package/dist/commands/taskCaptureSession.d.ts +3 -3
- package/dist/commands/taskCaptureSession.js +3 -3
- package/dist/commands/tweaks.js +25 -23
- package/dist/commands/upgrade.js +40 -31
- package/dist/commands/upload-content.d.ts +5 -5
- package/dist/commands/upload-content.js +130 -122
- package/dist/commands/upload-graph.d.ts +1 -1
- package/dist/commands/upload-graph.js +7 -11
- package/dist/commands/upload.d.ts +7 -7
- package/dist/commands/upload.js +196 -217
- package/dist/commands/validate.js +33 -32
- package/dist/commands/versionsBrowse.js +61 -51
- package/dist/commands/whoami.js +11 -14
- package/dist/commands/worker/archive-staging.d.ts +9 -0
- package/dist/commands/worker/archive-staging.js +257 -0
- package/dist/commands/worker/instrument-evidence.d.ts +1 -1
- package/dist/commands/worker/instrument-evidence.js +41 -43
- package/dist/commands/worker/provider-telemetry.d.ts +2 -1
- package/dist/commands/worker/provider-telemetry.js +181 -64
- package/dist/commands/worker/runtime.d.ts +18 -3
- package/dist/commands/worker/runtime.js +291 -239
- package/dist/commands/worker.d.ts +83 -22
- package/dist/commands/worker.js +1980 -1411
- package/dist/commands/workspaces.d.ts +2 -2
- package/dist/commands/workspaces.js +57 -57
- package/dist/config.js +23 -20
- package/dist/devAuth.d.ts +3 -3
- package/dist/devAuth.js +20 -17
- package/dist/environment.d.ts +1 -1
- package/dist/environment.js +34 -34
- package/dist/errors.d.ts +1 -1
- package/dist/errors.js +7 -7
- package/dist/externalAssetPackValidation.d.ts +1 -1
- package/dist/externalAssetPackValidation.js +9 -9
- package/dist/http.d.ts +1 -1
- package/dist/http.js +9 -9
- package/dist/index.js +657 -664
- package/dist/listingCaptureLease.js +23 -23
- package/dist/listingPreflight.d.ts +2 -2
- package/dist/listingPreflight.js +33 -33
- package/dist/messages.js +6 -14
- package/dist/output.js +9 -9
- package/dist/playwright.d.ts +1 -1
- package/dist/playwright.js +35 -40
- package/dist/proxyFetch.js +30 -27
- package/dist/recorderRelease.js +8 -8
- package/dist/refs.d.ts +5 -5
- package/dist/refs.js +40 -37
- package/dist/sessionCookie.js +15 -21
- package/dist/shellProbe.js +5 -5
- package/dist/taskSelection.d.ts +3 -3
- package/dist/taskSelection.js +67 -62
- package/dist/taskUtils.d.ts +1 -1
- package/dist/taskUtils.js +4 -4
- package/dist/tweakStaleness.d.ts +3 -3
- package/dist/tweakStaleness.js +8 -8
- package/dist/uploadLog.d.ts +3 -3
- package/dist/uploadLog.js +51 -51
- package/dist/versionCompare.js +3 -3
- package/dist/workerAppProject.d.ts +1 -1
- package/dist/workerAppProject.js +10 -17
- package/dist/workspaceAuth.d.ts +1 -1
- package/dist/workspaceAuth.js +28 -22
- package/node_modules/@playdrop/ai-client/dist/index.d.ts +35 -35
- package/node_modules/@playdrop/ai-client/dist/index.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/client.d.ts +34 -33
- package/node_modules/@playdrop/api-client/dist/client.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/core/errors.d.ts +1 -1
- package/node_modules/@playdrop/api-client/dist/core/errors.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/core/request.d.ts +2 -2
- package/node_modules/@playdrop/api-client/dist/core/request.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/admin.d.ts +2 -2
- package/node_modules/@playdrop/api-client/dist/domains/admin.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/admin.js +4 -1
- package/node_modules/@playdrop/api-client/dist/domains/agent-tasks.d.ts +4 -3
- package/node_modules/@playdrop/api-client/dist/domains/agent-tasks.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/agent-tasks.js +12 -0
- package/node_modules/@playdrop/api-client/dist/domains/ai.d.ts +2 -2
- package/node_modules/@playdrop/api-client/dist/domains/ai.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts +4 -4
- package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/asset-packs.d.ts +4 -4
- package/node_modules/@playdrop/api-client/dist/domains/asset-packs.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/assets.d.ts +4 -4
- package/node_modules/@playdrop/api-client/dist/domains/assets.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/auth.d.ts +2 -2
- package/node_modules/@playdrop/api-client/dist/domains/auth.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/chat.d.ts +2 -2
- package/node_modules/@playdrop/api-client/dist/domains/chat.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/comments.d.ts +3 -3
- package/node_modules/@playdrop/api-client/dist/domains/comments.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/free-credits.d.ts +2 -2
- package/node_modules/@playdrop/api-client/dist/domains/free-credits.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/me.d.ts +2 -2
- package/node_modules/@playdrop/api-client/dist/domains/me.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/payments.d.ts +2 -2
- package/node_modules/@playdrop/api-client/dist/domains/payments.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/player-meta.d.ts +2 -2
- package/node_modules/@playdrop/api-client/dist/domains/player-meta.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/playtrade.d.ts +3 -3
- package/node_modules/@playdrop/api-client/dist/domains/playtrade.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/playtrade.js +2 -2
- package/node_modules/@playdrop/api-client/dist/domains/search.d.ts +2 -2
- package/node_modules/@playdrop/api-client/dist/domains/search.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/tags.d.ts +2 -2
- package/node_modules/@playdrop/api-client/dist/domains/tags.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/index.d.ts +19 -18
- package/node_modules/@playdrop/api-client/dist/index.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/index.js +7 -2
- package/node_modules/@playdrop/boxel-core/dist/src/entity-cleaner.d.ts +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/entity-cleaner.js +47 -47
- package/node_modules/@playdrop/boxel-core/dist/src/entity-cleaner.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/entity-utils.d.ts +3 -3
- package/node_modules/@playdrop/boxel-core/dist/src/entity-utils.js +32 -47
- package/node_modules/@playdrop/boxel-core/dist/src/entity-utils.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/format-utils.js +16 -18
- package/node_modules/@playdrop/boxel-core/dist/src/format-utils.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/geometry.d.ts +2 -2
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/index.d.ts +4 -4
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/common.d.ts +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/index.d.ts +3 -3
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/textured-builder.d.ts +2 -2
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/textured-builder.js +44 -45
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/textured-builder.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/types.d.ts +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/voxel-builder.d.ts +2 -2
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/voxel-builder.js +40 -44
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/voxel-builder.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/builder.d.ts +2 -2
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/builder.js +30 -34
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/builder.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/index.d.ts +4 -4
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/index.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/scanner.d.ts +3 -3
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/scanner.js +23 -20
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/scanner.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/textures/artifacts.d.ts +3 -3
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/textures/artifacts.js +3 -3
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/textures/face-map.d.ts +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/textures/face-map.js +21 -21
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r6/textures/face-map.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/templates/index.d.ts +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/templates/index.js +2 -2
- package/node_modules/@playdrop/boxel-core/dist/src/index.d.ts +19 -19
- package/node_modules/@playdrop/boxel-core/dist/src/materials.d.ts +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/materials.js +12 -16
- package/node_modules/@playdrop/boxel-core/dist/src/materials.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/palette_tools.d.ts +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/palette_tools.js +38 -41
- package/node_modules/@playdrop/boxel-core/dist/src/palette_tools.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/serialization.d.ts +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/serialization.js +54 -56
- package/node_modules/@playdrop/boxel-core/dist/src/serialization.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/textures.d.ts +3 -3
- package/node_modules/@playdrop/boxel-core/dist/src/textures.js +39 -45
- package/node_modules/@playdrop/boxel-core/dist/src/textures.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/infer-face.d.ts +2 -2
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/infer-face.js +39 -43
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/infer-face.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/layer-mode.d.ts +3 -3
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/layer-mode.js +35 -35
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/layer-mode.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/merge-overlay.d.ts +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/merge-overlay.js +18 -18
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/merge-overlay.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/slice.d.ts +3 -3
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/slice.js +50 -48
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/slice.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/upscale.d.ts +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/upscale.js +22 -26
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/upscale.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/voxels/slice.d.ts +2 -2
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/voxels/slice.js +8 -8
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/voxels/slice.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/voxels/textured-to-voxel.d.ts +2 -2
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/voxels/textured-to-voxel.js +23 -28
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/voxels/textured-to-voxel.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/types.d.ts +8 -8
- package/node_modules/@playdrop/boxel-core/dist/src/validation.d.ts +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/validation.js +93 -95
- package/node_modules/@playdrop/boxel-core/dist/src/validation.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/voxels/greedy-mesher.d.ts +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/voxels/greedy-mesher.js +2 -2
- package/node_modules/@playdrop/boxel-core/dist/src/voxels/greedy-mesher.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/voxels.d.ts +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/voxels.js +2 -7
- package/node_modules/@playdrop/boxel-core/dist/src/voxels.js.map +1 -1
- package/node_modules/@playdrop/boxel-three/dist/src/animations.d.ts +3 -3
- package/node_modules/@playdrop/boxel-three/dist/src/builders.d.ts +4 -4
- package/node_modules/@playdrop/boxel-three/dist/src/context.d.ts +3 -3
- package/node_modules/@playdrop/boxel-three/dist/src/exporters/glb.d.ts +6 -6
- package/node_modules/@playdrop/boxel-three/dist/src/exporters/glb.js +9 -1
- package/node_modules/@playdrop/boxel-three/dist/src/exporters/image.d.ts +3 -3
- package/node_modules/@playdrop/boxel-three/dist/src/index.d.ts +17 -17
- package/node_modules/@playdrop/boxel-three/dist/src/instantiate.d.ts +9 -9
- package/node_modules/@playdrop/boxel-three/dist/src/nodes.d.ts +3 -3
- package/node_modules/@playdrop/boxel-three/dist/src/nodes.js +1 -1
- package/node_modules/@playdrop/boxel-three/dist/src/overlays.d.ts +2 -2
- package/node_modules/@playdrop/boxel-three/dist/src/primitives.d.ts +1 -1
- package/node_modules/@playdrop/boxel-three/dist/src/scene.d.ts +1 -1
- package/node_modules/@playdrop/boxel-three/dist/src/skinned-mesh.d.ts +2 -2
- package/node_modules/@playdrop/boxel-three/dist/src/texture-atlas.d.ts +1 -1
- package/node_modules/@playdrop/boxel-three/dist/src/textures.d.ts +3 -3
- package/node_modules/@playdrop/boxel-three/dist/src/types.d.ts +4 -4
- package/node_modules/@playdrop/boxel-three/dist/src/voxels/faces.d.ts +2 -2
- package/node_modules/@playdrop/boxel-three/dist/src/voxels/mesher.d.ts +1 -1
- package/node_modules/@playdrop/boxel-three/dist/test/fixtures/render-worker.d.ts +1 -1
- package/node_modules/@playdrop/config/client-meta.json +4 -4
- package/node_modules/@playdrop/config/dist/src/client-meta.d.ts +1 -1
- package/node_modules/@playdrop/config/dist/src/client-meta.d.ts.map +1 -1
- package/node_modules/@playdrop/config/dist/src/client-meta.js +10 -10
- package/node_modules/@playdrop/config/dist/src/constants.js +10 -10
- package/node_modules/@playdrop/config/dist/src/creator-plans.d.ts +1 -1
- package/node_modules/@playdrop/config/dist/src/creator-plans.d.ts.map +1 -1
- package/node_modules/@playdrop/config/dist/src/creator-plans.js +27 -28
- package/node_modules/@playdrop/config/dist/src/deployment.d.ts +4 -4
- package/node_modules/@playdrop/config/dist/src/deployment.d.ts.map +1 -1
- package/node_modules/@playdrop/config/dist/src/deployment.js +42 -41
- package/node_modules/@playdrop/config/dist/src/game-arena.js +12 -12
- package/node_modules/@playdrop/config/dist/src/index.d.ts +9 -9
- package/node_modules/@playdrop/config/dist/src/index.d.ts.map +1 -1
- package/node_modules/@playdrop/config/dist/src/index.js +24 -25
- package/node_modules/@playdrop/config/dist/src/public-deployment.d.ts +4 -4
- package/node_modules/@playdrop/config/dist/src/public-deployment.d.ts.map +1 -1
- package/node_modules/@playdrop/config/dist/src/public-deployment.js +41 -39
- package/node_modules/@playdrop/config/dist/src/runtime-client-meta.js +1 -3
- package/node_modules/@playdrop/config/dist/src/runtime-service-origins.d.ts.map +1 -1
- package/node_modules/@playdrop/config/dist/src/runtime-service-origins.js +23 -25
- package/node_modules/@playdrop/config/dist/src/server/fastify.d.ts +5 -5
- package/node_modules/@playdrop/config/dist/src/server/fastify.d.ts.map +1 -1
- package/node_modules/@playdrop/config/dist/src/server/fastify.js +44 -46
- package/node_modules/@playdrop/config/dist/src/server/logging.d.ts +1 -1
- package/node_modules/@playdrop/config/dist/src/server/logging.d.ts.map +1 -1
- package/node_modules/@playdrop/config/dist/src/server/logging.js +6 -8
- package/node_modules/@playdrop/config/dist/src/types.d.ts +2 -2
- package/node_modules/@playdrop/config/dist/test/creator-plans.test.js +12 -12
- package/node_modules/@playdrop/config/dist/test/deployment.test.js +59 -59
- package/node_modules/@playdrop/config/dist/test/fastify.test.js +4 -4
- package/node_modules/@playdrop/config/dist/test/game-arena.test.js +8 -8
- package/node_modules/@playdrop/config/dist/test/logging.test.js +6 -6
- package/node_modules/@playdrop/config/dist/test/validateClientEnvironment.test.js +57 -57
- package/node_modules/@playdrop/config/dist/tsconfig.tsbuildinfo +1 -1
- package/node_modules/@playdrop/types/dist/agent-task-playtest.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/agent-task-terminal.d.ts +2 -2
- package/node_modules/@playdrop/types/dist/agent-task-terminal.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/api.d.ts +314 -250
- package/node_modules/@playdrop/types/dist/api.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/api.js +40 -59
- package/node_modules/@playdrop/types/dist/app-capability-filters.d.ts +2 -2
- package/node_modules/@playdrop/types/dist/app-playtest-tape.d.ts +6 -6
- package/node_modules/@playdrop/types/dist/app-playtest-tape.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/app-review.d.ts +2 -2
- package/node_modules/@playdrop/types/dist/app-review.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/app-review.js +1 -6
- package/node_modules/@playdrop/types/dist/app.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/asset-pack.d.ts +5 -5
- package/node_modules/@playdrop/types/dist/asset-spec.d.ts +4 -4
- package/node_modules/@playdrop/types/dist/asset.d.ts +11 -11
- package/node_modules/@playdrop/types/dist/attachment-policy.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/chat.d.ts +3 -3
- package/node_modules/@playdrop/types/dist/creator-public-image.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/graph.d.ts +4 -4
- package/node_modules/@playdrop/types/dist/index.d.ts +25 -25
- package/node_modules/@playdrop/types/dist/instrument-evidence.d.ts +2 -2
- package/node_modules/@playdrop/types/dist/instrument-evidence.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/instrument-evidence.js +2 -11
- package/node_modules/@playdrop/types/dist/locale.d.ts +2 -2
- package/node_modules/@playdrop/types/dist/locale.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/locale.js +2 -17
- package/node_modules/@playdrop/types/dist/owned-assets.d.ts +2 -2
- package/node_modules/@playdrop/types/dist/owned-assets.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/player-meta.d.ts +3 -3
- package/node_modules/@playdrop/types/dist/player-meta.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/playtest-notes.d.ts +11 -11
- package/node_modules/@playdrop/types/dist/playtest-notes.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/playtrade.d.ts +8 -5
- package/node_modules/@playdrop/types/dist/playtrade.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/realtime.d.ts +1 -1
- package/node_modules/@playdrop/types/dist/tweaks.d.ts +4 -4
- package/node_modules/@playdrop/types/dist/tweaks.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/tweaks.js +12 -2
- package/node_modules/@playdrop/types/dist/version.d.ts +14 -14
- package/node_modules/@playdrop/types/dist/version.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/version.js +4 -1
- package/node_modules/@playdrop/vox-three/dist/src/index.d.ts +1 -1
- package/node_modules/@playdrop/vox-three/dist/src/vox.d.ts +2 -2
- package/node_modules/@playdrop/vox-three/dist/src/vox.js +20 -18
- package/node_modules/@playdrop/vox-three/dist/src/vox.js.map +1 -1
- package/node_modules/@playdrop/vox-three/dist/test/vox.test.js +11 -14
- package/node_modules/@playdrop/vox-three/dist/test/vox.test.js.map +1 -1
- package/package.json +1 -1
package/dist/apps/loadCheck.js
CHANGED
|
@@ -6,14 +6,21 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.PLAYDROP_SURFACE_CONTEXT_OPTIONS = void 0;
|
|
7
7
|
exports.formatConsoleValue = formatConsoleValue;
|
|
8
8
|
exports.formatBrowserConsoleMessage = formatBrowserConsoleMessage;
|
|
9
|
+
exports.isDuplicateBrowserResourceConsoleError = isDuplicateBrowserResourceConsoleError;
|
|
10
|
+
exports.isExpectedAnonymousSessionProbe = isExpectedAnonymousSessionProbe;
|
|
11
|
+
exports.waitForHostedLaunchState = waitForHostedLaunchState;
|
|
9
12
|
exports.resolveLoadCheckSurface = resolveLoadCheckSurface;
|
|
10
13
|
exports.cloneLoadCheckContextOptions = cloneLoadCheckContextOptions;
|
|
11
14
|
exports.isSoftwareWebGlRenderer = isSoftwareWebGlRenderer;
|
|
12
15
|
exports.dispatchPlaytestTapeToFrame = dispatchPlaytestTapeToFrame;
|
|
16
|
+
exports.findGameFrame = findGameFrame;
|
|
17
|
+
exports.describeHostedLaunchTimeout = describeHostedLaunchTimeout;
|
|
13
18
|
exports.verifyHostedEditorPhaseRoundTrip = verifyHostedEditorPhaseRoundTrip;
|
|
14
19
|
exports.formatHostedLoadCheckFailure = formatHostedLoadCheckFailure;
|
|
15
20
|
exports.redactHostedLoadCheckSecrets = redactHostedLoadCheckSecrets;
|
|
16
21
|
exports.runLocalHostedLoadCheck = runLocalHostedLoadCheck;
|
|
22
|
+
exports.buildUploadedHostedLoadCheckUrl = buildUploadedHostedLoadCheckUrl;
|
|
23
|
+
exports.buildUploadedHostedLoadCheckScreenshotPath = buildUploadedHostedLoadCheckScreenshotPath;
|
|
17
24
|
exports.runUploadedHostedLoadCheck = runUploadedHostedLoadCheck;
|
|
18
25
|
exports.comparePlaytestFrames = comparePlaytestFrames;
|
|
19
26
|
exports.runUploadedHostedPlaytestSmokeCheck = runUploadedHostedPlaytestSmokeCheck;
|
|
@@ -37,15 +44,16 @@ var surfaceProfiles_2 = require("./surfaceProfiles");
|
|
|
37
44
|
Object.defineProperty(exports, "PLAYDROP_SURFACE_CONTEXT_OPTIONS", { enumerable: true, get: function () { return surfaceProfiles_2.PLAYDROP_SURFACE_CONTEXT_OPTIONS; } });
|
|
38
45
|
const FRAME_SELECTOR = 'iframe[title="Game"]';
|
|
39
46
|
const DEFAULT_HOSTED_LOAD_TIMEOUT_MS = 15000;
|
|
47
|
+
const HOSTED_LAUNCH_DIAGNOSTIC_TIMEOUT_MS = 1000;
|
|
40
48
|
const PLAYTEST_INTERACTIVE_MEAN_DELTA_MIN = 0.002;
|
|
41
49
|
const PLAYTEST_INTERACTIVE_CHANGED_PIXEL_RATIO_MIN = 0.01;
|
|
42
|
-
const LOAD_CHECK_SURFACE_ORDER = [
|
|
50
|
+
const LOAD_CHECK_SURFACE_ORDER = ["DESKTOP", "MOBILE_LANDSCAPE", "MOBILE_PORTRAIT"];
|
|
43
51
|
const GOOGLE_TELEMETRY_HOSTS = [
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
52
|
+
"analytics.google.com",
|
|
53
|
+
"googletagmanager.com",
|
|
54
|
+
"google-analytics.com",
|
|
55
|
+
"googleadservices.com",
|
|
56
|
+
"doubleclick.net",
|
|
49
57
|
];
|
|
50
58
|
const SOFTWARE_WEBGL_RENDERER_PATTERNS = [
|
|
51
59
|
/\bswiftshader\b/i,
|
|
@@ -68,7 +76,7 @@ function isKnownGoogleTelemetryUrl(rawUrl) {
|
|
|
68
76
|
return false;
|
|
69
77
|
}
|
|
70
78
|
const hostname = parsed.hostname.toLowerCase();
|
|
71
|
-
if (hostname ===
|
|
79
|
+
if (hostname === "accounts.google.com" || hostname.endsWith(".accounts.google.com")) {
|
|
72
80
|
return false;
|
|
73
81
|
}
|
|
74
82
|
return GOOGLE_TELEMETRY_HOSTS.some((domain) => isHostOrSubdomain(hostname, domain));
|
|
@@ -77,24 +85,24 @@ function formatConsoleValue(value) {
|
|
|
77
85
|
if (value instanceof Error) {
|
|
78
86
|
return `${value.name}: ${value.message}`;
|
|
79
87
|
}
|
|
80
|
-
if (typeof value ===
|
|
88
|
+
if (typeof value === "string")
|
|
81
89
|
return value;
|
|
82
|
-
if (typeof value ===
|
|
90
|
+
if (typeof value === "number" || typeof value === "boolean" || value === null) {
|
|
83
91
|
return String(value);
|
|
84
92
|
}
|
|
85
|
-
if (typeof value ===
|
|
86
|
-
return
|
|
93
|
+
if (typeof value === "undefined") {
|
|
94
|
+
return "undefined";
|
|
87
95
|
}
|
|
88
|
-
if (typeof value ===
|
|
96
|
+
if (typeof value === "object") {
|
|
89
97
|
const namedError = value;
|
|
90
|
-
if (typeof namedError.message ===
|
|
91
|
-
return `${typeof namedError.name ===
|
|
98
|
+
if (typeof namedError.message === "string") {
|
|
99
|
+
return `${typeof namedError.name === "string" ? `${namedError.name}: ` : ""}${namedError.message}`;
|
|
92
100
|
}
|
|
93
101
|
try {
|
|
94
102
|
return JSON.stringify(value);
|
|
95
103
|
}
|
|
96
104
|
catch {
|
|
97
|
-
return
|
|
105
|
+
return "[object]";
|
|
98
106
|
}
|
|
99
107
|
}
|
|
100
108
|
return String(value);
|
|
@@ -103,14 +111,29 @@ function formatBrowserConsoleMessage(values, fallbackText) {
|
|
|
103
111
|
if (values.length === 0) {
|
|
104
112
|
return fallbackText;
|
|
105
113
|
}
|
|
106
|
-
const rendered = values.map(formatConsoleValue).join(
|
|
107
|
-
return rendered ===
|
|
114
|
+
const rendered = values.map(formatConsoleValue).join(" ");
|
|
115
|
+
return rendered === "{}" || rendered === "[object]" ? fallbackText : rendered;
|
|
116
|
+
}
|
|
117
|
+
function isDuplicateBrowserResourceConsoleError(type, message) {
|
|
118
|
+
return (type === "error" &&
|
|
119
|
+
/^Failed to load resource: the server responded with a status of \d{3}(?: \([^)]*\))?$/i.test(message.trim()));
|
|
120
|
+
}
|
|
121
|
+
function isExpectedAnonymousSessionProbe(input) {
|
|
122
|
+
if (input.hasExplicitToken || input.status !== 401 || input.method.toUpperCase() !== "GET") {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
return new URL(input.url).pathname === "/me";
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
108
131
|
}
|
|
109
132
|
function serializePayload(payload) {
|
|
110
133
|
if (payload === undefined) {
|
|
111
|
-
return
|
|
134
|
+
return "";
|
|
112
135
|
}
|
|
113
|
-
if (typeof payload ===
|
|
136
|
+
if (typeof payload === "string") {
|
|
114
137
|
return payload;
|
|
115
138
|
}
|
|
116
139
|
try {
|
|
@@ -121,75 +144,74 @@ function serializePayload(payload) {
|
|
|
121
144
|
}
|
|
122
145
|
}
|
|
123
146
|
function normalizeHostedLaunchStatePayload(payload) {
|
|
124
|
-
if (!payload || typeof payload !==
|
|
147
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
125
148
|
return null;
|
|
126
149
|
}
|
|
127
|
-
const state = typeof payload.state ===
|
|
150
|
+
const state = typeof payload.state === "string"
|
|
128
151
|
? payload.state.trim().toLowerCase()
|
|
129
|
-
:
|
|
130
|
-
if (state ===
|
|
131
|
-
return { state:
|
|
152
|
+
: "";
|
|
153
|
+
if (state === "ready") {
|
|
154
|
+
return { state: "ready" };
|
|
132
155
|
}
|
|
133
|
-
if (state ===
|
|
134
|
-
return { state:
|
|
156
|
+
if (state === "login_required") {
|
|
157
|
+
return { state: "login_required" };
|
|
135
158
|
}
|
|
136
|
-
if (state ===
|
|
137
|
-
return { state:
|
|
159
|
+
if (state === "controller_required") {
|
|
160
|
+
return { state: "controller_required" };
|
|
138
161
|
}
|
|
139
|
-
if (state ===
|
|
140
|
-
return { state:
|
|
162
|
+
if (state === "surface_unsupported") {
|
|
163
|
+
return { state: "surface_unsupported" };
|
|
141
164
|
}
|
|
142
|
-
if (state !==
|
|
165
|
+
if (state !== "error") {
|
|
143
166
|
return null;
|
|
144
167
|
}
|
|
145
|
-
const errorCode = typeof payload.errorCode ===
|
|
168
|
+
const errorCode = typeof payload.errorCode === "string"
|
|
146
169
|
? payload.errorCode.trim() || null
|
|
147
170
|
: null;
|
|
148
|
-
const message = typeof payload.message ===
|
|
171
|
+
const message = typeof payload.message === "string"
|
|
149
172
|
? payload.message.trim() || null
|
|
150
173
|
: null;
|
|
151
174
|
return {
|
|
152
|
-
state:
|
|
175
|
+
state: "error",
|
|
153
176
|
errorCode,
|
|
154
177
|
message,
|
|
155
178
|
};
|
|
156
179
|
}
|
|
157
180
|
function formatHostedLaunchError(state) {
|
|
158
|
-
const parts = [
|
|
181
|
+
const parts = ["Hosted app failed to launch."];
|
|
159
182
|
if (state.errorCode) {
|
|
160
183
|
parts.push(`errorCode=${state.errorCode}.`);
|
|
161
184
|
}
|
|
162
185
|
if (state.message) {
|
|
163
186
|
parts.push(state.message);
|
|
164
187
|
}
|
|
165
|
-
return parts.join(
|
|
166
|
-
}
|
|
167
|
-
function formatHostedLaunchStateMismatch(actualState, expectedState) {
|
|
168
|
-
if (expectedState === 'login_required') {
|
|
169
|
-
return `Hosted app reported ${actualState} instead of login_required.`;
|
|
170
|
-
}
|
|
171
|
-
if (expectedState === 'controller_required') {
|
|
172
|
-
return `Hosted app reported ${actualState} instead of controller_required.`;
|
|
173
|
-
}
|
|
174
|
-
if (expectedState === 'surface_unsupported') {
|
|
175
|
-
return `Hosted app reported ${actualState} instead of surface_unsupported.`;
|
|
176
|
-
}
|
|
177
|
-
return `Hosted app reported ${actualState} instead of ready.`;
|
|
188
|
+
return parts.join(" ");
|
|
178
189
|
}
|
|
179
190
|
function shouldSettleHostedLaunchWaiter(state, expectedState) {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
return
|
|
191
|
+
return state.state === "error" || state.state === expectedState;
|
|
192
|
+
}
|
|
193
|
+
function waitForHostedLaunchState(waiter, expectedState, timeoutMs) {
|
|
194
|
+
return new Promise((resolve, reject) => {
|
|
195
|
+
const timeout = setTimeout(() => {
|
|
196
|
+
reject(new Error(expectedState === "ready"
|
|
197
|
+
? "hosted_app_missing_ready: instrument_error:game_ready_timeout: Hosted app did not report sdk.host.ready() before the instrument timeout."
|
|
198
|
+
: `hosted_app_gate_missing: Hosted app did not report ${expectedState} before the load-check timeout.`));
|
|
199
|
+
}, timeoutMs);
|
|
200
|
+
waiter.then((state) => {
|
|
201
|
+
clearTimeout(timeout);
|
|
202
|
+
resolve(state);
|
|
203
|
+
}, (error) => {
|
|
204
|
+
clearTimeout(timeout);
|
|
205
|
+
reject(error);
|
|
206
|
+
});
|
|
207
|
+
});
|
|
184
208
|
}
|
|
185
209
|
function normalizeLoadCheckSurface(value) {
|
|
186
|
-
if (typeof value !==
|
|
210
|
+
if (typeof value !== "string") {
|
|
187
211
|
return null;
|
|
188
212
|
}
|
|
189
213
|
const normalized = value.trim().toUpperCase();
|
|
190
|
-
return LOAD_CHECK_SURFACE_ORDER.includes(normalized)
|
|
191
|
-
? normalized
|
|
192
|
-
: null;
|
|
214
|
+
return LOAD_CHECK_SURFACE_ORDER.includes(normalized) ? normalized : null;
|
|
193
215
|
}
|
|
194
216
|
function normalizeLoadCheckSurfaceTargets(value) {
|
|
195
217
|
if (!Array.isArray(value)) {
|
|
@@ -208,31 +230,31 @@ function resolveLoadCheckSurface(surfaceTargets, primarySurface) {
|
|
|
208
230
|
return surface;
|
|
209
231
|
}
|
|
210
232
|
}
|
|
211
|
-
return
|
|
233
|
+
return "DESKTOP";
|
|
212
234
|
}
|
|
213
235
|
function cloneLoadCheckContextOptions(surface) {
|
|
214
236
|
return (0, surfaceProfiles_1.clonePlaydropSurfaceContextOptions)(surface);
|
|
215
237
|
}
|
|
216
238
|
function resolvePostAuthHostedLaunchState(controllerMode) {
|
|
217
|
-
return controllerMode ===
|
|
239
|
+
return controllerMode === "REQUIRED" ? "controller_required" : "ready";
|
|
218
240
|
}
|
|
219
241
|
function resolveAnonymousHostedLaunchState(input) {
|
|
220
242
|
const surfaceTargets = normalizeLoadCheckSurfaceTargets(input.surfaceTargets);
|
|
221
243
|
if (!surfaceTargets.includes(input.captureSurface)) {
|
|
222
|
-
return
|
|
244
|
+
return "surface_unsupported";
|
|
223
245
|
}
|
|
224
|
-
if (input.authMode ===
|
|
225
|
-
return
|
|
246
|
+
if (input.authMode === "REQUIRED") {
|
|
247
|
+
return "login_required";
|
|
226
248
|
}
|
|
227
249
|
return resolvePostAuthHostedLaunchState(input.controllerMode);
|
|
228
250
|
}
|
|
229
251
|
function normalizeLoadCheckMessage(message) {
|
|
230
|
-
const normalized = typeof message ===
|
|
231
|
-
return normalized ||
|
|
252
|
+
const normalized = typeof message === "string" ? message.trim() : "";
|
|
253
|
+
return normalized || "Hosted app failed to launch.";
|
|
232
254
|
}
|
|
233
255
|
function buildPassedLoadCheckResult(input) {
|
|
234
256
|
return {
|
|
235
|
-
status:
|
|
257
|
+
status: "PASSED",
|
|
236
258
|
errorCode: null,
|
|
237
259
|
message: null,
|
|
238
260
|
checkedAt: new Date().toISOString(),
|
|
@@ -246,7 +268,7 @@ function buildPassedLoadCheckResult(input) {
|
|
|
246
268
|
}
|
|
247
269
|
function buildFailedLoadCheckResult(errorCode, message, artifactFingerprint, extra = {}) {
|
|
248
270
|
return {
|
|
249
|
-
status:
|
|
271
|
+
status: "FAILED",
|
|
250
272
|
errorCode,
|
|
251
273
|
message,
|
|
252
274
|
checkedAt: new Date().toISOString(),
|
|
@@ -260,20 +282,20 @@ function buildFailedLoadCheckResult(errorCode, message, artifactFingerprint, ext
|
|
|
260
282
|
}
|
|
261
283
|
function formatLoadCheckErrors(errors) {
|
|
262
284
|
if (!Array.isArray(errors) || errors.length === 0) {
|
|
263
|
-
return
|
|
285
|
+
return "";
|
|
264
286
|
}
|
|
265
287
|
const normalized = errors
|
|
266
|
-
.map((entry) => entry.replace(/\s+/g,
|
|
288
|
+
.map((entry) => entry.replace(/\s+/g, " ").trim())
|
|
267
289
|
.filter(Boolean)
|
|
268
290
|
.slice(0, 3)
|
|
269
|
-
.map((entry) => entry.length > 240 ? `${entry.slice(0, 237)}...` : entry);
|
|
291
|
+
.map((entry) => (entry.length > 240 ? `${entry.slice(0, 237)}...` : entry));
|
|
270
292
|
if (normalized.length === 0) {
|
|
271
|
-
return
|
|
293
|
+
return "";
|
|
272
294
|
}
|
|
273
|
-
return ` First error${normalized.length === 1 ?
|
|
295
|
+
return ` First error${normalized.length === 1 ? "" : "s"}: ${normalized.join(" | ")}`;
|
|
274
296
|
}
|
|
275
297
|
function isSoftwareWebGlRenderer(renderer) {
|
|
276
|
-
const normalized = typeof renderer ===
|
|
298
|
+
const normalized = typeof renderer === "string" ? renderer.trim() : "";
|
|
277
299
|
return normalized.length > 0 && SOFTWARE_WEBGL_RENDERER_PATTERNS.some((pattern) => pattern.test(normalized));
|
|
278
300
|
}
|
|
279
301
|
async function prepareProjectCheckGameFrame(frame) {
|
|
@@ -282,16 +304,18 @@ async function prepareProjectCheckGameFrame(frame) {
|
|
|
282
304
|
await element.scrollIntoViewIfNeeded().catch(() => { });
|
|
283
305
|
const box = await element.boundingBox();
|
|
284
306
|
if (!box) {
|
|
285
|
-
throw new Error(
|
|
307
|
+
throw new Error("project_check_game_frame_not_visible");
|
|
286
308
|
}
|
|
287
309
|
await element.focus().catch(() => { });
|
|
288
|
-
await frame
|
|
310
|
+
await frame
|
|
311
|
+
.evaluate(() => {
|
|
289
312
|
window.focus();
|
|
290
313
|
if (document.body) {
|
|
291
314
|
document.body.tabIndex = document.body.tabIndex >= 0 ? document.body.tabIndex : -1;
|
|
292
315
|
document.body.focus();
|
|
293
316
|
}
|
|
294
|
-
})
|
|
317
|
+
})
|
|
318
|
+
.catch(() => { });
|
|
295
319
|
return box;
|
|
296
320
|
}
|
|
297
321
|
finally {
|
|
@@ -300,12 +324,12 @@ async function prepareProjectCheckGameFrame(frame) {
|
|
|
300
324
|
}
|
|
301
325
|
function resolveTapePoint(box, event) {
|
|
302
326
|
return {
|
|
303
|
-
x: box.x +
|
|
304
|
-
y: box.y +
|
|
327
|
+
x: box.x + event.x * box.width,
|
|
328
|
+
y: box.y + event.y * box.height,
|
|
305
329
|
};
|
|
306
330
|
}
|
|
307
331
|
async function dispatchTouchPoints(session, type, points) {
|
|
308
|
-
await session.send(
|
|
332
|
+
await session.send("Input.dispatchTouchEvent", {
|
|
309
333
|
type,
|
|
310
334
|
touchPoints: Array.from(points.entries()).map(([id, point]) => ({
|
|
311
335
|
id,
|
|
@@ -319,7 +343,7 @@ async function dispatchTouchPoints(session, type, points) {
|
|
|
319
343
|
}
|
|
320
344
|
async function dispatchPlaytestTapeToFrame(page, frame, surface, tape, hooks = {}) {
|
|
321
345
|
const box = await prepareProjectCheckGameFrame(frame);
|
|
322
|
-
const mobile = surface !==
|
|
346
|
+
const mobile = surface !== "DESKTOP";
|
|
323
347
|
const session = mobile ? await page.context().newCDPSession(page) : null;
|
|
324
348
|
const touchPoints = new Map();
|
|
325
349
|
const startedAt = Date.now();
|
|
@@ -331,71 +355,71 @@ async function dispatchPlaytestTapeToFrame(page, frame, surface, tape, hooks = {
|
|
|
331
355
|
if (waitMs > 0) {
|
|
332
356
|
await page.waitForTimeout(waitMs);
|
|
333
357
|
}
|
|
334
|
-
if (event.type ===
|
|
358
|
+
if (event.type === "keyDown") {
|
|
335
359
|
await page.keyboard.down(event.key);
|
|
336
360
|
continue;
|
|
337
361
|
}
|
|
338
|
-
if (event.type ===
|
|
362
|
+
if (event.type === "keyUp") {
|
|
339
363
|
await page.keyboard.up(event.key);
|
|
340
364
|
continue;
|
|
341
365
|
}
|
|
342
|
-
if (!(
|
|
366
|
+
if (!("x" in event) || !("y" in event)) {
|
|
343
367
|
throw new Error(`project_check_playtest_event_unsupported:${event.type}`);
|
|
344
368
|
}
|
|
345
369
|
const point = resolveTapePoint(box, event);
|
|
346
|
-
const pointerId =
|
|
347
|
-
if (event.type ===
|
|
370
|
+
const pointerId = "pointerId" in event ? (event.pointerId ?? 0) : 0;
|
|
371
|
+
if (event.type === "tap") {
|
|
348
372
|
if (mobile && session) {
|
|
349
373
|
touchPoints.set(pointerId, point);
|
|
350
|
-
await dispatchTouchPoints(session,
|
|
374
|
+
await dispatchTouchPoints(session, "touchStart", touchPoints);
|
|
351
375
|
await page.waitForTimeout(event.durationMs ?? types_1.APP_PLAYTEST_DEFAULT_TAP_DURATION_MS);
|
|
352
376
|
touchPoints.delete(pointerId);
|
|
353
|
-
await dispatchTouchPoints(session,
|
|
377
|
+
await dispatchTouchPoints(session, "touchEnd", touchPoints);
|
|
354
378
|
}
|
|
355
379
|
else {
|
|
356
380
|
await page.mouse.move(point.x, point.y);
|
|
357
|
-
await page.mouse.down({ button:
|
|
381
|
+
await page.mouse.down({ button: "left" });
|
|
358
382
|
await page.waitForTimeout(event.durationMs ?? types_1.APP_PLAYTEST_DEFAULT_TAP_DURATION_MS);
|
|
359
|
-
await page.mouse.up({ button:
|
|
383
|
+
await page.mouse.up({ button: "left" });
|
|
360
384
|
}
|
|
361
385
|
continue;
|
|
362
386
|
}
|
|
363
|
-
if (event.type ===
|
|
387
|
+
if (event.type === "pointerDown") {
|
|
364
388
|
if (mobile && session) {
|
|
365
389
|
touchPoints.set(pointerId, point);
|
|
366
|
-
await dispatchTouchPoints(session,
|
|
390
|
+
await dispatchTouchPoints(session, "touchStart", touchPoints);
|
|
367
391
|
}
|
|
368
392
|
else {
|
|
369
393
|
await page.mouse.move(point.x, point.y);
|
|
370
|
-
await page.mouse.down({ button:
|
|
394
|
+
await page.mouse.down({ button: "left" });
|
|
371
395
|
}
|
|
372
396
|
continue;
|
|
373
397
|
}
|
|
374
|
-
if (event.type ===
|
|
398
|
+
if (event.type === "pointerMove") {
|
|
375
399
|
if (mobile && session) {
|
|
376
400
|
touchPoints.set(pointerId, point);
|
|
377
|
-
await dispatchTouchPoints(session,
|
|
401
|
+
await dispatchTouchPoints(session, "touchMove", touchPoints);
|
|
378
402
|
}
|
|
379
403
|
else {
|
|
380
404
|
await page.mouse.move(point.x, point.y);
|
|
381
405
|
}
|
|
382
406
|
continue;
|
|
383
407
|
}
|
|
384
|
-
if (event.type ===
|
|
408
|
+
if (event.type === "pointerUp") {
|
|
385
409
|
if (mobile && session) {
|
|
386
410
|
touchPoints.delete(pointerId);
|
|
387
|
-
await dispatchTouchPoints(session,
|
|
411
|
+
await dispatchTouchPoints(session, "touchEnd", touchPoints);
|
|
388
412
|
}
|
|
389
413
|
else {
|
|
390
414
|
await page.mouse.move(point.x, point.y);
|
|
391
|
-
await page.mouse.up({ button:
|
|
415
|
+
await page.mouse.up({ button: "left" });
|
|
392
416
|
}
|
|
393
417
|
}
|
|
394
418
|
}
|
|
395
419
|
catch (error) {
|
|
396
420
|
const detail = error instanceof Error ? error.message : String(error);
|
|
397
|
-
throw new Error(`project_check_playtest_action_failed: action ${eventIndex + 1} of ${tape.events.length} `
|
|
398
|
-
|
|
421
|
+
throw new Error(`project_check_playtest_action_failed: action ${eventIndex + 1} of ${tape.events.length} ` +
|
|
422
|
+
`(${event.type} at ${event.atMs} ms) failed: ${detail}`);
|
|
399
423
|
}
|
|
400
424
|
}
|
|
401
425
|
const remainingMs = tape.durationMs - (Date.now() - startedAt);
|
|
@@ -410,14 +434,47 @@ async function dispatchPlaytestTapeToFrame(page, frame, surface, tape, hooks = {
|
|
|
410
434
|
}
|
|
411
435
|
}
|
|
412
436
|
async function findGameFrame(page, timeoutMs) {
|
|
413
|
-
const handle = await page.waitForSelector(FRAME_SELECTOR, {
|
|
437
|
+
const handle = await page.waitForSelector(FRAME_SELECTOR, {
|
|
438
|
+
state: "attached",
|
|
439
|
+
timeout: timeoutMs,
|
|
440
|
+
});
|
|
414
441
|
const frame = await handle.contentFrame();
|
|
415
442
|
await handle.dispose().catch(() => { });
|
|
416
443
|
if (!frame) {
|
|
417
|
-
throw new Error(
|
|
444
|
+
throw new Error("project_check_game_frame_missing");
|
|
418
445
|
}
|
|
419
446
|
return frame;
|
|
420
447
|
}
|
|
448
|
+
async function describeHostedLaunchTimeout(page, lastCapturedState = null, timeoutMs = HOSTED_LAUNCH_DIAGNOSTIC_TIMEOUT_MS) {
|
|
449
|
+
let diagnosticTimeout = null;
|
|
450
|
+
const lastCapturedStateLabel = lastCapturedState?.state ?? "none";
|
|
451
|
+
try {
|
|
452
|
+
const state = await Promise.race([
|
|
453
|
+
page.evaluate(({ frameSelector }) => {
|
|
454
|
+
const hostedState = window.__playdropCaptureHostedLaunchState;
|
|
455
|
+
return {
|
|
456
|
+
documentReadyState: document.readyState,
|
|
457
|
+
visibilityState: document.visibilityState,
|
|
458
|
+
gameFrameCount: document.querySelectorAll(frameSelector).length,
|
|
459
|
+
hostedState: typeof hostedState?.state === "string" ? hostedState.state : null,
|
|
460
|
+
};
|
|
461
|
+
}, { frameSelector: FRAME_SELECTOR }),
|
|
462
|
+
new Promise((_resolve, reject) => {
|
|
463
|
+
diagnosticTimeout = setTimeout(() => reject(new Error("diagnostic_timeout")), timeoutMs);
|
|
464
|
+
}),
|
|
465
|
+
]);
|
|
466
|
+
return `[check][timeout] lastCapturedState=${lastCapturedStateLabel} documentReadyState=${state.documentReadyState} visibilityState=${state.visibilityState} gameFrameCount=${state.gameFrameCount} hostedState=${state.hostedState ?? "none"}`;
|
|
467
|
+
}
|
|
468
|
+
catch (error) {
|
|
469
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
470
|
+
return `[check][timeout] lastCapturedState=${lastCapturedStateLabel} pageStateUnavailable=${detail}`;
|
|
471
|
+
}
|
|
472
|
+
finally {
|
|
473
|
+
if (diagnosticTimeout) {
|
|
474
|
+
clearTimeout(diagnosticTimeout);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
421
478
|
async function verifyHostedEditorPhaseRoundTrip(page, frame, timeoutMs) {
|
|
422
479
|
const armReadySignal = async () => {
|
|
423
480
|
await frame.evaluate(() => {
|
|
@@ -432,9 +489,9 @@ async function verifyHostedEditorPhaseRoundTrip(page, frame, timeoutMs) {
|
|
|
432
489
|
return;
|
|
433
490
|
}
|
|
434
491
|
state.installed = true;
|
|
435
|
-
window.addEventListener(
|
|
492
|
+
window.addEventListener("playdrop:loading-state", (event) => {
|
|
436
493
|
const detail = event.detail;
|
|
437
|
-
if (detail?.status ===
|
|
494
|
+
if (detail?.status === "ready") {
|
|
438
495
|
state.signals.push({ editor: detail.editor === true });
|
|
439
496
|
}
|
|
440
497
|
});
|
|
@@ -443,48 +500,50 @@ async function verifyHostedEditorPhaseRoundTrip(page, frame, timeoutMs) {
|
|
|
443
500
|
const changePhaseAndWait = async (phase, editor) => {
|
|
444
501
|
await armReadySignal();
|
|
445
502
|
await page.evaluate((nextPhase) => {
|
|
446
|
-
window.postMessage({ type:
|
|
503
|
+
window.postMessage({ type: "playdrop-studio-runtime-state", active: true, phase: nextPhase }, window.location.origin);
|
|
447
504
|
}, phase);
|
|
448
505
|
await frame.waitForFunction((expectedEditor) => {
|
|
449
506
|
const runtimeWindow = window;
|
|
450
|
-
return runtimeWindow.__playdropEditorPhaseCheck?.signals?.some((signal) => signal.editor === expectedEditor) === true;
|
|
507
|
+
return (runtimeWindow.__playdropEditorPhaseCheck?.signals?.some((signal) => signal.editor === expectedEditor) === true);
|
|
451
508
|
}, editor, { timeout: timeoutMs });
|
|
452
509
|
};
|
|
453
510
|
try {
|
|
454
|
-
await changePhaseAndWait(
|
|
455
|
-
await changePhaseAndWait(
|
|
511
|
+
await changePhaseAndWait("play", false);
|
|
512
|
+
await changePhaseAndWait("editor", true);
|
|
456
513
|
}
|
|
457
514
|
catch (error) {
|
|
458
515
|
const detail = error instanceof Error ? error.message : String(error);
|
|
459
|
-
throw new Error(
|
|
460
|
-
|
|
516
|
+
throw new Error("project_check_editor_phase_switch_failed: The hosted bundle must switch Editor to Playtest and back " +
|
|
517
|
+
`inside the existing runtime frame. ${detail}`);
|
|
461
518
|
}
|
|
462
519
|
}
|
|
463
520
|
async function readWebGlRenderer(frame) {
|
|
464
521
|
const renderer = await frame.evaluate(() => {
|
|
465
|
-
const canvas = document.createElement(
|
|
466
|
-
const gl = (canvas.getContext(
|
|
522
|
+
const canvas = document.createElement("canvas");
|
|
523
|
+
const gl = (canvas.getContext("webgl2") ||
|
|
524
|
+
canvas.getContext("webgl") ||
|
|
525
|
+
canvas.getContext("experimental-webgl"));
|
|
467
526
|
if (!gl) {
|
|
468
|
-
return
|
|
527
|
+
return "";
|
|
469
528
|
}
|
|
470
|
-
const debugInfo = gl.getExtension(
|
|
529
|
+
const debugInfo = gl.getExtension("WEBGL_debug_renderer_info");
|
|
471
530
|
if (debugInfo) {
|
|
472
531
|
const unmaskedRenderer = gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL);
|
|
473
|
-
if (typeof unmaskedRenderer ===
|
|
532
|
+
if (typeof unmaskedRenderer === "string" && unmaskedRenderer.trim()) {
|
|
474
533
|
return unmaskedRenderer;
|
|
475
534
|
}
|
|
476
535
|
}
|
|
477
536
|
const rendererValue = gl.getParameter(gl.RENDERER);
|
|
478
|
-
return typeof rendererValue ===
|
|
537
|
+
return typeof rendererValue === "string" ? rendererValue : String(rendererValue ?? "");
|
|
479
538
|
});
|
|
480
|
-
const normalized = typeof renderer ===
|
|
539
|
+
const normalized = typeof renderer === "string" ? renderer.trim() : "";
|
|
481
540
|
if (!normalized) {
|
|
482
|
-
throw new Error(
|
|
541
|
+
throw new Error("project_check_webgl_renderer_missing");
|
|
483
542
|
}
|
|
484
543
|
return normalized;
|
|
485
544
|
}
|
|
486
545
|
async function saveScreenshot(page, screenshotPath) {
|
|
487
|
-
const normalizedPath = typeof screenshotPath ===
|
|
546
|
+
const normalizedPath = typeof screenshotPath === "string" ? screenshotPath.trim() : "";
|
|
488
547
|
if (!normalizedPath) {
|
|
489
548
|
return null;
|
|
490
549
|
}
|
|
@@ -493,11 +552,11 @@ async function saveScreenshot(page, screenshotPath) {
|
|
|
493
552
|
return normalizedPath;
|
|
494
553
|
}
|
|
495
554
|
async function saveGameFrameScreenshot(page, screenshotPath) {
|
|
496
|
-
const normalizedPath = typeof screenshotPath ===
|
|
555
|
+
const normalizedPath = typeof screenshotPath === "string" ? screenshotPath.trim() : "";
|
|
497
556
|
if (!normalizedPath)
|
|
498
557
|
return null;
|
|
499
558
|
await (0, promises_1.mkdir)((0, node_path_1.dirname)(normalizedPath), { recursive: true });
|
|
500
|
-
await page.locator(FRAME_SELECTOR).screenshot({ path: normalizedPath, type:
|
|
559
|
+
await page.locator(FRAME_SELECTOR).screenshot({ path: normalizedPath, type: "png" });
|
|
501
560
|
return normalizedPath;
|
|
502
561
|
}
|
|
503
562
|
function normalizeLoadCheckFailure(error, artifactFingerprint) {
|
|
@@ -508,44 +567,45 @@ function normalizeLoadCheckFailure(error, artifactFingerprint) {
|
|
|
508
567
|
return buildFailedLoadCheckResult(errorCodeMatch[1], message, artifactFingerprint);
|
|
509
568
|
}
|
|
510
569
|
const prefixedCodeMatch = message.match(/^([a-z0-9_]+):/i);
|
|
511
|
-
if (prefixedCodeMatch?.[1]
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
570
|
+
if (prefixedCodeMatch?.[1] &&
|
|
571
|
+
(prefixedCodeMatch[1].startsWith("hosted_app_") ||
|
|
572
|
+
prefixedCodeMatch[1].startsWith("local_dev_") ||
|
|
573
|
+
prefixedCodeMatch[1].startsWith("dev_auth_") ||
|
|
574
|
+
prefixedCodeMatch[1].startsWith("project_check_"))) {
|
|
516
575
|
return buildFailedLoadCheckResult(prefixedCodeMatch[1], message, artifactFingerprint);
|
|
517
576
|
}
|
|
518
577
|
if (/missing_init\b/i.test(message) || /hosted_app_missing_client_init:/i.test(message)) {
|
|
519
|
-
return buildFailedLoadCheckResult(
|
|
578
|
+
return buildFailedLoadCheckResult("missing_init", message, artifactFingerprint);
|
|
520
579
|
}
|
|
521
580
|
if (/missing_ready\b/i.test(message) || /hosted_app_missing_ready:/i.test(message)) {
|
|
522
|
-
return buildFailedLoadCheckResult(
|
|
581
|
+
return buildFailedLoadCheckResult("missing_ready", message, artifactFingerprint);
|
|
523
582
|
}
|
|
524
583
|
if (/sdk_mismatch\b/i.test(message) || /hosted_app_sdk_version_mismatch:/i.test(message)) {
|
|
525
|
-
return buildFailedLoadCheckResult(
|
|
584
|
+
return buildFailedLoadCheckResult("sdk_mismatch", message, artifactFingerprint);
|
|
526
585
|
}
|
|
527
586
|
if (/software renderer|swiftshader|webgl_renderer_software/i.test(message)) {
|
|
528
|
-
return buildFailedLoadCheckResult(
|
|
587
|
+
return buildFailedLoadCheckResult("webgl_renderer_software", message, artifactFingerprint);
|
|
529
588
|
}
|
|
530
|
-
return buildFailedLoadCheckResult(
|
|
589
|
+
return buildFailedLoadCheckResult("startup_failure", message, artifactFingerprint);
|
|
531
590
|
}
|
|
532
|
-
function formatHostedLoadCheckFailure(taskName, result, scope =
|
|
533
|
-
const scopeLabel = scope ===
|
|
534
|
-
?
|
|
535
|
-
: scope ===
|
|
536
|
-
?
|
|
537
|
-
: scope ===
|
|
538
|
-
?
|
|
539
|
-
:
|
|
540
|
-
const errorCode = result.errorCode?.trim() ||
|
|
591
|
+
function formatHostedLoadCheckFailure(taskName, result, scope = "local") {
|
|
592
|
+
const scopeLabel = scope === "final"
|
|
593
|
+
? "final owner play route"
|
|
594
|
+
: scope === "editor"
|
|
595
|
+
? "staged Editor phase"
|
|
596
|
+
: scope === "staged"
|
|
597
|
+
? "staged upload preview"
|
|
598
|
+
: "local hosted preview";
|
|
599
|
+
const errorCode = result.errorCode?.trim() || "startup_failure";
|
|
541
600
|
const message = normalizeLoadCheckMessage(result.message);
|
|
542
|
-
|
|
601
|
+
const screenshot = result.screenshotPath ? ` Screenshot: ${result.screenshotPath}.` : "";
|
|
602
|
+
return `hosted_load_check_failed: ${taskName} failed the ${scopeLabel} check (${errorCode}). ${message}${screenshot}`;
|
|
543
603
|
}
|
|
544
604
|
function redactHostedLoadCheckSecrets(text, sourceUrl) {
|
|
545
|
-
return (0, appUrls_1.redactTaskCaptureToken)(text, sourceUrl).replace(/("(?:authToken|appAccessToken|captureTaskToken|reviewToken)"\s*:\s*")[^"]+(")/g,
|
|
605
|
+
return (0, appUrls_1.redactTaskCaptureToken)(text, sourceUrl).replace(/("(?:authToken|appAccessToken|captureTaskToken|reviewToken)"\s*:\s*")[^"]+(")/g, "$1<redacted>$2");
|
|
546
606
|
}
|
|
547
607
|
async function runHostedLoadCheck(options) {
|
|
548
|
-
const expectedHostedLaunchState = options.expectedHostedLaunchState ??
|
|
608
|
+
const expectedHostedLaunchState = options.expectedHostedLaunchState ?? "ready";
|
|
549
609
|
const artifactFingerprint = options.artifactFingerprint ?? null;
|
|
550
610
|
const timeoutMs = options.timeoutMs ?? DEFAULT_HOSTED_LOAD_TIMEOUT_MS;
|
|
551
611
|
const errors = [];
|
|
@@ -560,15 +620,17 @@ async function runHostedLoadCheck(options) {
|
|
|
560
620
|
const hasExplicitToken = Boolean(options.token?.trim());
|
|
561
621
|
const shouldBootstrapSavedSession = options.savedSessionBootstrap !== false && hasExplicitToken;
|
|
562
622
|
const bootstrapToken = shouldBootstrapSavedSession ? options.token?.trim() || null : null;
|
|
563
|
-
const bootstrapUser = shouldBootstrapSavedSession ? options.user ?? null : null;
|
|
564
|
-
let currentTapeAction =
|
|
623
|
+
const bootstrapUser = shouldBootstrapSavedSession ? (options.user ?? null) : null;
|
|
624
|
+
let currentTapeAction = "";
|
|
565
625
|
const withTapeAction = (line) => (currentTapeAction ? `${line} after ${currentTapeAction}` : line);
|
|
566
626
|
let hostedLaunchWaiterSettled = false;
|
|
627
|
+
let lastHostedLaunchState = null;
|
|
567
628
|
let resolveHostedLaunchWaiter = null;
|
|
568
629
|
const hostedLaunchWaiter = new Promise((resolve) => {
|
|
569
630
|
resolveHostedLaunchWaiter = resolve;
|
|
570
631
|
});
|
|
571
632
|
const settleHostedLaunchWaiter = (state) => {
|
|
633
|
+
lastHostedLaunchState = state;
|
|
572
634
|
if (hostedLaunchWaiterSettled) {
|
|
573
635
|
return;
|
|
574
636
|
}
|
|
@@ -585,7 +647,7 @@ async function runHostedLoadCheck(options) {
|
|
|
585
647
|
await page.addInitScript(({ token, user }) => {
|
|
586
648
|
try {
|
|
587
649
|
if (token) {
|
|
588
|
-
window.localStorage.setItem(
|
|
650
|
+
window.localStorage.setItem("playdrop.accessToken", token);
|
|
589
651
|
}
|
|
590
652
|
}
|
|
591
653
|
catch {
|
|
@@ -593,60 +655,60 @@ async function runHostedLoadCheck(options) {
|
|
|
593
655
|
}
|
|
594
656
|
try {
|
|
595
657
|
if (user) {
|
|
596
|
-
window.localStorage.setItem(
|
|
658
|
+
window.localStorage.setItem("playdrop.user", JSON.stringify(user));
|
|
597
659
|
}
|
|
598
660
|
}
|
|
599
661
|
catch {
|
|
600
662
|
// Storage can be blocked by browser policy; the cookie remains authoritative.
|
|
601
663
|
}
|
|
602
664
|
try {
|
|
603
|
-
window.sessionStorage.removeItem(
|
|
665
|
+
window.sessionStorage.removeItem("playdrop.logoutReason");
|
|
604
666
|
}
|
|
605
667
|
catch {
|
|
606
668
|
// Ignore session storage failures.
|
|
607
669
|
}
|
|
608
670
|
}, { token: bootstrapToken, user: bootstrapUser });
|
|
609
671
|
}
|
|
610
|
-
await page.exposeBinding(
|
|
611
|
-
if (type ===
|
|
672
|
+
await page.exposeBinding("__playdropCaptureLog", async (_source, type, payload) => {
|
|
673
|
+
if (type === "hosted-launch-state") {
|
|
612
674
|
const normalizedHostedState = normalizeHostedLaunchStatePayload(payload);
|
|
613
675
|
if (normalizedHostedState) {
|
|
614
676
|
settleHostedLaunchWaiter(normalizedHostedState);
|
|
615
677
|
}
|
|
616
678
|
}
|
|
617
|
-
const normalizedType = typeof type ===
|
|
679
|
+
const normalizedType = typeof type === "string" ? type.toLowerCase() : "info";
|
|
618
680
|
if (/error|failed|denied|unreachable/.test(normalizedType)) {
|
|
619
681
|
const serialized = serializePayload(payload);
|
|
620
|
-
errors.push(redactReportText([
|
|
682
|
+
errors.push(redactReportText(["[check][custom]", normalizedType, serialized].filter(Boolean).join(" ")));
|
|
621
683
|
}
|
|
622
684
|
});
|
|
623
685
|
await page.addInitScript(() => {
|
|
624
|
-
window.addEventListener(
|
|
686
|
+
window.addEventListener("unhandledrejection", (event) => {
|
|
625
687
|
try {
|
|
626
|
-
console.error(
|
|
688
|
+
console.error("[check][unhandledrejection]", event.reason);
|
|
627
689
|
}
|
|
628
690
|
catch {
|
|
629
|
-
console.error(
|
|
691
|
+
console.error("[check][unhandledrejection]");
|
|
630
692
|
}
|
|
631
693
|
});
|
|
632
694
|
const bridgeWindow = window;
|
|
633
695
|
bridgeWindow.playdrop = bridgeWindow.playdrop || {};
|
|
634
696
|
bridgeWindow.__playdropCaptureHostedLaunchState = null;
|
|
635
697
|
const bridge = (type, payload) => {
|
|
636
|
-
if (type ===
|
|
698
|
+
if (type === "hosted-launch-state") {
|
|
637
699
|
bridgeWindow.__playdropCaptureHostedLaunchState = payload;
|
|
638
700
|
}
|
|
639
701
|
try {
|
|
640
702
|
const binding = bridgeWindow.__playdropCaptureLog;
|
|
641
|
-
if (typeof binding ===
|
|
703
|
+
if (typeof binding === "function") {
|
|
642
704
|
binding(type, payload);
|
|
643
705
|
}
|
|
644
706
|
}
|
|
645
707
|
catch (error) {
|
|
646
|
-
console.error(
|
|
708
|
+
console.error("[check][bridge-error]", error);
|
|
647
709
|
}
|
|
648
710
|
};
|
|
649
|
-
Object.defineProperty(bridgeWindow.playdrop,
|
|
711
|
+
Object.defineProperty(bridgeWindow.playdrop, "capture", {
|
|
650
712
|
value: bridge,
|
|
651
713
|
configurable: true,
|
|
652
714
|
writable: true,
|
|
@@ -656,26 +718,58 @@ async function runHostedLoadCheck(options) {
|
|
|
656
718
|
const type = message.type();
|
|
657
719
|
const args = await Promise.all(message.args().map((arg) => arg.jsonValue().catch(() => arg.toString())));
|
|
658
720
|
const rendered = formatBrowserConsoleMessage(args, message.text());
|
|
721
|
+
if (isDuplicateBrowserResourceConsoleError(type, rendered)) {
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
659
724
|
const line = redactReportText(withTapeAction(`[check][console:${type}] ${rendered}`));
|
|
660
|
-
if (type ===
|
|
725
|
+
if (type === "error" || type === "assert" || type === "trace") {
|
|
661
726
|
errors.push(line);
|
|
662
727
|
}
|
|
663
728
|
};
|
|
664
|
-
|
|
665
|
-
|
|
729
|
+
const pendingConsoleMessages = new Set();
|
|
730
|
+
const drainConsoleMessages = async () => {
|
|
731
|
+
if (pendingConsoleMessages.size === 0)
|
|
732
|
+
return;
|
|
733
|
+
let timeout = null;
|
|
734
|
+
const settled = await Promise.race([
|
|
735
|
+
Promise.allSettled([...pendingConsoleMessages]).then(() => true),
|
|
736
|
+
new Promise((resolve) => {
|
|
737
|
+
timeout = setTimeout(() => resolve(false), HOSTED_LAUNCH_DIAGNOSTIC_TIMEOUT_MS);
|
|
738
|
+
}),
|
|
739
|
+
]);
|
|
740
|
+
if (timeout)
|
|
741
|
+
clearTimeout(timeout);
|
|
742
|
+
if (!settled) {
|
|
743
|
+
warnings.push("[check][console-handler] Timed out while collecting browser console diagnostics.");
|
|
744
|
+
}
|
|
745
|
+
};
|
|
746
|
+
page.on("console", (message) => {
|
|
747
|
+
const pending = handleConsoleMessage(message)
|
|
748
|
+
.catch((error) => {
|
|
666
749
|
const text = redactReportText(error instanceof Error ? error.message : String(error));
|
|
667
|
-
|
|
668
|
-
})
|
|
750
|
+
warnings.push(`[check][console-handler] ${text}`);
|
|
751
|
+
})
|
|
752
|
+
.finally(() => pendingConsoleMessages.delete(pending));
|
|
753
|
+
pendingConsoleMessages.add(pending);
|
|
669
754
|
});
|
|
670
|
-
page.on(
|
|
755
|
+
page.on("pageerror", (error) => {
|
|
671
756
|
const text = redactReportText(error?.message ?? String(error));
|
|
672
757
|
const line = withTapeAction(`[check][pageerror] ${text}`);
|
|
673
758
|
errors.push(line);
|
|
674
759
|
});
|
|
675
|
-
page.on(
|
|
760
|
+
page.on("crash", () => {
|
|
761
|
+
const line = withTapeAction("[check][pagecrash] Hosted validator page crashed.");
|
|
762
|
+
errors.push(line);
|
|
763
|
+
settleHostedLaunchWaiter({
|
|
764
|
+
state: "error",
|
|
765
|
+
errorCode: "browser_page_crashed",
|
|
766
|
+
message: "The hosted validator page crashed before launch completed.",
|
|
767
|
+
});
|
|
768
|
+
});
|
|
769
|
+
page.on("requestfailed", (request) => {
|
|
676
770
|
const failure = request.failure();
|
|
677
|
-
const errorText = failure?.errorText ??
|
|
678
|
-
const text = redactReportText(`${request.method()} ${request.url()} - ${errorText ||
|
|
771
|
+
const errorText = failure?.errorText ?? "";
|
|
772
|
+
const text = redactReportText(`${request.method()} ${request.url()} - ${errorText || "failed"}`);
|
|
679
773
|
if (isKnownGoogleTelemetryUrl(request.url())) {
|
|
680
774
|
warnings.push(`[check][requestfailed] ${text}`);
|
|
681
775
|
return;
|
|
@@ -686,12 +780,21 @@ async function runHostedLoadCheck(options) {
|
|
|
686
780
|
const line = withTapeAction(`[check][requestfailed] ${text}`);
|
|
687
781
|
errors.push(line);
|
|
688
782
|
});
|
|
689
|
-
page.on(
|
|
783
|
+
page.on("response", (response) => {
|
|
690
784
|
const status = response.status();
|
|
691
785
|
if (status < 400) {
|
|
692
786
|
return;
|
|
693
787
|
}
|
|
694
788
|
const text = redactReportText(`${status} ${response.statusText()} ${response.url()}`);
|
|
789
|
+
if (isExpectedAnonymousSessionProbe({
|
|
790
|
+
url: response.url(),
|
|
791
|
+
status,
|
|
792
|
+
method: response.request().method(),
|
|
793
|
+
hasExplicitToken,
|
|
794
|
+
})) {
|
|
795
|
+
warnings.push(`[check][response] ${text}`);
|
|
796
|
+
return;
|
|
797
|
+
}
|
|
695
798
|
if (isKnownGoogleTelemetryUrl(response.url())) {
|
|
696
799
|
warnings.push(`[check][response] ${text}`);
|
|
697
800
|
return;
|
|
@@ -699,65 +802,83 @@ async function runHostedLoadCheck(options) {
|
|
|
699
802
|
const line = withTapeAction(`[check][response] ${text}`);
|
|
700
803
|
errors.push(line);
|
|
701
804
|
});
|
|
702
|
-
const
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
}
|
|
706
|
-
const readinessTimeoutMs = Math.min(Math.max(timeoutMs, 1000), 30000);
|
|
707
|
-
await page.waitForFunction(({ frameSelector }) => {
|
|
708
|
-
const frame = document.querySelector(frameSelector);
|
|
709
|
-
const state = window.__playdropCaptureHostedLaunchState;
|
|
710
|
-
if (frame) {
|
|
711
|
-
return 'frame';
|
|
805
|
+
const captureFailureScreenshot = async () => {
|
|
806
|
+
try {
|
|
807
|
+
screenshotPath = await saveScreenshot(page, options.failureScreenshotPath ?? options.screenshotPath);
|
|
712
808
|
}
|
|
713
|
-
|
|
714
|
-
|
|
809
|
+
catch (screenshotError) {
|
|
810
|
+
const detail = redactReportText(screenshotError instanceof Error ? screenshotError.message : String(screenshotError));
|
|
811
|
+
errors.push(`[check][screenshot] Failed to capture staged validation evidence: ${detail}`);
|
|
812
|
+
}
|
|
813
|
+
};
|
|
814
|
+
const captureCompletionScreenshot = async () => {
|
|
815
|
+
if (errors.length > 0) {
|
|
816
|
+
await captureFailureScreenshot();
|
|
817
|
+
return;
|
|
715
818
|
}
|
|
716
|
-
return null;
|
|
717
|
-
}, { frameSelector: FRAME_SELECTOR }, { timeout: readinessTimeoutMs });
|
|
718
|
-
const launchState = await Promise.race([
|
|
719
|
-
hostedLaunchWaiter,
|
|
720
|
-
page.waitForTimeout(timeoutMs).then(() => {
|
|
721
|
-
throw new Error(expectedHostedLaunchState === 'ready'
|
|
722
|
-
? 'hosted_app_missing_ready: instrument_error:game_ready_timeout: Hosted app did not report sdk.host.ready() before the instrument timeout.'
|
|
723
|
-
: `hosted_app_gate_missing: Hosted app did not report ${expectedHostedLaunchState} before the load-check timeout.`);
|
|
724
|
-
}),
|
|
725
|
-
]);
|
|
726
|
-
if (launchState.state === 'error') {
|
|
727
|
-
throw new Error(formatHostedLaunchError(launchState));
|
|
728
|
-
}
|
|
729
|
-
if (launchState.state !== expectedHostedLaunchState) {
|
|
730
|
-
throw new Error(formatHostedLaunchStateMismatch(launchState.state, expectedHostedLaunchState));
|
|
731
|
-
}
|
|
732
|
-
finalUrl = (0, appUrls_1.redactTaskCaptureSession)(page.url());
|
|
733
|
-
if (expectedHostedLaunchState !== 'ready') {
|
|
734
819
|
screenshotPath = await saveScreenshot(page, options.screenshotPath);
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
throw new Error(`project_check_webgl_renderer_software: WebGL renderer is "${webglRenderer}". Real hardware acceleration is required.`);
|
|
741
|
-
}
|
|
742
|
-
if (options.verifyEditorPhaseRoundTrip) {
|
|
743
|
-
await verifyHostedEditorPhaseRoundTrip(page, frame, readinessTimeoutMs);
|
|
744
|
-
}
|
|
745
|
-
await saveGameFrameScreenshot(page, options.readyGameFrameScreenshotPath);
|
|
746
|
-
if (options.playtestTape) {
|
|
747
|
-
if (!options.playtestSurface) {
|
|
748
|
-
throw new Error('project_check_playtest_surface_required');
|
|
820
|
+
};
|
|
821
|
+
try {
|
|
822
|
+
const response = await page.goto(options.targetUrl, { waitUntil: "domcontentloaded", timeout: 30000 });
|
|
823
|
+
if (response && !response.ok()) {
|
|
824
|
+
errors.push(`[check][navigation] ${response.status()} ${response.statusText()} ${reportTargetUrl}`);
|
|
749
825
|
}
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
}
|
|
826
|
+
const readinessTimeoutMs = Math.min(Math.max(timeoutMs, 1000), 30000);
|
|
827
|
+
let launchState;
|
|
828
|
+
try {
|
|
829
|
+
launchState = await waitForHostedLaunchState(hostedLaunchWaiter, expectedHostedLaunchState, timeoutMs);
|
|
830
|
+
}
|
|
831
|
+
catch (error) {
|
|
832
|
+
const timeoutContext = await describeHostedLaunchTimeout(page, lastHostedLaunchState);
|
|
833
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
834
|
+
throw new Error(`${detail} ${timeoutContext}`);
|
|
835
|
+
}
|
|
836
|
+
if (launchState.state === "error") {
|
|
837
|
+
throw new Error(formatHostedLaunchError(launchState));
|
|
838
|
+
}
|
|
839
|
+
finalUrl = (0, appUrls_1.redactTaskCaptureSession)(page.url());
|
|
840
|
+
if (expectedHostedLaunchState !== "ready") {
|
|
841
|
+
await drainConsoleMessages();
|
|
842
|
+
await captureCompletionScreenshot();
|
|
843
|
+
return;
|
|
844
|
+
}
|
|
845
|
+
const frame = await findGameFrame(page, readinessTimeoutMs);
|
|
846
|
+
webglRenderer = await readWebGlRenderer(frame);
|
|
847
|
+
if (isSoftwareWebGlRenderer(webglRenderer)) {
|
|
848
|
+
throw new Error(`project_check_webgl_renderer_software: WebGL renderer is "${webglRenderer}". Real hardware acceleration is required.`);
|
|
849
|
+
}
|
|
850
|
+
if (options.verifyEditorPhaseRoundTrip) {
|
|
851
|
+
await verifyHostedEditorPhaseRoundTrip(page, frame, readinessTimeoutMs);
|
|
852
|
+
}
|
|
853
|
+
await saveGameFrameScreenshot(page, options.readyGameFrameScreenshotPath);
|
|
854
|
+
if (options.playtestTape) {
|
|
855
|
+
if (!options.playtestSurface) {
|
|
856
|
+
throw new Error("project_check_playtest_surface_required");
|
|
857
|
+
}
|
|
858
|
+
await dispatchPlaytestTapeToFrame(page, frame, options.playtestSurface, options.playtestTape, {
|
|
859
|
+
onBeforeEvent: (eventIndex, event) => {
|
|
860
|
+
currentTapeAction = `action ${eventIndex + 1} of ${options.playtestTape.events.length} (${event.type} at ${event.atMs} ms)`;
|
|
861
|
+
},
|
|
862
|
+
});
|
|
863
|
+
}
|
|
864
|
+
else if (options.postReadyWaitMs && options.postReadyWaitMs > 0) {
|
|
865
|
+
await page.waitForTimeout(options.postReadyWaitMs);
|
|
866
|
+
}
|
|
867
|
+
await saveGameFrameScreenshot(page, options.gameFrameScreenshotPath);
|
|
868
|
+
await drainConsoleMessages();
|
|
869
|
+
await captureCompletionScreenshot();
|
|
755
870
|
}
|
|
756
|
-
|
|
757
|
-
await
|
|
871
|
+
catch (error) {
|
|
872
|
+
await drainConsoleMessages();
|
|
873
|
+
try {
|
|
874
|
+
finalUrl = (0, appUrls_1.redactTaskCaptureSession)(page.url());
|
|
875
|
+
}
|
|
876
|
+
catch {
|
|
877
|
+
// Keep the last known URL when the renderer is unavailable.
|
|
878
|
+
}
|
|
879
|
+
await captureFailureScreenshot();
|
|
880
|
+
throw error;
|
|
758
881
|
}
|
|
759
|
-
await saveGameFrameScreenshot(page, options.gameFrameScreenshotPath);
|
|
760
|
-
screenshotPath = await saveScreenshot(page, options.screenshotPath);
|
|
761
882
|
}, {
|
|
762
883
|
...options.contextOptions,
|
|
763
884
|
automationOrigin: new URL(options.targetUrl).origin,
|
|
@@ -780,7 +901,7 @@ async function runHostedLoadCheck(options) {
|
|
|
780
901
|
};
|
|
781
902
|
}
|
|
782
903
|
if (errors.length > 0) {
|
|
783
|
-
return buildFailedLoadCheckResult(
|
|
904
|
+
return buildFailedLoadCheckResult("startup_failure", `Hosted app emitted ${errors.length} console, page, or network error(s) during startup.${formatLoadCheckErrors(errors)}`, artifactFingerprint, {
|
|
784
905
|
screenshotPath,
|
|
785
906
|
webglRenderer,
|
|
786
907
|
targetUrl: finalUrl,
|
|
@@ -797,9 +918,9 @@ async function runHostedLoadCheck(options) {
|
|
|
797
918
|
});
|
|
798
919
|
}
|
|
799
920
|
async function startOrReuseMountedDevRuntime(input) {
|
|
800
|
-
const appTypeSlug = (0, appUrls_1.getAppTypeSlug)(input.task.type ??
|
|
921
|
+
const appTypeSlug = (0, appUrls_1.getAppTypeSlug)(input.task.type ?? "GAME");
|
|
801
922
|
const devRouterPort = input.devRouterPort ?? devServer_1.DEV_ROUTER_PORT;
|
|
802
|
-
const runtimeAssetManifest = input.task.hostingMode ===
|
|
923
|
+
const runtimeAssetManifest = input.task.hostingMode === "EXTERNAL" || input.task.externalUrl
|
|
803
924
|
? (0, devRuntimeAssets_1.createEmptyDevRuntimeAssetManifest)()
|
|
804
925
|
: await (0, devRuntimeAssets_1.buildDevRuntimeAssetManifest)({
|
|
805
926
|
client: input.client,
|
|
@@ -838,7 +959,7 @@ async function startOrReuseMountedDevRuntime(input) {
|
|
|
838
959
|
}
|
|
839
960
|
catch (error) {
|
|
840
961
|
const formatted = (0, dev_1.formatDevRuntimeAssetManifestFailure)(error);
|
|
841
|
-
const mountConflict = (0, devServer_1.parseMountConflictError)(error instanceof Error ? error.message :
|
|
962
|
+
const mountConflict = (0, devServer_1.parseMountConflictError)(error instanceof Error ? error.message : "");
|
|
842
963
|
if (mountConflict) {
|
|
843
964
|
throw new Error(`A different dev session already owns ${mountConflict.ref}. Active owner pid: ${mountConflict.ownerPid}.`);
|
|
844
965
|
}
|
|
@@ -855,14 +976,12 @@ async function runLocalHostedLoadCheck(input) {
|
|
|
855
976
|
task: input.task,
|
|
856
977
|
devRouterPort: input.devRouterPort,
|
|
857
978
|
});
|
|
858
|
-
const localDevPort = input.devRouterPort === undefined || input.devRouterPort === devServer_1.DEV_ROUTER_PORT
|
|
859
|
-
? null
|
|
860
|
-
: input.devRouterPort;
|
|
979
|
+
const localDevPort = input.devRouterPort === undefined || input.devRouterPort === devServer_1.DEV_ROUTER_PORT ? null : input.devRouterPort;
|
|
861
980
|
const localAppMetadata = mountedRuntime.runtimeAssetManifest.response.localAppMetadata;
|
|
862
981
|
const declaredSurfaces = normalizeLoadCheckSurfaceTargets(localAppMetadata.surfaceTargets);
|
|
863
982
|
const captureSurface = input.surface ?? resolveLoadCheckSurface(localAppMetadata.surfaceTargets, input.task.primarySurface);
|
|
864
983
|
if (!declaredSurfaces.includes(captureSurface)) {
|
|
865
|
-
return buildFailedLoadCheckResult(
|
|
984
|
+
return buildFailedLoadCheckResult("surface_unsupported", `Surface ${captureSurface} is not declared in surfaceTargets for "${input.task.name}".`, null);
|
|
866
985
|
}
|
|
867
986
|
const contextOptions = cloneLoadCheckContextOptions(captureSurface);
|
|
868
987
|
const anonymousExpectedState = resolveAnonymousHostedLaunchState({
|
|
@@ -874,7 +993,7 @@ async function runLocalHostedLoadCheck(input) {
|
|
|
874
993
|
targetUrl: (0, appUrls_1.buildPlatformDevUrl)(input.webBase, {
|
|
875
994
|
creatorUsername: input.creatorUsername,
|
|
876
995
|
appName: input.task.name,
|
|
877
|
-
appType: input.task.type ??
|
|
996
|
+
appType: input.task.type ?? "GAME",
|
|
878
997
|
launchCheck: true,
|
|
879
998
|
localDevPort,
|
|
880
999
|
captureSession: input.captureSession,
|
|
@@ -888,20 +1007,18 @@ async function runLocalHostedLoadCheck(input) {
|
|
|
888
1007
|
postReadyWaitMs: input.postReadyWaitMs,
|
|
889
1008
|
});
|
|
890
1009
|
}
|
|
891
|
-
if (anonymousExpectedState ===
|
|
1010
|
+
if (anonymousExpectedState === "login_required") {
|
|
892
1011
|
const registeredApp = await (0, registration_1.fetchRegisteredAppShell)(input.client, input.creatorUsername, input.task.name);
|
|
893
|
-
const canLaunchUnregisteredViewer = input.allowUnregisteredViewerLaunch === true
|
|
894
|
-
&& Boolean(input.token)
|
|
895
|
-
&& Boolean(input.currentUser);
|
|
1012
|
+
const canLaunchUnregisteredViewer = input.allowUnregisteredViewerLaunch === true && Boolean(input.token) && Boolean(input.currentUser);
|
|
896
1013
|
if (!registeredApp?.id && !canLaunchUnregisteredViewer) {
|
|
897
|
-
return buildFailedLoadCheckResult(
|
|
1014
|
+
return buildFailedLoadCheckResult("app_registration_required_for_auth_validation", `Auth-required hosted app "${input.task.name}" must be registered on PlayDrop before viewer load-check can run. Run "playdrop project create app ${input.task.name}" and try again.`, null);
|
|
898
1015
|
}
|
|
899
1016
|
const anonymousGateResult = await runHostedLoadCheck({
|
|
900
1017
|
targetUrl: (0, appUrls_1.buildPlatformDevUrl)(input.webBase, {
|
|
901
1018
|
creatorUsername: input.creatorUsername,
|
|
902
1019
|
appName: input.task.name,
|
|
903
|
-
appType: input.task.type ??
|
|
904
|
-
devAuth:
|
|
1020
|
+
appType: input.task.type ?? "GAME",
|
|
1021
|
+
devAuth: "anonymous",
|
|
905
1022
|
launchCheck: true,
|
|
906
1023
|
localDevPort,
|
|
907
1024
|
}),
|
|
@@ -909,15 +1026,15 @@ async function runLocalHostedLoadCheck(input) {
|
|
|
909
1026
|
expectedHostedLaunchState: anonymousExpectedState,
|
|
910
1027
|
contextOptions,
|
|
911
1028
|
});
|
|
912
|
-
if (anonymousGateResult.status !==
|
|
1029
|
+
if (anonymousGateResult.status !== "PASSED") {
|
|
913
1030
|
return anonymousGateResult;
|
|
914
1031
|
}
|
|
915
1032
|
return await runHostedLoadCheck({
|
|
916
1033
|
targetUrl: (0, appUrls_1.buildPlatformDevUrl)(input.webBase, {
|
|
917
1034
|
creatorUsername: input.creatorUsername,
|
|
918
1035
|
appName: input.task.name,
|
|
919
|
-
appType: input.task.type ??
|
|
920
|
-
devAuth:
|
|
1036
|
+
appType: input.task.type ?? "GAME",
|
|
1037
|
+
devAuth: "viewer",
|
|
921
1038
|
launchCheck: true,
|
|
922
1039
|
localDevPort,
|
|
923
1040
|
}),
|
|
@@ -937,8 +1054,8 @@ async function runLocalHostedLoadCheck(input) {
|
|
|
937
1054
|
targetUrl: (0, appUrls_1.buildPlatformDevUrl)(input.webBase, {
|
|
938
1055
|
creatorUsername: input.creatorUsername,
|
|
939
1056
|
appName: input.task.name,
|
|
940
|
-
appType: input.task.type ??
|
|
941
|
-
devAuth:
|
|
1057
|
+
appType: input.task.type ?? "GAME",
|
|
1058
|
+
devAuth: "anonymous",
|
|
942
1059
|
launchCheck: true,
|
|
943
1060
|
localDevPort,
|
|
944
1061
|
}),
|
|
@@ -963,14 +1080,26 @@ function appendLoadCheckParams(targetUrl, devAuth) {
|
|
|
963
1080
|
player: null,
|
|
964
1081
|
});
|
|
965
1082
|
}
|
|
1083
|
+
function buildUploadedHostedLoadCheckUrl(rawUrl, phase) {
|
|
1084
|
+
const url = new URL(rawUrl);
|
|
1085
|
+
url.searchParams.set("launchCheck", "1");
|
|
1086
|
+
if (phase === "editor") {
|
|
1087
|
+
url.searchParams.set("hostPhase", "editor");
|
|
1088
|
+
}
|
|
1089
|
+
return url.toString();
|
|
1090
|
+
}
|
|
1091
|
+
function buildUploadedHostedLoadCheckScreenshotPath(task, sessionId, phase) {
|
|
1092
|
+
const projectDir = typeof task.projectDir === "string" ? task.projectDir.trim() : "";
|
|
1093
|
+
if (!projectDir)
|
|
1094
|
+
return null;
|
|
1095
|
+
const safeName = task.name.replace(/[^a-zA-Z0-9._-]+/g, "-");
|
|
1096
|
+
const safeSessionId = sessionId.replace(/[^a-zA-Z0-9._-]+/g, "-");
|
|
1097
|
+
return (0, node_path_1.join)(projectDir, ".playdrop", "validation-evidence", `staged-${safeName}-${safeSessionId}-${phase ?? "play"}.png`);
|
|
1098
|
+
}
|
|
966
1099
|
async function runUploadedHostedLoadCheck(input) {
|
|
967
1100
|
const preview = await input.client.fetchAppUploadLaunchCheckPreview(input.creatorUsername, input.task.name, input.sessionId);
|
|
968
|
-
const withRequestedPhase = (rawUrl) =>
|
|
969
|
-
|
|
970
|
-
if (input.phase === 'editor')
|
|
971
|
-
url.searchParams.set('hostPhase', 'editor');
|
|
972
|
-
return url.toString();
|
|
973
|
-
};
|
|
1101
|
+
const withRequestedPhase = (rawUrl) => buildUploadedHostedLoadCheckUrl(rawUrl, input.phase);
|
|
1102
|
+
const failureScreenshotPath = input.screenshotPath ?? buildUploadedHostedLoadCheckScreenshotPath(input.task, input.sessionId, input.phase);
|
|
974
1103
|
const captureSurface = resolveLoadCheckSurface(preview.localAppMetadata.surfaceTargets, input.task.primarySurface);
|
|
975
1104
|
const contextOptions = cloneLoadCheckContextOptions(captureSurface);
|
|
976
1105
|
const anonymousExpectedState = resolveAnonymousHostedLaunchState({
|
|
@@ -988,6 +1117,7 @@ async function runUploadedHostedLoadCheck(input) {
|
|
|
988
1117
|
expectedHostedLaunchState: resolvePostAuthHostedLaunchState(preview.localAppMetadata.controllerMode),
|
|
989
1118
|
contextOptions,
|
|
990
1119
|
screenshotPath: input.screenshotPath,
|
|
1120
|
+
failureScreenshotPath,
|
|
991
1121
|
readyGameFrameScreenshotPath: input.readyGameFrameScreenshotPath,
|
|
992
1122
|
gameFrameScreenshotPath: input.gameFrameScreenshotPath,
|
|
993
1123
|
playtestTape: input.playtestTape,
|
|
@@ -996,24 +1126,26 @@ async function runUploadedHostedLoadCheck(input) {
|
|
|
996
1126
|
verifyEditorPhaseRoundTrip: input.verifyEditorPhaseRoundTrip,
|
|
997
1127
|
});
|
|
998
1128
|
}
|
|
999
|
-
else if (anonymousExpectedState ===
|
|
1129
|
+
else if (anonymousExpectedState === "login_required") {
|
|
1000
1130
|
if (!preview.app?.id) {
|
|
1001
|
-
result = buildFailedLoadCheckResult(
|
|
1131
|
+
result = buildFailedLoadCheckResult("app_registration_required_for_auth_validation", `Auth-required hosted app "${input.task.name}" must be registered on PlayDrop before viewer load-check can run.`, preview.session.manifestHash);
|
|
1002
1132
|
}
|
|
1003
1133
|
else {
|
|
1004
1134
|
const anonymousGateResult = await runHostedLoadCheck({
|
|
1005
|
-
targetUrl: withRequestedPhase(appendLoadCheckParams(preview.launchUrl,
|
|
1135
|
+
targetUrl: withRequestedPhase(appendLoadCheckParams(preview.launchUrl, "anonymous")),
|
|
1006
1136
|
artifactFingerprint: preview.session.manifestHash,
|
|
1007
1137
|
timeoutMs: input.timeoutMs,
|
|
1008
1138
|
expectedHostedLaunchState: anonymousExpectedState,
|
|
1009
1139
|
contextOptions,
|
|
1140
|
+
screenshotPath: input.screenshotPath,
|
|
1141
|
+
failureScreenshotPath,
|
|
1010
1142
|
});
|
|
1011
|
-
if (anonymousGateResult.status !==
|
|
1143
|
+
if (anonymousGateResult.status !== "PASSED") {
|
|
1012
1144
|
result = anonymousGateResult;
|
|
1013
1145
|
}
|
|
1014
1146
|
else {
|
|
1015
1147
|
result = await runHostedLoadCheck({
|
|
1016
|
-
targetUrl: withRequestedPhase(appendLoadCheckParams(preview.launchUrl,
|
|
1148
|
+
targetUrl: withRequestedPhase(appendLoadCheckParams(preview.launchUrl, "viewer")),
|
|
1017
1149
|
artifactFingerprint: preview.session.manifestHash,
|
|
1018
1150
|
timeoutMs: input.timeoutMs,
|
|
1019
1151
|
expectedHostedLaunchState: resolvePostAuthHostedLaunchState(preview.localAppMetadata.controllerMode),
|
|
@@ -1022,6 +1154,7 @@ async function runUploadedHostedLoadCheck(input) {
|
|
|
1022
1154
|
savedSessionBootstrap: true,
|
|
1023
1155
|
contextOptions,
|
|
1024
1156
|
screenshotPath: input.screenshotPath,
|
|
1157
|
+
failureScreenshotPath,
|
|
1025
1158
|
readyGameFrameScreenshotPath: input.readyGameFrameScreenshotPath,
|
|
1026
1159
|
gameFrameScreenshotPath: input.gameFrameScreenshotPath,
|
|
1027
1160
|
playtestTape: input.playtestTape,
|
|
@@ -1034,12 +1167,13 @@ async function runUploadedHostedLoadCheck(input) {
|
|
|
1034
1167
|
}
|
|
1035
1168
|
else {
|
|
1036
1169
|
result = await runHostedLoadCheck({
|
|
1037
|
-
targetUrl: withRequestedPhase(appendLoadCheckParams(preview.launchUrl,
|
|
1170
|
+
targetUrl: withRequestedPhase(appendLoadCheckParams(preview.launchUrl, "anonymous")),
|
|
1038
1171
|
artifactFingerprint: preview.session.manifestHash,
|
|
1039
1172
|
timeoutMs: input.timeoutMs,
|
|
1040
1173
|
expectedHostedLaunchState: anonymousExpectedState,
|
|
1041
1174
|
contextOptions,
|
|
1042
1175
|
screenshotPath: input.screenshotPath,
|
|
1176
|
+
failureScreenshotPath,
|
|
1043
1177
|
readyGameFrameScreenshotPath: input.readyGameFrameScreenshotPath,
|
|
1044
1178
|
gameFrameScreenshotPath: input.gameFrameScreenshotPath,
|
|
1045
1179
|
playtestTape: input.playtestTape,
|
|
@@ -1055,8 +1189,8 @@ async function runUploadedHostedLoadCheck(input) {
|
|
|
1055
1189
|
message: result.message,
|
|
1056
1190
|
});
|
|
1057
1191
|
const recordedLaunchCheck = recorded.session.launchCheck;
|
|
1058
|
-
if (recordedLaunchCheck.status !==
|
|
1059
|
-
return buildFailedLoadCheckResult(
|
|
1192
|
+
if (recordedLaunchCheck.status !== "PASSED" && recordedLaunchCheck.status !== "FAILED") {
|
|
1193
|
+
return buildFailedLoadCheckResult("startup_failure", "Upload load-check was not recorded on the staged session.", preview.session.manifestHash);
|
|
1060
1194
|
}
|
|
1061
1195
|
return {
|
|
1062
1196
|
...result,
|
|
@@ -1068,22 +1202,20 @@ async function runUploadedHostedLoadCheck(input) {
|
|
|
1068
1202
|
};
|
|
1069
1203
|
}
|
|
1070
1204
|
async function hashFile(path) {
|
|
1071
|
-
return (0, node_crypto_1.createHash)(
|
|
1205
|
+
return (0, node_crypto_1.createHash)("sha256")
|
|
1206
|
+
.update(await (0, promises_1.readFile)(path))
|
|
1207
|
+
.digest("hex");
|
|
1072
1208
|
}
|
|
1073
1209
|
async function comparePlaytestFrames(firstPath, secondPath, thresholds = {
|
|
1074
1210
|
meanDeltaMin: PLAYTEST_INTERACTIVE_MEAN_DELTA_MIN,
|
|
1075
1211
|
changedPixelRatioMin: PLAYTEST_INTERACTIVE_CHANGED_PIXEL_RATIO_MIN,
|
|
1076
1212
|
}) {
|
|
1077
1213
|
const readPixels = async (path) => {
|
|
1078
|
-
return await (0, sharp_1.default)(path)
|
|
1079
|
-
.resize(64, 64, { fit: 'fill' })
|
|
1080
|
-
.greyscale()
|
|
1081
|
-
.raw()
|
|
1082
|
-
.toBuffer();
|
|
1214
|
+
return await (0, sharp_1.default)(path).resize(64, 64, { fit: "fill" }).greyscale().raw().toBuffer();
|
|
1083
1215
|
};
|
|
1084
1216
|
const [first, second] = await Promise.all([readPixels(firstPath), readPixels(secondPath)]);
|
|
1085
1217
|
if (first.length !== second.length || first.length === 0) {
|
|
1086
|
-
throw new Error(
|
|
1218
|
+
throw new Error("playtest_smoke_check_frame_shape_mismatch: idle and tape screenshots have different dimensions");
|
|
1087
1219
|
}
|
|
1088
1220
|
let deltaTotal = 0;
|
|
1089
1221
|
let changedPixels = 0;
|
|
@@ -1105,12 +1237,12 @@ async function runUploadedHostedPlaytestSmokeCheck(input) {
|
|
|
1105
1237
|
const primarySurface = input.task.primarySurface;
|
|
1106
1238
|
const fullTape = primarySurface ? input.task.playtestTapes?.[primarySurface] : null;
|
|
1107
1239
|
if (!primarySurface || !fullTape) {
|
|
1108
|
-
throw new Error(
|
|
1240
|
+
throw new Error("agent_task_playtest_smoke_check_primary_tape_required");
|
|
1109
1241
|
}
|
|
1110
|
-
const tempDir = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(),
|
|
1242
|
+
const tempDir = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "playdrop-playtest-smoke-"));
|
|
1111
1243
|
const paths = {
|
|
1112
|
-
zeroInput: (0, node_path_1.join)(tempDir,
|
|
1113
|
-
fullTape: (0, node_path_1.join)(tempDir,
|
|
1244
|
+
zeroInput: (0, node_path_1.join)(tempDir, "zero-input.png"),
|
|
1245
|
+
fullTape: (0, node_path_1.join)(tempDir, "full-tape.png"),
|
|
1114
1246
|
};
|
|
1115
1247
|
const run = async (label, options) => {
|
|
1116
1248
|
console.log(`[upload] Playtest smoke check: ${label} from sdk.host.ready().`);
|
|
@@ -1125,38 +1257,38 @@ async function runUploadedHostedPlaytestSmokeCheck(input) {
|
|
|
1125
1257
|
captureSession: input.captureSession,
|
|
1126
1258
|
...options,
|
|
1127
1259
|
});
|
|
1128
|
-
if (result.status !==
|
|
1129
|
-
throw new Error(`agent_task_playtest_smoke_check_runtime_failed: ${label} failed. `
|
|
1130
|
-
|
|
1260
|
+
if (result.status !== "PASSED") {
|
|
1261
|
+
throw new Error(`agent_task_playtest_smoke_check_runtime_failed: ${label} failed. ` +
|
|
1262
|
+
formatHostedLoadCheckFailure(input.task.name, result, "staged"));
|
|
1131
1263
|
}
|
|
1132
1264
|
if (!result.artifactFingerprint) {
|
|
1133
|
-
throw new Error(
|
|
1265
|
+
throw new Error("agent_task_playtest_smoke_check_artifact_fingerprint_missing");
|
|
1134
1266
|
}
|
|
1135
1267
|
return result;
|
|
1136
1268
|
};
|
|
1137
1269
|
try {
|
|
1138
|
-
const zeroInput = await run(
|
|
1270
|
+
const zeroInput = await run("zero-input", {
|
|
1139
1271
|
postReadyWaitMs: fullTape.durationMs,
|
|
1140
1272
|
gameFrameScreenshotPath: paths.zeroInput,
|
|
1141
1273
|
});
|
|
1142
|
-
const full = await run(
|
|
1274
|
+
const full = await run("full-tape", {
|
|
1143
1275
|
playtestTape: fullTape,
|
|
1144
1276
|
gameFrameScreenshotPath: paths.fullTape,
|
|
1145
1277
|
});
|
|
1146
1278
|
const artifactFingerprint = full.artifactFingerprint;
|
|
1147
1279
|
if (zeroInput.artifactFingerprint !== artifactFingerprint) {
|
|
1148
|
-
throw new Error(
|
|
1280
|
+
throw new Error("agent_task_playtest_smoke_check_artifact_changed_during_check");
|
|
1149
1281
|
}
|
|
1150
1282
|
const comparison = await comparePlaytestFrames(paths.zeroInput, paths.fullTape);
|
|
1151
1283
|
if (!comparison.passed) {
|
|
1152
|
-
throw new Error(`agent_task_playtest_smoke_check_not_interactive: The validator completed all ${fullTape.events.length} tape actions `
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1284
|
+
throw new Error(`agent_task_playtest_smoke_check_not_interactive: The validator completed all ${fullTape.events.length} tape actions ` +
|
|
1285
|
+
`without a crash, but the result looked the same as running the game for ${fullTape.durationMs} ms without input ` +
|
|
1286
|
+
`(mean pixel change ${comparison.meanDelta.toFixed(4)}, changed pixels ${(comparison.changedPixelRatio * 100).toFixed(2)}%). ` +
|
|
1287
|
+
"Make sure the tape performs the core gameplay action and produces a visible response, then rerun the tape check.");
|
|
1156
1288
|
}
|
|
1157
1289
|
const evidence = {
|
|
1158
1290
|
version: 2,
|
|
1159
|
-
source:
|
|
1291
|
+
source: "CLI_MACHINE",
|
|
1160
1292
|
artifactFingerprint,
|
|
1161
1293
|
primarySurface,
|
|
1162
1294
|
primaryVerb: fullTape.primaryVerb,
|
|
@@ -1167,8 +1299,8 @@ async function runUploadedHostedPlaytestSmokeCheck(input) {
|
|
|
1167
1299
|
comparison,
|
|
1168
1300
|
};
|
|
1169
1301
|
const recorded = await input.client.recordAppUploadCausalCheck(input.creatorUsername, input.task.name, input.sessionId, { evidence });
|
|
1170
|
-
if (recorded.session.causalCheck.status !==
|
|
1171
|
-
throw new Error(
|
|
1302
|
+
if (recorded.session.causalCheck.status !== "PASSED") {
|
|
1303
|
+
throw new Error("agent_task_playtest_smoke_check_not_recorded");
|
|
1172
1304
|
}
|
|
1173
1305
|
console.log(`[upload] Playtest smoke check passed for ${input.creatorUsername}/${input.task.name}.`);
|
|
1174
1306
|
return evidence;
|