@playdrop/playdrop-cli 0.5.4 → 0.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/config/client-meta.json +4 -4
- package/dist/apps/upload.js +226 -80
- package/dist/assetSpecs.d.ts +1 -0
- package/dist/assetSpecs.js +22 -1
- package/dist/assets/model-artifacts.d.ts +2 -2
- package/dist/assets/model-artifacts.js +1 -1
- package/dist/captureRuntime.d.ts +1 -0
- package/dist/captureRuntime.js +3 -2
- package/dist/catalogue.d.ts +33 -8
- package/dist/catalogue.js +364 -46
- package/dist/commandContext.d.ts +5 -1
- package/dist/commandContext.js +90 -29
- package/dist/commands/browse.d.ts +3 -0
- package/dist/commands/browse.js +90 -17
- package/dist/commands/build.js +1 -1
- package/dist/commands/capture.d.ts +3 -0
- package/dist/commands/capture.js +121 -9
- package/dist/commands/captureListing.d.ts +2 -0
- package/dist/commands/captureListing.js +157 -61
- package/dist/commands/create.js +6 -28
- package/dist/commands/createRemixContent.js +4 -26
- package/dist/commands/creations.js +2 -3
- package/dist/commands/detail.js +24 -2
- package/dist/commands/dev.d.ts +8 -1
- package/dist/commands/dev.js +180 -2
- package/dist/commands/devRuntimeAssets.d.ts +34 -0
- package/dist/commands/devRuntimeAssets.js +308 -0
- package/dist/commands/devServer.d.ts +11 -0
- package/dist/commands/devServer.js +196 -13
- package/dist/commands/init.js +6 -24
- package/dist/commands/search.d.ts +4 -0
- package/dist/commands/search.js +68 -11
- package/dist/commands/upload-content.d.ts +3 -3
- package/dist/commands/upload-content.js +19 -38
- package/dist/commands/upload.js +67 -77
- package/dist/commands/validate.js +13 -20
- package/dist/commands/whoami.js +23 -26
- package/dist/devAuth.d.ts +16 -0
- package/dist/devAuth.js +60 -0
- package/dist/index.js +22 -4
- package/dist/taskSelection.js +4 -3
- package/dist/taskUtils.d.ts +2 -2
- package/dist/taskUtils.js +1 -1
- package/dist/uploadLog.d.ts +1 -1
- package/node_modules/@playdrop/ai-client/package.json +1 -1
- package/node_modules/@playdrop/api-client/dist/client.d.ts +44 -114
- package/node_modules/@playdrop/api-client/dist/client.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/client.js +22 -0
- package/node_modules/@playdrop/api-client/dist/domains/admin.d.ts +2 -1
- package/node_modules/@playdrop/api-client/dist/domains/admin.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/admin.js +11 -0
- package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts +11 -19
- package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/apps.js +116 -106
- package/node_modules/@playdrop/api-client/dist/domains/assets.d.ts +2 -1
- package/node_modules/@playdrop/api-client/dist/domains/assets.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/assets.js +13 -0
- package/node_modules/@playdrop/api-client/dist/domains/payments.d.ts +5 -5
- package/node_modules/@playdrop/api-client/dist/domains/payments.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/payments.js +8 -8
- package/node_modules/@playdrop/api-client/dist/domains/search.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/search.js +24 -2
- package/node_modules/@playdrop/api-client/dist/domains/tags.d.ts +13 -1
- package/node_modules/@playdrop/api-client/dist/domains/tags.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/tags.js +52 -0
- package/node_modules/@playdrop/api-client/dist/index.d.ts +28 -29
- package/node_modules/@playdrop/api-client/dist/index.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/index.js +26 -8
- package/node_modules/@playdrop/api-client/package.json +1 -1
- package/node_modules/@playdrop/boxel-core/package.json +1 -1
- package/node_modules/@playdrop/boxel-three/package.json +1 -1
- package/node_modules/@playdrop/config/client-meta.json +4 -4
- package/node_modules/@playdrop/config/package.json +1 -1
- package/node_modules/@playdrop/types/dist/api.d.ts +130 -3
- package/node_modules/@playdrop/types/dist/api.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/api.js +23 -0
- package/node_modules/@playdrop/types/dist/app-capability-filters.d.ts +24 -0
- package/node_modules/@playdrop/types/dist/app-capability-filters.d.ts.map +1 -0
- package/node_modules/@playdrop/types/dist/app-capability-filters.js +72 -0
- package/node_modules/@playdrop/types/dist/asset-pack.d.ts +3 -2
- package/node_modules/@playdrop/types/dist/asset-pack.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/asset.d.ts +2 -3
- package/node_modules/@playdrop/types/dist/asset.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/asset.js +1 -1
- package/node_modules/@playdrop/types/dist/index.d.ts +2 -0
- package/node_modules/@playdrop/types/dist/index.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/index.js +2 -0
- package/node_modules/@playdrop/types/dist/owned-assets.d.ts +21 -0
- package/node_modules/@playdrop/types/dist/owned-assets.d.ts.map +1 -0
- package/node_modules/@playdrop/types/dist/owned-assets.js +35 -0
- package/node_modules/@playdrop/types/dist/player-meta.d.ts +28 -0
- package/node_modules/@playdrop/types/dist/player-meta.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/version.d.ts +111 -1
- package/node_modules/@playdrop/types/dist/version.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/version.js +4 -0
- package/node_modules/@playdrop/types/package.json +1 -1
- package/node_modules/@playdrop/vox-three/package.json +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -36,7 +36,8 @@ playdrop project publish .
|
|
|
36
36
|
- CLI docs: [playdrop.ai/docs/cli](https://www.playdrop.ai/docs/cli)
|
|
37
37
|
- Runtime docs: [playdrop.ai/docs/runtime](https://www.playdrop.ai/docs/runtime)
|
|
38
38
|
- Templates and demos: [playdrop.ai/docs/examples#templates-and-demos](https://www.playdrop.ai/docs/examples#templates-and-demos)
|
|
39
|
-
-
|
|
39
|
+
- Canonical public plugin repo: [github.com/playdrop-ai/playdrop-plugin](https://github.com/playdrop-ai/playdrop-plugin)
|
|
40
|
+
- Legacy public skill surface: [skills.sh/playdrop-ai/playdrop-skills/playdrop](https://skills.sh/playdrop-ai/playdrop-skills/playdrop)
|
|
40
41
|
|
|
41
42
|
## Live Examples
|
|
42
43
|
|
package/config/client-meta.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.5.
|
|
2
|
+
"version": "0.5.6",
|
|
3
3
|
"build": 1,
|
|
4
4
|
"platforms": {
|
|
5
5
|
"ios": {
|
|
@@ -26,11 +26,11 @@
|
|
|
26
26
|
},
|
|
27
27
|
"clients": {
|
|
28
28
|
"web": {
|
|
29
|
-
"minimumVersion": "0.5.
|
|
29
|
+
"minimumVersion": "0.5.6",
|
|
30
30
|
"minimumBuild": 1
|
|
31
31
|
},
|
|
32
32
|
"admin": {
|
|
33
|
-
"minimumVersion": "0.5.
|
|
33
|
+
"minimumVersion": "0.5.6",
|
|
34
34
|
"minimumBuild": 1
|
|
35
35
|
},
|
|
36
36
|
"apple": {
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"minimumBuild": 1
|
|
39
39
|
},
|
|
40
40
|
"cli": {
|
|
41
|
-
"minimumVersion": "0.5.
|
|
41
|
+
"minimumVersion": "0.5.6"
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
}
|
package/dist/apps/upload.js
CHANGED
|
@@ -1,11 +1,28 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.uploadApp = uploadApp;
|
|
4
|
+
const node_crypto_1 = require("node:crypto");
|
|
4
5
|
const types_1 = require("@playdrop/types");
|
|
5
6
|
const config_1 = require("@playdrop/config");
|
|
6
7
|
const node_fs_1 = require("node:fs");
|
|
7
8
|
const node_path_1 = require("node:path");
|
|
8
9
|
const http_1 = require("../http");
|
|
10
|
+
const EXTENSION_TO_MIME = {
|
|
11
|
+
'.png': 'image/png',
|
|
12
|
+
'.jpg': 'image/jpeg',
|
|
13
|
+
'.jpeg': 'image/jpeg',
|
|
14
|
+
'.webp': 'image/webp',
|
|
15
|
+
'.gif': 'image/gif',
|
|
16
|
+
'.mp4': 'video/mp4',
|
|
17
|
+
'.webm': 'video/webm',
|
|
18
|
+
'.mp3': 'audio/mpeg',
|
|
19
|
+
'.wav': 'audio/wav',
|
|
20
|
+
'.ogg': 'audio/ogg',
|
|
21
|
+
'.json': 'application/json',
|
|
22
|
+
'.gltf': 'model/gltf+json',
|
|
23
|
+
'.glb': 'model/gltf-binary',
|
|
24
|
+
'.vox': 'application/octet-stream',
|
|
25
|
+
};
|
|
9
26
|
function isPngSignature(content) {
|
|
10
27
|
if (content.length < 8) {
|
|
11
28
|
return false;
|
|
@@ -50,6 +67,59 @@ function createFileFromPath(filePath, mimeType, maxBytes, sizeCode, typeCode, la
|
|
|
50
67
|
size: content.length,
|
|
51
68
|
};
|
|
52
69
|
}
|
|
70
|
+
async function bufferFromFile(file) {
|
|
71
|
+
return Buffer.from(await file.arrayBuffer());
|
|
72
|
+
}
|
|
73
|
+
function computeSha256Hex(buffer) {
|
|
74
|
+
return (0, node_crypto_1.createHash)('sha256').update(buffer).digest('hex');
|
|
75
|
+
}
|
|
76
|
+
function resolveMimeType(filename, fallback = 'application/octet-stream') {
|
|
77
|
+
const type = EXTENSION_TO_MIME[(0, node_path_1.extname)(filename).toLowerCase()];
|
|
78
|
+
return type ?? fallback;
|
|
79
|
+
}
|
|
80
|
+
async function buildPreparedSessionFile(fileKey, file) {
|
|
81
|
+
const buffer = await bufferFromFile(file);
|
|
82
|
+
return {
|
|
83
|
+
fileKey,
|
|
84
|
+
file,
|
|
85
|
+
filename: file.name,
|
|
86
|
+
contentType: file.type || resolveMimeType(file.name),
|
|
87
|
+
sizeBytes: buffer.length,
|
|
88
|
+
sha256: computeSha256Hex(buffer),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function createGenericFileFromPath(filePath) {
|
|
92
|
+
const content = (0, node_fs_1.readFileSync)(filePath);
|
|
93
|
+
const name = (0, node_path_1.basename)(filePath);
|
|
94
|
+
return new File([content], name, { type: resolveMimeType(name) });
|
|
95
|
+
}
|
|
96
|
+
async function prepareOwnedAssetUpload(task) {
|
|
97
|
+
const files = await Promise.all(Object.entries(task.filePaths).map(async ([role, filePath]) => {
|
|
98
|
+
const file = createGenericFileFromPath(filePath);
|
|
99
|
+
const buffer = await bufferFromFile(file);
|
|
100
|
+
return {
|
|
101
|
+
role,
|
|
102
|
+
file,
|
|
103
|
+
filename: file.name,
|
|
104
|
+
contentType: file.type || resolveMimeType(file.name),
|
|
105
|
+
sizeBytes: buffer.length,
|
|
106
|
+
sha256: computeSha256Hex(buffer),
|
|
107
|
+
};
|
|
108
|
+
}));
|
|
109
|
+
return {
|
|
110
|
+
uploadKey: task.name,
|
|
111
|
+
runtimeKey: task.runtimeKey,
|
|
112
|
+
name: task.name,
|
|
113
|
+
category: task.category,
|
|
114
|
+
subcategory: task.subcategory,
|
|
115
|
+
format: task.format || (0, node_path_1.extname)(files[0]?.filename || '').replace(/^\./, '').toUpperCase() || 'GLB',
|
|
116
|
+
visibility: task.visibility,
|
|
117
|
+
assetSpec: task.assetSpec,
|
|
118
|
+
shopListed: task.shopListed,
|
|
119
|
+
shopPriceCredits: task.shopPriceCredits,
|
|
120
|
+
files,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
53
123
|
// eslint-disable-next-line complexity -- Source archive failures need distinct guidance when publish-only media is already excluded.
|
|
54
124
|
function ensurePreparedArtifactSize(file, maxBytes, code, label, task) {
|
|
55
125
|
if (!file) {
|
|
@@ -109,21 +179,99 @@ async function uploadAppVersion(client, task, artifacts, options) {
|
|
|
109
179
|
const achievements = task.achievements ?? [];
|
|
110
180
|
const leaderboards = task.leaderboards ?? [];
|
|
111
181
|
const listing = task.listing;
|
|
112
|
-
const
|
|
182
|
+
const preparedSessionFiles = [];
|
|
183
|
+
const preparedOwnedAssets = await Promise.all(task.ownedAssets.map((ownedAsset) => prepareOwnedAssetUpload(ownedAsset)));
|
|
184
|
+
for (const ownedAsset of preparedOwnedAssets) {
|
|
185
|
+
for (const file of ownedAsset.files) {
|
|
186
|
+
totalUploadBytes = accumulateUploadBytes(task.name, totalUploadBytes, file.sizeBytes);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
for (const definition of achievements) {
|
|
190
|
+
const prepared = createFileFromPath(definition.iconPath, 'image/png', config_1.MAX_VERSION_ICON_BYTES, 'achievement_icon_too_large', 'invalid_achievement_icon_content_type', `Achievement icon "${definition.key}"`, isPngSignature);
|
|
191
|
+
totalUploadBytes = pushPreparedFile(task.name, totalUploadBytes, prepared);
|
|
192
|
+
preparedSessionFiles.push(await buildPreparedSessionFile(`achievementIcon:${definition.key}`, prepared.file));
|
|
193
|
+
}
|
|
194
|
+
if (task.hostingMode === 'EXTERNAL') {
|
|
195
|
+
// no hosted files
|
|
196
|
+
}
|
|
197
|
+
else if (artifacts) {
|
|
198
|
+
totalUploadBytes = pushPreparedArtifact(task, totalUploadBytes, artifacts.bundleFile, config_1.MAX_VERSION_BUNDLE_BYTES, 'bundle_too_large', 'Bundle');
|
|
199
|
+
totalUploadBytes = pushPreparedArtifact(task, totalUploadBytes, artifacts.sourceFile, config_1.MAX_APP_SOURCE_BYTES, 'source_too_large', 'Source archive');
|
|
200
|
+
preparedSessionFiles.push(await buildPreparedSessionFile('bundle', artifacts.bundleFile));
|
|
201
|
+
preparedSessionFiles.push(await buildPreparedSessionFile('source', artifacts.sourceFile));
|
|
202
|
+
if (!options?.skipEcs) {
|
|
203
|
+
if (artifacts.ecsFile) {
|
|
204
|
+
totalUploadBytes = pushPreparedArtifact(task, totalUploadBytes, artifacts.ecsFile, config_1.MAX_APP_ECS_BYTES, 'ecs_too_large', 'ECS config');
|
|
205
|
+
preparedSessionFiles.push(await buildPreparedSessionFile('ecs', artifacts.ecsFile));
|
|
206
|
+
}
|
|
207
|
+
if (artifacts.serverFile) {
|
|
208
|
+
totalUploadBytes = pushPreparedArtifact(task, totalUploadBytes, artifacts.serverFile, config_1.MAX_APP_SERVER_BYTES, 'server_too_large', 'Server.js');
|
|
209
|
+
preparedSessionFiles.push(await buildPreparedSessionFile('server', artifacts.serverFile));
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (listing?.iconPath) {
|
|
214
|
+
const prepared = createFileFromPath(listing.iconPath, 'image/png', config_1.MAX_VERSION_ICON_BYTES, 'icon_too_large', 'invalid_icon_content_type', 'Icon', isPngSignature);
|
|
215
|
+
totalUploadBytes = pushPreparedFile(task.name, totalUploadBytes, prepared);
|
|
216
|
+
preparedSessionFiles.push(await buildPreparedSessionFile('icon', prepared.file));
|
|
217
|
+
}
|
|
218
|
+
if (listing?.heroPortraitPath) {
|
|
219
|
+
const prepared = createFileFromPath(listing.heroPortraitPath, 'image/png', config_1.MAX_VERSION_HERO_BYTES, 'hero_portrait_too_large', 'invalid_hero_portrait_content_type', 'Hero portrait', isPngSignature);
|
|
220
|
+
totalUploadBytes = pushPreparedFile(task.name, totalUploadBytes, prepared);
|
|
221
|
+
preparedSessionFiles.push(await buildPreparedSessionFile('heroPortrait', prepared.file));
|
|
222
|
+
}
|
|
223
|
+
if (listing?.heroLandscapePath) {
|
|
224
|
+
const prepared = createFileFromPath(listing.heroLandscapePath, 'image/png', config_1.MAX_VERSION_HERO_BYTES, 'hero_landscape_too_large', 'invalid_hero_landscape_content_type', 'Hero landscape', isPngSignature);
|
|
225
|
+
totalUploadBytes = pushPreparedFile(task.name, totalUploadBytes, prepared);
|
|
226
|
+
preparedSessionFiles.push(await buildPreparedSessionFile('heroLandscape', prepared.file));
|
|
227
|
+
}
|
|
228
|
+
for (let index = 0; index < (listing?.screenshotPortraitPaths ?? []).length; index += 1) {
|
|
229
|
+
const filePath = listing.screenshotPortraitPaths[index];
|
|
230
|
+
const prepared = createFileFromPath(filePath, 'image/png', config_1.MAX_VERSION_SCREENSHOT_BYTES, 'screenshot_portrait_too_large', 'invalid_screenshot_portrait_content_type', `Portrait screenshot ${index + 1}`, isPngSignature);
|
|
231
|
+
totalUploadBytes = pushPreparedFile(task.name, totalUploadBytes, prepared);
|
|
232
|
+
preparedSessionFiles.push(await buildPreparedSessionFile(`screenshotsPortrait:${index}`, prepared.file));
|
|
233
|
+
}
|
|
234
|
+
for (let index = 0; index < (listing?.screenshotLandscapePaths ?? []).length; index += 1) {
|
|
235
|
+
const filePath = listing.screenshotLandscapePaths[index];
|
|
236
|
+
const prepared = createFileFromPath(filePath, 'image/png', config_1.MAX_VERSION_SCREENSHOT_BYTES, 'screenshot_landscape_too_large', 'invalid_screenshot_landscape_content_type', `Landscape screenshot ${index + 1}`, isPngSignature);
|
|
237
|
+
totalUploadBytes = pushPreparedFile(task.name, totalUploadBytes, prepared);
|
|
238
|
+
preparedSessionFiles.push(await buildPreparedSessionFile(`screenshotsLandscape:${index}`, prepared.file));
|
|
239
|
+
}
|
|
240
|
+
for (let index = 0; index < (listing?.videoPortraitPaths ?? []).length; index += 1) {
|
|
241
|
+
const filePath = listing.videoPortraitPaths[index];
|
|
242
|
+
const prepared = createFileFromPath(filePath, 'video/mp4', config_1.MAX_VERSION_VIDEO_BYTES, 'video_portrait_too_large', 'invalid_video_portrait_content_type', `Portrait video ${index + 1}`, isMp4Signature);
|
|
243
|
+
totalUploadBytes = pushPreparedFile(task.name, totalUploadBytes, prepared);
|
|
244
|
+
preparedSessionFiles.push(await buildPreparedSessionFile(`videosPortrait:${index}`, prepared.file));
|
|
245
|
+
}
|
|
246
|
+
for (let index = 0; index < (listing?.videoLandscapePaths ?? []).length; index += 1) {
|
|
247
|
+
const filePath = listing.videoLandscapePaths[index];
|
|
248
|
+
const prepared = createFileFromPath(filePath, 'video/mp4', config_1.MAX_VERSION_VIDEO_BYTES, 'video_landscape_too_large', 'invalid_video_landscape_content_type', `Landscape video ${index + 1}`, isMp4Signature);
|
|
249
|
+
totalUploadBytes = pushPreparedFile(task.name, totalUploadBytes, prepared);
|
|
250
|
+
preparedSessionFiles.push(await buildPreparedSessionFile(`videosLandscape:${index}`, prepared.file));
|
|
251
|
+
}
|
|
252
|
+
const initializeRequest = {
|
|
113
253
|
version: task.version,
|
|
114
254
|
releaseNotes: task.releaseNotes,
|
|
115
255
|
visibility: task.versionVisibility,
|
|
116
256
|
remixRef: task.remix,
|
|
257
|
+
usesAssets: task.uses.assets.map((entry) => ({
|
|
258
|
+
ref: entry.ref,
|
|
259
|
+
...(entry.runtimeKey ? { runtimeKey: entry.runtimeKey } : {}),
|
|
260
|
+
})),
|
|
261
|
+
usesPacks: task.uses.packs,
|
|
117
262
|
tags: task.tags,
|
|
118
263
|
clearTags: options?.clearTags,
|
|
119
264
|
surfaceTargets: task.surfaceTargets,
|
|
120
|
-
assetSpecSupport: task.assetSpecSupport
|
|
265
|
+
assetSpecSupport: (task.assetSpecSupport ?? []).map((row) => ({
|
|
266
|
+
assetSpecRef: row.assetSpec,
|
|
267
|
+
versionRange: row.versionRange,
|
|
268
|
+
capabilities: row.capabilities,
|
|
269
|
+
})),
|
|
121
270
|
entryPoint: artifacts?.metadata?.entryPoint ?? undefined,
|
|
122
271
|
authMode: task.authMode,
|
|
123
272
|
controllerMode: task.controllerMode,
|
|
124
273
|
previewable: task.previewable,
|
|
125
274
|
skipReview: options?.skipReview,
|
|
126
|
-
// App metadata (used when creating app if it doesn't exist)
|
|
127
275
|
displayName: task.displayName,
|
|
128
276
|
description: task.description,
|
|
129
277
|
type: task.type,
|
|
@@ -148,93 +296,91 @@ async function uploadAppVersion(client, task, artifacts, options) {
|
|
|
148
296
|
...(definition.unitLabel ? { unitLabel: definition.unitLabel } : {}),
|
|
149
297
|
status: definition.status,
|
|
150
298
|
})),
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}),
|
|
299
|
+
externalUrl: task.hostingMode === 'EXTERNAL' ? task.externalUrl : undefined,
|
|
300
|
+
files: preparedSessionFiles.map((file) => ({
|
|
301
|
+
fileKey: file.fileKey,
|
|
302
|
+
filename: file.filename,
|
|
303
|
+
contentType: file.contentType,
|
|
304
|
+
sizeBytes: file.sizeBytes,
|
|
305
|
+
sha256: file.sha256,
|
|
306
|
+
})),
|
|
307
|
+
ownedAssets: preparedOwnedAssets.map((ownedAsset) => ({
|
|
308
|
+
uploadKey: ownedAsset.uploadKey,
|
|
309
|
+
runtimeKey: ownedAsset.runtimeKey,
|
|
310
|
+
name: ownedAsset.name,
|
|
311
|
+
category: ownedAsset.category,
|
|
312
|
+
subcategory: ownedAsset.subcategory,
|
|
313
|
+
format: ownedAsset.format,
|
|
314
|
+
assetSpec: ownedAsset.assetSpec,
|
|
315
|
+
visibility: ownedAsset.visibility,
|
|
316
|
+
shopListed: ownedAsset.shopListed,
|
|
317
|
+
shopPriceCredits: ownedAsset.shopPriceCredits,
|
|
318
|
+
files: ownedAsset.files.map((file) => ({
|
|
319
|
+
role: file.role,
|
|
320
|
+
filename: file.filename,
|
|
321
|
+
contentType: file.contentType,
|
|
322
|
+
sizeBytes: file.sizeBytes,
|
|
323
|
+
sha256: file.sha256,
|
|
324
|
+
})),
|
|
325
|
+
})),
|
|
159
326
|
};
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
327
|
+
let sessionId = null;
|
|
328
|
+
try {
|
|
329
|
+
const initialized = await client.initializeAppUpload(creatorUsername, task.name, initializeRequest);
|
|
330
|
+
sessionId = initialized.session.id;
|
|
331
|
+
if (initialized.status !== 'finalized') {
|
|
332
|
+
const uploadedFileKeys = new Set(initialized.session.uploadedFileKeys);
|
|
333
|
+
const uploadedOwnedAssetKeys = new Set(initialized.session.uploadedOwnedAssetKeys);
|
|
334
|
+
for (const file of preparedSessionFiles) {
|
|
335
|
+
if (uploadedFileKeys.has(file.fileKey)) {
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
await client.uploadAppSessionFile(creatorUsername, task.name, sessionId, file.fileKey, {
|
|
339
|
+
file: file.file,
|
|
340
|
+
filename: file.filename,
|
|
341
|
+
});
|
|
175
342
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
343
|
+
for (const ownedAsset of preparedOwnedAssets) {
|
|
344
|
+
if (uploadedOwnedAssetKeys.has(ownedAsset.uploadKey)) {
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
347
|
+
await client.uploadAppSessionOwnedAsset(creatorUsername, task.name, sessionId, ownedAsset.uploadKey, {
|
|
348
|
+
files: ownedAsset.files.map((file) => ({
|
|
349
|
+
fieldName: file.role,
|
|
350
|
+
file: file.file,
|
|
351
|
+
filename: file.filename,
|
|
352
|
+
})),
|
|
353
|
+
});
|
|
179
354
|
}
|
|
180
355
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
if (
|
|
185
|
-
|
|
186
|
-
totalUploadBytes = pushPreparedFile(task.name, totalUploadBytes, prepared);
|
|
187
|
-
versionOptions.icon = prepared.file;
|
|
188
|
-
}
|
|
189
|
-
if (listing.heroPortraitPath) {
|
|
190
|
-
const prepared = createFileFromPath(listing.heroPortraitPath, 'image/png', config_1.MAX_VERSION_HERO_BYTES, 'hero_portrait_too_large', 'invalid_hero_portrait_content_type', 'Hero portrait', isPngSignature);
|
|
191
|
-
totalUploadBytes = pushPreparedFile(task.name, totalUploadBytes, prepared);
|
|
192
|
-
versionOptions.heroPortrait = prepared.file;
|
|
193
|
-
}
|
|
194
|
-
if (listing.heroLandscapePath) {
|
|
195
|
-
const prepared = createFileFromPath(listing.heroLandscapePath, 'image/png', config_1.MAX_VERSION_HERO_BYTES, 'hero_landscape_too_large', 'invalid_hero_landscape_content_type', 'Hero landscape', isPngSignature);
|
|
196
|
-
totalUploadBytes = pushPreparedFile(task.name, totalUploadBytes, prepared);
|
|
197
|
-
versionOptions.heroLandscape = prepared.file;
|
|
198
|
-
}
|
|
199
|
-
if ((listing.screenshotPortraitPaths ?? []).length > 0) {
|
|
200
|
-
versionOptions.screenshotsPortrait = listing.screenshotPortraitPaths.map((filePath, index) => {
|
|
201
|
-
const prepared = createFileFromPath(filePath, 'image/png', config_1.MAX_VERSION_SCREENSHOT_BYTES, 'screenshot_portrait_too_large', 'invalid_screenshot_portrait_content_type', `Portrait screenshot ${index + 1}`, isPngSignature);
|
|
202
|
-
totalUploadBytes = pushPreparedFile(task.name, totalUploadBytes, prepared);
|
|
203
|
-
return prepared.file;
|
|
204
|
-
});
|
|
356
|
+
const finalized = initialized.status === 'finalized'
|
|
357
|
+
? initialized.finalized
|
|
358
|
+
: await client.finalizeAppUpload(creatorUsername, task.name, sessionId);
|
|
359
|
+
if (!finalized) {
|
|
360
|
+
throw new Error(`Upload failed for ${task.name}: missing finalized app upload payload`);
|
|
205
361
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
}
|
|
213
|
-
if ((listing.videoPortraitPaths ?? []).length > 0) {
|
|
214
|
-
versionOptions.videosPortrait = listing.videoPortraitPaths.map((filePath, index) => {
|
|
215
|
-
const prepared = createFileFromPath(filePath, 'video/mp4', config_1.MAX_VERSION_VIDEO_BYTES, 'video_portrait_too_large', 'invalid_video_portrait_content_type', `Portrait video ${index + 1}`, isMp4Signature);
|
|
216
|
-
totalUploadBytes = pushPreparedFile(task.name, totalUploadBytes, prepared);
|
|
217
|
-
return prepared.file;
|
|
218
|
-
});
|
|
219
|
-
}
|
|
220
|
-
if ((listing.videoLandscapePaths ?? []).length > 0) {
|
|
221
|
-
versionOptions.videosLandscape = listing.videoLandscapePaths.map((filePath, index) => {
|
|
222
|
-
const prepared = createFileFromPath(filePath, 'video/mp4', config_1.MAX_VERSION_VIDEO_BYTES, 'video_landscape_too_large', 'invalid_video_landscape_content_type', `Landscape video ${index + 1}`, isMp4Signature);
|
|
223
|
-
totalUploadBytes = pushPreparedFile(task.name, totalUploadBytes, prepared);
|
|
224
|
-
return prepared.file;
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
try {
|
|
229
|
-
const response = await client.uploadAppVersion(creatorUsername, task.name, versionOptions);
|
|
362
|
+
const version = initialized.status === 'finalized'
|
|
363
|
+
? finalized?.version
|
|
364
|
+
: finalized.version;
|
|
365
|
+
const versionNodeId = initialized.status === 'finalized'
|
|
366
|
+
? finalized?.versionNodeId
|
|
367
|
+
: finalized.versionNodeId;
|
|
230
368
|
return {
|
|
231
369
|
versionCreated: true,
|
|
232
|
-
version:
|
|
233
|
-
versionId: typeof
|
|
234
|
-
versionNodeId: typeof
|
|
370
|
+
version: version?.version,
|
|
371
|
+
versionId: typeof version?.id === 'number' ? version.id : undefined,
|
|
372
|
+
versionNodeId: typeof versionNodeId === 'string' ? versionNodeId : undefined,
|
|
235
373
|
};
|
|
236
374
|
}
|
|
237
375
|
catch (unknownError) {
|
|
376
|
+
if (sessionId) {
|
|
377
|
+
try {
|
|
378
|
+
await client.abortAppUpload(creatorUsername, task.name, sessionId);
|
|
379
|
+
}
|
|
380
|
+
catch {
|
|
381
|
+
// keep original error
|
|
382
|
+
}
|
|
383
|
+
}
|
|
238
384
|
if (unknownError instanceof types_1.UnsupportedClientError) {
|
|
239
385
|
(0, http_1.handleUnsupportedError)(unknownError, 'Upload app version');
|
|
240
386
|
}
|
package/dist/assetSpecs.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type AssetSpecCapability, type AssetSpecContract, type AssetSpecValidationKind } from '@playdrop/types';
|
|
2
|
+
export declare function resolveCustomAssetRoleContentType(filePath: string, roleContract?: Pick<AssetSpecContract['roles'][number], 'contentTypes'> | null): string;
|
|
2
3
|
export type AssetSpecSupportDeclaration = {
|
|
3
4
|
assetSpec: string;
|
|
4
5
|
versionRange: string;
|
package/dist/assetSpecs.js
CHANGED
|
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.resolveCustomAssetRoleContentType = resolveCustomAssetRoleContentType;
|
|
6
7
|
exports.normalizeAssetSpecSupportEntries = normalizeAssetSpecSupportEntries;
|
|
7
8
|
exports.loadAndValidateAssetSpecContract = loadAndValidateAssetSpecContract;
|
|
8
9
|
exports.validateCustomAssetFilesAgainstContract = validateCustomAssetFilesAgainstContract;
|
|
@@ -54,6 +55,26 @@ function createContractValidator() {
|
|
|
54
55
|
function resolveMimeTypeFromPath(filePath) {
|
|
55
56
|
return LOCAL_EXTENSION_TO_MIME[(0, node_path_1.extname)(filePath).toLowerCase()] ?? 'application/octet-stream';
|
|
56
57
|
}
|
|
58
|
+
function resolveCustomAssetRoleContentType(filePath, roleContract) {
|
|
59
|
+
const resolvedContentType = resolveMimeTypeFromPath(filePath);
|
|
60
|
+
if (!roleContract) {
|
|
61
|
+
return resolvedContentType;
|
|
62
|
+
}
|
|
63
|
+
const normalizedResolvedContentType = resolvedContentType.toLowerCase();
|
|
64
|
+
for (const contentType of roleContract.contentTypes) {
|
|
65
|
+
const normalizedContentType = contentType.trim().toLowerCase();
|
|
66
|
+
if (normalizedContentType === normalizedResolvedContentType) {
|
|
67
|
+
return contentType.trim();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if ((0, node_path_1.extname)(filePath).toLowerCase() === '.json') {
|
|
71
|
+
const jsonLikeContentType = roleContract.contentTypes.find((contentType) => (0, types_1.isJsonLikeAssetSpecContentType)(contentType));
|
|
72
|
+
if (jsonLikeContentType) {
|
|
73
|
+
return jsonLikeContentType.trim();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return resolvedContentType;
|
|
77
|
+
}
|
|
57
78
|
// eslint-disable-next-line complexity
|
|
58
79
|
function normalizeAssetSpecSupportEntries(raw, errors, context) {
|
|
59
80
|
if (raw === undefined || raw === null) {
|
|
@@ -211,7 +232,7 @@ function validateCustomAssetFilesAgainstContract(contract, filePaths, errors, co
|
|
|
211
232
|
errors.push(`${context} role "${rawRole}" must use one of ${allowedExtensions.join(', ')}.`);
|
|
212
233
|
}
|
|
213
234
|
}
|
|
214
|
-
const resolvedContentType =
|
|
235
|
+
const resolvedContentType = resolveCustomAssetRoleContentType(filePath, roleContract);
|
|
215
236
|
const normalizedContentTypes = roleContract.contentTypes.map((value) => value.trim().toLowerCase());
|
|
216
237
|
if (!normalizedContentTypes.includes(resolvedContentType.toLowerCase())) {
|
|
217
238
|
errors.push(`${context} role "${rawRole}" must use one of ${normalizedContentTypes.join(', ')}.`);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { AssetTask,
|
|
2
|
-
type ModelAssetTask = AssetTask |
|
|
1
|
+
import type { AssetTask, OwnedAssetTask, PackOwnedAssetTask } from '../catalogue';
|
|
2
|
+
type ModelAssetTask = AssetTask | OwnedAssetTask | PackOwnedAssetTask;
|
|
3
3
|
export declare function isGeneratedModel3DAssetTask(task: ModelAssetTask, format?: string): boolean;
|
|
4
4
|
export declare function assertGeneratedModel3DAssetRoles(task: ModelAssetTask, format?: string): void;
|
|
5
5
|
export declare function prepareModel3DAssetArtifacts(task: ModelAssetTask, options?: {
|
|
@@ -100,7 +100,7 @@ function normalizeRole(role) {
|
|
|
100
100
|
return role.trim().toLowerCase();
|
|
101
101
|
}
|
|
102
102
|
function resolveTaskLabel(task) {
|
|
103
|
-
if (task.kind === '
|
|
103
|
+
if ('kind' in task && task.kind === 'owned-asset') {
|
|
104
104
|
return `${task.appName}:${task.name}`;
|
|
105
105
|
}
|
|
106
106
|
return task.name;
|
package/dist/captureRuntime.d.ts
CHANGED
package/dist/captureRuntime.js
CHANGED
|
@@ -114,8 +114,9 @@ async function runCapture(options) {
|
|
|
114
114
|
const outputLines = [];
|
|
115
115
|
const expectedUrl = options.expectedUrl ? normalizeComparableUrl(options.expectedUrl) : null;
|
|
116
116
|
const hasExplicitLogin = Boolean(options.login?.username);
|
|
117
|
-
const
|
|
118
|
-
const
|
|
117
|
+
const shouldBootstrapSavedSession = options.savedSessionBootstrap !== false && !hasExplicitLogin;
|
|
118
|
+
const bootstrapToken = shouldBootstrapSavedSession ? options.token?.trim() || null : null;
|
|
119
|
+
const bootstrapUser = shouldBootstrapSavedSession ? options.user ?? null : null;
|
|
119
120
|
let finalUrl = options.targetUrl;
|
|
120
121
|
const record = (level, message) => {
|
|
121
122
|
const line = `[capture][${level}] ${message}`;
|
package/dist/catalogue.d.ts
CHANGED
|
@@ -53,7 +53,7 @@ export type AppCatalogueEntry = {
|
|
|
53
53
|
username?: string;
|
|
54
54
|
achievements?: AppAchievementCatalogueDefinition[];
|
|
55
55
|
leaderboards?: AppLeaderboardCatalogueDefinition[];
|
|
56
|
-
|
|
56
|
+
ownedAssets?: OwnedAssetCatalogueEntry[];
|
|
57
57
|
assetSpecSupport?: AppMetadataAssetSpecSupport[];
|
|
58
58
|
uses?: {
|
|
59
59
|
assets?: string[];
|
|
@@ -99,8 +99,9 @@ export type AssetSpecCatalogueEntry = {
|
|
|
99
99
|
successorVersion?: string;
|
|
100
100
|
username?: string;
|
|
101
101
|
};
|
|
102
|
-
export type
|
|
102
|
+
export type OwnedAssetCatalogueEntry = {
|
|
103
103
|
name: string;
|
|
104
|
+
runtimeKey?: string;
|
|
104
105
|
category?: string;
|
|
105
106
|
subcategory?: string;
|
|
106
107
|
format?: string;
|
|
@@ -115,6 +116,7 @@ export type AssetPackCatalogueEntry = {
|
|
|
115
116
|
remix?: string;
|
|
116
117
|
username?: string;
|
|
117
118
|
assets?: string[];
|
|
119
|
+
ownedAssets?: OwnedAssetCatalogueEntry[];
|
|
118
120
|
hostingMode?: string;
|
|
119
121
|
externalUrl?: string;
|
|
120
122
|
downloadUrl?: string;
|
|
@@ -145,6 +147,10 @@ export type ResolvedListingAssets = {
|
|
|
145
147
|
videoPortraitPaths: string[];
|
|
146
148
|
videoLandscapePaths: string[];
|
|
147
149
|
};
|
|
150
|
+
export type AppDependencyAssetRef = {
|
|
151
|
+
ref: string;
|
|
152
|
+
runtimeKey?: string;
|
|
153
|
+
};
|
|
148
154
|
export type AppTask = {
|
|
149
155
|
kind: 'app';
|
|
150
156
|
name: string;
|
|
@@ -181,10 +187,10 @@ export type AppTask = {
|
|
|
181
187
|
achievements: ResolvedAppAchievementDefinition[];
|
|
182
188
|
leaderboards: ResolvedAppLeaderboardDefinition[];
|
|
183
189
|
remix?: string;
|
|
184
|
-
|
|
190
|
+
ownedAssets: OwnedAssetTask[];
|
|
185
191
|
assetSpecSupport: AssetSpecSupportDeclaration[];
|
|
186
192
|
uses: {
|
|
187
|
-
assets:
|
|
193
|
+
assets: AppDependencyAssetRef[];
|
|
188
194
|
packs: string[];
|
|
189
195
|
};
|
|
190
196
|
graph: {
|
|
@@ -211,6 +217,7 @@ export type AssetTask = {
|
|
|
211
217
|
shopPriceCredits?: number;
|
|
212
218
|
files: Record<string, string>;
|
|
213
219
|
filePaths: Record<string, string>;
|
|
220
|
+
assetSpecContract?: AssetSpecContract;
|
|
214
221
|
relations?: Array<{
|
|
215
222
|
type: string;
|
|
216
223
|
to: string;
|
|
@@ -236,13 +243,30 @@ export type AssetSpecTask = {
|
|
|
236
243
|
documentationPath?: string;
|
|
237
244
|
contract: AssetSpecContract;
|
|
238
245
|
};
|
|
239
|
-
export type
|
|
240
|
-
kind: '
|
|
246
|
+
export type OwnedAssetTask = {
|
|
247
|
+
kind: 'owned-asset';
|
|
241
248
|
appName: string;
|
|
242
249
|
name: string;
|
|
250
|
+
runtimeKey?: string;
|
|
243
251
|
cataloguePath: string;
|
|
244
252
|
category: string;
|
|
245
|
-
subcategory: string;
|
|
253
|
+
subcategory: string | null;
|
|
254
|
+
assetSpec?: string;
|
|
255
|
+
assetSpecContract?: AssetSpecContract;
|
|
256
|
+
format?: string;
|
|
257
|
+
visibility?: string;
|
|
258
|
+
shopListed?: boolean;
|
|
259
|
+
shopPriceCredits?: number;
|
|
260
|
+
files: Record<string, string>;
|
|
261
|
+
filePaths: Record<string, string>;
|
|
262
|
+
};
|
|
263
|
+
export type PackOwnedAssetTask = {
|
|
264
|
+
name: string;
|
|
265
|
+
cataloguePath: string;
|
|
266
|
+
category: string;
|
|
267
|
+
subcategory: string | null;
|
|
268
|
+
assetSpec?: string;
|
|
269
|
+
assetSpecContract?: AssetSpecContract;
|
|
246
270
|
format?: string;
|
|
247
271
|
visibility?: string;
|
|
248
272
|
shopListed?: boolean;
|
|
@@ -259,6 +283,7 @@ export type AssetPackTask = {
|
|
|
259
283
|
username?: string;
|
|
260
284
|
remix?: string;
|
|
261
285
|
assets: string[];
|
|
286
|
+
ownedAssets: PackOwnedAssetTask[];
|
|
262
287
|
tags: string[];
|
|
263
288
|
hostingMode?: AppHostingMode;
|
|
264
289
|
externalUrl?: string;
|
|
@@ -279,7 +304,7 @@ export type CatalogueLoadResult = {
|
|
|
279
304
|
apps: AppTask[];
|
|
280
305
|
assetSpecs: AssetSpecTask[];
|
|
281
306
|
assets: AssetTask[];
|
|
282
|
-
|
|
307
|
+
ownedAssets: OwnedAssetTask[];
|
|
283
308
|
assetPacks: AssetPackTask[];
|
|
284
309
|
warnings: string[];
|
|
285
310
|
errors: string[];
|