@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/catalogue.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.APP_DESCRIPTION_RECOMMENDED_MIN_WORDS = exports.APP_DESCRIPTION_RECOMMENDED_MIN_CHARACTERS = void 0;
|
|
3
|
+
exports.APP_RELEASE_NOTES_MAX_CHARACTERS = exports.APP_DESCRIPTION_RECOMMENDED_MIN_WORDS = exports.APP_DESCRIPTION_RECOMMENDED_MIN_CHARACTERS = void 0;
|
|
4
|
+
exports.normalizeAppReleaseNotes = normalizeAppReleaseNotes;
|
|
4
5
|
exports.validateAppMetadata = validateAppMetadata;
|
|
5
6
|
exports.resolveCatalogueTagGroups = resolveCatalogueTagGroups;
|
|
6
7
|
exports.resolveCatalogueEntries = resolveCatalogueEntries;
|
|
@@ -15,19 +16,20 @@ const assetSpecs_1 = require("./assetSpecs");
|
|
|
15
16
|
const catalogue_utils_1 = require("./catalogue-utils");
|
|
16
17
|
const HEX_COLOR_REGEX = /^#?(?:[0-9a-fA-F]{6})$/;
|
|
17
18
|
const SEMVER_REGEX = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/;
|
|
18
|
-
const LEGACY_CATALOGUE_VERSION_KEY = [
|
|
19
|
+
const LEGACY_CATALOGUE_VERSION_KEY = ["schema", "Version"].join("");
|
|
19
20
|
const ASSET_CATEGORY_SET = new Set(types_1.ASSET_CATEGORY_VALUES);
|
|
20
|
-
const LISTING_IMAGE_EXTENSIONS = new Set([
|
|
21
|
-
const LISTING_VIDEO_EXTENSIONS = new Set([
|
|
21
|
+
const LISTING_IMAGE_EXTENSIONS = new Set([".png"]);
|
|
22
|
+
const LISTING_VIDEO_EXTENSIONS = new Set([".mp4"]);
|
|
22
23
|
exports.APP_DESCRIPTION_RECOMMENDED_MIN_CHARACTERS = 120;
|
|
23
24
|
exports.APP_DESCRIPTION_RECOMMENDED_MIN_WORDS = 20;
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
const
|
|
25
|
+
exports.APP_RELEASE_NOTES_MAX_CHARACTERS = 240;
|
|
26
|
+
const BOXEL_GENERATED_FILE_ROLES = new Set(["mesh", "preview"]);
|
|
27
|
+
const BOXEL_REQUIRED_FILE_ROLES = ["primary", "mesh", "preview"];
|
|
28
|
+
const VOX_GENERATED_FILE_ROLES = new Set(["boxel", "mesh", "preview"]);
|
|
29
|
+
const VOX_REQUIRED_FILE_ROLES = ["primary", "boxel", "mesh", "preview"];
|
|
28
30
|
const MAX_CONTENT_TAGS_PER_ITEM = 20;
|
|
29
|
-
const APP_ALLOWED_LICENSES = new Set([
|
|
30
|
-
const ASSET_ALLOWED_LICENSES = new Set([
|
|
31
|
+
const APP_ALLOWED_LICENSES = new Set(["CLOSED", "PLAYDROP", "MIT"]);
|
|
32
|
+
const ASSET_ALLOWED_LICENSES = new Set(["CLOSED", "PLAYDROP", "CC0"]);
|
|
31
33
|
const ASSET_SUBCATEGORY_SET_BY_CATEGORY = types_1.ASSET_SUBCATEGORY_DEFINITIONS.reduce((acc, definition) => {
|
|
32
34
|
if (!acc[definition.category]) {
|
|
33
35
|
acc[definition.category] = new Set();
|
|
@@ -35,28 +37,47 @@ const ASSET_SUBCATEGORY_SET_BY_CATEGORY = types_1.ASSET_SUBCATEGORY_DEFINITIONS.
|
|
|
35
37
|
acc[definition.category].add(definition.subcategory);
|
|
36
38
|
return acc;
|
|
37
39
|
}, {});
|
|
40
|
+
function normalizeAppReleaseNotes(value, options = {}) {
|
|
41
|
+
if (value === undefined) {
|
|
42
|
+
if (options.required) {
|
|
43
|
+
throw new Error("agent_task_release_notes_missing: catalogue releaseNotes must contain one line with 1..240 characters.");
|
|
44
|
+
}
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
const releaseNotes = value.trim();
|
|
48
|
+
if (!releaseNotes) {
|
|
49
|
+
throw new Error("agent_task_release_notes_missing: catalogue releaseNotes must contain one line with 1..240 characters.");
|
|
50
|
+
}
|
|
51
|
+
if (/[\r\n]/.test(releaseNotes)) {
|
|
52
|
+
throw new Error(`agent_task_release_notes_multiline: releaseNotes has ${releaseNotes.split(/\r\n|\r|\n/).length} lines; expected one line with 1..240 characters.`);
|
|
53
|
+
}
|
|
54
|
+
if (releaseNotes.length > exports.APP_RELEASE_NOTES_MAX_CHARACTERS) {
|
|
55
|
+
throw new Error(`agent_task_release_notes_too_long: releaseNotes length=${releaseNotes.length}; expected 1..240 characters on one line.`);
|
|
56
|
+
}
|
|
57
|
+
return releaseNotes;
|
|
58
|
+
}
|
|
38
59
|
function normalizeFileRole(role) {
|
|
39
60
|
return role.trim().toLowerCase();
|
|
40
61
|
}
|
|
41
62
|
function inferGeneratedModel3DFormat(category, subcategory, format, files) {
|
|
42
|
-
if (category !==
|
|
63
|
+
if (category !== "MODEL_3D" || !(0, types_1.isModel3DAssetSubcategory)("MODEL_3D", subcategory)) {
|
|
43
64
|
return null;
|
|
44
65
|
}
|
|
45
|
-
const normalizedFormat = typeof format ===
|
|
46
|
-
if (normalizedFormat ===
|
|
66
|
+
const normalizedFormat = typeof format === "string" ? format.trim().toUpperCase() : "";
|
|
67
|
+
if (normalizedFormat === "PLAYDROP_BOXEL_JSON" || normalizedFormat === "VOX") {
|
|
47
68
|
return normalizedFormat;
|
|
48
69
|
}
|
|
49
|
-
const primary = typeof files?.primary ===
|
|
50
|
-
if (primary.endsWith(
|
|
51
|
-
return
|
|
70
|
+
const primary = typeof files?.primary === "string" ? files.primary.trim().toLowerCase() : "";
|
|
71
|
+
if (primary.endsWith(".boxel.json")) {
|
|
72
|
+
return "PLAYDROP_BOXEL_JSON";
|
|
52
73
|
}
|
|
53
|
-
if (primary.endsWith(
|
|
54
|
-
return
|
|
74
|
+
if (primary.endsWith(".vox")) {
|
|
75
|
+
return "VOX";
|
|
55
76
|
}
|
|
56
77
|
return null;
|
|
57
78
|
}
|
|
58
79
|
function getGeneratedModel3DFileRoles(format) {
|
|
59
|
-
if (format ===
|
|
80
|
+
if (format === "VOX") {
|
|
60
81
|
return {
|
|
61
82
|
generated: VOX_GENERATED_FILE_ROLES,
|
|
62
83
|
required: VOX_REQUIRED_FILE_ROLES,
|
|
@@ -69,15 +90,15 @@ function getGeneratedModel3DFileRoles(format) {
|
|
|
69
90
|
}
|
|
70
91
|
function extractGlbJsonChunk(buffer) {
|
|
71
92
|
if (buffer.length < 20) {
|
|
72
|
-
throw new Error(
|
|
93
|
+
throw new Error("invalid_glb_payload");
|
|
73
94
|
}
|
|
74
|
-
if (buffer.toString(
|
|
75
|
-
throw new Error(
|
|
95
|
+
if (buffer.toString("utf8", 0, 4) !== "glTF") {
|
|
96
|
+
throw new Error("invalid_glb_payload");
|
|
76
97
|
}
|
|
77
98
|
const version = buffer.readUInt32LE(4);
|
|
78
99
|
const declaredLength = buffer.readUInt32LE(8);
|
|
79
100
|
if (version !== 2 || declaredLength !== buffer.length) {
|
|
80
|
-
throw new Error(
|
|
101
|
+
throw new Error("invalid_glb_payload");
|
|
81
102
|
}
|
|
82
103
|
let offset = 12;
|
|
83
104
|
while (offset + 8 <= buffer.length) {
|
|
@@ -85,45 +106,41 @@ function extractGlbJsonChunk(buffer) {
|
|
|
85
106
|
const chunkType = buffer.readUInt32LE(offset + 4);
|
|
86
107
|
offset += 8;
|
|
87
108
|
if (offset + chunkLength > buffer.length) {
|
|
88
|
-
throw new Error(
|
|
109
|
+
throw new Error("invalid_glb_payload");
|
|
89
110
|
}
|
|
90
111
|
const chunk = buffer.subarray(offset, offset + chunkLength);
|
|
91
112
|
offset += chunkLength;
|
|
92
|
-
if (chunkType !==
|
|
113
|
+
if (chunkType !== 0x4e4f534a) {
|
|
93
114
|
continue;
|
|
94
115
|
}
|
|
95
|
-
const payload = chunk.toString(
|
|
116
|
+
const payload = chunk.toString("utf8").replace(/\0+$/, "").trimEnd();
|
|
96
117
|
try {
|
|
97
118
|
const parsed = JSON.parse(payload);
|
|
98
|
-
if (!parsed || typeof parsed !==
|
|
99
|
-
throw new Error(
|
|
119
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
120
|
+
throw new Error("invalid_glb_payload");
|
|
100
121
|
}
|
|
101
122
|
return parsed;
|
|
102
123
|
}
|
|
103
124
|
catch {
|
|
104
|
-
throw new Error(
|
|
125
|
+
throw new Error("invalid_glb_payload");
|
|
105
126
|
}
|
|
106
127
|
}
|
|
107
|
-
throw new Error(
|
|
128
|
+
throw new Error("invalid_glb_payload");
|
|
108
129
|
}
|
|
109
130
|
function validateSelfContainedGlb(filePath) {
|
|
110
131
|
const gltf = extractGlbJsonChunk((0, node_fs_1.readFileSync)(filePath));
|
|
111
132
|
const buffers = Array.isArray(gltf.buffers) ? gltf.buffers : [];
|
|
112
133
|
for (const entry of buffers) {
|
|
113
|
-
const uri = entry && typeof entry ===
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
if (uri && !uri.startsWith('data:')) {
|
|
117
|
-
throw new Error('glb_external_dependency_unsupported');
|
|
134
|
+
const uri = entry && typeof entry === "object" && typeof entry.uri === "string" ? entry.uri.trim() : "";
|
|
135
|
+
if (uri && !uri.startsWith("data:")) {
|
|
136
|
+
throw new Error("glb_external_dependency_unsupported");
|
|
118
137
|
}
|
|
119
138
|
}
|
|
120
139
|
const images = Array.isArray(gltf.images) ? gltf.images : [];
|
|
121
140
|
for (const entry of images) {
|
|
122
|
-
const uri = entry && typeof entry ===
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
if (uri && !uri.startsWith('data:')) {
|
|
126
|
-
throw new Error('glb_external_dependency_unsupported');
|
|
141
|
+
const uri = entry && typeof entry === "object" && typeof entry.uri === "string" ? entry.uri.trim() : "";
|
|
142
|
+
if (uri && !uri.startsWith("data:")) {
|
|
143
|
+
throw new Error("glb_external_dependency_unsupported");
|
|
127
144
|
}
|
|
128
145
|
}
|
|
129
146
|
}
|
|
@@ -166,10 +183,10 @@ function validateListingAssetPath(baseDir, relativePath, fieldName) {
|
|
|
166
183
|
}
|
|
167
184
|
function validateListingAssetExtension(relativePath, fieldName, kind) {
|
|
168
185
|
const extension = (0, node_path_1.extname)(relativePath).toLowerCase();
|
|
169
|
-
if (kind ===
|
|
186
|
+
if (kind === "image" && !LISTING_IMAGE_EXTENSIONS.has(extension)) {
|
|
170
187
|
return `${fieldName} must use a .png file. Received "${relativePath}".`;
|
|
171
188
|
}
|
|
172
|
-
if (kind ===
|
|
189
|
+
if (kind === "video" && !LISTING_VIDEO_EXTENSIONS.has(extension)) {
|
|
173
190
|
return `${fieldName} must use a .mp4 file. Received "${relativePath}".`;
|
|
174
191
|
}
|
|
175
192
|
return undefined;
|
|
@@ -199,14 +216,8 @@ function resolveListingMediaEntries(input) {
|
|
|
199
216
|
for (let index = 0; index < input.value.length; index += 1) {
|
|
200
217
|
const value = input.value[index];
|
|
201
218
|
const fieldName = `${input.fieldName}[${index}]`;
|
|
202
|
-
const objectValue = value && typeof value ===
|
|
203
|
-
|
|
204
|
-
: null;
|
|
205
|
-
const relativePath = typeof value === 'string'
|
|
206
|
-
? value.trim()
|
|
207
|
-
: typeof objectValue?.path === 'string'
|
|
208
|
-
? objectValue.path.trim()
|
|
209
|
-
: '';
|
|
219
|
+
const objectValue = value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
220
|
+
const relativePath = typeof value === "string" ? value.trim() : typeof objectValue?.path === "string" ? objectValue.path.trim() : "";
|
|
210
221
|
if (!relativePath) {
|
|
211
222
|
return { paths, metadata, error: `${fieldName} must be a path string or an object with a non-empty path` };
|
|
212
223
|
}
|
|
@@ -222,22 +233,26 @@ function resolveListingMediaEntries(input) {
|
|
|
222
233
|
fileKey: `${input.fileKeyPrefix}:${index}`,
|
|
223
234
|
};
|
|
224
235
|
if (objectValue) {
|
|
225
|
-
for (const key of [
|
|
236
|
+
for (const key of ["title", "alt", "caption", "description"]) {
|
|
226
237
|
const raw = objectValue[key];
|
|
227
238
|
if (raw === undefined) {
|
|
228
239
|
continue;
|
|
229
240
|
}
|
|
230
|
-
if (typeof raw !==
|
|
241
|
+
if (typeof raw !== "string" || raw.trim().length === 0) {
|
|
231
242
|
return { paths, metadata, error: `${fieldName}.${key} must be a non-empty string when provided` };
|
|
232
243
|
}
|
|
233
244
|
const normalized = raw.trim();
|
|
234
245
|
if (normalized.length > LISTING_MEDIA_TEXT_LIMITS[key]) {
|
|
235
|
-
return {
|
|
246
|
+
return {
|
|
247
|
+
paths,
|
|
248
|
+
metadata,
|
|
249
|
+
error: `${fieldName}.${key} must be ${LISTING_MEDIA_TEXT_LIMITS[key]} characters or fewer`,
|
|
250
|
+
};
|
|
236
251
|
}
|
|
237
252
|
item[key] = normalized;
|
|
238
253
|
}
|
|
239
254
|
if (objectValue.slug !== undefined) {
|
|
240
|
-
if (typeof objectValue.slug !==
|
|
255
|
+
if (typeof objectValue.slug !== "string" || !LISTING_MEDIA_SLUG_REGEX.test(objectValue.slug.trim())) {
|
|
241
256
|
return { paths, metadata, error: `${fieldName}.slug must use lowercase words separated by hyphens` };
|
|
242
257
|
}
|
|
243
258
|
item.slug = objectValue.slug.trim();
|
|
@@ -252,7 +267,7 @@ function normalizeCatalogueRemixRef(value, expectedKind, errors, context) {
|
|
|
252
267
|
if (value === undefined || value === null) {
|
|
253
268
|
return undefined;
|
|
254
269
|
}
|
|
255
|
-
if (typeof value !==
|
|
270
|
+
if (typeof value !== "string") {
|
|
256
271
|
errors.push(`${context} remix must be a canonical ${expectedKind} version ref string.`);
|
|
257
272
|
return undefined;
|
|
258
273
|
}
|
|
@@ -278,15 +293,15 @@ function normalizeCatalogueRelations(value, errors, context) {
|
|
|
278
293
|
}
|
|
279
294
|
const rows = [];
|
|
280
295
|
for (const relation of value) {
|
|
281
|
-
if (!relation || typeof relation !==
|
|
296
|
+
if (!relation || typeof relation !== "object") {
|
|
282
297
|
continue;
|
|
283
298
|
}
|
|
284
|
-
const type = typeof relation.type ===
|
|
285
|
-
const to = typeof relation.to ===
|
|
299
|
+
const type = typeof relation.type === "string" ? relation.type.trim().toUpperCase() : "";
|
|
300
|
+
const to = typeof relation.to === "string" ? relation.to.trim() : "";
|
|
286
301
|
if (!type || !to) {
|
|
287
302
|
continue;
|
|
288
303
|
}
|
|
289
|
-
if (type ===
|
|
304
|
+
if (type === "REMIXES") {
|
|
290
305
|
errors.push(`${context} relations must not use REMIXES. Use the dedicated remix field instead.`);
|
|
291
306
|
continue;
|
|
292
307
|
}
|
|
@@ -303,17 +318,17 @@ function normalizeCatalogueAppDependencyAssets(value, errors, context) {
|
|
|
303
318
|
const seenRuntimeKeys = new Set();
|
|
304
319
|
for (let index = 0; index < value.length; index += 1) {
|
|
305
320
|
const entry = value[index];
|
|
306
|
-
const ref = typeof entry ===
|
|
321
|
+
const ref = typeof entry === "string"
|
|
307
322
|
? entry.trim()
|
|
308
|
-
: entry && typeof entry ===
|
|
323
|
+
: entry && typeof entry === "object" && !Array.isArray(entry) && typeof entry.ref === "string"
|
|
309
324
|
? entry.ref.trim()
|
|
310
|
-
:
|
|
325
|
+
: "";
|
|
311
326
|
if (!ref) {
|
|
312
327
|
errors.push(`${context} uses.assets[${index}] must be a non-empty asset ref string or { ref, runtimeKey } object.`);
|
|
313
328
|
continue;
|
|
314
329
|
}
|
|
315
330
|
const parsed = (0, types_1.parseContentVersionRef)(ref);
|
|
316
|
-
if (!parsed || parsed.kind !==
|
|
331
|
+
if (!parsed || parsed.kind !== "asset") {
|
|
317
332
|
errors.push(`${context} uses.assets[${index}] must be a canonical asset version ref.`);
|
|
318
333
|
continue;
|
|
319
334
|
}
|
|
@@ -322,9 +337,9 @@ function normalizeCatalogueAppDependencyAssets(value, errors, context) {
|
|
|
322
337
|
errors.push(`${context} uses.assets contains duplicate ref "${normalizedRef}".`);
|
|
323
338
|
continue;
|
|
324
339
|
}
|
|
325
|
-
const runtimeKey = entry && typeof entry ===
|
|
340
|
+
const runtimeKey = entry && typeof entry === "object" && !Array.isArray(entry) && typeof entry.runtimeKey === "string"
|
|
326
341
|
? entry.runtimeKey.trim()
|
|
327
|
-
:
|
|
342
|
+
: "";
|
|
328
343
|
if (runtimeKey) {
|
|
329
344
|
if (seenRuntimeKeys.has(runtimeKey)) {
|
|
330
345
|
errors.push(`${context} uses.assets contains duplicate runtimeKey "${runtimeKey}".`);
|
|
@@ -352,12 +367,12 @@ function normalizeCatalogueAppDependencyPacks(value, errors, context) {
|
|
|
352
367
|
const seenRefs = new Set();
|
|
353
368
|
for (let index = 0; index < value.length; index += 1) {
|
|
354
369
|
const entry = value[index];
|
|
355
|
-
if (typeof entry !==
|
|
370
|
+
if (typeof entry !== "string" || entry.trim().length === 0) {
|
|
356
371
|
errors.push(`${context} uses.packs[${index}] must be a non-empty pack version ref string. Use "pack:playdrop/name@version", not { ref, runtimeKey }.`);
|
|
357
372
|
continue;
|
|
358
373
|
}
|
|
359
374
|
const parsed = (0, types_1.parseContentVersionRef)(entry.trim());
|
|
360
|
-
if (!parsed || parsed.kind !==
|
|
375
|
+
if (!parsed || parsed.kind !== "pack") {
|
|
361
376
|
errors.push(`${context} uses.packs[${index}] must be a canonical pack version ref.`);
|
|
362
377
|
continue;
|
|
363
378
|
}
|
|
@@ -372,7 +387,7 @@ function normalizeCatalogueAppDependencyPacks(value, errors, context) {
|
|
|
372
387
|
return rows;
|
|
373
388
|
}
|
|
374
389
|
function rejectLegacyShopPriceCoinsField(value, errors, context) {
|
|
375
|
-
if (Object.prototype.hasOwnProperty.call(value,
|
|
390
|
+
if (Object.prototype.hasOwnProperty.call(value, "shopPriceCoins")) {
|
|
376
391
|
errors.push(`${context} uses legacy shopPriceCoins. Rename it to shopPriceCredits.`);
|
|
377
392
|
return true;
|
|
378
393
|
}
|
|
@@ -390,11 +405,11 @@ function normalizeCatalogueTags(value, errors, context) {
|
|
|
390
405
|
const seenRefs = new Set();
|
|
391
406
|
for (let index = 0; index < value.length; index += 1) {
|
|
392
407
|
const entry = value[index];
|
|
393
|
-
if (typeof entry !==
|
|
408
|
+
if (typeof entry !== "string" || entry.trim().length === 0) {
|
|
394
409
|
errors.push(`${context} tags[${index}] must be a non-empty canonical tag ref string.`);
|
|
395
410
|
continue;
|
|
396
411
|
}
|
|
397
|
-
let normalizedRef =
|
|
412
|
+
let normalizedRef = "";
|
|
398
413
|
try {
|
|
399
414
|
normalizedRef = (0, types_1.normalizeTagRef)(entry);
|
|
400
415
|
}
|
|
@@ -426,9 +441,9 @@ function normalizeCatalogueAllowedTargetKinds(value, errors, context) {
|
|
|
426
441
|
const seenKinds = new Set();
|
|
427
442
|
for (let index = 0; index < value.length; index += 1) {
|
|
428
443
|
const entry = value[index];
|
|
429
|
-
const normalizedKind = typeof entry ===
|
|
444
|
+
const normalizedKind = typeof entry === "string" ? entry.trim().toUpperCase() : "";
|
|
430
445
|
if (!types_1.SAVED_ITEM_KIND_VALUES.includes(normalizedKind)) {
|
|
431
|
-
errors.push(`${context} allowedTargetKinds[${index}] must be one of ${types_1.SAVED_ITEM_KIND_VALUES.join(
|
|
446
|
+
errors.push(`${context} allowedTargetKinds[${index}] must be one of ${types_1.SAVED_ITEM_KIND_VALUES.join(", ")}.`);
|
|
432
447
|
continue;
|
|
433
448
|
}
|
|
434
449
|
const savedItemKind = normalizedKind;
|
|
@@ -442,8 +457,8 @@ function normalizeCatalogueAllowedTargetKinds(value, errors, context) {
|
|
|
442
457
|
return normalizedKinds;
|
|
443
458
|
}
|
|
444
459
|
function normalizeRelativePath(root, target) {
|
|
445
|
-
const rel = (0, node_path_1.relative)(root, target).split(node_path_1.sep).join(
|
|
446
|
-
return rel.replace(/^\/+/,
|
|
460
|
+
const rel = (0, node_path_1.relative)(root, target).split(node_path_1.sep).join("/");
|
|
461
|
+
return rel.replace(/^\/+/, "");
|
|
447
462
|
}
|
|
448
463
|
function tryRealpath(path) {
|
|
449
464
|
try {
|
|
@@ -455,34 +470,34 @@ function tryRealpath(path) {
|
|
|
455
470
|
}
|
|
456
471
|
function normalizePathForComparison(path) {
|
|
457
472
|
const actual = tryRealpath(path) ?? (0, node_path_1.resolve)(path);
|
|
458
|
-
return actual.split(node_path_1.sep).join(
|
|
473
|
+
return actual.split(node_path_1.sep).join("/");
|
|
459
474
|
}
|
|
460
475
|
function shouldSkipDirectory(name) {
|
|
461
476
|
if (!name)
|
|
462
477
|
return true;
|
|
463
|
-
if (name ===
|
|
478
|
+
if (name === "." || name === "..")
|
|
464
479
|
return true;
|
|
465
|
-
if (name.startsWith(
|
|
480
|
+
if (name.startsWith("."))
|
|
466
481
|
return true;
|
|
467
|
-
return name ===
|
|
482
|
+
return name === "node_modules";
|
|
468
483
|
}
|
|
469
484
|
function readCatalogueFile(rootDir, filePath) {
|
|
470
485
|
try {
|
|
471
|
-
const raw = (0, node_fs_1.readFileSync)(filePath,
|
|
486
|
+
const raw = (0, node_fs_1.readFileSync)(filePath, "utf8");
|
|
472
487
|
const parsed = JSON.parse(raw);
|
|
473
|
-
if (!parsed || typeof parsed !==
|
|
474
|
-
return { error:
|
|
488
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
489
|
+
return { error: "catalogue.json must contain a JSON object" };
|
|
475
490
|
}
|
|
476
491
|
if (Object.prototype.hasOwnProperty.call(parsed, LEGACY_CATALOGUE_VERSION_KEY)) {
|
|
477
|
-
return { error:
|
|
492
|
+
return { error: "catalogue.json must not define the legacy top-level version field. Remove it and retry." };
|
|
478
493
|
}
|
|
479
|
-
const forbiddenKeys = [
|
|
494
|
+
const forbiddenKeys = ["avatars", "humanoids", "creatures", "blocks", "games"];
|
|
480
495
|
for (const key of forbiddenKeys) {
|
|
481
496
|
if (Object.prototype.hasOwnProperty.call(parsed, key)) {
|
|
482
497
|
return { error: `catalogue.json forbids top-level "${key}"` };
|
|
483
498
|
}
|
|
484
499
|
}
|
|
485
|
-
const allowedKeys = new Set([
|
|
500
|
+
const allowedKeys = new Set(["apps", "tagGroups", "assetSpecs", "assets", "assetPacks"]);
|
|
486
501
|
const unknownKeys = Object.keys(parsed).filter((key) => !allowedKeys.has(key));
|
|
487
502
|
if (unknownKeys.length > 0) {
|
|
488
503
|
return {
|
|
@@ -490,18 +505,18 @@ function readCatalogueFile(rootDir, filePath) {
|
|
|
490
505
|
};
|
|
491
506
|
}
|
|
492
507
|
const directory = (0, node_path_1.dirname)(filePath);
|
|
493
|
-
const relativePath = (0, node_path_1.relative)(rootDir, filePath) ||
|
|
508
|
+
const relativePath = (0, node_path_1.relative)(rootDir, filePath) || "catalogue.json";
|
|
494
509
|
return {
|
|
495
510
|
file: {
|
|
496
511
|
absolutePath: filePath,
|
|
497
|
-
relativePath: relativePath.replace(/\\/g,
|
|
512
|
+
relativePath: relativePath.replace(/\\/g, "/"),
|
|
498
513
|
directory,
|
|
499
514
|
data: parsed,
|
|
500
515
|
},
|
|
501
516
|
};
|
|
502
517
|
}
|
|
503
518
|
catch (error) {
|
|
504
|
-
return { error: `Invalid catalogue.json (${error?.message ||
|
|
519
|
+
return { error: `Invalid catalogue.json (${error?.message || "failed to parse JSON"})` };
|
|
505
520
|
}
|
|
506
521
|
}
|
|
507
522
|
function walkCatalogueTree(rootDir) {
|
|
@@ -521,11 +536,11 @@ function walkCatalogueTree(rootDir) {
|
|
|
521
536
|
if (visited.has(realPath))
|
|
522
537
|
continue;
|
|
523
538
|
visited.add(realPath);
|
|
524
|
-
const candidate = (0, node_path_1.join)(displayPath,
|
|
539
|
+
const candidate = (0, node_path_1.join)(displayPath, "catalogue.json");
|
|
525
540
|
if ((0, node_fs_1.existsSync)(candidate) && (0, node_fs_1.statSync)(candidate).isFile()) {
|
|
526
541
|
const result = readCatalogueFile(rootDir, candidate);
|
|
527
542
|
if (result.error) {
|
|
528
|
-
errors.push(`[${normalizeRelativePath(rootDir, candidate) ||
|
|
543
|
+
errors.push(`[${normalizeRelativePath(rootDir, candidate) || "catalogue.json"}] ${result.error}`);
|
|
529
544
|
}
|
|
530
545
|
else if (result.file) {
|
|
531
546
|
files.push(result.file);
|
|
@@ -565,7 +580,7 @@ function isCatalogueMarker(file) {
|
|
|
565
580
|
}
|
|
566
581
|
const packageJsonCache = new Map();
|
|
567
582
|
function resolvePackageJson(projectDir) {
|
|
568
|
-
const candidate = (0, node_path_1.join)(projectDir,
|
|
583
|
+
const candidate = (0, node_path_1.join)(projectDir, "package.json");
|
|
569
584
|
if (!(0, node_fs_1.existsSync)(candidate)) {
|
|
570
585
|
return null;
|
|
571
586
|
}
|
|
@@ -582,44 +597,44 @@ function readPackageJsonManifest(packageJsonPath) {
|
|
|
582
597
|
return packageJsonCache.get(packageJsonPath);
|
|
583
598
|
}
|
|
584
599
|
try {
|
|
585
|
-
const raw = (0, node_fs_1.readFileSync)(packageJsonPath,
|
|
600
|
+
const raw = (0, node_fs_1.readFileSync)(packageJsonPath, "utf8");
|
|
586
601
|
const parsed = raw.trim() ? JSON.parse(raw) : {};
|
|
587
602
|
packageJsonCache.set(packageJsonPath, parsed);
|
|
588
603
|
return parsed;
|
|
589
604
|
}
|
|
590
605
|
catch (error) {
|
|
591
|
-
throw new Error(error?.message ||
|
|
606
|
+
throw new Error(error?.message || "failed to parse package.json");
|
|
592
607
|
}
|
|
593
608
|
}
|
|
594
609
|
function collectMissingMetadata(entry) {
|
|
595
610
|
const missing = [];
|
|
596
|
-
if (typeof entry.displayName !==
|
|
597
|
-
missing.push(
|
|
611
|
+
if (typeof entry.displayName !== "string" || !entry.displayName.trim()) {
|
|
612
|
+
missing.push("displayName");
|
|
598
613
|
}
|
|
599
|
-
if (typeof entry.description !==
|
|
600
|
-
missing.push(
|
|
614
|
+
if (typeof entry.description !== "string" || !entry.description.trim()) {
|
|
615
|
+
missing.push("description");
|
|
601
616
|
}
|
|
602
|
-
const emojiProvided = Object.prototype.hasOwnProperty.call(entry,
|
|
617
|
+
const emojiProvided = Object.prototype.hasOwnProperty.call(entry, "emoji");
|
|
603
618
|
if (!emojiProvided) {
|
|
604
|
-
missing.push(
|
|
619
|
+
missing.push("emoji");
|
|
605
620
|
}
|
|
606
621
|
else {
|
|
607
622
|
const emojiValue = entry.emoji;
|
|
608
623
|
if (emojiValue !== null) {
|
|
609
|
-
if (typeof emojiValue !==
|
|
610
|
-
missing.push(
|
|
624
|
+
if (typeof emojiValue !== "string" || !emojiValue.trim()) {
|
|
625
|
+
missing.push("emoji");
|
|
611
626
|
}
|
|
612
627
|
}
|
|
613
628
|
}
|
|
614
|
-
const colorProvided = Object.prototype.hasOwnProperty.call(entry,
|
|
629
|
+
const colorProvided = Object.prototype.hasOwnProperty.call(entry, "color");
|
|
615
630
|
if (!colorProvided) {
|
|
616
|
-
missing.push(
|
|
631
|
+
missing.push("color");
|
|
617
632
|
}
|
|
618
633
|
else {
|
|
619
634
|
const colorValue = entry.color;
|
|
620
635
|
if (colorValue !== null) {
|
|
621
|
-
if (typeof colorValue !==
|
|
622
|
-
missing.push(
|
|
636
|
+
if (typeof colorValue !== "string" || !colorValue.trim()) {
|
|
637
|
+
missing.push("color");
|
|
623
638
|
}
|
|
624
639
|
}
|
|
625
640
|
}
|
|
@@ -627,45 +642,45 @@ function collectMissingMetadata(entry) {
|
|
|
627
642
|
}
|
|
628
643
|
function formatMissingMetadata(missing) {
|
|
629
644
|
if (missing.length === 0)
|
|
630
|
-
return
|
|
645
|
+
return "";
|
|
631
646
|
if (missing.length === 1)
|
|
632
647
|
return `no ${missing[0]} provided`;
|
|
633
648
|
if (missing.length === 2)
|
|
634
649
|
return `no ${missing[0]} or ${missing[1]} provided`;
|
|
635
650
|
const last = missing[missing.length - 1];
|
|
636
|
-
const initial = missing.slice(0, -1).join(
|
|
651
|
+
const initial = missing.slice(0, -1).join(", ");
|
|
637
652
|
return `no ${initial}, or ${last} provided`;
|
|
638
653
|
}
|
|
639
654
|
function normalizeAssetVisibilityValue(raw, errors, label) {
|
|
640
|
-
if (typeof raw !==
|
|
655
|
+
if (typeof raw !== "string" || raw.trim().length === 0) {
|
|
641
656
|
return undefined;
|
|
642
657
|
}
|
|
643
658
|
const normalized = raw.trim().toUpperCase();
|
|
644
|
-
if (normalized !==
|
|
659
|
+
if (normalized !== "PUBLIC" && normalized !== "PRIVATE") {
|
|
645
660
|
errors.push(`[${label}] visibility must be PUBLIC or PRIVATE.`);
|
|
646
661
|
return undefined;
|
|
647
662
|
}
|
|
648
663
|
return normalized;
|
|
649
664
|
}
|
|
650
665
|
function normalizeContentLicenseValue(raw, errors, label, allowedValues, defaultValue = types_1.DEFAULT_CONTENT_LICENSE) {
|
|
651
|
-
if (raw === undefined || raw === null || (typeof raw ===
|
|
666
|
+
if (raw === undefined || raw === null || (typeof raw === "string" && raw.trim().length === 0)) {
|
|
652
667
|
return defaultValue;
|
|
653
668
|
}
|
|
654
669
|
const parsed = (0, types_1.parseContentLicense)(raw);
|
|
655
670
|
if (!parsed || !allowedValues.has(parsed)) {
|
|
656
|
-
errors.push(`[${label}] license must be one of ${Array.from(allowedValues).join(
|
|
671
|
+
errors.push(`[${label}] license must be one of ${Array.from(allowedValues).join(", ")}.`);
|
|
657
672
|
return null;
|
|
658
673
|
}
|
|
659
674
|
return parsed;
|
|
660
675
|
}
|
|
661
676
|
function normalizeInheritedContentLicenseValue(raw, inheritedValue, errors, label, allowedValues) {
|
|
662
|
-
if (raw === undefined || raw === null || (typeof raw ===
|
|
677
|
+
if (raw === undefined || raw === null || (typeof raw === "string" && raw.trim().length === 0)) {
|
|
663
678
|
return inheritedValue;
|
|
664
679
|
}
|
|
665
680
|
return normalizeContentLicenseValue(raw, errors, label, allowedValues);
|
|
666
681
|
}
|
|
667
682
|
function normalizeAssetCategoryValue(raw, errors, label) {
|
|
668
|
-
if (typeof raw !==
|
|
683
|
+
if (typeof raw !== "string" || raw.trim().length === 0) {
|
|
669
684
|
errors.push(`${label} must define category.`);
|
|
670
685
|
return null;
|
|
671
686
|
}
|
|
@@ -677,17 +692,17 @@ function normalizeAssetCategoryValue(raw, errors, label) {
|
|
|
677
692
|
return normalized;
|
|
678
693
|
}
|
|
679
694
|
function normalizeAssetSubcategoryValue(raw, category, errors, label) {
|
|
680
|
-
if (category ===
|
|
695
|
+
if (category === "CUSTOM") {
|
|
681
696
|
if (raw === undefined || raw === null) {
|
|
682
697
|
return null;
|
|
683
698
|
}
|
|
684
|
-
if (typeof raw ===
|
|
699
|
+
if (typeof raw === "string" && raw.trim().length === 0) {
|
|
685
700
|
return null;
|
|
686
701
|
}
|
|
687
702
|
errors.push(`${label} must not define subcategory when category is CUSTOM.`);
|
|
688
703
|
return null;
|
|
689
704
|
}
|
|
690
|
-
if (typeof raw !==
|
|
705
|
+
if (typeof raw !== "string" || raw.trim().length === 0) {
|
|
691
706
|
errors.push(`${label} must define subcategory.`);
|
|
692
707
|
return null;
|
|
693
708
|
}
|
|
@@ -708,19 +723,19 @@ function validateOptionalEmoji(value, hasEmojiField, errors) {
|
|
|
708
723
|
if (!hasEmojiField) {
|
|
709
724
|
return emoji;
|
|
710
725
|
}
|
|
711
|
-
if (emoji !== null && typeof emoji ===
|
|
726
|
+
if (emoji !== null && typeof emoji === "string") {
|
|
712
727
|
const trimmed = emoji.trim();
|
|
713
728
|
if (!trimmed) {
|
|
714
729
|
return null;
|
|
715
730
|
}
|
|
716
731
|
if (Array.from(trimmed).length > 4) {
|
|
717
|
-
errors.push(
|
|
732
|
+
errors.push("emoji must be a single character");
|
|
718
733
|
return emoji;
|
|
719
734
|
}
|
|
720
735
|
return trimmed;
|
|
721
736
|
}
|
|
722
737
|
if (emoji !== null) {
|
|
723
|
-
errors.push(
|
|
738
|
+
errors.push("emoji must be a string or null");
|
|
724
739
|
}
|
|
725
740
|
return emoji;
|
|
726
741
|
}
|
|
@@ -729,19 +744,19 @@ function validateOptionalColor(value, hasColorField, errors) {
|
|
|
729
744
|
if (!hasColorField) {
|
|
730
745
|
return color;
|
|
731
746
|
}
|
|
732
|
-
if (color !== null && typeof color ===
|
|
747
|
+
if (color !== null && typeof color === "string") {
|
|
733
748
|
const trimmed = color.trim();
|
|
734
749
|
if (!trimmed) {
|
|
735
750
|
return null;
|
|
736
751
|
}
|
|
737
752
|
if (!HEX_COLOR_REGEX.test(trimmed)) {
|
|
738
|
-
errors.push(
|
|
753
|
+
errors.push("color must be a hex RGB value like #FFAA00");
|
|
739
754
|
return color;
|
|
740
755
|
}
|
|
741
|
-
return trimmed.startsWith(
|
|
756
|
+
return trimmed.startsWith("#") ? trimmed.toUpperCase() : `#${trimmed.toUpperCase()}`;
|
|
742
757
|
}
|
|
743
758
|
if (color !== null) {
|
|
744
|
-
errors.push(
|
|
759
|
+
errors.push("color must be a string or null");
|
|
745
760
|
}
|
|
746
761
|
return color;
|
|
747
762
|
}
|
|
@@ -750,17 +765,17 @@ function validateAchievementDefinitions(value, catalogueDir, errors) {
|
|
|
750
765
|
return [];
|
|
751
766
|
}
|
|
752
767
|
if (!Array.isArray(value)) {
|
|
753
|
-
errors.push(
|
|
768
|
+
errors.push("achievements must be an array");
|
|
754
769
|
return [];
|
|
755
770
|
}
|
|
756
771
|
const seenKeys = new Set();
|
|
757
772
|
const definitions = [];
|
|
758
773
|
value.forEach((entry, index) => {
|
|
759
|
-
if (!entry || typeof entry !==
|
|
774
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
760
775
|
errors.push(`achievements[${index}] must be an object`);
|
|
761
776
|
return;
|
|
762
777
|
}
|
|
763
|
-
const key = typeof entry.key ===
|
|
778
|
+
const key = typeof entry.key === "string" ? entry.key.trim() : "";
|
|
764
779
|
if (!(0, types_1.isPlayerMetaKey)(key)) {
|
|
765
780
|
errors.push(`achievements[${index}].key must match the player meta key format`);
|
|
766
781
|
return;
|
|
@@ -770,33 +785,33 @@ function validateAchievementDefinitions(value, catalogueDir, errors) {
|
|
|
770
785
|
return;
|
|
771
786
|
}
|
|
772
787
|
seenKeys.add(key);
|
|
773
|
-
const displayName = typeof entry.displayName ===
|
|
788
|
+
const displayName = typeof entry.displayName === "string" ? entry.displayName.trim() : "";
|
|
774
789
|
if (!displayName) {
|
|
775
790
|
errors.push(`achievements[${index}].displayName must be a non-empty string`);
|
|
776
791
|
return;
|
|
777
792
|
}
|
|
778
|
-
const description = typeof entry.description ===
|
|
793
|
+
const description = typeof entry.description === "string" ? entry.description.trim() : "";
|
|
779
794
|
if (!description) {
|
|
780
795
|
errors.push(`achievements[${index}].description must be a non-empty string`);
|
|
781
796
|
return;
|
|
782
797
|
}
|
|
783
798
|
const kind = (0, types_1.parseAppAchievementKind)(entry.kind);
|
|
784
799
|
if (!kind) {
|
|
785
|
-
errors.push(`achievements[${index}].kind must be one of ${types_1.APP_ACHIEVEMENT_KIND_VALUES.join(
|
|
800
|
+
errors.push(`achievements[${index}].kind must be one of ${types_1.APP_ACHIEVEMENT_KIND_VALUES.join(", ")}`);
|
|
786
801
|
return;
|
|
787
802
|
}
|
|
788
803
|
const hidden = Boolean(entry.hidden);
|
|
789
|
-
const status = (0, types_1.parsePlayerMetaDefinitionStatus)(entry.status ??
|
|
804
|
+
const status = (0, types_1.parsePlayerMetaDefinitionStatus)(entry.status ?? "ACTIVE");
|
|
790
805
|
if (!status) {
|
|
791
|
-
errors.push(`achievements[${index}].status must be one of ${types_1.PLAYER_META_DEFINITION_STATUS_VALUES.join(
|
|
806
|
+
errors.push(`achievements[${index}].status must be one of ${types_1.PLAYER_META_DEFINITION_STATUS_VALUES.join(", ")}`);
|
|
792
807
|
return;
|
|
793
808
|
}
|
|
794
|
-
const icon = typeof entry.icon ===
|
|
809
|
+
const icon = typeof entry.icon === "string" ? entry.icon.trim() : "";
|
|
795
810
|
if (!icon) {
|
|
796
811
|
errors.push(`achievements[${index}].icon must be a non-empty PNG path`);
|
|
797
812
|
return;
|
|
798
813
|
}
|
|
799
|
-
const iconExtensionError = validateListingAssetExtension(icon, `achievements[${index}].icon`,
|
|
814
|
+
const iconExtensionError = validateListingAssetExtension(icon, `achievements[${index}].icon`, "image");
|
|
800
815
|
if (iconExtensionError) {
|
|
801
816
|
errors.push(iconExtensionError);
|
|
802
817
|
return;
|
|
@@ -808,7 +823,7 @@ function validateAchievementDefinitions(value, catalogueDir, errors) {
|
|
|
808
823
|
}
|
|
809
824
|
const iconPath = resolveListingAssetPath(catalogueDir, icon);
|
|
810
825
|
let maxProgress;
|
|
811
|
-
if (kind ===
|
|
826
|
+
if (kind === "INCREMENTAL") {
|
|
812
827
|
if (!Number.isSafeInteger(entry.maxProgress) || Number(entry.maxProgress) <= 0) {
|
|
813
828
|
errors.push(`achievements[${index}].maxProgress must be a positive safe integer`);
|
|
814
829
|
return;
|
|
@@ -838,17 +853,17 @@ function validateLeaderboardDefinitions(value, errors) {
|
|
|
838
853
|
return [];
|
|
839
854
|
}
|
|
840
855
|
if (!Array.isArray(value)) {
|
|
841
|
-
errors.push(
|
|
856
|
+
errors.push("leaderboards must be an array");
|
|
842
857
|
return [];
|
|
843
858
|
}
|
|
844
859
|
const seenKeys = new Set();
|
|
845
860
|
const definitions = [];
|
|
846
861
|
value.forEach((entry, index) => {
|
|
847
|
-
if (!entry || typeof entry !==
|
|
862
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
848
863
|
errors.push(`leaderboards[${index}] must be an object`);
|
|
849
864
|
return;
|
|
850
865
|
}
|
|
851
|
-
const key = typeof entry.key ===
|
|
866
|
+
const key = typeof entry.key === "string" ? entry.key.trim() : "";
|
|
852
867
|
if (!(0, types_1.isPlayerMetaKey)(key)) {
|
|
853
868
|
errors.push(`leaderboards[${index}].key must match the player meta key format`);
|
|
854
869
|
return;
|
|
@@ -858,36 +873,36 @@ function validateLeaderboardDefinitions(value, errors) {
|
|
|
858
873
|
return;
|
|
859
874
|
}
|
|
860
875
|
seenKeys.add(key);
|
|
861
|
-
const displayName = typeof entry.displayName ===
|
|
876
|
+
const displayName = typeof entry.displayName === "string" ? entry.displayName.trim() : "";
|
|
862
877
|
if (!displayName) {
|
|
863
878
|
errors.push(`leaderboards[${index}].displayName must be a non-empty string`);
|
|
864
879
|
return;
|
|
865
880
|
}
|
|
866
|
-
const description = typeof entry.description ===
|
|
881
|
+
const description = typeof entry.description === "string" ? entry.description.trim() : "";
|
|
867
882
|
if (!description) {
|
|
868
883
|
errors.push(`leaderboards[${index}].description must be a non-empty string`);
|
|
869
884
|
return;
|
|
870
885
|
}
|
|
871
886
|
const scoreType = (0, types_1.parseAppLeaderboardScoreType)(entry.scoreType);
|
|
872
887
|
if (!scoreType) {
|
|
873
|
-
errors.push(`leaderboards[${index}].scoreType must be one of ${types_1.APP_LEADERBOARD_SCORE_TYPE_VALUES.join(
|
|
888
|
+
errors.push(`leaderboards[${index}].scoreType must be one of ${types_1.APP_LEADERBOARD_SCORE_TYPE_VALUES.join(", ")}`);
|
|
874
889
|
return;
|
|
875
890
|
}
|
|
876
891
|
const sort = (0, types_1.parseAppLeaderboardSort)(entry.sort);
|
|
877
892
|
if (!sort) {
|
|
878
|
-
errors.push(`leaderboards[${index}].sort must be one of ${types_1.APP_LEADERBOARD_SORT_VALUES.join(
|
|
893
|
+
errors.push(`leaderboards[${index}].sort must be one of ${types_1.APP_LEADERBOARD_SORT_VALUES.join(", ")}`);
|
|
879
894
|
return;
|
|
880
895
|
}
|
|
881
896
|
if (!(0, types_1.isValidLeaderboardDefinitionCombination)(scoreType, sort)) {
|
|
882
897
|
errors.push(`leaderboards[${index}] must use INTEGER + DESC or TIME_MS + ASC`);
|
|
883
898
|
return;
|
|
884
899
|
}
|
|
885
|
-
const status = (0, types_1.parsePlayerMetaDefinitionStatus)(entry.status ??
|
|
900
|
+
const status = (0, types_1.parsePlayerMetaDefinitionStatus)(entry.status ?? "ACTIVE");
|
|
886
901
|
if (!status) {
|
|
887
|
-
errors.push(`leaderboards[${index}].status must be one of ${types_1.PLAYER_META_DEFINITION_STATUS_VALUES.join(
|
|
902
|
+
errors.push(`leaderboards[${index}].status must be one of ${types_1.PLAYER_META_DEFINITION_STATUS_VALUES.join(", ")}`);
|
|
888
903
|
return;
|
|
889
904
|
}
|
|
890
|
-
const unitLabel = typeof entry.unitLabel ===
|
|
905
|
+
const unitLabel = typeof entry.unitLabel === "string" && entry.unitLabel.trim().length > 0
|
|
891
906
|
? entry.unitLabel.trim()
|
|
892
907
|
: undefined;
|
|
893
908
|
definitions.push({
|
|
@@ -909,26 +924,26 @@ function validateAppMetadata(entry) {
|
|
|
909
924
|
if (missingFields.length > 0) {
|
|
910
925
|
warnings.push(formatMissingMetadata(missingFields));
|
|
911
926
|
}
|
|
912
|
-
const emoji = validateOptionalEmoji(entry.emoji, Object.prototype.hasOwnProperty.call(entry,
|
|
913
|
-
const color = validateOptionalColor(entry.color, Object.prototype.hasOwnProperty.call(entry,
|
|
927
|
+
const emoji = validateOptionalEmoji(entry.emoji, Object.prototype.hasOwnProperty.call(entry, "emoji"), errors);
|
|
928
|
+
const color = validateOptionalColor(entry.color, Object.prototype.hasOwnProperty.call(entry, "color"), errors);
|
|
914
929
|
let type;
|
|
915
|
-
if (typeof entry.type ===
|
|
930
|
+
if (typeof entry.type === "string" && entry.type.trim()) {
|
|
916
931
|
const parsedType = (0, types_1.parseAppType)(entry.type.trim());
|
|
917
932
|
if (!parsedType) {
|
|
918
|
-
errors.push(`type must be one of ${types_1.APP_TYPE_VALUES.join(
|
|
933
|
+
errors.push(`type must be one of ${types_1.APP_TYPE_VALUES.join(", ")}`);
|
|
919
934
|
}
|
|
920
935
|
else {
|
|
921
936
|
type = parsedType;
|
|
922
937
|
}
|
|
923
938
|
}
|
|
924
939
|
let displayName;
|
|
925
|
-
if (typeof entry.displayName ===
|
|
940
|
+
if (typeof entry.displayName === "string" && entry.displayName.trim()) {
|
|
926
941
|
displayName = entry.displayName.trim();
|
|
927
942
|
}
|
|
928
943
|
let subtitle;
|
|
929
|
-
if (Object.prototype.hasOwnProperty.call(entry,
|
|
930
|
-
if (typeof entry.subtitle !==
|
|
931
|
-
errors.push(
|
|
944
|
+
if (Object.prototype.hasOwnProperty.call(entry, "subtitle")) {
|
|
945
|
+
if (typeof entry.subtitle !== "string" || entry.subtitle.trim().length === 0) {
|
|
946
|
+
errors.push("subtitle must be a non-empty string when provided");
|
|
932
947
|
}
|
|
933
948
|
else if (entry.subtitle.trim().length > types_1.APP_SUBTITLE_MAX_LENGTH) {
|
|
934
949
|
errors.push(`subtitle must be ${types_1.APP_SUBTITLE_MAX_LENGTH} characters or fewer`);
|
|
@@ -938,25 +953,25 @@ function validateAppMetadata(entry) {
|
|
|
938
953
|
}
|
|
939
954
|
}
|
|
940
955
|
let description;
|
|
941
|
-
if (typeof entry.description ===
|
|
956
|
+
if (typeof entry.description === "string") {
|
|
942
957
|
description = entry.description;
|
|
943
958
|
const normalizedDescription = description.trim();
|
|
944
959
|
const wordCount = normalizedDescription ? normalizedDescription.split(/\s+/).length : 0;
|
|
945
|
-
if (normalizedDescription
|
|
946
|
-
|
|
947
|
-
|
|
960
|
+
if (normalizedDescription &&
|
|
961
|
+
(normalizedDescription.length < exports.APP_DESCRIPTION_RECOMMENDED_MIN_CHARACTERS ||
|
|
962
|
+
wordCount < exports.APP_DESCRIPTION_RECOMMENDED_MIN_WORDS)) {
|
|
948
963
|
warnings.push(`description is ${normalizedDescription.length} characters and ${wordCount} words. Aim for at least ${exports.APP_DESCRIPTION_RECOMMENDED_MIN_CHARACTERS} characters and ${exports.APP_DESCRIPTION_RECOMMENDED_MIN_WORDS} words describing what the player does, the objective, and the game's distinctive hook.`);
|
|
949
964
|
}
|
|
950
965
|
}
|
|
951
966
|
let editorSupported;
|
|
952
|
-
if (Object.prototype.hasOwnProperty.call(entry,
|
|
953
|
-
if (typeof entry.editorSupported !==
|
|
954
|
-
errors.push(
|
|
967
|
+
if (Object.prototype.hasOwnProperty.call(entry, "editorSupported")) {
|
|
968
|
+
if (typeof entry.editorSupported !== "boolean") {
|
|
969
|
+
errors.push("editorSupported must be true or false");
|
|
955
970
|
}
|
|
956
971
|
else {
|
|
957
972
|
editorSupported = entry.editorSupported;
|
|
958
|
-
if (editorSupported && entry.hostingMode ===
|
|
959
|
-
errors.push(
|
|
973
|
+
if (editorSupported && entry.hostingMode === "EXTERNAL") {
|
|
974
|
+
errors.push("editorSupported can only be true for HOSTED apps, not external apps");
|
|
960
975
|
}
|
|
961
976
|
}
|
|
962
977
|
}
|
|
@@ -968,20 +983,20 @@ function validateAppMetadata(entry) {
|
|
|
968
983
|
surfaceTargetsMap = { ...parsed.map };
|
|
969
984
|
}
|
|
970
985
|
catch (error) {
|
|
971
|
-
errors.push(error?.message ||
|
|
986
|
+
errors.push(error?.message || "invalid surfaceTargets");
|
|
972
987
|
}
|
|
973
988
|
let primarySurface;
|
|
974
|
-
if (Object.prototype.hasOwnProperty.call(entry,
|
|
989
|
+
if (Object.prototype.hasOwnProperty.call(entry, "primarySurface")) {
|
|
975
990
|
primarySurface = (0, types_1.parseAppSurface)(entry.primarySurface) ?? undefined;
|
|
976
991
|
if (!primarySurface) {
|
|
977
|
-
errors.push(
|
|
992
|
+
errors.push("primarySurface must be DESKTOP, MOBILE_LANDSCAPE, or MOBILE_PORTRAIT");
|
|
978
993
|
}
|
|
979
994
|
else if (!surfaceTargetsList.includes(primarySurface)) {
|
|
980
995
|
errors.push(`primarySurface ${primarySurface} must also be enabled in surfaceTargets`);
|
|
981
996
|
}
|
|
982
997
|
}
|
|
983
998
|
let playtestTapes;
|
|
984
|
-
if (Object.prototype.hasOwnProperty.call(entry,
|
|
999
|
+
if (Object.prototype.hasOwnProperty.call(entry, "playtestTapes")) {
|
|
985
1000
|
const normalized = (0, types_1.normalizeAppPlaytestTapes)(entry.playtestTapes, {
|
|
986
1001
|
surfaceTargets: surfaceTargetsList,
|
|
987
1002
|
requireComplete: true,
|
|
@@ -1011,32 +1026,32 @@ function validateAppMetadata(entry) {
|
|
|
1011
1026
|
};
|
|
1012
1027
|
}
|
|
1013
1028
|
function formatAppPlaytestTapeError(error) {
|
|
1014
|
-
const parts = error.split(
|
|
1029
|
+
const parts = error.split(":");
|
|
1015
1030
|
const code = parts[0] ?? error;
|
|
1016
|
-
const surface = parts[1] ??
|
|
1017
|
-
if (code ===
|
|
1018
|
-
const count = parts[2] ??
|
|
1031
|
+
const surface = parts[1] ?? "UNKNOWN";
|
|
1032
|
+
if (code === "playtest_tape_start_only_input_left_active") {
|
|
1033
|
+
const count = parts[2] ?? "unknown";
|
|
1019
1034
|
return `${error}: playtestTapes.${surface}.startOnlyEventCount=${count} ends while an input is held; include the matching keyUp or pointerUp inside the startup prefix.`;
|
|
1020
1035
|
}
|
|
1021
|
-
if (code ===
|
|
1022
|
-
const verb = parts[2] ??
|
|
1036
|
+
if (code === "playtest_tape_primary_verb_events_required") {
|
|
1037
|
+
const verb = parts[2] ?? "unknown";
|
|
1023
1038
|
return `${error}: playtestTapes.${surface} must demonstrate primaryVerb "${verb}" at least once after startup; secondary controls are allowed.`;
|
|
1024
1039
|
}
|
|
1025
|
-
if (code ===
|
|
1026
|
-
const verb = parts[2] ??
|
|
1040
|
+
if (code === "playtest_tape_primary_verb_gesture_required") {
|
|
1041
|
+
const verb = parts[2] ?? "unknown";
|
|
1027
1042
|
return `${error}: playtestTapes.${surface} primaryVerb "${verb}" requires one complete pointerDown, pointerMove, pointerUp gesture after startup.`;
|
|
1028
1043
|
}
|
|
1029
|
-
if (code ===
|
|
1030
|
-
const index = parts[2] ??
|
|
1044
|
+
if (code === "invalid_playtest_tape_key") {
|
|
1045
|
+
const index = parts[2] ?? "unknown";
|
|
1031
1046
|
return `${error}: playtestTapes.${surface}.events[${index}].key must be a browser key string with 1..64 characters; use a single space " " for the space bar.`;
|
|
1032
1047
|
}
|
|
1033
|
-
if (code ===
|
|
1034
|
-
const index = parts[2] ??
|
|
1035
|
-
const actual = parts[3] ??
|
|
1048
|
+
if (code === "invalid_playtest_tape_event_type") {
|
|
1049
|
+
const index = parts[2] ?? "unknown";
|
|
1050
|
+
const actual = parts[3] ?? "missing";
|
|
1036
1051
|
return `${error}: playtestTapes.${surface}.events[${index}].type="${actual}"; expected tap, pointerDown, pointerMove, pointerUp, keyDown, or keyUp.`;
|
|
1037
1052
|
}
|
|
1038
|
-
if (code ===
|
|
1039
|
-
const index = parts[2] ??
|
|
1053
|
+
if (code === "invalid_playtest_tape_success_signal_description") {
|
|
1054
|
+
const index = parts[2] ?? "unknown";
|
|
1040
1055
|
return `${error}: playtestTapes.${surface}.successSignals[${index}].description must contain 1..240 characters.`;
|
|
1041
1056
|
}
|
|
1042
1057
|
return error;
|
|
@@ -1046,10 +1061,10 @@ function buildAppTasks(rootDir, catalogues, options) {
|
|
|
1046
1061
|
const warnings = [];
|
|
1047
1062
|
const errors = [];
|
|
1048
1063
|
const seen = new Map();
|
|
1049
|
-
const filterName = options.filterName ? options.filterName.trim() :
|
|
1050
|
-
const filterFile = options.filterFile ? normalizePathForComparison(options.filterFile) :
|
|
1064
|
+
const filterName = options.filterName ? options.filterName.trim() : "";
|
|
1065
|
+
const filterFile = options.filterFile ? normalizePathForComparison(options.filterFile) : "";
|
|
1051
1066
|
for (const file of catalogues) {
|
|
1052
|
-
const label = file.relativePath ||
|
|
1067
|
+
const label = file.relativePath || "catalogue.json";
|
|
1053
1068
|
const rawApps = Array.isArray(file.data.apps) ? file.data.apps : [];
|
|
1054
1069
|
if (rawApps.length === 0) {
|
|
1055
1070
|
const legacyGames = Array.isArray(file.data.games);
|
|
@@ -1059,23 +1074,23 @@ function buildAppTasks(rootDir, catalogues, options) {
|
|
|
1059
1074
|
continue;
|
|
1060
1075
|
}
|
|
1061
1076
|
for (const entry of rawApps) {
|
|
1062
|
-
if (!entry || typeof entry !==
|
|
1077
|
+
if (!entry || typeof entry !== "object") {
|
|
1063
1078
|
errors.push(`[${label}] Invalid app entry; expected object.`);
|
|
1064
1079
|
continue;
|
|
1065
1080
|
}
|
|
1066
|
-
const rawName = typeof entry.name ===
|
|
1081
|
+
const rawName = typeof entry.name === "string" ? entry.name.trim() : "";
|
|
1067
1082
|
if (!rawName) {
|
|
1068
1083
|
errors.push(`[${label}] App entry is missing a name.`);
|
|
1069
1084
|
continue;
|
|
1070
1085
|
}
|
|
1071
1086
|
const ownedAssetNameMatch = filterName
|
|
1072
|
-
? Array.isArray(entry.ownedAssets)
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1087
|
+
? Array.isArray(entry.ownedAssets) &&
|
|
1088
|
+
entry.ownedAssets.some((ownedAsset) => ownedAsset &&
|
|
1089
|
+
typeof ownedAsset === "object" &&
|
|
1090
|
+
typeof ownedAsset.name === "string" &&
|
|
1091
|
+
ownedAsset.name.trim() === filterName)
|
|
1077
1092
|
: true;
|
|
1078
|
-
const fileField = typeof entry.file ===
|
|
1093
|
+
const fileField = typeof entry.file === "string" && entry.file.trim().length > 0
|
|
1079
1094
|
? entry.file.trim()
|
|
1080
1095
|
: `${rawName}.html`;
|
|
1081
1096
|
const absoluteFilePath = (0, node_path_1.resolve)(file.directory, fileField);
|
|
@@ -1091,13 +1106,13 @@ function buildAppTasks(rootDir, catalogues, options) {
|
|
|
1091
1106
|
}
|
|
1092
1107
|
seen.set(rawName, label);
|
|
1093
1108
|
// Check for external mode early (before file validation)
|
|
1094
|
-
const rawExternalUrlEarly = typeof entry.externalUrl ===
|
|
1095
|
-
const rawHostingModeEarly = typeof entry.hostingMode ===
|
|
1096
|
-
const isExternalMode = rawHostingModeEarly ===
|
|
1109
|
+
const rawExternalUrlEarly = typeof entry.externalUrl === "string" ? entry.externalUrl.trim() : "";
|
|
1110
|
+
const rawHostingModeEarly = typeof entry.hostingMode === "string" ? entry.hostingMode.trim().toUpperCase() : "";
|
|
1111
|
+
const isExternalMode = rawHostingModeEarly === "EXTERNAL" || (rawExternalUrlEarly && rawExternalUrlEarly.startsWith("https://"));
|
|
1097
1112
|
// Only validate file path for non-external apps
|
|
1098
1113
|
if (!isExternalMode) {
|
|
1099
1114
|
const relativeToProject = (0, node_path_1.relative)(file.directory, absoluteFilePath);
|
|
1100
|
-
if (relativeToProject.startsWith(
|
|
1115
|
+
if (relativeToProject.startsWith("..")) {
|
|
1101
1116
|
errors.push(`[${label}] Skipping ${rawName}: file path escapes project directory (${fileField}).`);
|
|
1102
1117
|
continue;
|
|
1103
1118
|
}
|
|
@@ -1111,15 +1126,15 @@ function buildAppTasks(rootDir, catalogues, options) {
|
|
|
1111
1126
|
try {
|
|
1112
1127
|
packageManifest = readPackageJsonManifest(packageJsonPath);
|
|
1113
1128
|
const scripts = packageManifest?.scripts;
|
|
1114
|
-
if (scripts && typeof scripts.validate ===
|
|
1129
|
+
if (scripts && typeof scripts.validate === "string" && scripts.validate.trim().length > 0) {
|
|
1115
1130
|
hasValidateScript = true;
|
|
1116
1131
|
}
|
|
1117
|
-
if (scripts && typeof scripts.format ===
|
|
1132
|
+
if (scripts && typeof scripts.format === "string" && scripts.format.trim().length > 0) {
|
|
1118
1133
|
hasFormatScript = true;
|
|
1119
1134
|
}
|
|
1120
1135
|
}
|
|
1121
1136
|
catch (error) {
|
|
1122
|
-
errors.push(`[${label}] Skipping ${rawName}: failed to read package.json (${error?.message ||
|
|
1137
|
+
errors.push(`[${label}] Skipping ${rawName}: failed to read package.json (${error?.message || "unknown error"}).`);
|
|
1123
1138
|
continue;
|
|
1124
1139
|
}
|
|
1125
1140
|
}
|
|
@@ -1142,11 +1157,11 @@ function buildAppTasks(rootDir, catalogues, options) {
|
|
|
1142
1157
|
}
|
|
1143
1158
|
const metadata = validateAppMetadata(entry);
|
|
1144
1159
|
if (metadata.errors.length > 0) {
|
|
1145
|
-
errors.push(`[${label}] Skipping ${rawName}: ${metadata.errors.join(
|
|
1160
|
+
errors.push(`[${label}] Skipping ${rawName}: ${metadata.errors.join("; ")}`);
|
|
1146
1161
|
continue;
|
|
1147
1162
|
}
|
|
1148
1163
|
if (metadata.warnings.length > 0) {
|
|
1149
|
-
warnings.push(`${label} (${metadata.warnings.join(
|
|
1164
|
+
warnings.push(`${label} (${metadata.warnings.join("; ")})`);
|
|
1150
1165
|
}
|
|
1151
1166
|
const errorCountBeforePlayerMeta = errors.length;
|
|
1152
1167
|
const achievements = validateAchievementDefinitions(entry.achievements, file.directory, errors);
|
|
@@ -1162,7 +1177,7 @@ function buildAppTasks(rootDir, catalogues, options) {
|
|
|
1162
1177
|
warnings.push(`${label} (${rawName}: package.json missing "format" script)`);
|
|
1163
1178
|
}
|
|
1164
1179
|
// Parse versioning fields.
|
|
1165
|
-
const rawVersion = typeof entry.version ===
|
|
1180
|
+
const rawVersion = typeof entry.version === "string" ? entry.version.trim() : "";
|
|
1166
1181
|
if (!rawVersion) {
|
|
1167
1182
|
errors.push(`[${label}] Skipping ${rawName}: app version is required (e.g., "1.0.0").`);
|
|
1168
1183
|
continue;
|
|
@@ -1172,93 +1187,106 @@ function buildAppTasks(rootDir, catalogues, options) {
|
|
|
1172
1187
|
continue;
|
|
1173
1188
|
}
|
|
1174
1189
|
const version = rawVersion;
|
|
1175
|
-
|
|
1190
|
+
let releaseNotes;
|
|
1191
|
+
if (Object.prototype.hasOwnProperty.call(entry, "releaseNotes")) {
|
|
1192
|
+
if (typeof entry.releaseNotes !== "string") {
|
|
1193
|
+
errors.push(`[${label}] Skipping ${rawName}: releaseNotes must be a string containing one line with 1..240 characters.`);
|
|
1194
|
+
continue;
|
|
1195
|
+
}
|
|
1196
|
+
try {
|
|
1197
|
+
releaseNotes = normalizeAppReleaseNotes(entry.releaseNotes);
|
|
1198
|
+
}
|
|
1199
|
+
catch (error) {
|
|
1200
|
+
errors.push(`[${label}] Skipping ${rawName}: ${error instanceof Error ? error.message : String(error)}`);
|
|
1201
|
+
continue;
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1176
1204
|
let versionVisibility;
|
|
1177
|
-
const rawVisibility = typeof entry.visibility ===
|
|
1205
|
+
const rawVisibility = typeof entry.visibility === "string" ? entry.visibility.trim().toUpperCase() : "";
|
|
1178
1206
|
if (rawVisibility) {
|
|
1179
1207
|
if (!types_1.APP_VERSION_VISIBILITY_VALUES.includes(rawVisibility)) {
|
|
1180
|
-
errors.push(`[${label}] Skipping ${rawName}: visibility must be one of ${types_1.APP_VERSION_VISIBILITY_VALUES.join(
|
|
1208
|
+
errors.push(`[${label}] Skipping ${rawName}: visibility must be one of ${types_1.APP_VERSION_VISIBILITY_VALUES.join(", ")}.`);
|
|
1181
1209
|
continue;
|
|
1182
1210
|
}
|
|
1183
1211
|
versionVisibility = rawVisibility;
|
|
1184
1212
|
}
|
|
1185
1213
|
let hostingMode;
|
|
1186
|
-
const rawHostingMode = typeof entry.hostingMode ===
|
|
1214
|
+
const rawHostingMode = typeof entry.hostingMode === "string" ? entry.hostingMode.trim().toUpperCase() : "";
|
|
1187
1215
|
if (rawHostingMode) {
|
|
1188
1216
|
if (!types_1.APP_HOSTING_MODE_VALUES.includes(rawHostingMode)) {
|
|
1189
|
-
errors.push(`[${label}] Skipping ${rawName}: hostingMode must be one of ${types_1.APP_HOSTING_MODE_VALUES.join(
|
|
1217
|
+
errors.push(`[${label}] Skipping ${rawName}: hostingMode must be one of ${types_1.APP_HOSTING_MODE_VALUES.join(", ")}.`);
|
|
1190
1218
|
continue;
|
|
1191
1219
|
}
|
|
1192
1220
|
hostingMode = rawHostingMode;
|
|
1193
1221
|
}
|
|
1194
1222
|
let authMode;
|
|
1195
|
-
const rawAuthMode = typeof entry.authMode ===
|
|
1223
|
+
const rawAuthMode = typeof entry.authMode === "string" ? entry.authMode.trim().toUpperCase() : "";
|
|
1196
1224
|
if (rawAuthMode) {
|
|
1197
1225
|
if (!types_1.APP_AUTH_MODE_VALUES.includes(rawAuthMode)) {
|
|
1198
|
-
errors.push(`[${label}] Skipping ${rawName}: authMode must be one of ${types_1.APP_AUTH_MODE_VALUES.join(
|
|
1226
|
+
errors.push(`[${label}] Skipping ${rawName}: authMode must be one of ${types_1.APP_AUTH_MODE_VALUES.join(", ")}.`);
|
|
1199
1227
|
continue;
|
|
1200
1228
|
}
|
|
1201
1229
|
authMode = rawAuthMode;
|
|
1202
1230
|
}
|
|
1203
1231
|
let controllerMode;
|
|
1204
|
-
const rawControllerMode = typeof entry.controllerMode ===
|
|
1232
|
+
const rawControllerMode = typeof entry.controllerMode === "string" ? entry.controllerMode.trim().toUpperCase() : "";
|
|
1205
1233
|
if (rawControllerMode) {
|
|
1206
1234
|
if (!types_1.APP_CONTROLLER_MODE_VALUES.includes(rawControllerMode)) {
|
|
1207
|
-
errors.push(`[${label}] Skipping ${rawName}: controllerMode must be one of ${types_1.APP_CONTROLLER_MODE_VALUES.join(
|
|
1235
|
+
errors.push(`[${label}] Skipping ${rawName}: controllerMode must be one of ${types_1.APP_CONTROLLER_MODE_VALUES.join(", ")}.`);
|
|
1208
1236
|
continue;
|
|
1209
1237
|
}
|
|
1210
1238
|
controllerMode = rawControllerMode;
|
|
1211
1239
|
}
|
|
1212
1240
|
let previewable;
|
|
1213
|
-
if (Object.prototype.hasOwnProperty.call(entry,
|
|
1214
|
-
if (typeof entry.previewable !==
|
|
1241
|
+
if (Object.prototype.hasOwnProperty.call(entry, "previewable")) {
|
|
1242
|
+
if (typeof entry.previewable !== "boolean") {
|
|
1215
1243
|
errors.push(`[${label}] Skipping ${rawName}: previewable must be true or false.`);
|
|
1216
1244
|
continue;
|
|
1217
1245
|
}
|
|
1218
1246
|
previewable = Boolean(entry.previewable);
|
|
1219
1247
|
}
|
|
1220
1248
|
let editorSupported;
|
|
1221
|
-
if (Object.prototype.hasOwnProperty.call(entry,
|
|
1222
|
-
if (typeof entry.editorSupported !==
|
|
1249
|
+
if (Object.prototype.hasOwnProperty.call(entry, "editorSupported")) {
|
|
1250
|
+
if (typeof entry.editorSupported !== "boolean") {
|
|
1223
1251
|
errors.push(`[${label}] Skipping ${rawName}: editorSupported must be true or false.`);
|
|
1224
1252
|
continue;
|
|
1225
1253
|
}
|
|
1226
1254
|
editorSupported = Boolean(entry.editorSupported);
|
|
1227
1255
|
}
|
|
1228
1256
|
let externalUrl;
|
|
1229
|
-
const rawExternalUrl = typeof entry.externalUrl ===
|
|
1257
|
+
const rawExternalUrl = typeof entry.externalUrl === "string" ? entry.externalUrl.trim() : "";
|
|
1230
1258
|
if (rawExternalUrl) {
|
|
1231
|
-
if (!rawExternalUrl.startsWith(
|
|
1259
|
+
if (!rawExternalUrl.startsWith("https://")) {
|
|
1232
1260
|
errors.push(`[${label}] Skipping ${rawName}: externalUrl must be an HTTPS URL.`);
|
|
1233
1261
|
continue;
|
|
1234
1262
|
}
|
|
1235
1263
|
externalUrl = rawExternalUrl;
|
|
1236
1264
|
}
|
|
1237
1265
|
// Validate hostingMode / externalUrl consistency
|
|
1238
|
-
if (hostingMode ===
|
|
1266
|
+
if (hostingMode === "EXTERNAL" && !externalUrl) {
|
|
1239
1267
|
errors.push(`[${label}] Skipping ${rawName}: externalUrl is required when hostingMode is EXTERNAL.`);
|
|
1240
1268
|
continue;
|
|
1241
1269
|
}
|
|
1242
|
-
if (externalUrl && hostingMode !==
|
|
1270
|
+
if (externalUrl && hostingMode !== "EXTERNAL") {
|
|
1243
1271
|
// Auto-set hostingMode to EXTERNAL if externalUrl is provided
|
|
1244
|
-
hostingMode =
|
|
1272
|
+
hostingMode = "EXTERNAL";
|
|
1245
1273
|
}
|
|
1246
|
-
if ((hostingMode ??
|
|
1274
|
+
if ((hostingMode ?? "HOSTED") === "EXTERNAL" && previewable) {
|
|
1247
1275
|
errors.push(`[${label}] Skipping ${rawName}: previewable can only be true for HOSTED apps.`);
|
|
1248
1276
|
continue;
|
|
1249
1277
|
}
|
|
1250
|
-
if ((hostingMode ??
|
|
1278
|
+
if ((hostingMode ?? "HOSTED") === "EXTERNAL" && editorSupported) {
|
|
1251
1279
|
errors.push(`[${label}] Skipping ${rawName}: editorSupported can only be true for HOSTED apps.`);
|
|
1252
1280
|
continue;
|
|
1253
1281
|
}
|
|
1254
|
-
const defaultLicense = (hostingMode ??
|
|
1282
|
+
const defaultLicense = (hostingMode ?? "HOSTED") === "EXTERNAL" ? "CLOSED" : types_1.DEFAULT_CONTENT_LICENSE;
|
|
1255
1283
|
const license = normalizeContentLicenseValue(entry.license, errors, `${label} app "${rawName}"`, APP_ALLOWED_LICENSES, defaultLicense);
|
|
1256
1284
|
if (!license) {
|
|
1257
1285
|
continue;
|
|
1258
1286
|
}
|
|
1259
1287
|
// Parse username field (admin-only)
|
|
1260
1288
|
let username;
|
|
1261
|
-
const rawUsername = typeof entry.username ===
|
|
1289
|
+
const rawUsername = typeof entry.username === "string" ? entry.username.trim() : "";
|
|
1262
1290
|
if (rawUsername) {
|
|
1263
1291
|
username = rawUsername;
|
|
1264
1292
|
}
|
|
@@ -1266,7 +1294,7 @@ function buildAppTasks(rootDir, catalogues, options) {
|
|
|
1266
1294
|
let listing;
|
|
1267
1295
|
const rawListing = entry.listing;
|
|
1268
1296
|
let listingValidationFailed = false;
|
|
1269
|
-
if (rawListing && typeof rawListing ===
|
|
1297
|
+
if (options.listingAssets !== "ignore" && rawListing && typeof rawListing === "object") {
|
|
1270
1298
|
const resolvedListing = {
|
|
1271
1299
|
screenshotPortraitPaths: [],
|
|
1272
1300
|
screenshotLandscapePaths: [],
|
|
@@ -1275,15 +1303,15 @@ function buildAppTasks(rootDir, catalogues, options) {
|
|
|
1275
1303
|
mediaMetadata: [],
|
|
1276
1304
|
};
|
|
1277
1305
|
// Icon
|
|
1278
|
-
if (typeof rawListing.icon ===
|
|
1306
|
+
if (typeof rawListing.icon === "string" && rawListing.icon.trim()) {
|
|
1279
1307
|
const iconPath = rawListing.icon.trim();
|
|
1280
|
-
const extensionError = validateListingAssetExtension(iconPath,
|
|
1308
|
+
const extensionError = validateListingAssetExtension(iconPath, "listing.icon", "image");
|
|
1281
1309
|
if (extensionError) {
|
|
1282
1310
|
errors.push(`[${label}] Skipping ${rawName}: ${extensionError}`);
|
|
1283
1311
|
listingValidationFailed = true;
|
|
1284
1312
|
}
|
|
1285
1313
|
else {
|
|
1286
|
-
const validationError = validateListingAssetPath(file.directory, iconPath,
|
|
1314
|
+
const validationError = validateListingAssetPath(file.directory, iconPath, "listing.icon");
|
|
1287
1315
|
if (validationError) {
|
|
1288
1316
|
errors.push(`[${label}] Skipping ${rawName}: ${validationError}`);
|
|
1289
1317
|
listingValidationFailed = true;
|
|
@@ -1294,20 +1322,20 @@ function buildAppTasks(rootDir, catalogues, options) {
|
|
|
1294
1322
|
}
|
|
1295
1323
|
}
|
|
1296
1324
|
// Hero images
|
|
1297
|
-
const rawHeroPortrait = typeof rawListing.heroPortrait ===
|
|
1325
|
+
const rawHeroPortrait = typeof rawListing.heroPortrait === "string" && rawListing.heroPortrait.trim()
|
|
1298
1326
|
? rawListing.heroPortrait.trim()
|
|
1299
|
-
: typeof rawListing.heroPortraitPath ===
|
|
1327
|
+
: typeof rawListing.heroPortraitPath === "string" && rawListing.heroPortraitPath.trim()
|
|
1300
1328
|
? rawListing.heroPortraitPath.trim()
|
|
1301
|
-
:
|
|
1329
|
+
: "";
|
|
1302
1330
|
if (!listingValidationFailed && rawHeroPortrait) {
|
|
1303
1331
|
const heroPortraitPath = rawHeroPortrait;
|
|
1304
|
-
const extensionError = validateListingAssetExtension(heroPortraitPath,
|
|
1332
|
+
const extensionError = validateListingAssetExtension(heroPortraitPath, "listing.heroPortrait", "image");
|
|
1305
1333
|
if (extensionError) {
|
|
1306
1334
|
errors.push(`[${label}] Skipping ${rawName}: ${extensionError}`);
|
|
1307
1335
|
listingValidationFailed = true;
|
|
1308
1336
|
}
|
|
1309
1337
|
else {
|
|
1310
|
-
const validationError = validateListingAssetPath(file.directory, heroPortraitPath,
|
|
1338
|
+
const validationError = validateListingAssetPath(file.directory, heroPortraitPath, "listing.heroPortrait");
|
|
1311
1339
|
if (validationError) {
|
|
1312
1340
|
errors.push(`[${label}] Skipping ${rawName}: ${validationError}`);
|
|
1313
1341
|
listingValidationFailed = true;
|
|
@@ -1317,20 +1345,20 @@ function buildAppTasks(rootDir, catalogues, options) {
|
|
|
1317
1345
|
}
|
|
1318
1346
|
}
|
|
1319
1347
|
}
|
|
1320
|
-
const rawHeroLandscape = typeof rawListing.heroLandscape ===
|
|
1348
|
+
const rawHeroLandscape = typeof rawListing.heroLandscape === "string" && rawListing.heroLandscape.trim()
|
|
1321
1349
|
? rawListing.heroLandscape.trim()
|
|
1322
|
-
: typeof rawListing.heroLandscapePath ===
|
|
1350
|
+
: typeof rawListing.heroLandscapePath === "string" && rawListing.heroLandscapePath.trim()
|
|
1323
1351
|
? rawListing.heroLandscapePath.trim()
|
|
1324
|
-
:
|
|
1352
|
+
: "";
|
|
1325
1353
|
if (!listingValidationFailed && rawHeroLandscape) {
|
|
1326
1354
|
const heroLandscapePath = rawHeroLandscape;
|
|
1327
|
-
const extensionError = validateListingAssetExtension(heroLandscapePath,
|
|
1355
|
+
const extensionError = validateListingAssetExtension(heroLandscapePath, "listing.heroLandscape", "image");
|
|
1328
1356
|
if (extensionError) {
|
|
1329
1357
|
errors.push(`[${label}] Skipping ${rawName}: ${extensionError}`);
|
|
1330
1358
|
listingValidationFailed = true;
|
|
1331
1359
|
}
|
|
1332
1360
|
else {
|
|
1333
|
-
const validationError = validateListingAssetPath(file.directory, heroLandscapePath,
|
|
1361
|
+
const validationError = validateListingAssetPath(file.directory, heroLandscapePath, "listing.heroLandscape");
|
|
1334
1362
|
if (validationError) {
|
|
1335
1363
|
errors.push(`[${label}] Skipping ${rawName}: ${validationError}`);
|
|
1336
1364
|
listingValidationFailed = true;
|
|
@@ -1341,10 +1369,34 @@ function buildAppTasks(rootDir, catalogues, options) {
|
|
|
1341
1369
|
}
|
|
1342
1370
|
}
|
|
1343
1371
|
for (const mediaField of [
|
|
1344
|
-
{
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1372
|
+
{
|
|
1373
|
+
value: rawListing.screenshotsPortrait,
|
|
1374
|
+
fieldName: "listing.screenshotsPortrait",
|
|
1375
|
+
kind: "image",
|
|
1376
|
+
fileKeyPrefix: "screenshotsPortrait",
|
|
1377
|
+
target: resolvedListing.screenshotPortraitPaths,
|
|
1378
|
+
},
|
|
1379
|
+
{
|
|
1380
|
+
value: rawListing.screenshotsLandscape,
|
|
1381
|
+
fieldName: "listing.screenshotsLandscape",
|
|
1382
|
+
kind: "image",
|
|
1383
|
+
fileKeyPrefix: "screenshotsLandscape",
|
|
1384
|
+
target: resolvedListing.screenshotLandscapePaths,
|
|
1385
|
+
},
|
|
1386
|
+
{
|
|
1387
|
+
value: rawListing.videosPortrait,
|
|
1388
|
+
fieldName: "listing.videosPortrait",
|
|
1389
|
+
kind: "video",
|
|
1390
|
+
fileKeyPrefix: "videosPortrait",
|
|
1391
|
+
target: resolvedListing.videoPortraitPaths,
|
|
1392
|
+
},
|
|
1393
|
+
{
|
|
1394
|
+
value: rawListing.videosLandscape,
|
|
1395
|
+
fieldName: "listing.videosLandscape",
|
|
1396
|
+
kind: "video",
|
|
1397
|
+
fileKeyPrefix: "videosLandscape",
|
|
1398
|
+
target: resolvedListing.videoLandscapePaths,
|
|
1399
|
+
},
|
|
1348
1400
|
]) {
|
|
1349
1401
|
if (listingValidationFailed)
|
|
1350
1402
|
break;
|
|
@@ -1363,14 +1415,16 @@ function buildAppTasks(rootDir, catalogues, options) {
|
|
|
1363
1415
|
mediaField.target.push(...resolvedMedia.paths);
|
|
1364
1416
|
resolvedListing.mediaMetadata.push(...resolvedMedia.metadata);
|
|
1365
1417
|
}
|
|
1366
|
-
if (!listingValidationFailed &&
|
|
1418
|
+
if (!listingValidationFailed &&
|
|
1419
|
+
typeof rawListing.captureReport === "string" &&
|
|
1420
|
+
rawListing.captureReport.trim()) {
|
|
1367
1421
|
const captureReportPath = rawListing.captureReport.trim();
|
|
1368
|
-
if ((0, node_path_1.extname)(captureReportPath).toLowerCase() !==
|
|
1422
|
+
if ((0, node_path_1.extname)(captureReportPath).toLowerCase() !== ".json") {
|
|
1369
1423
|
errors.push(`[${label}] Skipping ${rawName}: listing.captureReport must point to a JSON file.`);
|
|
1370
1424
|
listingValidationFailed = true;
|
|
1371
1425
|
}
|
|
1372
1426
|
else {
|
|
1373
|
-
const validationError = validateListingAssetPath(file.directory, captureReportPath,
|
|
1427
|
+
const validationError = validateListingAssetPath(file.directory, captureReportPath, "listing.captureReport");
|
|
1374
1428
|
if (validationError) {
|
|
1375
1429
|
errors.push(`[${label}] Skipping ${rawName}: ${validationError}`);
|
|
1376
1430
|
listingValidationFailed = true;
|
|
@@ -1392,7 +1446,7 @@ function buildAppTasks(rootDir, catalogues, options) {
|
|
|
1392
1446
|
continue;
|
|
1393
1447
|
}
|
|
1394
1448
|
let design;
|
|
1395
|
-
if (Object.prototype.hasOwnProperty.call(entry,
|
|
1449
|
+
if (Object.prototype.hasOwnProperty.call(entry, "design")) {
|
|
1396
1450
|
const parsedDesign = (0, types_1.normalizeAppVersionDesign)(entry.design);
|
|
1397
1451
|
if (!parsedDesign.ok) {
|
|
1398
1452
|
errors.push(`[${label}] App "${rawName}" ${parsedDesign.error}`);
|
|
@@ -1401,7 +1455,7 @@ function buildAppTasks(rootDir, catalogues, options) {
|
|
|
1401
1455
|
design = parsedDesign.value;
|
|
1402
1456
|
}
|
|
1403
1457
|
let tweaks;
|
|
1404
|
-
if (Object.prototype.hasOwnProperty.call(entry,
|
|
1458
|
+
if (Object.prototype.hasOwnProperty.call(entry, "tweaks")) {
|
|
1405
1459
|
const parsedTweaks = (0, types_1.normalizeAppTweaks)(entry.tweaks);
|
|
1406
1460
|
if (!parsedTweaks.ok) {
|
|
1407
1461
|
errors.push(`[${label}] App "${rawName}" ${parsedTweaks.error}`);
|
|
@@ -1410,13 +1464,13 @@ function buildAppTasks(rootDir, catalogues, options) {
|
|
|
1410
1464
|
tweaks = parsedTweaks.value;
|
|
1411
1465
|
}
|
|
1412
1466
|
const errorCountBeforeTagMetadata = errors.length;
|
|
1413
|
-
const remix = normalizeCatalogueRemixRef(entry.remix,
|
|
1467
|
+
const remix = normalizeCatalogueRemixRef(entry.remix, "app", errors, `[${label}] App "${rawName}"`);
|
|
1414
1468
|
const tags = normalizeCatalogueTags(entry.tags, errors, `[${label}] App "${rawName}"`);
|
|
1415
1469
|
const graphRelations = normalizeCatalogueRelations(entry.relations, errors, `[${label}] App "${rawName}"`) ?? [];
|
|
1416
1470
|
if (errors.length > errorCountBeforeTagMetadata) {
|
|
1417
1471
|
continue;
|
|
1418
1472
|
}
|
|
1419
|
-
if (Object.prototype.hasOwnProperty.call(entry,
|
|
1473
|
+
if (Object.prototype.hasOwnProperty.call(entry, "embeddedAssets")) {
|
|
1420
1474
|
errors.push(`[${label}] App "${rawName}" uses legacy embeddedAssets. Rename it to ownedAssets.`);
|
|
1421
1475
|
continue;
|
|
1422
1476
|
}
|
|
@@ -1426,12 +1480,12 @@ function buildAppTasks(rootDir, catalogues, options) {
|
|
|
1426
1480
|
: [];
|
|
1427
1481
|
for (let ownedIndex = 0; ownedIndex < rawOwnedAssets.length; ownedIndex++) {
|
|
1428
1482
|
const owned = rawOwnedAssets[ownedIndex];
|
|
1429
|
-
const ownedName = typeof owned?.name ===
|
|
1483
|
+
const ownedName = typeof owned?.name === "string" ? owned.name.trim() : "";
|
|
1430
1484
|
if (!ownedName) {
|
|
1431
1485
|
errors.push(`[${label}] Skipping ${rawName}: ownedAssets[${ownedIndex}] is missing a name.`);
|
|
1432
1486
|
continue;
|
|
1433
1487
|
}
|
|
1434
|
-
const runtimeKey = typeof owned?.runtimeKey ===
|
|
1488
|
+
const runtimeKey = typeof owned?.runtimeKey === "string" && owned.runtimeKey.trim().length > 0
|
|
1435
1489
|
? owned.runtimeKey.trim()
|
|
1436
1490
|
: undefined;
|
|
1437
1491
|
const category = normalizeAssetCategoryValue(owned?.category, errors, `[${label}] Skipping ${rawName}: owned asset "${ownedName}"`);
|
|
@@ -1439,20 +1493,22 @@ function buildAppTasks(rootDir, catalogues, options) {
|
|
|
1439
1493
|
continue;
|
|
1440
1494
|
}
|
|
1441
1495
|
const subcategory = normalizeAssetSubcategoryValue(owned?.subcategory, category, errors, `[${label}] Skipping ${rawName}: owned asset "${ownedName}"`);
|
|
1442
|
-
if (subcategory === null && category !==
|
|
1496
|
+
if (subcategory === null && category !== "CUSTOM") {
|
|
1443
1497
|
continue;
|
|
1444
1498
|
}
|
|
1445
|
-
const fileEntries = owned?.files && typeof owned.files ===
|
|
1499
|
+
const fileEntries = owned?.files && typeof owned.files === "object" && !Array.isArray(owned.files)
|
|
1446
1500
|
? owned.files
|
|
1447
1501
|
: null;
|
|
1448
1502
|
const assetSpec = (0, assetSpecs_1.normalizeAssetSpecVersionRef)(owned?.assetSpec, errors, `[${label}] App "${rawName}" owned asset "${ownedName}"`);
|
|
1449
|
-
const ownedFormat = typeof owned?.format ===
|
|
1503
|
+
const ownedFormat = typeof owned?.format === "string" && owned.format.trim().length > 0
|
|
1450
1504
|
? owned.format.trim().toUpperCase()
|
|
1451
1505
|
: undefined;
|
|
1452
1506
|
const generatedModel3DFormat = subcategory
|
|
1453
1507
|
? inferGeneratedModel3DFormat(category, subcategory, ownedFormat, fileEntries)
|
|
1454
1508
|
: null;
|
|
1455
|
-
const generatedModel3DRoles = generatedModel3DFormat
|
|
1509
|
+
const generatedModel3DRoles = generatedModel3DFormat
|
|
1510
|
+
? getGeneratedModel3DFileRoles(generatedModel3DFormat)
|
|
1511
|
+
: null;
|
|
1456
1512
|
if (!fileEntries || Object.keys(fileEntries).length === 0) {
|
|
1457
1513
|
errors.push(`[${label}] Skipping ${rawName}: owned asset "${ownedName}" must define files.`);
|
|
1458
1514
|
continue;
|
|
@@ -1461,7 +1517,7 @@ function buildAppTasks(rootDir, catalogues, options) {
|
|
|
1461
1517
|
const filePaths = {};
|
|
1462
1518
|
let ownedAssetHasError = false;
|
|
1463
1519
|
for (const [role, fileValue] of Object.entries(fileEntries)) {
|
|
1464
|
-
if (typeof fileValue !==
|
|
1520
|
+
if (typeof fileValue !== "string" || !fileValue.trim()) {
|
|
1465
1521
|
errors.push(`[${label}] Skipping ${rawName}: owned asset "${ownedName}" file role "${role}" must be a non-empty string path.`);
|
|
1466
1522
|
ownedAssetHasError = true;
|
|
1467
1523
|
continue;
|
|
@@ -1472,7 +1528,7 @@ function buildAppTasks(rootDir, catalogues, options) {
|
|
|
1472
1528
|
const allowMissingGenerated = Boolean(generatedModel3DRoles?.generated.has(normalizedRole));
|
|
1473
1529
|
if (!(0, node_fs_1.existsSync)(absoluteEmbeddedPath) || !(0, node_fs_1.statSync)(absoluteEmbeddedPath).isFile()) {
|
|
1474
1530
|
if (allowMissingGenerated) {
|
|
1475
|
-
files[role] = relativeEmbeddedPath.replace(/\\/g,
|
|
1531
|
+
files[role] = relativeEmbeddedPath.replace(/\\/g, "/");
|
|
1476
1532
|
filePaths[role] = absoluteEmbeddedPath;
|
|
1477
1533
|
continue;
|
|
1478
1534
|
}
|
|
@@ -1480,7 +1536,7 @@ function buildAppTasks(rootDir, catalogues, options) {
|
|
|
1480
1536
|
ownedAssetHasError = true;
|
|
1481
1537
|
continue;
|
|
1482
1538
|
}
|
|
1483
|
-
files[role] = relativeEmbeddedPath.replace(/\\/g,
|
|
1539
|
+
files[role] = relativeEmbeddedPath.replace(/\\/g, "/");
|
|
1484
1540
|
filePaths[role] = absoluteEmbeddedPath;
|
|
1485
1541
|
}
|
|
1486
1542
|
if (ownedAssetHasError) {
|
|
@@ -1490,26 +1546,26 @@ function buildAppTasks(rootDir, catalogues, options) {
|
|
|
1490
1546
|
const normalizedRoles = new Set(Object.keys(files).map((role) => normalizeFileRole(role)));
|
|
1491
1547
|
const missingRoles = generatedModel3DRoles.required.filter((role) => !normalizedRoles.has(role));
|
|
1492
1548
|
if (missingRoles.length > 0) {
|
|
1493
|
-
errors.push(`[${label}] Skipping ${rawName}: owned asset "${ownedName}" with subcategory=${subcategory} must define files.${missingRoles.join(
|
|
1549
|
+
errors.push(`[${label}] Skipping ${rawName}: owned asset "${ownedName}" with subcategory=${subcategory} must define files.${missingRoles.join(" and files.")}.`);
|
|
1494
1550
|
continue;
|
|
1495
1551
|
}
|
|
1496
1552
|
}
|
|
1497
|
-
if (category ===
|
|
1498
|
-
const hasAtlas = Object.prototype.hasOwnProperty.call(files,
|
|
1499
|
-
const hasManifest = Object.prototype.hasOwnProperty.call(files,
|
|
1553
|
+
if (category === "SPRITESHEET") {
|
|
1554
|
+
const hasAtlas = Object.prototype.hasOwnProperty.call(files, "atlas");
|
|
1555
|
+
const hasManifest = Object.prototype.hasOwnProperty.call(files, "manifest");
|
|
1500
1556
|
if (!hasAtlas || !hasManifest) {
|
|
1501
1557
|
errors.push(`[${label}] Skipping ${rawName}: owned asset "${ownedName}" with category=SPRITESHEET must define files.atlas and files.manifest.`);
|
|
1502
1558
|
continue;
|
|
1503
1559
|
}
|
|
1504
1560
|
}
|
|
1505
|
-
if (category ===
|
|
1506
|
-
const hasPrimary = Object.prototype.hasOwnProperty.call(files,
|
|
1561
|
+
if (category === "AUDIO") {
|
|
1562
|
+
const hasPrimary = Object.prototype.hasOwnProperty.call(files, "primary");
|
|
1507
1563
|
if (!hasPrimary) {
|
|
1508
1564
|
errors.push(`[${label}] Skipping ${rawName}: owned asset "${ownedName}" with category=AUDIO must define files.primary.`);
|
|
1509
1565
|
continue;
|
|
1510
1566
|
}
|
|
1511
1567
|
}
|
|
1512
|
-
if (category ===
|
|
1568
|
+
if (category === "MODEL_3D" && ownedFormat === "GLB") {
|
|
1513
1569
|
const primaryPath = filePaths.primary;
|
|
1514
1570
|
if (!primaryPath) {
|
|
1515
1571
|
errors.push(`[${label}] Skipping ${rawName}: owned asset "${ownedName}" with format=GLB must define files.primary.`);
|
|
@@ -1519,8 +1575,8 @@ function buildAppTasks(rootDir, catalogues, options) {
|
|
|
1519
1575
|
validateSelfContainedGlb(primaryPath);
|
|
1520
1576
|
}
|
|
1521
1577
|
catch (error) {
|
|
1522
|
-
const message = typeof error?.message ===
|
|
1523
|
-
if (message ===
|
|
1578
|
+
const message = typeof error?.message === "string" ? error.message : "invalid_glb_payload";
|
|
1579
|
+
if (message === "glb_external_dependency_unsupported") {
|
|
1524
1580
|
errors.push(`[${label}] Skipping ${rawName}: owned asset "${ownedName}" GLB must embed all buffers and textures instead of referencing external files.`);
|
|
1525
1581
|
}
|
|
1526
1582
|
else {
|
|
@@ -1541,12 +1597,14 @@ function buildAppTasks(rootDir, catalogues, options) {
|
|
|
1541
1597
|
continue;
|
|
1542
1598
|
}
|
|
1543
1599
|
ownedAssets.push({
|
|
1544
|
-
kind:
|
|
1600
|
+
kind: "owned-asset",
|
|
1545
1601
|
appName: rawName,
|
|
1546
1602
|
name: ownedName,
|
|
1547
1603
|
runtimeKey,
|
|
1548
|
-
displayName: typeof owned?.displayName ===
|
|
1549
|
-
|
|
1604
|
+
displayName: typeof owned?.displayName === "string" && owned.displayName.trim().length > 0
|
|
1605
|
+
? owned.displayName.trim()
|
|
1606
|
+
: undefined,
|
|
1607
|
+
description: typeof owned?.description === "string" ? owned.description : undefined,
|
|
1550
1608
|
cataloguePath: label,
|
|
1551
1609
|
category,
|
|
1552
1610
|
subcategory,
|
|
@@ -1555,7 +1613,7 @@ function buildAppTasks(rootDir, catalogues, options) {
|
|
|
1555
1613
|
visibility: normalizeAssetVisibilityValue(owned?.visibility, errors, `${label} owned asset "${ownedName}"`),
|
|
1556
1614
|
license: ownedLicense,
|
|
1557
1615
|
tags: ownedTags,
|
|
1558
|
-
shopListed: typeof owned?.shopListed ===
|
|
1616
|
+
shopListed: typeof owned?.shopListed === "boolean" ? owned.shopListed : undefined,
|
|
1559
1617
|
shopPriceCredits: Number.isInteger(owned?.shopPriceCredits) ? Number(owned.shopPriceCredits) : undefined,
|
|
1560
1618
|
files,
|
|
1561
1619
|
filePaths,
|
|
@@ -1591,13 +1649,13 @@ function buildAppTasks(rootDir, catalogues, options) {
|
|
|
1591
1649
|
continue;
|
|
1592
1650
|
}
|
|
1593
1651
|
tasks.push({
|
|
1594
|
-
kind:
|
|
1652
|
+
kind: "app",
|
|
1595
1653
|
name: rawName,
|
|
1596
1654
|
cataloguePath: label,
|
|
1597
1655
|
catalogueAbsolutePath: file.absolutePath,
|
|
1598
1656
|
catalogueDir: file.directory,
|
|
1599
1657
|
filePath: absoluteFilePath,
|
|
1600
|
-
relativeFile: fileField.replace(/\\/g,
|
|
1658
|
+
relativeFile: fileField.replace(/\\/g, "/"),
|
|
1601
1659
|
projectDir: file.directory,
|
|
1602
1660
|
packageJsonPath,
|
|
1603
1661
|
isTypescriptProject,
|
|
@@ -1653,15 +1711,17 @@ function buildAssetSpecTasks(catalogues) {
|
|
|
1653
1711
|
const errors = [];
|
|
1654
1712
|
const seen = new Map();
|
|
1655
1713
|
for (const file of catalogues) {
|
|
1656
|
-
const label = file.relativePath ||
|
|
1657
|
-
const rawAssetSpecs = Array.isArray(file.data.assetSpecs)
|
|
1714
|
+
const label = file.relativePath || "catalogue.json";
|
|
1715
|
+
const rawAssetSpecs = Array.isArray(file.data.assetSpecs)
|
|
1716
|
+
? file.data.assetSpecs
|
|
1717
|
+
: [];
|
|
1658
1718
|
for (let index = 0; index < rawAssetSpecs.length; index += 1) {
|
|
1659
1719
|
const entry = rawAssetSpecs[index];
|
|
1660
|
-
if (!entry || typeof entry !==
|
|
1720
|
+
if (!entry || typeof entry !== "object") {
|
|
1661
1721
|
errors.push(`[${label}] Invalid assetSpecs[${index}] entry; expected object.`);
|
|
1662
1722
|
continue;
|
|
1663
1723
|
}
|
|
1664
|
-
const name = typeof entry.name ===
|
|
1724
|
+
const name = typeof entry.name === "string" ? entry.name.trim() : "";
|
|
1665
1725
|
if (!name) {
|
|
1666
1726
|
errors.push(`[${label}] assetSpecs[${index}] is missing a name.`);
|
|
1667
1727
|
continue;
|
|
@@ -1669,7 +1729,7 @@ function buildAssetSpecTasks(catalogues) {
|
|
|
1669
1729
|
if (!(0, assetSpecs_1.validateAssetSpecName)(name, errors, `[${label}] Asset spec "${name}"`)) {
|
|
1670
1730
|
continue;
|
|
1671
1731
|
}
|
|
1672
|
-
const version = typeof entry.version ===
|
|
1732
|
+
const version = typeof entry.version === "string" ? entry.version.trim() : "";
|
|
1673
1733
|
if (!SEMVER_REGEX.test(version)) {
|
|
1674
1734
|
errors.push(`[${label}] Asset spec "${name}" must define a semver version.`);
|
|
1675
1735
|
continue;
|
|
@@ -1680,12 +1740,12 @@ function buildAssetSpecTasks(catalogues) {
|
|
|
1680
1740
|
continue;
|
|
1681
1741
|
}
|
|
1682
1742
|
seen.set(dedupeKey, label);
|
|
1683
|
-
const displayName = typeof entry.displayName ===
|
|
1743
|
+
const displayName = typeof entry.displayName === "string" ? entry.displayName.trim() : "";
|
|
1684
1744
|
if (!displayName) {
|
|
1685
1745
|
errors.push(`[${label}] Asset spec "${name}@${version}" must define displayName.`);
|
|
1686
1746
|
continue;
|
|
1687
1747
|
}
|
|
1688
|
-
const symbol = typeof entry.symbol ===
|
|
1748
|
+
const symbol = typeof entry.symbol === "string" ? entry.symbol.trim() : "";
|
|
1689
1749
|
if (!symbol) {
|
|
1690
1750
|
errors.push(`[${label}] Asset spec "${name}@${version}" must define symbol.`);
|
|
1691
1751
|
continue;
|
|
@@ -1695,7 +1755,7 @@ function buildAssetSpecTasks(catalogues) {
|
|
|
1695
1755
|
errors.push(`[${label}] Asset spec "${name}@${version}" symbol file not found at ${symbolPath}.`);
|
|
1696
1756
|
continue;
|
|
1697
1757
|
}
|
|
1698
|
-
const contractField = typeof entry.contract ===
|
|
1758
|
+
const contractField = typeof entry.contract === "string" ? entry.contract.trim() : "";
|
|
1699
1759
|
if (!contractField) {
|
|
1700
1760
|
errors.push(`[${label}] Asset spec "${name}@${version}" must define contract.`);
|
|
1701
1761
|
continue;
|
|
@@ -1705,10 +1765,10 @@ function buildAssetSpecTasks(catalogues) {
|
|
|
1705
1765
|
continue;
|
|
1706
1766
|
}
|
|
1707
1767
|
let validationKind = loadedContract.validationKind;
|
|
1708
|
-
if (typeof entry.validationKind ===
|
|
1768
|
+
if (typeof entry.validationKind === "string" && entry.validationKind.trim().length > 0) {
|
|
1709
1769
|
const normalizedValidationKind = entry.validationKind.trim().toUpperCase();
|
|
1710
1770
|
if (!types_1.ASSET_SPEC_VALIDATION_KIND_VALUES.includes(normalizedValidationKind)) {
|
|
1711
|
-
errors.push(`[${label}] Asset spec "${name}@${version}" validationKind must be one of ${types_1.ASSET_SPEC_VALIDATION_KIND_VALUES.join(
|
|
1771
|
+
errors.push(`[${label}] Asset spec "${name}@${version}" validationKind must be one of ${types_1.ASSET_SPEC_VALIDATION_KIND_VALUES.join(", ")}.`);
|
|
1712
1772
|
continue;
|
|
1713
1773
|
}
|
|
1714
1774
|
if (normalizedValidationKind !== loadedContract.validationKind) {
|
|
@@ -1717,24 +1777,24 @@ function buildAssetSpecTasks(catalogues) {
|
|
|
1717
1777
|
}
|
|
1718
1778
|
validationKind = normalizedValidationKind;
|
|
1719
1779
|
}
|
|
1720
|
-
let status =
|
|
1721
|
-
if (typeof entry.status ===
|
|
1780
|
+
let status = "ACTIVE";
|
|
1781
|
+
if (typeof entry.status === "string" && entry.status.trim().length > 0) {
|
|
1722
1782
|
const normalizedStatus = entry.status.trim().toUpperCase();
|
|
1723
1783
|
if (!types_1.ASSET_SPEC_STATUS_VALUES.includes(normalizedStatus)) {
|
|
1724
|
-
errors.push(`[${label}] Asset spec "${name}@${version}" status must be one of ${types_1.ASSET_SPEC_STATUS_VALUES.join(
|
|
1784
|
+
errors.push(`[${label}] Asset spec "${name}@${version}" status must be one of ${types_1.ASSET_SPEC_STATUS_VALUES.join(", ")}.`);
|
|
1725
1785
|
continue;
|
|
1726
1786
|
}
|
|
1727
1787
|
status = normalizedStatus;
|
|
1728
1788
|
}
|
|
1729
1789
|
const visibility = normalizeAssetVisibilityValue(entry.visibility, errors, `${label} asset spec "${name}@${version}"`);
|
|
1730
|
-
const successorVersion = typeof entry.successorVersion ===
|
|
1790
|
+
const successorVersion = typeof entry.successorVersion === "string" && entry.successorVersion.trim().length > 0
|
|
1731
1791
|
? entry.successorVersion.trim()
|
|
1732
1792
|
: undefined;
|
|
1733
1793
|
if (successorVersion && !SEMVER_REGEX.test(successorVersion)) {
|
|
1734
1794
|
errors.push(`[${label}] Asset spec "${name}@${version}" successorVersion must be a semver string.`);
|
|
1735
1795
|
continue;
|
|
1736
1796
|
}
|
|
1737
|
-
const documentationField = typeof entry.documentation ===
|
|
1797
|
+
const documentationField = typeof entry.documentation === "string" && entry.documentation.trim().length > 0
|
|
1738
1798
|
? entry.documentation.trim()
|
|
1739
1799
|
: undefined;
|
|
1740
1800
|
const documentationPath = documentationField ? (0, node_path_1.resolve)(file.directory, documentationField) : undefined;
|
|
@@ -1743,19 +1803,19 @@ function buildAssetSpecTasks(catalogues) {
|
|
|
1743
1803
|
continue;
|
|
1744
1804
|
}
|
|
1745
1805
|
tasks.push({
|
|
1746
|
-
kind:
|
|
1806
|
+
kind: "asset-spec",
|
|
1747
1807
|
name,
|
|
1748
1808
|
version,
|
|
1749
1809
|
displayName,
|
|
1750
|
-
description: typeof entry.description ===
|
|
1810
|
+
description: typeof entry.description === "string" ? entry.description.trim() || undefined : undefined,
|
|
1751
1811
|
cataloguePath: label,
|
|
1752
1812
|
catalogueAbsolutePath: file.absolutePath,
|
|
1753
1813
|
catalogueDir: file.directory,
|
|
1754
|
-
username: typeof entry.username ===
|
|
1814
|
+
username: typeof entry.username === "string" && entry.username.trim().length > 0 ? entry.username.trim() : undefined,
|
|
1755
1815
|
visibility,
|
|
1756
1816
|
validationKind,
|
|
1757
1817
|
status,
|
|
1758
|
-
releaseNotes: typeof entry.releaseNotes ===
|
|
1818
|
+
releaseNotes: typeof entry.releaseNotes === "string" ? entry.releaseNotes : undefined,
|
|
1759
1819
|
successorVersion,
|
|
1760
1820
|
symbolPath,
|
|
1761
1821
|
contractPath: loadedContract.contractPath,
|
|
@@ -1786,21 +1846,21 @@ function buildAssetTasks(catalogues, assetSpecTasks) {
|
|
|
1786
1846
|
}
|
|
1787
1847
|
}
|
|
1788
1848
|
for (const file of catalogues) {
|
|
1789
|
-
const label = file.relativePath ||
|
|
1849
|
+
const label = file.relativePath || "catalogue.json";
|
|
1790
1850
|
const rawAssets = Array.isArray(file.data.assets) ? file.data.assets : [];
|
|
1791
1851
|
for (let index = 0; index < rawAssets.length; index++) {
|
|
1792
1852
|
const entry = rawAssets[index];
|
|
1793
|
-
if (!entry || typeof entry !==
|
|
1853
|
+
if (!entry || typeof entry !== "object") {
|
|
1794
1854
|
errors.push(`[${label}] Invalid assets[${index}] entry; expected object.`);
|
|
1795
1855
|
continue;
|
|
1796
1856
|
}
|
|
1797
|
-
const name = typeof entry.name ===
|
|
1857
|
+
const name = typeof entry.name === "string" ? entry.name.trim() : "";
|
|
1798
1858
|
if (!name) {
|
|
1799
1859
|
errors.push(`[${label}] assets[${index}] is missing a name.`);
|
|
1800
1860
|
continue;
|
|
1801
1861
|
}
|
|
1802
|
-
const hasDisplayName = Object.prototype.hasOwnProperty.call(entry,
|
|
1803
|
-
const displayName = typeof entry.displayName ===
|
|
1862
|
+
const hasDisplayName = Object.prototype.hasOwnProperty.call(entry, "displayName");
|
|
1863
|
+
const displayName = typeof entry.displayName === "string" ? entry.displayName.trim() : "";
|
|
1804
1864
|
if (hasDisplayName && displayName.length === 0) {
|
|
1805
1865
|
errors.push(`[${label}] Asset "${name}" displayName must be a non-empty string.`);
|
|
1806
1866
|
continue;
|
|
@@ -1812,7 +1872,7 @@ function buildAssetTasks(catalogues, assetSpecTasks) {
|
|
|
1812
1872
|
}
|
|
1813
1873
|
seen.set(dedupeKey, label);
|
|
1814
1874
|
const assetSpec = (0, assetSpecs_1.normalizeAssetSpecVersionRef)(entry.assetSpec, errors, `[${label}] Asset "${name}"`);
|
|
1815
|
-
const isCustomAsset = typeof assetSpec ===
|
|
1875
|
+
const isCustomAsset = typeof assetSpec === "string";
|
|
1816
1876
|
let category;
|
|
1817
1877
|
let subcategory;
|
|
1818
1878
|
let format;
|
|
@@ -1832,15 +1892,20 @@ function buildAssetTasks(catalogues, assetSpecTasks) {
|
|
|
1832
1892
|
if (!subcategory) {
|
|
1833
1893
|
continue;
|
|
1834
1894
|
}
|
|
1835
|
-
format =
|
|
1895
|
+
format =
|
|
1896
|
+
typeof entry.format === "string" && entry.format.trim().length > 0
|
|
1897
|
+
? entry.format.trim().toUpperCase()
|
|
1898
|
+
: undefined;
|
|
1836
1899
|
}
|
|
1837
|
-
const fileEntries = entry.files && typeof entry.files ===
|
|
1900
|
+
const fileEntries = entry.files && typeof entry.files === "object" && !Array.isArray(entry.files)
|
|
1838
1901
|
? entry.files
|
|
1839
1902
|
: null;
|
|
1840
1903
|
const generatedModel3DFormat = !isCustomAsset && category && subcategory
|
|
1841
1904
|
? inferGeneratedModel3DFormat(category, subcategory, format, fileEntries)
|
|
1842
1905
|
: null;
|
|
1843
|
-
const generatedModel3DRoles = generatedModel3DFormat
|
|
1906
|
+
const generatedModel3DRoles = generatedModel3DFormat
|
|
1907
|
+
? getGeneratedModel3DFileRoles(generatedModel3DFormat)
|
|
1908
|
+
: null;
|
|
1844
1909
|
if (!fileEntries || Object.keys(fileEntries).length === 0) {
|
|
1845
1910
|
errors.push(`[${label}] Asset "${name}" must define files.`);
|
|
1846
1911
|
continue;
|
|
@@ -1849,7 +1914,7 @@ function buildAssetTasks(catalogues, assetSpecTasks) {
|
|
|
1849
1914
|
const filePaths = {};
|
|
1850
1915
|
let hasError = false;
|
|
1851
1916
|
for (const [role, value] of Object.entries(fileEntries)) {
|
|
1852
|
-
if (typeof value !==
|
|
1917
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
1853
1918
|
errors.push(`[${label}] Asset "${name}" file role "${role}" must be a non-empty string path.`);
|
|
1854
1919
|
hasError = true;
|
|
1855
1920
|
continue;
|
|
@@ -1860,7 +1925,7 @@ function buildAssetTasks(catalogues, assetSpecTasks) {
|
|
|
1860
1925
|
const allowMissingGenerated = Boolean(generatedModel3DRoles?.generated.has(normalizedRole));
|
|
1861
1926
|
if (!(0, node_fs_1.existsSync)(absolutePath) || !(0, node_fs_1.statSync)(absolutePath).isFile()) {
|
|
1862
1927
|
if (allowMissingGenerated) {
|
|
1863
|
-
files[role] = relativePath.replace(/\\/g,
|
|
1928
|
+
files[role] = relativePath.replace(/\\/g, "/");
|
|
1864
1929
|
filePaths[role] = absolutePath;
|
|
1865
1930
|
continue;
|
|
1866
1931
|
}
|
|
@@ -1868,7 +1933,7 @@ function buildAssetTasks(catalogues, assetSpecTasks) {
|
|
|
1868
1933
|
hasError = true;
|
|
1869
1934
|
continue;
|
|
1870
1935
|
}
|
|
1871
|
-
files[role] = relativePath.replace(/\\/g,
|
|
1936
|
+
files[role] = relativePath.replace(/\\/g, "/");
|
|
1872
1937
|
filePaths[role] = absolutePath;
|
|
1873
1938
|
}
|
|
1874
1939
|
if (hasError) {
|
|
@@ -1878,35 +1943,33 @@ function buildAssetTasks(catalogues, assetSpecTasks) {
|
|
|
1878
1943
|
const normalizedRoles = new Set(Object.keys(files).map((role) => normalizeFileRole(role)));
|
|
1879
1944
|
const missingRoles = generatedModel3DRoles.required.filter((role) => !normalizedRoles.has(role));
|
|
1880
1945
|
if (missingRoles.length > 0) {
|
|
1881
|
-
errors.push(`[${label}] Asset "${name}" with subcategory=${subcategory} must define files.${missingRoles.join(
|
|
1946
|
+
errors.push(`[${label}] Asset "${name}" with subcategory=${subcategory} must define files.${missingRoles.join(" and files.")}.`);
|
|
1882
1947
|
continue;
|
|
1883
1948
|
}
|
|
1884
1949
|
}
|
|
1885
|
-
if (!isCustomAsset && category ===
|
|
1886
|
-
const hasAtlas = Object.prototype.hasOwnProperty.call(files,
|
|
1887
|
-
const hasManifest = Object.prototype.hasOwnProperty.call(files,
|
|
1950
|
+
if (!isCustomAsset && category === "SPRITESHEET") {
|
|
1951
|
+
const hasAtlas = Object.prototype.hasOwnProperty.call(files, "atlas");
|
|
1952
|
+
const hasManifest = Object.prototype.hasOwnProperty.call(files, "manifest");
|
|
1888
1953
|
if (!hasAtlas || !hasManifest) {
|
|
1889
1954
|
errors.push(`[${label}] Asset "${name}" with category=SPRITESHEET must define files.atlas and files.manifest.`);
|
|
1890
1955
|
continue;
|
|
1891
1956
|
}
|
|
1892
1957
|
}
|
|
1893
|
-
if (!isCustomAsset && category ===
|
|
1894
|
-
const hasPrimary = Object.prototype.hasOwnProperty.call(files,
|
|
1958
|
+
if (!isCustomAsset && category === "AUDIO") {
|
|
1959
|
+
const hasPrimary = Object.prototype.hasOwnProperty.call(files, "primary");
|
|
1895
1960
|
if (!hasPrimary) {
|
|
1896
1961
|
errors.push(`[${label}] Asset "${name}" with category=AUDIO must define files.primary.`);
|
|
1897
1962
|
continue;
|
|
1898
1963
|
}
|
|
1899
1964
|
}
|
|
1900
1965
|
const errorCountBeforeTagMetadata = errors.length;
|
|
1901
|
-
const remix = normalizeCatalogueRemixRef(entry.remix,
|
|
1966
|
+
const remix = normalizeCatalogueRemixRef(entry.remix, "asset", errors, `[${label}] Asset "${name}"`);
|
|
1902
1967
|
const tags = normalizeCatalogueTags(entry.tags, errors, `[${label}] Asset "${name}"`);
|
|
1903
1968
|
const relations = normalizeCatalogueRelations(entry.relations, errors, `[${label}] Asset "${name}"`);
|
|
1904
1969
|
if (errors.length > errorCountBeforeTagMetadata) {
|
|
1905
1970
|
continue;
|
|
1906
1971
|
}
|
|
1907
|
-
const username = typeof entry.username ===
|
|
1908
|
-
? entry.username.trim()
|
|
1909
|
-
: undefined;
|
|
1972
|
+
const username = typeof entry.username === "string" && entry.username.trim().length > 0 ? entry.username.trim() : undefined;
|
|
1910
1973
|
if (rejectLegacyShopPriceCoinsField(entry, errors, `[${label}] Asset "${name}"`)) {
|
|
1911
1974
|
continue;
|
|
1912
1975
|
}
|
|
@@ -1931,7 +1994,7 @@ function buildAssetTasks(catalogues, assetSpecTasks) {
|
|
|
1931
1994
|
continue;
|
|
1932
1995
|
}
|
|
1933
1996
|
tasks.push({
|
|
1934
|
-
kind:
|
|
1997
|
+
kind: "asset",
|
|
1935
1998
|
name,
|
|
1936
1999
|
displayName: displayName || undefined,
|
|
1937
2000
|
cataloguePath: label,
|
|
@@ -1944,7 +2007,7 @@ function buildAssetTasks(catalogues, assetSpecTasks) {
|
|
|
1944
2007
|
visibility: normalizeAssetVisibilityValue(entry.visibility, errors, `${label} asset "${name}"`),
|
|
1945
2008
|
license,
|
|
1946
2009
|
tags,
|
|
1947
|
-
shopListed: typeof entry.shopListed ===
|
|
2010
|
+
shopListed: typeof entry.shopListed === "boolean" ? entry.shopListed : undefined,
|
|
1948
2011
|
shopPriceCredits: Number.isInteger(entry.shopPriceCredits) ? Number(entry.shopPriceCredits) : undefined,
|
|
1949
2012
|
files,
|
|
1950
2013
|
filePaths,
|
|
@@ -1961,21 +2024,23 @@ function buildAssetPackTasks(catalogues) {
|
|
|
1961
2024
|
const errors = [];
|
|
1962
2025
|
const seen = new Map();
|
|
1963
2026
|
for (const file of catalogues) {
|
|
1964
|
-
const label = file.relativePath ||
|
|
1965
|
-
const rawPacks = Array.isArray(file.data.assetPacks)
|
|
2027
|
+
const label = file.relativePath || "catalogue.json";
|
|
2028
|
+
const rawPacks = Array.isArray(file.data.assetPacks)
|
|
2029
|
+
? file.data.assetPacks
|
|
2030
|
+
: [];
|
|
1966
2031
|
for (let index = 0; index < rawPacks.length; index++) {
|
|
1967
2032
|
const entry = rawPacks[index];
|
|
1968
|
-
if (!entry || typeof entry !==
|
|
2033
|
+
if (!entry || typeof entry !== "object") {
|
|
1969
2034
|
errors.push(`[${label}] Invalid assetPacks[${index}] entry; expected object.`);
|
|
1970
2035
|
continue;
|
|
1971
2036
|
}
|
|
1972
|
-
const name = typeof entry.name ===
|
|
2037
|
+
const name = typeof entry.name === "string" ? entry.name.trim() : "";
|
|
1973
2038
|
if (!name) {
|
|
1974
2039
|
errors.push(`[${label}] assetPacks[${index}] is missing a name.`);
|
|
1975
2040
|
continue;
|
|
1976
2041
|
}
|
|
1977
|
-
const displayName = typeof entry.displayName ===
|
|
1978
|
-
const version = typeof entry.version ===
|
|
2042
|
+
const displayName = typeof entry.displayName === "string" ? entry.displayName.trim() : "";
|
|
2043
|
+
const version = typeof entry.version === "string" ? entry.version.trim() : "";
|
|
1979
2044
|
if (!SEMVER_REGEX.test(version)) {
|
|
1980
2045
|
errors.push(`[${label}] Asset pack "${name}" must define a semver version.`);
|
|
1981
2046
|
continue;
|
|
@@ -1991,7 +2056,9 @@ function buildAssetPackTasks(catalogues) {
|
|
|
1991
2056
|
continue;
|
|
1992
2057
|
}
|
|
1993
2058
|
const assets = Array.isArray(entry.assets)
|
|
1994
|
-
? entry.assets
|
|
2059
|
+
? entry.assets
|
|
2060
|
+
.filter((value) => typeof value === "string" && value.trim().length > 0)
|
|
2061
|
+
.map((value) => value.trim())
|
|
1995
2062
|
: [];
|
|
1996
2063
|
const ownedAssets = [];
|
|
1997
2064
|
const rawOwnedAssets = Array.isArray(entry.ownedAssets)
|
|
@@ -1999,12 +2066,12 @@ function buildAssetPackTasks(catalogues) {
|
|
|
1999
2066
|
: [];
|
|
2000
2067
|
for (let ownedIndex = 0; ownedIndex < rawOwnedAssets.length; ownedIndex++) {
|
|
2001
2068
|
const owned = rawOwnedAssets[ownedIndex];
|
|
2002
|
-
const ownedName = typeof owned?.name ===
|
|
2069
|
+
const ownedName = typeof owned?.name === "string" ? owned.name.trim() : "";
|
|
2003
2070
|
if (!ownedName) {
|
|
2004
2071
|
errors.push(`[${label}] Skipping asset pack "${name}": ownedAssets[${ownedIndex}] is missing a name.`);
|
|
2005
2072
|
continue;
|
|
2006
2073
|
}
|
|
2007
|
-
if (typeof owned?.runtimeKey ===
|
|
2074
|
+
if (typeof owned?.runtimeKey === "string" && owned.runtimeKey.trim().length > 0) {
|
|
2008
2075
|
errors.push(`[${label}] Skipping asset pack "${name}": owned asset "${ownedName}" must not define runtimeKey.`);
|
|
2009
2076
|
continue;
|
|
2010
2077
|
}
|
|
@@ -2013,20 +2080,22 @@ function buildAssetPackTasks(catalogues) {
|
|
|
2013
2080
|
continue;
|
|
2014
2081
|
}
|
|
2015
2082
|
const subcategory = normalizeAssetSubcategoryValue(owned?.subcategory, category, errors, `[${label}] Skipping asset pack "${name}": owned asset "${ownedName}"`);
|
|
2016
|
-
if (subcategory === null && category !==
|
|
2083
|
+
if (subcategory === null && category !== "CUSTOM") {
|
|
2017
2084
|
continue;
|
|
2018
2085
|
}
|
|
2019
|
-
const fileEntries = owned?.files && typeof owned.files ===
|
|
2086
|
+
const fileEntries = owned?.files && typeof owned.files === "object" && !Array.isArray(owned.files)
|
|
2020
2087
|
? owned.files
|
|
2021
2088
|
: null;
|
|
2022
2089
|
const assetSpec = (0, assetSpecs_1.normalizeAssetSpecVersionRef)(owned?.assetSpec, errors, `[${label}] Asset pack "${name}" owned asset "${ownedName}"`);
|
|
2023
|
-
const ownedFormat = typeof owned?.format ===
|
|
2090
|
+
const ownedFormat = typeof owned?.format === "string" && owned.format.trim().length > 0
|
|
2024
2091
|
? owned.format.trim().toUpperCase()
|
|
2025
2092
|
: undefined;
|
|
2026
2093
|
const generatedModel3DFormat = subcategory
|
|
2027
2094
|
? inferGeneratedModel3DFormat(category, subcategory, ownedFormat, fileEntries)
|
|
2028
2095
|
: null;
|
|
2029
|
-
const generatedModel3DRoles = generatedModel3DFormat
|
|
2096
|
+
const generatedModel3DRoles = generatedModel3DFormat
|
|
2097
|
+
? getGeneratedModel3DFileRoles(generatedModel3DFormat)
|
|
2098
|
+
: null;
|
|
2030
2099
|
if (!fileEntries || Object.keys(fileEntries).length === 0) {
|
|
2031
2100
|
errors.push(`[${label}] Skipping asset pack "${name}": owned asset "${ownedName}" must define files.`);
|
|
2032
2101
|
continue;
|
|
@@ -2035,7 +2104,7 @@ function buildAssetPackTasks(catalogues) {
|
|
|
2035
2104
|
const filePaths = {};
|
|
2036
2105
|
let ownedAssetHasError = false;
|
|
2037
2106
|
for (const [role, fileValue] of Object.entries(fileEntries)) {
|
|
2038
|
-
if (typeof fileValue !==
|
|
2107
|
+
if (typeof fileValue !== "string" || !fileValue.trim()) {
|
|
2039
2108
|
errors.push(`[${label}] Skipping asset pack "${name}": owned asset "${ownedName}" file role "${role}" must be a non-empty string path.`);
|
|
2040
2109
|
ownedAssetHasError = true;
|
|
2041
2110
|
continue;
|
|
@@ -2046,7 +2115,7 @@ function buildAssetPackTasks(catalogues) {
|
|
|
2046
2115
|
const allowMissingGenerated = Boolean(generatedModel3DRoles?.generated.has(normalizedRole));
|
|
2047
2116
|
if (!(0, node_fs_1.existsSync)(absoluteOwnedPath) || !(0, node_fs_1.statSync)(absoluteOwnedPath).isFile()) {
|
|
2048
2117
|
if (allowMissingGenerated) {
|
|
2049
|
-
files[role] = relativeOwnedPath.replace(/\\/g,
|
|
2118
|
+
files[role] = relativeOwnedPath.replace(/\\/g, "/");
|
|
2050
2119
|
filePaths[role] = absoluteOwnedPath;
|
|
2051
2120
|
continue;
|
|
2052
2121
|
}
|
|
@@ -2054,7 +2123,7 @@ function buildAssetPackTasks(catalogues) {
|
|
|
2054
2123
|
ownedAssetHasError = true;
|
|
2055
2124
|
continue;
|
|
2056
2125
|
}
|
|
2057
|
-
files[role] = relativeOwnedPath.replace(/\\/g,
|
|
2126
|
+
files[role] = relativeOwnedPath.replace(/\\/g, "/");
|
|
2058
2127
|
filePaths[role] = absoluteOwnedPath;
|
|
2059
2128
|
}
|
|
2060
2129
|
if (ownedAssetHasError) {
|
|
@@ -2064,19 +2133,19 @@ function buildAssetPackTasks(catalogues) {
|
|
|
2064
2133
|
const normalizedRoles = new Set(Object.keys(files).map((role) => normalizeFileRole(role)));
|
|
2065
2134
|
const missingRoles = generatedModel3DRoles.required.filter((role) => !normalizedRoles.has(role));
|
|
2066
2135
|
if (missingRoles.length > 0) {
|
|
2067
|
-
errors.push(`[${label}] Skipping asset pack "${name}": owned asset "${ownedName}" with subcategory=${subcategory} must define files.${missingRoles.join(
|
|
2136
|
+
errors.push(`[${label}] Skipping asset pack "${name}": owned asset "${ownedName}" with subcategory=${subcategory} must define files.${missingRoles.join(" and files.")}.`);
|
|
2068
2137
|
continue;
|
|
2069
2138
|
}
|
|
2070
2139
|
}
|
|
2071
|
-
if (category ===
|
|
2072
|
-
const hasAtlas = Object.prototype.hasOwnProperty.call(files,
|
|
2073
|
-
const hasManifest = Object.prototype.hasOwnProperty.call(files,
|
|
2140
|
+
if (category === "SPRITESHEET") {
|
|
2141
|
+
const hasAtlas = Object.prototype.hasOwnProperty.call(files, "atlas");
|
|
2142
|
+
const hasManifest = Object.prototype.hasOwnProperty.call(files, "manifest");
|
|
2074
2143
|
if (!hasAtlas || !hasManifest) {
|
|
2075
2144
|
errors.push(`[${label}] Skipping asset pack "${name}": owned asset "${ownedName}" with category=SPRITESHEET must define files.atlas and files.manifest.`);
|
|
2076
2145
|
continue;
|
|
2077
2146
|
}
|
|
2078
2147
|
}
|
|
2079
|
-
if (category ===
|
|
2148
|
+
if (category === "AUDIO" && !Object.prototype.hasOwnProperty.call(files, "primary")) {
|
|
2080
2149
|
errors.push(`[${label}] Skipping asset pack "${name}": owned asset "${ownedName}" with category=AUDIO must define files.primary.`);
|
|
2081
2150
|
continue;
|
|
2082
2151
|
}
|
|
@@ -2094,8 +2163,10 @@ function buildAssetPackTasks(catalogues) {
|
|
|
2094
2163
|
ownedAssets.push({
|
|
2095
2164
|
name: ownedName,
|
|
2096
2165
|
cataloguePath: label,
|
|
2097
|
-
displayName: typeof owned?.displayName ===
|
|
2098
|
-
|
|
2166
|
+
displayName: typeof owned?.displayName === "string" && owned.displayName.trim().length > 0
|
|
2167
|
+
? owned.displayName.trim()
|
|
2168
|
+
: undefined,
|
|
2169
|
+
description: typeof owned?.description === "string" ? owned.description : undefined,
|
|
2099
2170
|
category,
|
|
2100
2171
|
subcategory,
|
|
2101
2172
|
assetSpec,
|
|
@@ -2103,23 +2174,23 @@ function buildAssetPackTasks(catalogues) {
|
|
|
2103
2174
|
visibility: normalizeAssetVisibilityValue(owned?.visibility, errors, `${label} asset pack "${name}" owned asset "${ownedName}"`),
|
|
2104
2175
|
license: ownedLicense,
|
|
2105
2176
|
tags: ownedTags,
|
|
2106
|
-
shopListed: typeof owned?.shopListed ===
|
|
2177
|
+
shopListed: typeof owned?.shopListed === "boolean" ? owned.shopListed : undefined,
|
|
2107
2178
|
shopPriceCredits: Number.isInteger(owned?.shopPriceCredits) ? Number(owned.shopPriceCredits) : undefined,
|
|
2108
2179
|
files,
|
|
2109
2180
|
filePaths,
|
|
2110
2181
|
});
|
|
2111
2182
|
}
|
|
2112
2183
|
let hostingMode;
|
|
2113
|
-
const rawHostingMode = typeof entry.hostingMode ===
|
|
2184
|
+
const rawHostingMode = typeof entry.hostingMode === "string" ? entry.hostingMode.trim().toUpperCase() : "";
|
|
2114
2185
|
if (rawHostingMode) {
|
|
2115
2186
|
if (!types_1.APP_HOSTING_MODE_VALUES.includes(rawHostingMode)) {
|
|
2116
|
-
errors.push(`[${label}] Asset pack "${name}" hostingMode must be one of ${types_1.APP_HOSTING_MODE_VALUES.join(
|
|
2187
|
+
errors.push(`[${label}] Asset pack "${name}" hostingMode must be one of ${types_1.APP_HOSTING_MODE_VALUES.join(", ")}.`);
|
|
2117
2188
|
continue;
|
|
2118
2189
|
}
|
|
2119
2190
|
hostingMode = rawHostingMode;
|
|
2120
2191
|
}
|
|
2121
2192
|
let externalUrl;
|
|
2122
|
-
const rawExternalUrl = typeof entry.externalUrl ===
|
|
2193
|
+
const rawExternalUrl = typeof entry.externalUrl === "string" ? entry.externalUrl.trim() : "";
|
|
2123
2194
|
if (rawExternalUrl) {
|
|
2124
2195
|
if (!/^https:\/\//i.test(rawExternalUrl)) {
|
|
2125
2196
|
errors.push(`[${label}] Asset pack "${name}" externalUrl must be an HTTPS URL.`);
|
|
@@ -2128,7 +2199,7 @@ function buildAssetPackTasks(catalogues) {
|
|
|
2128
2199
|
externalUrl = rawExternalUrl;
|
|
2129
2200
|
}
|
|
2130
2201
|
let downloadUrl;
|
|
2131
|
-
const rawDownloadUrl = typeof entry.downloadUrl ===
|
|
2202
|
+
const rawDownloadUrl = typeof entry.downloadUrl === "string" ? entry.downloadUrl.trim() : "";
|
|
2132
2203
|
if (rawDownloadUrl) {
|
|
2133
2204
|
if (!/^https:\/\//i.test(rawDownloadUrl)) {
|
|
2134
2205
|
errors.push(`[${label}] Asset pack "${name}" downloadUrl must be an HTTPS URL.`);
|
|
@@ -2136,38 +2207,36 @@ function buildAssetPackTasks(catalogues) {
|
|
|
2136
2207
|
}
|
|
2137
2208
|
downloadUrl = rawDownloadUrl;
|
|
2138
2209
|
}
|
|
2139
|
-
if (hostingMode ===
|
|
2210
|
+
if (hostingMode === "EXTERNAL" && !externalUrl) {
|
|
2140
2211
|
errors.push(`[${label}] Asset pack "${name}" externalUrl is required when hostingMode is EXTERNAL.`);
|
|
2141
2212
|
continue;
|
|
2142
2213
|
}
|
|
2143
|
-
if (externalUrl && hostingMode !==
|
|
2144
|
-
hostingMode =
|
|
2214
|
+
if (externalUrl && hostingMode !== "EXTERNAL") {
|
|
2215
|
+
hostingMode = "EXTERNAL";
|
|
2145
2216
|
}
|
|
2146
|
-
if (assets.length === 0 && ownedAssets.length === 0 && hostingMode !==
|
|
2217
|
+
if (assets.length === 0 && ownedAssets.length === 0 && hostingMode !== "EXTERNAL") {
|
|
2147
2218
|
errors.push(`[${label}] Asset pack "${name}" must reference at least one asset.`);
|
|
2148
2219
|
continue;
|
|
2149
2220
|
}
|
|
2150
2221
|
const errorCountBeforeTagMetadata = errors.length;
|
|
2151
|
-
const remix = normalizeCatalogueRemixRef(entry.remix,
|
|
2222
|
+
const remix = normalizeCatalogueRemixRef(entry.remix, "pack", errors, `[${label}] Asset pack "${name}@${version}"`);
|
|
2152
2223
|
const tags = normalizeCatalogueTags(entry.tags, errors, `[${label}] Asset pack "${name}@${version}"`);
|
|
2153
2224
|
const relations = normalizeCatalogueRelations(entry.relations, errors, `[${label}] Asset pack "${name}@${version}"`);
|
|
2154
2225
|
if (errors.length > errorCountBeforeTagMetadata) {
|
|
2155
2226
|
continue;
|
|
2156
2227
|
}
|
|
2157
|
-
const username = typeof entry.username ===
|
|
2158
|
-
|
|
2159
|
-
: undefined;
|
|
2160
|
-
const visibility = typeof entry.visibility === 'string' && entry.visibility.trim().length > 0
|
|
2228
|
+
const username = typeof entry.username === "string" && entry.username.trim().length > 0 ? entry.username.trim() : undefined;
|
|
2229
|
+
const visibility = typeof entry.visibility === "string" && entry.visibility.trim().length > 0
|
|
2161
2230
|
? entry.visibility.trim().toUpperCase()
|
|
2162
2231
|
: undefined;
|
|
2163
|
-
if (visibility && visibility !==
|
|
2232
|
+
if (visibility && visibility !== "PUBLIC" && visibility !== "PRIVATE") {
|
|
2164
2233
|
errors.push(`[${label}] Asset pack "${name}" visibility must be PUBLIC or PRIVATE.`);
|
|
2165
2234
|
continue;
|
|
2166
2235
|
}
|
|
2167
2236
|
let listing;
|
|
2168
2237
|
const rawListing = entry.listing;
|
|
2169
2238
|
let listingValidationFailed = false;
|
|
2170
|
-
if (rawListing && typeof rawListing ===
|
|
2239
|
+
if (rawListing && typeof rawListing === "object") {
|
|
2171
2240
|
const resolvedListing = {
|
|
2172
2241
|
screenshotPortraitPaths: [],
|
|
2173
2242
|
screenshotLandscapePaths: [],
|
|
@@ -2175,15 +2244,15 @@ function buildAssetPackTasks(catalogues) {
|
|
|
2175
2244
|
videoLandscapePaths: [],
|
|
2176
2245
|
mediaMetadata: [],
|
|
2177
2246
|
};
|
|
2178
|
-
if (typeof rawListing.icon ===
|
|
2247
|
+
if (typeof rawListing.icon === "string" && rawListing.icon.trim()) {
|
|
2179
2248
|
const iconPath = rawListing.icon.trim();
|
|
2180
|
-
const extensionError = validateListingAssetExtension(iconPath,
|
|
2249
|
+
const extensionError = validateListingAssetExtension(iconPath, "assetPacks.listing.icon", "image");
|
|
2181
2250
|
if (extensionError) {
|
|
2182
2251
|
errors.push(`[${label}] Skipping asset pack "${name}": ${extensionError}`);
|
|
2183
2252
|
listingValidationFailed = true;
|
|
2184
2253
|
}
|
|
2185
2254
|
else {
|
|
2186
|
-
const validationError = validateListingAssetPath(file.directory, iconPath,
|
|
2255
|
+
const validationError = validateListingAssetPath(file.directory, iconPath, "assetPacks.listing.icon");
|
|
2187
2256
|
if (validationError) {
|
|
2188
2257
|
errors.push(`[${label}] Skipping asset pack "${name}": ${validationError}`);
|
|
2189
2258
|
listingValidationFailed = true;
|
|
@@ -2193,15 +2262,15 @@ function buildAssetPackTasks(catalogues) {
|
|
|
2193
2262
|
}
|
|
2194
2263
|
}
|
|
2195
2264
|
}
|
|
2196
|
-
if (!listingValidationFailed && typeof rawListing.heroPortrait ===
|
|
2265
|
+
if (!listingValidationFailed && typeof rawListing.heroPortrait === "string" && rawListing.heroPortrait.trim()) {
|
|
2197
2266
|
const heroPortraitPath = rawListing.heroPortrait.trim();
|
|
2198
|
-
const extensionError = validateListingAssetExtension(heroPortraitPath,
|
|
2267
|
+
const extensionError = validateListingAssetExtension(heroPortraitPath, "assetPacks.listing.heroPortrait", "image");
|
|
2199
2268
|
if (extensionError) {
|
|
2200
2269
|
errors.push(`[${label}] Skipping asset pack "${name}": ${extensionError}`);
|
|
2201
2270
|
listingValidationFailed = true;
|
|
2202
2271
|
}
|
|
2203
2272
|
else {
|
|
2204
|
-
const validationError = validateListingAssetPath(file.directory, heroPortraitPath,
|
|
2273
|
+
const validationError = validateListingAssetPath(file.directory, heroPortraitPath, "assetPacks.listing.heroPortrait");
|
|
2205
2274
|
if (validationError) {
|
|
2206
2275
|
errors.push(`[${label}] Skipping asset pack "${name}": ${validationError}`);
|
|
2207
2276
|
listingValidationFailed = true;
|
|
@@ -2211,15 +2280,17 @@ function buildAssetPackTasks(catalogues) {
|
|
|
2211
2280
|
}
|
|
2212
2281
|
}
|
|
2213
2282
|
}
|
|
2214
|
-
if (!listingValidationFailed &&
|
|
2283
|
+
if (!listingValidationFailed &&
|
|
2284
|
+
typeof rawListing.heroLandscape === "string" &&
|
|
2285
|
+
rawListing.heroLandscape.trim()) {
|
|
2215
2286
|
const heroLandscapePath = rawListing.heroLandscape.trim();
|
|
2216
|
-
const extensionError = validateListingAssetExtension(heroLandscapePath,
|
|
2287
|
+
const extensionError = validateListingAssetExtension(heroLandscapePath, "assetPacks.listing.heroLandscape", "image");
|
|
2217
2288
|
if (extensionError) {
|
|
2218
2289
|
errors.push(`[${label}] Skipping asset pack "${name}": ${extensionError}`);
|
|
2219
2290
|
listingValidationFailed = true;
|
|
2220
2291
|
}
|
|
2221
2292
|
else {
|
|
2222
|
-
const validationError = validateListingAssetPath(file.directory, heroLandscapePath,
|
|
2293
|
+
const validationError = validateListingAssetPath(file.directory, heroLandscapePath, "assetPacks.listing.heroLandscape");
|
|
2223
2294
|
if (validationError) {
|
|
2224
2295
|
errors.push(`[${label}] Skipping asset pack "${name}": ${validationError}`);
|
|
2225
2296
|
listingValidationFailed = true;
|
|
@@ -2230,10 +2301,34 @@ function buildAssetPackTasks(catalogues) {
|
|
|
2230
2301
|
}
|
|
2231
2302
|
}
|
|
2232
2303
|
for (const mediaField of [
|
|
2233
|
-
{
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2304
|
+
{
|
|
2305
|
+
value: rawListing.screenshotsPortrait,
|
|
2306
|
+
fieldName: "assetPacks.listing.screenshotsPortrait",
|
|
2307
|
+
kind: "image",
|
|
2308
|
+
fileKeyPrefix: "screenshotsPortrait",
|
|
2309
|
+
target: resolvedListing.screenshotPortraitPaths,
|
|
2310
|
+
},
|
|
2311
|
+
{
|
|
2312
|
+
value: rawListing.screenshotsLandscape,
|
|
2313
|
+
fieldName: "assetPacks.listing.screenshotsLandscape",
|
|
2314
|
+
kind: "image",
|
|
2315
|
+
fileKeyPrefix: "screenshotsLandscape",
|
|
2316
|
+
target: resolvedListing.screenshotLandscapePaths,
|
|
2317
|
+
},
|
|
2318
|
+
{
|
|
2319
|
+
value: rawListing.videosPortrait,
|
|
2320
|
+
fieldName: "assetPacks.listing.videosPortrait",
|
|
2321
|
+
kind: "video",
|
|
2322
|
+
fileKeyPrefix: "videosPortrait",
|
|
2323
|
+
target: resolvedListing.videoPortraitPaths,
|
|
2324
|
+
},
|
|
2325
|
+
{
|
|
2326
|
+
value: rawListing.videosLandscape,
|
|
2327
|
+
fieldName: "assetPacks.listing.videosLandscape",
|
|
2328
|
+
kind: "video",
|
|
2329
|
+
fileKeyPrefix: "videosLandscape",
|
|
2330
|
+
target: resolvedListing.videoLandscapePaths,
|
|
2331
|
+
},
|
|
2237
2332
|
]) {
|
|
2238
2333
|
if (listingValidationFailed)
|
|
2239
2334
|
break;
|
|
@@ -2257,15 +2352,15 @@ function buildAssetPackTasks(catalogues) {
|
|
|
2257
2352
|
}
|
|
2258
2353
|
listing = resolvedListing;
|
|
2259
2354
|
}
|
|
2260
|
-
const previewApp = typeof entry.previewApp ===
|
|
2355
|
+
const previewApp = typeof entry.previewApp === "string"
|
|
2261
2356
|
? String(entry.previewApp).trim()
|
|
2262
|
-
:
|
|
2357
|
+
: "";
|
|
2263
2358
|
if (previewApp.length > 0) {
|
|
2264
2359
|
errors.push(`[${label}] Skipping asset pack "${name}": previewApp is no longer supported during publish. Upload the pack first, then set previewAppVersionId explicitly with creations asset-packs update.`);
|
|
2265
2360
|
continue;
|
|
2266
2361
|
}
|
|
2267
2362
|
tasks.push({
|
|
2268
|
-
kind:
|
|
2363
|
+
kind: "asset-pack",
|
|
2269
2364
|
name,
|
|
2270
2365
|
displayName: displayName || undefined,
|
|
2271
2366
|
version,
|
|
@@ -2279,7 +2374,7 @@ function buildAssetPackTasks(catalogues) {
|
|
|
2279
2374
|
downloadUrl,
|
|
2280
2375
|
visibility,
|
|
2281
2376
|
license,
|
|
2282
|
-
releaseNotes: typeof entry.releaseNotes ===
|
|
2377
|
+
releaseNotes: typeof entry.releaseNotes === "string" ? entry.releaseNotes : undefined,
|
|
2283
2378
|
tags,
|
|
2284
2379
|
listing,
|
|
2285
2380
|
relations,
|
|
@@ -2296,13 +2391,13 @@ function resolveCatalogueTagGroups(rootDir) {
|
|
|
2296
2391
|
errors: discoveryErrors,
|
|
2297
2392
|
};
|
|
2298
2393
|
}
|
|
2299
|
-
const rootCatalogue = files.find((file) => file.relativePath ===
|
|
2300
|
-
const hasNestedCatalogues = files.some((file) => file.relativePath !==
|
|
2394
|
+
const rootCatalogue = files.find((file) => file.relativePath === "catalogue.json");
|
|
2395
|
+
const hasNestedCatalogues = files.some((file) => file.relativePath !== "catalogue.json");
|
|
2301
2396
|
if (rootCatalogue && hasNestedCatalogues && !isCatalogueMarker(rootCatalogue)) {
|
|
2302
2397
|
return {
|
|
2303
2398
|
groups: [],
|
|
2304
2399
|
errors: [
|
|
2305
|
-
|
|
2400
|
+
"[catalogue.json] Workspace root catalogue.json must be {} when nested catalogue.json files exist below it.",
|
|
2306
2401
|
],
|
|
2307
2402
|
};
|
|
2308
2403
|
}
|
|
@@ -2310,15 +2405,15 @@ function resolveCatalogueTagGroups(rootDir) {
|
|
|
2310
2405
|
const errors = [];
|
|
2311
2406
|
const seenGroupSlugs = new Set();
|
|
2312
2407
|
for (const file of files) {
|
|
2313
|
-
const label = file.relativePath ||
|
|
2408
|
+
const label = file.relativePath || "catalogue.json";
|
|
2314
2409
|
const rawGroups = Array.isArray(file.data.tagGroups) ? file.data.tagGroups : [];
|
|
2315
2410
|
for (let index = 0; index < rawGroups.length; index += 1) {
|
|
2316
2411
|
const entry = rawGroups[index];
|
|
2317
|
-
if (!entry || typeof entry !==
|
|
2412
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
2318
2413
|
errors.push(`[${label}] tagGroups[${index}] must be an object.`);
|
|
2319
2414
|
continue;
|
|
2320
2415
|
}
|
|
2321
|
-
const slug = typeof entry.slug ===
|
|
2416
|
+
const slug = typeof entry.slug === "string" ? entry.slug.trim().toLowerCase() : "";
|
|
2322
2417
|
if (!slug || !(0, types_1.isValidTagSlug)(slug)) {
|
|
2323
2418
|
errors.push(`[${label}] tagGroups[${index}] slug must use lowercase slug format.`);
|
|
2324
2419
|
continue;
|
|
@@ -2327,7 +2422,7 @@ function resolveCatalogueTagGroups(rootDir) {
|
|
|
2327
2422
|
errors.push(`[${label}] Duplicate tag group slug "${slug}".`);
|
|
2328
2423
|
continue;
|
|
2329
2424
|
}
|
|
2330
|
-
const displayName = typeof entry.displayName ===
|
|
2425
|
+
const displayName = typeof entry.displayName === "string" ? entry.displayName.trim() : "";
|
|
2331
2426
|
if (!displayName) {
|
|
2332
2427
|
errors.push(`[${label}] tagGroups[${index}] displayName must be a non-empty string.`);
|
|
2333
2428
|
continue;
|
|
@@ -2342,11 +2437,11 @@ function resolveCatalogueTagGroups(rootDir) {
|
|
|
2342
2437
|
const seenTagSlugs = new Set();
|
|
2343
2438
|
for (let tagIndex = 0; tagIndex < entry.tags.length; tagIndex += 1) {
|
|
2344
2439
|
const rawTag = entry.tags[tagIndex];
|
|
2345
|
-
if (!rawTag || typeof rawTag !==
|
|
2440
|
+
if (!rawTag || typeof rawTag !== "object" || Array.isArray(rawTag)) {
|
|
2346
2441
|
errors.push(`[${label}] Tag group "${slug}" tags[${tagIndex}] must be an object.`);
|
|
2347
2442
|
continue;
|
|
2348
2443
|
}
|
|
2349
|
-
const tagSlug = typeof rawTag.slug ===
|
|
2444
|
+
const tagSlug = typeof rawTag.slug === "string" ? rawTag.slug.trim().toLowerCase() : "";
|
|
2350
2445
|
if (!tagSlug || !(0, types_1.isValidTagSlug)(tagSlug)) {
|
|
2351
2446
|
errors.push(`[${label}] Tag group "${slug}" tags[${tagIndex}] slug must use lowercase slug format.`);
|
|
2352
2447
|
continue;
|
|
@@ -2355,7 +2450,7 @@ function resolveCatalogueTagGroups(rootDir) {
|
|
|
2355
2450
|
errors.push(`[${label}] Tag group "${slug}" contains duplicate tag slug "${tagSlug}".`);
|
|
2356
2451
|
continue;
|
|
2357
2452
|
}
|
|
2358
|
-
const tagDisplayName = typeof rawTag.displayName ===
|
|
2453
|
+
const tagDisplayName = typeof rawTag.displayName === "string" ? rawTag.displayName.trim() : "";
|
|
2359
2454
|
if (!tagDisplayName) {
|
|
2360
2455
|
errors.push(`[${label}] Tag group "${slug}" tag "${tagSlug}" displayName must be a non-empty string.`);
|
|
2361
2456
|
continue;
|
|
@@ -2400,8 +2495,8 @@ function resolveCatalogueEntries(rootDir, options = {}) {
|
|
|
2400
2495
|
errors: discoveryErrors,
|
|
2401
2496
|
};
|
|
2402
2497
|
}
|
|
2403
|
-
const rootCatalogue = files.find((file) => file.relativePath ===
|
|
2404
|
-
const hasNestedCatalogues = files.some((file) => file.relativePath !==
|
|
2498
|
+
const rootCatalogue = files.find((file) => file.relativePath === "catalogue.json");
|
|
2499
|
+
const hasNestedCatalogues = files.some((file) => file.relativePath !== "catalogue.json");
|
|
2405
2500
|
if (rootCatalogue && hasNestedCatalogues && !isCatalogueMarker(rootCatalogue)) {
|
|
2406
2501
|
return {
|
|
2407
2502
|
apps: [],
|
|
@@ -2411,7 +2506,7 @@ function resolveCatalogueEntries(rootDir, options = {}) {
|
|
|
2411
2506
|
assetPacks: [],
|
|
2412
2507
|
warnings: [],
|
|
2413
2508
|
errors: [
|
|
2414
|
-
|
|
2509
|
+
"[catalogue.json] Workspace root catalogue.json must be {} when nested catalogue.json files exist below it.",
|
|
2415
2510
|
],
|
|
2416
2511
|
};
|
|
2417
2512
|
}
|
|
@@ -2437,7 +2532,7 @@ function findNearestCatalogue(startDir) {
|
|
|
2437
2532
|
const visited = new Set();
|
|
2438
2533
|
while (!visited.has(current)) {
|
|
2439
2534
|
visited.add(current);
|
|
2440
|
-
const candidate = (0, node_path_1.join)(current,
|
|
2535
|
+
const candidate = (0, node_path_1.join)(current, "catalogue.json");
|
|
2441
2536
|
if ((0, node_fs_1.existsSync)(candidate) && (0, node_fs_1.statSync)(candidate).isFile()) {
|
|
2442
2537
|
return candidate;
|
|
2443
2538
|
}
|
|
@@ -2455,70 +2550,70 @@ function resolveWorkspaceApp(options) {
|
|
|
2455
2550
|
if ((0, node_fs_1.existsSync)(absolute)) {
|
|
2456
2551
|
const stats = (0, node_fs_1.statSync)(absolute);
|
|
2457
2552
|
if (stats.isDirectory()) {
|
|
2458
|
-
const cataloguePath = (0, node_path_1.join)(absolute,
|
|
2553
|
+
const cataloguePath = (0, node_path_1.join)(absolute, "catalogue.json");
|
|
2459
2554
|
if (!(0, node_fs_1.existsSync)(cataloguePath)) {
|
|
2460
2555
|
return {
|
|
2461
|
-
kind:
|
|
2462
|
-
reason:
|
|
2556
|
+
kind: "not_found",
|
|
2557
|
+
reason: "catalogue_missing",
|
|
2463
2558
|
message: `Directory ${absolute} does not contain catalogue.json.`,
|
|
2464
2559
|
};
|
|
2465
2560
|
}
|
|
2466
|
-
return { kind:
|
|
2561
|
+
return { kind: "directory", cataloguePath };
|
|
2467
2562
|
}
|
|
2468
|
-
if (!stats.isFile() || (0, node_path_1.extname)(absolute).toLowerCase() !==
|
|
2563
|
+
if (!stats.isFile() || (0, node_path_1.extname)(absolute).toLowerCase() !== ".html") {
|
|
2469
2564
|
return {
|
|
2470
|
-
kind:
|
|
2471
|
-
reason:
|
|
2565
|
+
kind: "not_found",
|
|
2566
|
+
reason: "app_missing",
|
|
2472
2567
|
message: `Target ${absolute} is not an app HTML file.`,
|
|
2473
2568
|
};
|
|
2474
2569
|
}
|
|
2475
|
-
return { kind:
|
|
2570
|
+
return { kind: "file", htmlPath: absolute };
|
|
2476
2571
|
}
|
|
2477
2572
|
const lookup = resolveCatalogueEntries(cwd, { filterName: target });
|
|
2478
2573
|
if (lookup.errors.length > 0) {
|
|
2479
2574
|
return {
|
|
2480
|
-
kind:
|
|
2481
|
-
reason:
|
|
2575
|
+
kind: "not_found",
|
|
2576
|
+
reason: "app_missing",
|
|
2482
2577
|
message: lookup.errors[0],
|
|
2483
2578
|
};
|
|
2484
2579
|
}
|
|
2485
2580
|
if (lookup.apps.length > 1) {
|
|
2486
2581
|
return {
|
|
2487
|
-
kind:
|
|
2488
|
-
reason:
|
|
2582
|
+
kind: "not_found",
|
|
2583
|
+
reason: "multiple_apps",
|
|
2489
2584
|
message: `Multiple catalogue entries share the app name "${target}".`,
|
|
2490
2585
|
};
|
|
2491
2586
|
}
|
|
2492
2587
|
if (lookup.apps.length === 1) {
|
|
2493
|
-
return { kind:
|
|
2588
|
+
return { kind: "app", task: lookup.apps[0], warnings: lookup.warnings };
|
|
2494
2589
|
}
|
|
2495
2590
|
return {
|
|
2496
|
-
kind:
|
|
2497
|
-
reason:
|
|
2591
|
+
kind: "not_found",
|
|
2592
|
+
reason: "app_missing",
|
|
2498
2593
|
message: `App "${target}" was not found in any catalogue.json under ${cwd}.`,
|
|
2499
2594
|
};
|
|
2500
2595
|
}
|
|
2501
2596
|
const nearestCatalogue = findNearestCatalogue(cwd);
|
|
2502
2597
|
if (!nearestCatalogue) {
|
|
2503
2598
|
return {
|
|
2504
|
-
kind:
|
|
2505
|
-
reason:
|
|
2506
|
-
message:
|
|
2599
|
+
kind: "not_found",
|
|
2600
|
+
reason: "catalogue_missing",
|
|
2601
|
+
message: "Run this command inside a workspace with catalogue.json or provide a path.",
|
|
2507
2602
|
};
|
|
2508
2603
|
}
|
|
2509
2604
|
const catalogueDir = (0, node_path_1.dirname)(nearestCatalogue);
|
|
2510
2605
|
const lookup = resolveCatalogueEntries(catalogueDir);
|
|
2511
2606
|
if (lookup.errors.length > 0) {
|
|
2512
2607
|
return {
|
|
2513
|
-
kind:
|
|
2514
|
-
reason:
|
|
2608
|
+
kind: "not_found",
|
|
2609
|
+
reason: "app_missing",
|
|
2515
2610
|
message: lookup.errors[0],
|
|
2516
2611
|
};
|
|
2517
2612
|
}
|
|
2518
2613
|
if (lookup.apps.length === 0) {
|
|
2519
2614
|
return {
|
|
2520
|
-
kind:
|
|
2521
|
-
reason:
|
|
2615
|
+
kind: "not_found",
|
|
2616
|
+
reason: "no_apps",
|
|
2522
2617
|
message: `No apps found in ${nearestCatalogue}.`,
|
|
2523
2618
|
};
|
|
2524
2619
|
}
|
|
@@ -2526,21 +2621,21 @@ function resolveWorkspaceApp(options) {
|
|
|
2526
2621
|
const matched = lookup.apps.find((task) => task.name === appName);
|
|
2527
2622
|
if (!matched) {
|
|
2528
2623
|
return {
|
|
2529
|
-
kind:
|
|
2530
|
-
reason:
|
|
2624
|
+
kind: "not_found",
|
|
2625
|
+
reason: "app_missing",
|
|
2531
2626
|
message: `App "${appName}" was not found in ${nearestCatalogue}.`,
|
|
2532
2627
|
};
|
|
2533
2628
|
}
|
|
2534
|
-
return { kind:
|
|
2629
|
+
return { kind: "app", task: matched, warnings: lookup.warnings };
|
|
2535
2630
|
}
|
|
2536
2631
|
if (lookup.apps.length > 1) {
|
|
2537
2632
|
return {
|
|
2538
|
-
kind:
|
|
2539
|
-
reason:
|
|
2633
|
+
kind: "not_found",
|
|
2634
|
+
reason: "multiple_apps",
|
|
2540
2635
|
message: `Multiple apps found in ${nearestCatalogue}. Specify the app name to continue.`,
|
|
2541
2636
|
};
|
|
2542
2637
|
}
|
|
2543
|
-
return { kind:
|
|
2638
|
+
return { kind: "app", task: lookup.apps[0], warnings: lookup.warnings };
|
|
2544
2639
|
}
|
|
2545
2640
|
function formatMetadataWarnings(warnings) {
|
|
2546
2641
|
return new Set(warnings);
|
|
@@ -2551,7 +2646,7 @@ function formatMissingMetadataWarnings(task) {
|
|
|
2551
2646
|
}
|
|
2552
2647
|
return `${task.cataloguePath} (${formatMissingMetadata(task.missingMetadata)})`;
|
|
2553
2648
|
}
|
|
2554
|
-
function findAppTaskByFile(htmlPath) {
|
|
2649
|
+
function findAppTaskByFile(htmlPath, options = {}) {
|
|
2555
2650
|
const normalizedFilePath = normalizePathForComparison(htmlPath);
|
|
2556
2651
|
const visited = new Set();
|
|
2557
2652
|
let currentDir = (0, node_path_1.dirname)(normalizedFilePath);
|
|
@@ -2559,6 +2654,7 @@ function findAppTaskByFile(htmlPath) {
|
|
|
2559
2654
|
visited.add(currentDir);
|
|
2560
2655
|
const result = resolveCatalogueEntries(currentDir, {
|
|
2561
2656
|
filterFile: normalizedFilePath,
|
|
2657
|
+
listingAssets: options.listingAssets,
|
|
2562
2658
|
});
|
|
2563
2659
|
if (result.errors.length > 0) {
|
|
2564
2660
|
return { errors: result.errors, warnings: [], task: undefined };
|