@playdrop/playdrop-cli 0.3.8-build.2 → 0.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config/client-meta.json +5 -5
- package/dist/apps/build.js +45 -39
- package/dist/catalogue-utils.js +23 -18
- package/dist/catalogue.d.ts +0 -2
- package/dist/catalogue.js +54 -41
- package/dist/clientInfo.js +16 -2
- package/dist/commands/browse.js +66 -23
- package/dist/commands/capture.js +3 -2
- package/dist/commands/create.js +49 -46
- package/dist/commands/createRemixContent.js +29 -44
- package/dist/commands/creations.js +51 -13
- package/dist/commands/devServer.d.ts +1 -1
- package/dist/commands/devShared.js +1 -1
- package/dist/commands/generation.js +91 -74
- package/dist/commands/gettingStarted.js +1 -1
- package/dist/commands/search.js +29 -1
- package/dist/commands/upload-content.d.ts +70 -0
- package/dist/commands/upload-content.js +627 -0
- package/dist/commands/upload-graph.d.ts +23 -0
- package/dist/commands/upload-graph.js +108 -0
- package/dist/commands/upload.js +264 -543
- package/dist/http.d.ts +1 -1
- package/dist/playwright.d.ts +12 -4
- package/dist/proxyFetch.js +3 -2
- package/node_modules/@playdrop/ai-client/dist/index.d.ts.map +1 -1
- package/node_modules/@playdrop/ai-client/dist/index.js +74 -54
- package/node_modules/@playdrop/api-client/dist/client.d.ts +34 -14
- package/node_modules/@playdrop/api-client/dist/client.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/client.js +6 -8
- package/node_modules/@playdrop/api-client/dist/core/errors.js +11 -11
- package/node_modules/@playdrop/api-client/dist/core/request.d.ts +2 -0
- package/node_modules/@playdrop/api-client/dist/core/request.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/core/request.js +10 -3
- package/node_modules/@playdrop/api-client/dist/domains/admin.d.ts +12 -10
- package/node_modules/@playdrop/api-client/dist/domains/admin.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/admin.js +33 -30
- package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts +8 -1
- package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/apps.js +140 -124
- package/node_modules/@playdrop/api-client/dist/domains/asset-packs.d.ts +9 -5
- package/node_modules/@playdrop/api-client/dist/domains/asset-packs.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/asset-packs.js +151 -88
- package/node_modules/@playdrop/api-client/dist/domains/assets.d.ts +4 -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 +153 -112
- package/node_modules/@playdrop/api-client/dist/domains/auth.d.ts +3 -1
- package/node_modules/@playdrop/api-client/dist/domains/auth.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/auth.js +21 -0
- package/node_modules/@playdrop/api-client/dist/domains/me.d.ts +6 -2
- package/node_modules/@playdrop/api-client/dist/domains/me.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/me.js +13 -2
- package/node_modules/@playdrop/api-client/dist/domains/payments.d.ts +1 -0
- package/node_modules/@playdrop/api-client/dist/domains/payments.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/payments.js +10 -0
- package/node_modules/@playdrop/api-client/dist/index.d.ts +65 -24
- package/node_modules/@playdrop/api-client/dist/index.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/index.js +38 -13
- package/node_modules/@playdrop/boxel-core/dist/src/entity-cleaner.js +2 -0
- package/node_modules/@playdrop/boxel-core/dist/src/entity-cleaner.js.map +1 -1
- package/node_modules/@playdrop/boxel-core/dist/src/humanoid/r15/textured-builder.js +1 -1
- 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/voxel-builder.js +1 -1
- 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.js +95 -75
- 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/scanner.d.ts +2 -3
- 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/face-map.js +4 -4
- 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/palette_tools.d.ts +2 -2
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/textured-boxes/slice.d.ts +5 -5
- package/node_modules/@playdrop/boxel-core/dist/src/transforms/voxels/textured-to-voxel.d.ts +3 -3
- package/node_modules/@playdrop/boxel-core/dist/src/types.d.ts +25 -25
- package/node_modules/@playdrop/boxel-core/dist/src/validation.js +2 -1
- package/node_modules/@playdrop/boxel-core/dist/src/validation.js.map +1 -1
- package/node_modules/@playdrop/boxel-three/dist/src/exporters/glb.js +5 -0
- package/node_modules/@playdrop/boxel-three/package.json +3 -3
- package/node_modules/@playdrop/config/client-meta.json +5 -5
- package/node_modules/@playdrop/config/dist/src/index.js +6 -6
- package/node_modules/@playdrop/config/dist/test/validateClientEnvironment.test.js +2 -2
- package/node_modules/@playdrop/config/dist/tsconfig.tsbuildinfo +1 -1
- package/node_modules/@playdrop/types/dist/api.d.ts +33 -8
- package/node_modules/@playdrop/types/dist/api.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/api.js +16 -8
- package/node_modules/@playdrop/types/dist/asset-pack.d.ts +107 -11
- package/node_modules/@playdrop/types/dist/asset-pack.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/asset-pack.js +2 -0
- package/node_modules/@playdrop/types/dist/asset.d.ts +15 -0
- package/node_modules/@playdrop/types/dist/asset.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/asset.js +1 -0
- package/node_modules/@playdrop/types/dist/ecs.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/ecs.js +10 -6
- package/node_modules/@playdrop/types/dist/entity.d.ts +5 -10
- package/node_modules/@playdrop/types/dist/entity.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/entity.js +40 -23
- package/node_modules/@playdrop/types/dist/graph.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/graph.js +13 -5
- package/node_modules/@playdrop/types/dist/version.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/version.js +7 -3
- package/node_modules/@playdrop/vox-three/dist/src/vox.d.ts +1 -0
- package/node_modules/@playdrop/vox-three/dist/src/vox.js +15 -6
- package/node_modules/@playdrop/vox-three/dist/src/vox.js.map +1 -1
- package/node_modules/@playdrop/vox-three/dist/test/vox.test.js +16 -0
- package/node_modules/@playdrop/vox-three/dist/test/vox.test.js.map +1 -1
- package/node_modules/@playdrop/vox-three/package.json +3 -3
- package/package.json +1 -1
package/config/client-meta.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.3.
|
|
2
|
+
"version": "0.3.9",
|
|
3
3
|
"build": 1,
|
|
4
4
|
"platforms": {
|
|
5
5
|
"ios": {
|
|
@@ -26,19 +26,19 @@
|
|
|
26
26
|
},
|
|
27
27
|
"clients": {
|
|
28
28
|
"web": {
|
|
29
|
-
"minimumVersion": "0.3.
|
|
29
|
+
"minimumVersion": "0.3.9",
|
|
30
30
|
"minimumBuild": 1
|
|
31
31
|
},
|
|
32
32
|
"admin": {
|
|
33
|
-
"minimumVersion": "0.3.
|
|
33
|
+
"minimumVersion": "0.3.9",
|
|
34
34
|
"minimumBuild": 1
|
|
35
35
|
},
|
|
36
36
|
"apple": {
|
|
37
|
-
"minimumVersion": "0.3.
|
|
37
|
+
"minimumVersion": "0.3.9",
|
|
38
38
|
"minimumBuild": 1
|
|
39
39
|
},
|
|
40
40
|
"cli": {
|
|
41
|
-
"minimumVersion": "0.3.
|
|
41
|
+
"minimumVersion": "0.3.9",
|
|
42
42
|
"minimumBuild": 1
|
|
43
43
|
}
|
|
44
44
|
}
|
package/dist/apps/build.js
CHANGED
|
@@ -182,6 +182,47 @@ function isPathWithinRoot(rootDir, targetPath) {
|
|
|
182
182
|
const rootPrefix = normalizedRoot.endsWith(node_path_1.sep) ? normalizedRoot : `${normalizedRoot}${node_path_1.sep}`;
|
|
183
183
|
return normalizedTarget.startsWith(rootPrefix);
|
|
184
184
|
}
|
|
185
|
+
function readProjectDirEntries(current) {
|
|
186
|
+
try {
|
|
187
|
+
return (0, node_fs_1.readdirSync)(current, { withFileTypes: true });
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
return [];
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
function appendIncludedProjectFile(rootDir, includePath, fileByRelativePath, results, enforceReservedBundleNames) {
|
|
194
|
+
const normalizedIncludePath = (0, node_path_1.normalize)(includePath).split(node_path_1.sep).join('/');
|
|
195
|
+
if (!normalizedIncludePath || normalizedIncludePath === '.') {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
if (normalizedIncludePath.startsWith('..')) {
|
|
199
|
+
throw new Error(`[apps][build] Invalid include file path "${includePath}".`);
|
|
200
|
+
}
|
|
201
|
+
if (fileByRelativePath.has(normalizedIncludePath)) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
const absolutePath = (0, node_path_1.resolve)(rootDir, normalizedIncludePath);
|
|
205
|
+
if (!isPathWithinRoot(rootDir, absolutePath)) {
|
|
206
|
+
throw new Error(`[apps][build] Included file "${includePath}" escapes the project root.`);
|
|
207
|
+
}
|
|
208
|
+
if (!(0, node_fs_1.existsSync)(absolutePath)) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
const stats = (0, node_fs_1.statSync)(absolutePath);
|
|
212
|
+
if (!stats.isFile()) {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
if (enforceReservedBundleNames && isReservedBundlePath(normalizedIncludePath)) {
|
|
216
|
+
throw new Error(`[apps][build] Reserved bundle filename "${normalizedIncludePath}" is not allowed. Use dedicated source/ecs/server upload fields.`);
|
|
217
|
+
}
|
|
218
|
+
const record = {
|
|
219
|
+
absolutePath,
|
|
220
|
+
relativePath: normalizedIncludePath,
|
|
221
|
+
mtime: stats.mtime,
|
|
222
|
+
};
|
|
223
|
+
results.push(record);
|
|
224
|
+
fileByRelativePath.set(record.relativePath, record);
|
|
225
|
+
}
|
|
185
226
|
function collectProjectFiles(rootDir, rules, options) {
|
|
186
227
|
const includeRelativeFiles = options?.includeRelativeFiles ?? [];
|
|
187
228
|
const enforceReservedBundleNames = options?.enforceReservedBundleNames ?? false;
|
|
@@ -194,13 +235,7 @@ function collectProjectFiles(rootDir, rules, options) {
|
|
|
194
235
|
if (visited.has(current))
|
|
195
236
|
continue;
|
|
196
237
|
visited.add(current);
|
|
197
|
-
|
|
198
|
-
try {
|
|
199
|
-
dirEntries = (0, node_fs_1.readdirSync)(current, { withFileTypes: true });
|
|
200
|
-
}
|
|
201
|
-
catch {
|
|
202
|
-
continue;
|
|
203
|
-
}
|
|
238
|
+
const dirEntries = readProjectDirEntries(current);
|
|
204
239
|
for (const entry of dirEntries) {
|
|
205
240
|
const absolutePath = (0, node_path_1.join)(current, entry.name);
|
|
206
241
|
const relativePath = normalizeRelativePath(rootDir, absolutePath);
|
|
@@ -238,37 +273,7 @@ function collectProjectFiles(rootDir, rules, options) {
|
|
|
238
273
|
}
|
|
239
274
|
}
|
|
240
275
|
for (const includePath of includeRelativeFiles) {
|
|
241
|
-
|
|
242
|
-
if (!normalizedIncludePath || normalizedIncludePath === '.') {
|
|
243
|
-
continue;
|
|
244
|
-
}
|
|
245
|
-
if (normalizedIncludePath.startsWith('..')) {
|
|
246
|
-
throw new Error(`[apps][build] Invalid include file path "${includePath}".`);
|
|
247
|
-
}
|
|
248
|
-
if (fileByRelativePath.has(normalizedIncludePath)) {
|
|
249
|
-
continue;
|
|
250
|
-
}
|
|
251
|
-
const absolutePath = (0, node_path_1.resolve)(rootDir, normalizedIncludePath);
|
|
252
|
-
if (!isPathWithinRoot(rootDir, absolutePath)) {
|
|
253
|
-
throw new Error(`[apps][build] Included file "${includePath}" escapes the project root.`);
|
|
254
|
-
}
|
|
255
|
-
if (!(0, node_fs_1.existsSync)(absolutePath)) {
|
|
256
|
-
continue;
|
|
257
|
-
}
|
|
258
|
-
const stats = (0, node_fs_1.statSync)(absolutePath);
|
|
259
|
-
if (!stats.isFile()) {
|
|
260
|
-
continue;
|
|
261
|
-
}
|
|
262
|
-
if (enforceReservedBundleNames && isReservedBundlePath(normalizedIncludePath)) {
|
|
263
|
-
throw new Error(`[apps][build] Reserved bundle filename "${normalizedIncludePath}" is not allowed. Use dedicated source/ecs/server upload fields.`);
|
|
264
|
-
}
|
|
265
|
-
const record = {
|
|
266
|
-
absolutePath,
|
|
267
|
-
relativePath: normalizedIncludePath,
|
|
268
|
-
mtime: stats.mtime,
|
|
269
|
-
};
|
|
270
|
-
results.push(record);
|
|
271
|
-
fileByRelativePath.set(record.relativePath, record);
|
|
276
|
+
appendIncludedProjectFile(rootDir, includePath, fileByRelativePath, results, enforceReservedBundleNames);
|
|
272
277
|
}
|
|
273
278
|
results.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
|
|
274
279
|
return results;
|
|
@@ -326,6 +331,7 @@ const CRC32_TABLE = (() => {
|
|
|
326
331
|
}
|
|
327
332
|
return table;
|
|
328
333
|
})();
|
|
334
|
+
const ZIP_REGULAR_FILE_MODE = 0o100644;
|
|
329
335
|
function crc32(buffer) {
|
|
330
336
|
let crc = 0 ^ -1;
|
|
331
337
|
for (let i = 0; i < buffer.length; i += 1) {
|
|
@@ -410,7 +416,7 @@ function createZipArchive(entries) {
|
|
|
410
416
|
cursor += 2;
|
|
411
417
|
centralHeader.writeUInt16LE(0, cursor);
|
|
412
418
|
cursor += 2;
|
|
413
|
-
centralHeader.writeUInt32LE(0, cursor);
|
|
419
|
+
centralHeader.writeUInt32LE((ZIP_REGULAR_FILE_MODE << 16) >>> 0, cursor);
|
|
414
420
|
cursor += 4;
|
|
415
421
|
centralHeader.writeUInt32LE(offset, cursor);
|
|
416
422
|
cursor += 4;
|
package/dist/catalogue-utils.js
CHANGED
|
@@ -42,6 +42,27 @@ function parseSurfaceTargets(input) {
|
|
|
42
42
|
return { map, list };
|
|
43
43
|
}
|
|
44
44
|
const DEFAULT_SURFACE_TARGETS_OBJECT = { desktop: true, mobileLandscape: false, mobilePortrait: false };
|
|
45
|
+
function parseCatalogueEntries(cataloguePath) {
|
|
46
|
+
try {
|
|
47
|
+
const raw = (0, node_fs_1.readFileSync)(cataloguePath, 'utf8');
|
|
48
|
+
const data = raw.trim() ? JSON.parse(raw) : {};
|
|
49
|
+
const entries = Array.isArray(data) ? data : data?.apps;
|
|
50
|
+
return Array.isArray(entries)
|
|
51
|
+
? entries.filter((entry) => Boolean(entry && typeof entry === 'object'))
|
|
52
|
+
: [];
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
throw new Error(`Failed to parse ${cataloguePath}. Fix the JSON and retry.`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function findMatchingCatalogueEntries(apps, relativePath, fileName) {
|
|
59
|
+
return apps.filter((entry) => {
|
|
60
|
+
const entryName = typeof entry.name === 'string' ? entry.name.trim() : '';
|
|
61
|
+
const entryFile = typeof entry.file === 'string' ? entry.file.trim() : `${entryName}.html`;
|
|
62
|
+
const normalized = entryFile.replace(/\\/g, '/');
|
|
63
|
+
return normalized === relativePath || normalized === fileName;
|
|
64
|
+
});
|
|
65
|
+
}
|
|
45
66
|
function cloneSurfaceTargets(targets) {
|
|
46
67
|
return {
|
|
47
68
|
list: [...targets.list],
|
|
@@ -54,25 +75,9 @@ function findAppCatalogueInfo(htmlPath) {
|
|
|
54
75
|
for (let dir = (0, node_path_1.dirname)(absolute);; dir = (0, node_path_1.dirname)(dir)) {
|
|
55
76
|
const cataloguePath = (0, node_path_1.join)(dir, 'catalogue.json');
|
|
56
77
|
if ((0, node_fs_1.existsSync)(cataloguePath) && (0, node_fs_1.statSync)(cataloguePath).isFile()) {
|
|
57
|
-
let entries;
|
|
58
|
-
try {
|
|
59
|
-
const raw = (0, node_fs_1.readFileSync)(cataloguePath, 'utf8');
|
|
60
|
-
const data = raw.trim() ? JSON.parse(raw) : {};
|
|
61
|
-
entries = Array.isArray(data) ? data : data?.apps;
|
|
62
|
-
}
|
|
63
|
-
catch {
|
|
64
|
-
throw new Error(`Failed to parse ${cataloguePath}. Fix the JSON and retry.`);
|
|
65
|
-
}
|
|
66
|
-
const apps = Array.isArray(entries) ? entries : [];
|
|
67
78
|
const rel = (0, node_path_1.relative)(dir, absolute).replace(/\\/g, '/');
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
return false;
|
|
71
|
-
const entryName = typeof entry.name === 'string' ? entry.name.trim() : '';
|
|
72
|
-
const entryFile = typeof entry.file === 'string' ? entry.file.trim() : `${entryName}.html`;
|
|
73
|
-
const normalized = entryFile.replace(/\\/g, '/');
|
|
74
|
-
return normalized === rel || normalized === fileName;
|
|
75
|
-
});
|
|
79
|
+
const apps = parseCatalogueEntries(cataloguePath);
|
|
80
|
+
const matches = findMatchingCatalogueEntries(apps, rel, fileName);
|
|
76
81
|
if (matches.length > 1) {
|
|
77
82
|
throw new Error(`Multiple entries in ${cataloguePath} reference ${absolute}.`);
|
|
78
83
|
}
|
package/dist/catalogue.d.ts
CHANGED
|
@@ -85,7 +85,6 @@ export type AssetPackCatalogueEntry = {
|
|
|
85
85
|
hostingMode?: string;
|
|
86
86
|
externalUrl?: string;
|
|
87
87
|
downloadUrl?: string;
|
|
88
|
-
previewApp?: string;
|
|
89
88
|
visibility?: string;
|
|
90
89
|
releaseNotes?: string;
|
|
91
90
|
listing?: AppListingConfig;
|
|
@@ -200,7 +199,6 @@ export type AssetPackTask = {
|
|
|
200
199
|
hostingMode?: AppHostingMode;
|
|
201
200
|
externalUrl?: string;
|
|
202
201
|
downloadUrl?: string;
|
|
203
|
-
previewApp?: string;
|
|
204
202
|
visibility?: string;
|
|
205
203
|
releaseNotes?: string;
|
|
206
204
|
listing?: ResolvedListingAssets;
|
package/dist/catalogue.js
CHANGED
|
@@ -396,6 +396,48 @@ function normalizeAssetSubcategoryValue(raw, category, errors, label) {
|
|
|
396
396
|
}
|
|
397
397
|
return normalized;
|
|
398
398
|
}
|
|
399
|
+
function validateOptionalEmoji(value, hasEmojiField, errors) {
|
|
400
|
+
const emoji = value;
|
|
401
|
+
if (!hasEmojiField) {
|
|
402
|
+
return emoji;
|
|
403
|
+
}
|
|
404
|
+
if (emoji !== null && typeof emoji === 'string') {
|
|
405
|
+
const trimmed = emoji.trim();
|
|
406
|
+
if (!trimmed) {
|
|
407
|
+
return null;
|
|
408
|
+
}
|
|
409
|
+
if (Array.from(trimmed).length > 4) {
|
|
410
|
+
errors.push('emoji must be a single character');
|
|
411
|
+
return emoji;
|
|
412
|
+
}
|
|
413
|
+
return trimmed;
|
|
414
|
+
}
|
|
415
|
+
if (emoji !== null) {
|
|
416
|
+
errors.push('emoji must be a string or null');
|
|
417
|
+
}
|
|
418
|
+
return emoji;
|
|
419
|
+
}
|
|
420
|
+
function validateOptionalColor(value, hasColorField, errors) {
|
|
421
|
+
const color = value;
|
|
422
|
+
if (!hasColorField) {
|
|
423
|
+
return color;
|
|
424
|
+
}
|
|
425
|
+
if (color !== null && typeof color === 'string') {
|
|
426
|
+
const trimmed = color.trim();
|
|
427
|
+
if (!trimmed) {
|
|
428
|
+
return null;
|
|
429
|
+
}
|
|
430
|
+
if (!HEX_COLOR_REGEX.test(trimmed)) {
|
|
431
|
+
errors.push('color must be a hex RGB value like #FFAA00');
|
|
432
|
+
return color;
|
|
433
|
+
}
|
|
434
|
+
return trimmed.startsWith('#') ? trimmed.toUpperCase() : `#${trimmed.toUpperCase()}`;
|
|
435
|
+
}
|
|
436
|
+
if (color !== null) {
|
|
437
|
+
errors.push('color must be a string or null');
|
|
438
|
+
}
|
|
439
|
+
return color;
|
|
440
|
+
}
|
|
399
441
|
function validateAppMetadata(entry) {
|
|
400
442
|
const warnings = [];
|
|
401
443
|
const errors = [];
|
|
@@ -403,42 +445,8 @@ function validateAppMetadata(entry) {
|
|
|
403
445
|
if (missingFields.length > 0) {
|
|
404
446
|
warnings.push(formatMissingMetadata(missingFields));
|
|
405
447
|
}
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
if (emoji !== null && typeof emoji === 'string') {
|
|
409
|
-
const trimmed = emoji.trim();
|
|
410
|
-
if (!trimmed) {
|
|
411
|
-
emoji = null;
|
|
412
|
-
}
|
|
413
|
-
else if (Array.from(trimmed).length > 4) {
|
|
414
|
-
errors.push('emoji must be a single character');
|
|
415
|
-
}
|
|
416
|
-
else {
|
|
417
|
-
emoji = trimmed;
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
else if (emoji !== null) {
|
|
421
|
-
errors.push('emoji must be a string or null');
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
let color = entry.color;
|
|
425
|
-
if (Object.prototype.hasOwnProperty.call(entry, 'color')) {
|
|
426
|
-
if (color !== null && typeof color === 'string') {
|
|
427
|
-
const trimmed = color.trim();
|
|
428
|
-
if (!trimmed) {
|
|
429
|
-
color = null;
|
|
430
|
-
}
|
|
431
|
-
else if (!HEX_COLOR_REGEX.test(trimmed)) {
|
|
432
|
-
errors.push('color must be a hex RGB value like #FFAA00');
|
|
433
|
-
}
|
|
434
|
-
else {
|
|
435
|
-
color = trimmed.startsWith('#') ? trimmed.toUpperCase() : `#${trimmed.toUpperCase()}`;
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
else if (color !== null) {
|
|
439
|
-
errors.push('color must be a string or null');
|
|
440
|
-
}
|
|
441
|
-
}
|
|
448
|
+
const emoji = validateOptionalEmoji(entry.emoji, Object.prototype.hasOwnProperty.call(entry, 'emoji'), errors);
|
|
449
|
+
const color = validateOptionalColor(entry.color, Object.prototype.hasOwnProperty.call(entry, 'color'), errors);
|
|
442
450
|
let type;
|
|
443
451
|
if (typeof entry.type === 'string' && entry.type.trim()) {
|
|
444
452
|
const parsedType = (0, types_1.parseAppType)(entry.type.trim());
|
|
@@ -626,7 +634,6 @@ function buildAppTasks(rootDir, catalogues, options) {
|
|
|
626
634
|
serverPath = absoluteServerPath;
|
|
627
635
|
}
|
|
628
636
|
// Parse versioning fields (required in schema v2)
|
|
629
|
-
let version;
|
|
630
637
|
const rawVersion = typeof entry.version === 'string' ? entry.version.trim() : '';
|
|
631
638
|
if (!rawVersion) {
|
|
632
639
|
errors.push(`[${label}] Skipping ${rawName}: app version is required in schema v2 (e.g., "1.0.0").`);
|
|
@@ -636,7 +643,7 @@ function buildAppTasks(rootDir, catalogues, options) {
|
|
|
636
643
|
errors.push(`[${label}] Skipping ${rawName}: version must be in Major.Minor.Patch format (e.g., "1.0.0").`);
|
|
637
644
|
continue;
|
|
638
645
|
}
|
|
639
|
-
version = rawVersion;
|
|
646
|
+
const version = rawVersion;
|
|
640
647
|
const releaseNotes = typeof entry.releaseNotes === 'string' ? entry.releaseNotes : undefined;
|
|
641
648
|
let versionVisibility;
|
|
642
649
|
const rawVisibility = typeof entry.visibility === 'string' ? entry.visibility.trim().toUpperCase() : '';
|
|
@@ -936,7 +943,7 @@ function buildAppTasks(rootDir, catalogues, options) {
|
|
|
936
943
|
category,
|
|
937
944
|
subcategory,
|
|
938
945
|
format: embeddedFormat,
|
|
939
|
-
visibility: normalizeAssetVisibilityValue(embedded?.visibility, errors, `${label} embedded asset
|
|
946
|
+
visibility: normalizeAssetVisibilityValue(embedded?.visibility, errors, `${label} embedded asset "${embeddedName}"`),
|
|
940
947
|
shopListed: typeof embedded?.shopListed === 'boolean' ? embedded.shopListed : undefined,
|
|
941
948
|
shopPriceCredits: Number.isInteger(embedded?.shopPriceCredits) ? Number(embedded.shopPriceCredits) : undefined,
|
|
942
949
|
files,
|
|
@@ -1107,7 +1114,7 @@ function buildAssetTasks(catalogues) {
|
|
|
1107
1114
|
username,
|
|
1108
1115
|
remix,
|
|
1109
1116
|
format,
|
|
1110
|
-
visibility: normalizeAssetVisibilityValue(entry.visibility, errors, `${label} asset
|
|
1117
|
+
visibility: normalizeAssetVisibilityValue(entry.visibility, errors, `${label} asset "${name}"`),
|
|
1111
1118
|
shopListed: typeof entry.shopListed === 'boolean' ? entry.shopListed : undefined,
|
|
1112
1119
|
shopPriceCredits: Number.isInteger(entry.shopPriceCredits) ? Number(entry.shopPriceCredits) : undefined,
|
|
1113
1120
|
files,
|
|
@@ -1351,6 +1358,13 @@ function buildAssetPackTasks(catalogues) {
|
|
|
1351
1358
|
}
|
|
1352
1359
|
listing = resolvedListing;
|
|
1353
1360
|
}
|
|
1361
|
+
const previewApp = typeof entry.previewApp === 'string'
|
|
1362
|
+
? String(entry.previewApp).trim()
|
|
1363
|
+
: '';
|
|
1364
|
+
if (previewApp.length > 0) {
|
|
1365
|
+
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.`);
|
|
1366
|
+
continue;
|
|
1367
|
+
}
|
|
1354
1368
|
tasks.push({
|
|
1355
1369
|
kind: 'asset-pack',
|
|
1356
1370
|
name,
|
|
@@ -1363,7 +1377,6 @@ function buildAssetPackTasks(catalogues) {
|
|
|
1363
1377
|
hostingMode,
|
|
1364
1378
|
externalUrl,
|
|
1365
1379
|
downloadUrl,
|
|
1366
|
-
previewApp: typeof entry.previewApp === 'string' && entry.previewApp.trim().length > 0 ? entry.previewApp.trim() : undefined,
|
|
1367
1380
|
visibility,
|
|
1368
1381
|
releaseNotes: typeof entry.releaseNotes === 'string' ? entry.releaseNotes : undefined,
|
|
1369
1382
|
listing,
|
package/dist/clientInfo.js
CHANGED
|
@@ -12,14 +12,28 @@ const os_1 = __importDefault(require("os"));
|
|
|
12
12
|
const config_1 = require("@playdrop/config");
|
|
13
13
|
function mapPlatform(platform) {
|
|
14
14
|
switch (platform) {
|
|
15
|
+
case 'aix':
|
|
16
|
+
return 'aix';
|
|
17
|
+
case 'android':
|
|
18
|
+
return 'android';
|
|
19
|
+
case 'cygwin':
|
|
20
|
+
return 'cygwin';
|
|
15
21
|
case 'darwin':
|
|
16
22
|
return 'macos';
|
|
23
|
+
case 'freebsd':
|
|
24
|
+
return 'freebsd';
|
|
25
|
+
case 'haiku':
|
|
26
|
+
return 'haiku';
|
|
17
27
|
case 'win32':
|
|
18
28
|
return 'windows';
|
|
19
29
|
case 'linux':
|
|
20
30
|
return 'linux';
|
|
21
|
-
|
|
22
|
-
return
|
|
31
|
+
case 'netbsd':
|
|
32
|
+
return 'netbsd';
|
|
33
|
+
case 'openbsd':
|
|
34
|
+
return 'openbsd';
|
|
35
|
+
case 'sunos':
|
|
36
|
+
return 'sunos';
|
|
23
37
|
}
|
|
24
38
|
}
|
|
25
39
|
function detectPlatformVersion() {
|
package/dist/commands/browse.js
CHANGED
|
@@ -86,6 +86,34 @@ function itemSummary(item) {
|
|
|
86
86
|
}
|
|
87
87
|
return 'asset-pack';
|
|
88
88
|
}
|
|
89
|
+
function formatCountValue(value) {
|
|
90
|
+
return value.toLocaleString('en-US');
|
|
91
|
+
}
|
|
92
|
+
function buildCountsSuffix(item) {
|
|
93
|
+
const parts = [];
|
|
94
|
+
if (item.kind === 'app' && typeof item.item.playCount === 'number') {
|
|
95
|
+
if (typeof item.item.viewCount === 'number') {
|
|
96
|
+
parts.push(`view ${formatCountValue(item.item.viewCount)}`);
|
|
97
|
+
}
|
|
98
|
+
parts.push(`launch ${formatCountValue(item.item.playCount)}`);
|
|
99
|
+
}
|
|
100
|
+
if (item.kind === 'asset' && typeof item.item.playCount === 'number') {
|
|
101
|
+
if (typeof item.item.viewCount === 'number') {
|
|
102
|
+
parts.push(`view ${formatCountValue(item.item.viewCount)}`);
|
|
103
|
+
}
|
|
104
|
+
parts.push(`play ${formatCountValue(item.item.playCount)}`);
|
|
105
|
+
}
|
|
106
|
+
if (typeof item.item.likeCount === 'number') {
|
|
107
|
+
parts.push(`like ${formatCountValue(item.item.likeCount)}`);
|
|
108
|
+
}
|
|
109
|
+
if (typeof item.item.remixCount === 'number') {
|
|
110
|
+
parts.push(`remix ${formatCountValue(item.item.remixCount)}`);
|
|
111
|
+
}
|
|
112
|
+
if (typeof item.item.commentCount === 'number') {
|
|
113
|
+
parts.push(`comment ${formatCountValue(item.item.commentCount)}`);
|
|
114
|
+
}
|
|
115
|
+
return parts.length > 0 ? ` | ${parts.join(' | ')}` : '';
|
|
116
|
+
}
|
|
89
117
|
async function collectPaginatedItems(fetchPage) {
|
|
90
118
|
const items = [];
|
|
91
119
|
let offset = 0;
|
|
@@ -119,6 +147,12 @@ function sliceMergedItems(items, limit, offset) {
|
|
|
119
147
|
},
|
|
120
148
|
};
|
|
121
149
|
}
|
|
150
|
+
function requirePagination(pagination, context) {
|
|
151
|
+
if (!pagination) {
|
|
152
|
+
throw new Error(`missing_pagination:${context}`);
|
|
153
|
+
}
|
|
154
|
+
return pagination;
|
|
155
|
+
}
|
|
122
156
|
async function resolveCreatorUsername(creator, command, resolveMe) {
|
|
123
157
|
if (creator === 'all') {
|
|
124
158
|
return null;
|
|
@@ -151,12 +185,13 @@ async function browse(options = {}) {
|
|
|
151
185
|
process.exitCode = 1;
|
|
152
186
|
return;
|
|
153
187
|
}
|
|
154
|
-
const
|
|
155
|
-
if (options.appType !== undefined && !
|
|
188
|
+
const parsedAppType = options.appType ? (0, types_1.parseAppType)(options.appType.trim().toUpperCase()) : undefined;
|
|
189
|
+
if (options.appType !== undefined && !parsedAppType) {
|
|
156
190
|
(0, messages_1.printErrorWithHelp)(`App type "${options.appType}" is not supported.`, ['Use one of: game, demo, tool, template.'], { command: 'browse' });
|
|
157
191
|
process.exitCode = 1;
|
|
158
192
|
return;
|
|
159
193
|
}
|
|
194
|
+
const appType = parsedAppType ?? undefined;
|
|
160
195
|
const assetCategory = parseAssetCategory(options.assetCategory);
|
|
161
196
|
if (assetCategory === null) {
|
|
162
197
|
(0, messages_1.printErrorWithHelp)(`Asset category "${options.assetCategory}" is not supported.`, ['Use a canonical asset category like IMAGE, VIDEO, AUDIO, SPRITESHEET, or MODEL_3D.'], { command: 'browse' });
|
|
@@ -197,29 +232,23 @@ async function browse(options = {}) {
|
|
|
197
232
|
const items = [];
|
|
198
233
|
let pagination;
|
|
199
234
|
if (kind === 'app') {
|
|
200
|
-
let apps = [];
|
|
201
235
|
if (creatorUsername) {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
const response = await client.fetchAppsByCreator(creatorUsername);
|
|
208
|
-
apps = response.apps ?? [];
|
|
209
|
-
}
|
|
236
|
+
const response = creator === 'me'
|
|
237
|
+
? await client.fetchMyApps({ limit, offset, type: appType })
|
|
238
|
+
: await client.fetchAppsByCreator(creatorUsername, { limit, offset, type: appType });
|
|
239
|
+
items.push(...(response.apps ?? []).map((item) => toBrowseItem('app', item)));
|
|
240
|
+
pagination = requirePagination(response.pagination, 'browse_app_creator');
|
|
210
241
|
}
|
|
211
242
|
else if (appType) {
|
|
212
|
-
const response = await client.fetchAppsByType(appType);
|
|
213
|
-
|
|
243
|
+
const response = await client.fetchAppsByType(appType, { limit, offset });
|
|
244
|
+
items.push(...(response.apps ?? []).map((item) => toBrowseItem('app', item)));
|
|
245
|
+
pagination = requirePagination(response.pagination, 'browse_app_type');
|
|
214
246
|
}
|
|
215
247
|
else {
|
|
216
|
-
const response = await client.fetchApps();
|
|
217
|
-
|
|
248
|
+
const response = await client.fetchApps({ limit, offset });
|
|
249
|
+
items.push(...(response.apps ?? []).map((item) => toBrowseItem('app', item)));
|
|
250
|
+
pagination = requirePagination(response.pagination, 'browse_app_all');
|
|
218
251
|
}
|
|
219
|
-
const filtered = appType ? apps.filter((app) => app.type === appType) : apps;
|
|
220
|
-
const sliced = sliceMergedItems(filtered, limit, offset);
|
|
221
|
-
items.push(...sliced.items.map((item) => toBrowseItem('app', item)));
|
|
222
|
-
pagination = sliced.pagination;
|
|
223
252
|
}
|
|
224
253
|
else if (kind === 'asset') {
|
|
225
254
|
const response = creatorUsername
|
|
@@ -238,8 +267,22 @@ async function browse(options = {}) {
|
|
|
238
267
|
else {
|
|
239
268
|
const [apps, assets, packs] = await Promise.all([
|
|
240
269
|
creatorUsername
|
|
241
|
-
? (
|
|
242
|
-
|
|
270
|
+
? collectPaginatedItems((pageLimit, pageOffset) => (creator === 'me'
|
|
271
|
+
? client.fetchMyApps({ limit: pageLimit, offset: pageOffset, type: appType })
|
|
272
|
+
: client.fetchAppsByCreator(creatorUsername, { limit: pageLimit, offset: pageOffset, type: appType }))
|
|
273
|
+
.then((response) => ({
|
|
274
|
+
items: response.apps ?? [],
|
|
275
|
+
pagination: requirePagination(response.pagination, 'browse_all_creator_apps'),
|
|
276
|
+
})))
|
|
277
|
+
: (appType
|
|
278
|
+
? collectPaginatedItems((pageLimit, pageOffset) => client.fetchAppsByType(appType, { limit: pageLimit, offset: pageOffset }).then((response) => ({
|
|
279
|
+
items: response.apps ?? [],
|
|
280
|
+
pagination: requirePagination(response.pagination, 'browse_all_type_apps'),
|
|
281
|
+
})))
|
|
282
|
+
: collectPaginatedItems((pageLimit, pageOffset) => client.fetchApps({ limit: pageLimit, offset: pageOffset }).then((response) => ({
|
|
283
|
+
items: response.apps ?? [],
|
|
284
|
+
pagination: requirePagination(response.pagination, 'browse_all_apps'),
|
|
285
|
+
})))),
|
|
243
286
|
creatorUsername
|
|
244
287
|
? collectPaginatedItems((pageLimit, pageOffset) => client.listAssetsForCreator(creatorUsername, {
|
|
245
288
|
limit: pageLimit,
|
|
@@ -280,7 +323,7 @@ async function browse(options = {}) {
|
|
|
280
323
|
}))),
|
|
281
324
|
]);
|
|
282
325
|
const combined = [
|
|
283
|
-
...
|
|
326
|
+
...apps.items.map((item) => toBrowseItem('app', item)),
|
|
284
327
|
...assets.items.map((item) => toBrowseItem('asset', item)),
|
|
285
328
|
...packs.items.map((item) => toBrowseItem('asset-pack', item)),
|
|
286
329
|
];
|
|
@@ -309,7 +352,7 @@ async function browse(options = {}) {
|
|
|
309
352
|
}
|
|
310
353
|
for (const [index, item] of items.entries()) {
|
|
311
354
|
const displayName = item.item.displayName || item.item.name;
|
|
312
|
-
console.log(`${index + 1}. [${item.kind}] ${item.ref} | ${displayName} | @${item.item.creatorUsername} | ${itemSummary(item)}`);
|
|
355
|
+
console.log(`${index + 1}. [${item.kind}] ${item.ref} | ${displayName} | @${item.item.creatorUsername} | ${itemSummary(item)}${buildCountsSuffix(item)}`);
|
|
313
356
|
}
|
|
314
357
|
console.log('\nNext: run "playdrop detail <creator>/<kind>/<name>" to inspect one item.');
|
|
315
358
|
}
|
package/dist/commands/capture.js
CHANGED
|
@@ -35,6 +35,8 @@ function formatAppTypeSlug(type) {
|
|
|
35
35
|
return 'template';
|
|
36
36
|
case 'DEMO':
|
|
37
37
|
return 'demo';
|
|
38
|
+
case undefined:
|
|
39
|
+
return 'game';
|
|
38
40
|
default:
|
|
39
41
|
return 'game';
|
|
40
42
|
}
|
|
@@ -414,8 +416,7 @@ async function capture(targetArg, options = {}) {
|
|
|
414
416
|
projectInfo,
|
|
415
417
|
});
|
|
416
418
|
signalHandler = () => {
|
|
417
|
-
cleanup()
|
|
418
|
-
.finally(() => process.exit(130));
|
|
419
|
+
void cleanup().finally(() => process.exit(130));
|
|
419
420
|
};
|
|
420
421
|
process.on('SIGINT', signalHandler);
|
|
421
422
|
process.on('SIGTERM', signalHandler);
|
package/dist/commands/create.js
CHANGED
|
@@ -602,55 +602,58 @@ function parseRemixRef(value) {
|
|
|
602
602
|
ref: (0, types_1.formatContentVersionRef)(parsed),
|
|
603
603
|
};
|
|
604
604
|
}
|
|
605
|
+
function parseRemixScaffoldResponse(response, version) {
|
|
606
|
+
const remixMode = typeof response?.remixMode === 'string' ? response.remixMode : '';
|
|
607
|
+
const archiveUrl = typeof response?.archiveUrl === 'string' && response.archiveUrl.trim().length > 0
|
|
608
|
+
? response.archiveUrl.trim()
|
|
609
|
+
: null;
|
|
610
|
+
const htmlContent = typeof response?.html === 'string' ? response.html : null;
|
|
611
|
+
const externalListingUrl = typeof response?.externalListingUrl === 'string' && response.externalListingUrl.trim().length > 0
|
|
612
|
+
? response.externalListingUrl.trim()
|
|
613
|
+
: null;
|
|
614
|
+
const metadata = response.metadata;
|
|
615
|
+
const entryPoint = extractEntryPointFromMetadata(metadata);
|
|
616
|
+
if (remixMode === 'archive') {
|
|
617
|
+
if (!archiveUrl) {
|
|
618
|
+
throw new Error('Remix response missing archiveUrl');
|
|
619
|
+
}
|
|
620
|
+
return {
|
|
621
|
+
html: null,
|
|
622
|
+
metadata,
|
|
623
|
+
archiveUrl,
|
|
624
|
+
entryPoint,
|
|
625
|
+
sourceVersion: version,
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
if (remixMode === 'inline-html') {
|
|
629
|
+
if (!htmlContent) {
|
|
630
|
+
throw new Error('Remix response missing html');
|
|
631
|
+
}
|
|
632
|
+
return {
|
|
633
|
+
html: htmlContent,
|
|
634
|
+
metadata,
|
|
635
|
+
entryPoint,
|
|
636
|
+
sourceVersion: null,
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
if (remixMode === 'external-metadata-only') {
|
|
640
|
+
if (!externalListingUrl) {
|
|
641
|
+
throw new Error('Remix response missing externalListingUrl');
|
|
642
|
+
}
|
|
643
|
+
return {
|
|
644
|
+
html: null,
|
|
645
|
+
metadata,
|
|
646
|
+
externalListingUrl,
|
|
647
|
+
entryPoint,
|
|
648
|
+
sourceVersion: null,
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
throw new Error(`Unsupported remix mode: ${remixMode || 'missing'}`);
|
|
652
|
+
}
|
|
605
653
|
async function fetchRemixScaffold(client, creator, app, version) {
|
|
606
654
|
try {
|
|
607
655
|
const response = await client.fetchRemixScaffold(creator, app, version);
|
|
608
|
-
|
|
609
|
-
const archiveUrl = typeof response?.archiveUrl === 'string' && response.archiveUrl.trim().length > 0
|
|
610
|
-
? response.archiveUrl.trim()
|
|
611
|
-
: null;
|
|
612
|
-
const htmlContent = typeof response?.html === 'string' ? response.html : null;
|
|
613
|
-
const externalListingUrl = typeof response?.externalListingUrl === 'string' && response.externalListingUrl.trim().length > 0
|
|
614
|
-
? response.externalListingUrl.trim()
|
|
615
|
-
: null;
|
|
616
|
-
const metadata = response.metadata;
|
|
617
|
-
const entryPoint = extractEntryPointFromMetadata(metadata);
|
|
618
|
-
if (remixMode === 'archive') {
|
|
619
|
-
if (!archiveUrl) {
|
|
620
|
-
throw new Error('Remix response missing archiveUrl');
|
|
621
|
-
}
|
|
622
|
-
return {
|
|
623
|
-
html: null,
|
|
624
|
-
metadata,
|
|
625
|
-
archiveUrl,
|
|
626
|
-
entryPoint,
|
|
627
|
-
sourceVersion: version,
|
|
628
|
-
};
|
|
629
|
-
}
|
|
630
|
-
if (remixMode === 'inline-html') {
|
|
631
|
-
if (!htmlContent) {
|
|
632
|
-
throw new Error('Remix response missing html');
|
|
633
|
-
}
|
|
634
|
-
return {
|
|
635
|
-
html: htmlContent,
|
|
636
|
-
metadata,
|
|
637
|
-
entryPoint,
|
|
638
|
-
sourceVersion: null,
|
|
639
|
-
};
|
|
640
|
-
}
|
|
641
|
-
if (remixMode === 'external-metadata-only') {
|
|
642
|
-
if (!externalListingUrl) {
|
|
643
|
-
throw new Error('Remix response missing externalListingUrl');
|
|
644
|
-
}
|
|
645
|
-
return {
|
|
646
|
-
html: null,
|
|
647
|
-
metadata,
|
|
648
|
-
externalListingUrl,
|
|
649
|
-
entryPoint,
|
|
650
|
-
sourceVersion: null,
|
|
651
|
-
};
|
|
652
|
-
}
|
|
653
|
-
throw new Error(`Unsupported remix mode: ${remixMode || 'missing'}`);
|
|
656
|
+
return parseRemixScaffoldResponse(response, version);
|
|
654
657
|
}
|
|
655
658
|
catch (unknownError) {
|
|
656
659
|
if (unknownError instanceof types_1.UnsupportedClientError) {
|