@playdrop/playdrop-cli 0.4.1 → 0.5.0
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 +4 -4
- package/config/client-meta.json +7 -7
- package/dist/apps/upload.js +26 -0
- package/dist/captureRuntime.js +2 -9
- package/dist/catalogue.d.ts +18 -1
- package/dist/catalogue.js +198 -0
- package/dist/commands/changelog.d.ts +1 -0
- package/dist/commands/changelog.js +7 -0
- package/dist/commands/create.js +2 -0
- package/dist/commands/documentation.js +24 -4
- package/dist/commands/publicPages.d.ts +1 -0
- package/dist/commands/publicPages.js +32 -0
- package/dist/index.js +19 -6
- package/dist/sessionCookie.d.ts +18 -0
- package/dist/sessionCookie.js +88 -0
- package/dist/taskSelection.js +2 -0
- package/node_modules/@playdrop/ai-client/package.json +1 -1
- package/node_modules/@playdrop/api-client/dist/client.d.ts +43 -1
- package/node_modules/@playdrop/api-client/dist/client.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/client.js +6 -0
- package/node_modules/@playdrop/api-client/dist/domains/admin.d.ts +7 -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 +63 -0
- package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/apps.js +13 -0
- package/node_modules/@playdrop/api-client/dist/domains/player-meta.d.ts +42 -0
- package/node_modules/@playdrop/api-client/dist/domains/player-meta.d.ts.map +1 -0
- package/node_modules/@playdrop/api-client/dist/domains/player-meta.js +123 -0
- package/node_modules/@playdrop/api-client/dist/index.d.ts +28 -1
- package/node_modules/@playdrop/api-client/dist/index.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/index.js +45 -0
- 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 +7 -7
- package/node_modules/@playdrop/config/dist/src/creator-docs.d.ts +24 -0
- package/node_modules/@playdrop/config/dist/src/creator-docs.d.ts.map +1 -0
- package/node_modules/@playdrop/config/dist/src/creator-docs.js +253 -0
- package/node_modules/@playdrop/config/dist/src/creator-faq.d.ts +17 -0
- package/node_modules/@playdrop/config/dist/src/creator-faq.d.ts.map +1 -0
- package/node_modules/@playdrop/config/dist/src/creator-faq.js +141 -0
- package/node_modules/@playdrop/config/dist/test/creator-docs.test.d.ts +2 -0
- package/node_modules/@playdrop/config/dist/test/creator-docs.test.d.ts.map +1 -0
- package/node_modules/@playdrop/config/dist/test/creator-docs.test.js +36 -0
- package/node_modules/@playdrop/config/dist/tsconfig.tsbuildinfo +1 -1
- package/node_modules/@playdrop/config/package.json +1 -1
- package/node_modules/@playdrop/types/dist/api.d.ts +8 -1
- package/node_modules/@playdrop/types/dist/api.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/index.d.ts +1 -0
- package/node_modules/@playdrop/types/dist/index.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/index.js +1 -0
- package/node_modules/@playdrop/types/dist/player-meta.d.ts +212 -0
- package/node_modules/@playdrop/types/dist/player-meta.d.ts.map +1 -0
- package/node_modules/@playdrop/types/dist/player-meta.js +114 -0
- package/node_modules/@playdrop/types/dist/version.d.ts +20 -0
- package/node_modules/@playdrop/types/dist/version.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/version.js +31 -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/dist/commands/gettingStarted.d.ts +0 -1
- package/dist/commands/gettingStarted.js +0 -26
package/README.md
CHANGED
|
@@ -32,10 +32,10 @@ playdrop project publish .
|
|
|
32
32
|
## Links
|
|
33
33
|
|
|
34
34
|
- Website: [playdrop.ai](https://www.playdrop.ai/)
|
|
35
|
-
- Getting started: [playdrop.ai/
|
|
35
|
+
- Getting started: [playdrop.ai/getting-started](https://www.playdrop.ai/getting-started)
|
|
36
36
|
- CLI docs: [playdrop.ai/docs/cli](https://www.playdrop.ai/docs/cli)
|
|
37
|
-
-
|
|
38
|
-
- Templates and demos: [playdrop.ai/docs/
|
|
37
|
+
- Runtime docs: [playdrop.ai/docs/runtime](https://www.playdrop.ai/docs/runtime)
|
|
38
|
+
- Templates and demos: [playdrop.ai/docs/examples#templates-and-demos](https://www.playdrop.ai/docs/examples#templates-and-demos)
|
|
39
39
|
- Public Playdrop skill: [skills.sh/playdrop-ai/playdrop-skills/playdrop](https://skills.sh/playdrop-ai/playdrop-skills/playdrop)
|
|
40
40
|
|
|
41
41
|
## Live Examples
|
|
@@ -100,5 +100,5 @@ playdrop project publish .
|
|
|
100
100
|
|
|
101
101
|
```bash
|
|
102
102
|
playdrop documentation browse
|
|
103
|
-
playdrop documentation read
|
|
103
|
+
playdrop documentation read runtime
|
|
104
104
|
```
|
package/config/client-meta.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.
|
|
3
|
-
"build":
|
|
2
|
+
"version": "0.5.0",
|
|
3
|
+
"build": 2,
|
|
4
4
|
"platforms": {
|
|
5
5
|
"ios": {
|
|
6
6
|
"minimumVersion": "16.0"
|
|
@@ -26,19 +26,19 @@
|
|
|
26
26
|
},
|
|
27
27
|
"clients": {
|
|
28
28
|
"web": {
|
|
29
|
-
"minimumVersion": "0.
|
|
30
|
-
"minimumBuild":
|
|
29
|
+
"minimumVersion": "0.5.0",
|
|
30
|
+
"minimumBuild": 2
|
|
31
31
|
},
|
|
32
32
|
"admin": {
|
|
33
|
-
"minimumVersion": "0.
|
|
34
|
-
"minimumBuild":
|
|
33
|
+
"minimumVersion": "0.5.0",
|
|
34
|
+
"minimumBuild": 2
|
|
35
35
|
},
|
|
36
36
|
"apple": {
|
|
37
37
|
"minimumVersion": "0.3.10",
|
|
38
38
|
"minimumBuild": 1
|
|
39
39
|
},
|
|
40
40
|
"cli": {
|
|
41
|
-
"minimumVersion": "0.
|
|
41
|
+
"minimumVersion": "0.5.0"
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
}
|
package/dist/apps/upload.js
CHANGED
|
@@ -58,12 +58,38 @@ async function uploadAppVersion(client, task, artifacts, options) {
|
|
|
58
58
|
remixRef: task.remix,
|
|
59
59
|
surfaceTargets: task.surfaceTargets,
|
|
60
60
|
entryPoint: artifacts?.metadata?.entryPoint ?? undefined,
|
|
61
|
+
authMode: task.authMode,
|
|
62
|
+
controllerMode: task.controllerMode,
|
|
63
|
+
previewable: task.previewable,
|
|
61
64
|
// App metadata (used when creating app if it doesn't exist)
|
|
62
65
|
displayName: task.displayName,
|
|
63
66
|
description: task.description,
|
|
64
67
|
type: task.type,
|
|
65
68
|
emoji: task.emoji ?? undefined,
|
|
66
69
|
color: task.color ?? undefined,
|
|
70
|
+
achievements: task.achievements.map((definition) => ({
|
|
71
|
+
key: definition.key,
|
|
72
|
+
displayName: definition.displayName,
|
|
73
|
+
description: definition.description,
|
|
74
|
+
kind: definition.kind,
|
|
75
|
+
...(definition.hidden ? { hidden: true } : {}),
|
|
76
|
+
...(definition.maxProgress !== undefined ? { maxProgress: definition.maxProgress } : {}),
|
|
77
|
+
icon: definition.icon,
|
|
78
|
+
status: definition.status,
|
|
79
|
+
})),
|
|
80
|
+
leaderboards: task.leaderboards.map((definition) => ({
|
|
81
|
+
key: definition.key,
|
|
82
|
+
displayName: definition.displayName,
|
|
83
|
+
description: definition.description,
|
|
84
|
+
scoreType: definition.scoreType,
|
|
85
|
+
sort: definition.sort,
|
|
86
|
+
...(definition.unitLabel ? { unitLabel: definition.unitLabel } : {}),
|
|
87
|
+
status: definition.status,
|
|
88
|
+
})),
|
|
89
|
+
achievementIconFiles: task.achievements.map((definition) => ({
|
|
90
|
+
key: definition.key,
|
|
91
|
+
file: createFileFromPath(definition.iconPath, 'image/png', isPngSignature),
|
|
92
|
+
})),
|
|
67
93
|
};
|
|
68
94
|
// For EXTERNAL hosting mode, use externalUrl instead of bundle
|
|
69
95
|
if (task.hostingMode === 'EXTERNAL') {
|
package/dist/captureRuntime.js
CHANGED
|
@@ -7,7 +7,7 @@ exports.runCapture = runCapture;
|
|
|
7
7
|
const promises_1 = require("node:fs/promises");
|
|
8
8
|
const node_path_1 = require("node:path");
|
|
9
9
|
const playwright_1 = require("./playwright");
|
|
10
|
-
const
|
|
10
|
+
const sessionCookie_1 = require("./sessionCookie");
|
|
11
11
|
const FRAME_SELECTOR = 'iframe[title="Game"]';
|
|
12
12
|
const LOGIN_BUTTON_NAME = 'Sign in to play';
|
|
13
13
|
exports.CAPTURE_LOG_LEVEL_VALUES = ['debug', 'info', 'warn', 'error'];
|
|
@@ -142,14 +142,7 @@ async function runCapture(options) {
|
|
|
142
142
|
await (0, playwright_1.withChromiumPage)(async ({ context, page }) => {
|
|
143
143
|
const targetOrigin = new URL(options.targetUrl).origin;
|
|
144
144
|
if (bootstrapToken) {
|
|
145
|
-
await context.addCookies(
|
|
146
|
-
{
|
|
147
|
-
name: ACCESS_TOKEN_COOKIE_NAME,
|
|
148
|
-
value: bootstrapToken,
|
|
149
|
-
url: targetOrigin,
|
|
150
|
-
path: '/',
|
|
151
|
-
},
|
|
152
|
-
]);
|
|
145
|
+
await context.addCookies((0, sessionCookie_1.buildCaptureAccessTokenCookies)(options.targetUrl, bootstrapToken));
|
|
153
146
|
}
|
|
154
147
|
if (bootstrapToken || bootstrapUser) {
|
|
155
148
|
await page.addInitScript(({ token, user }) => {
|
package/dist/catalogue.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type AppSurface, type AppType, type AppHostingMode, type AppVersionVisibility } from '@playdrop/types';
|
|
1
|
+
import { type AppSurface, type AppType, type AppHostingMode, type AppAuthMode, type AppControllerMode, type AppVersionVisibility, type AppAchievementCatalogueDefinition, type AppLeaderboardCatalogueDefinition, type PlayerMetaDefinitionStatus } from '@playdrop/types';
|
|
2
2
|
export type CatalogueJson = {
|
|
3
3
|
apps?: Array<Record<string, unknown>>;
|
|
4
4
|
assets?: Array<Record<string, unknown>>;
|
|
@@ -17,6 +17,13 @@ export type AppListingConfig = {
|
|
|
17
17
|
videosPortrait?: string[];
|
|
18
18
|
videosLandscape?: string[];
|
|
19
19
|
};
|
|
20
|
+
export type ResolvedAppAchievementDefinition = Omit<AppAchievementCatalogueDefinition, 'status'> & {
|
|
21
|
+
iconPath: string;
|
|
22
|
+
status: PlayerMetaDefinitionStatus;
|
|
23
|
+
};
|
|
24
|
+
export type ResolvedAppLeaderboardDefinition = Omit<AppLeaderboardCatalogueDefinition, 'status'> & {
|
|
25
|
+
status: PlayerMetaDefinitionStatus;
|
|
26
|
+
};
|
|
20
27
|
export type AppCatalogueEntry = {
|
|
21
28
|
name: string;
|
|
22
29
|
displayName?: string;
|
|
@@ -35,9 +42,14 @@ export type AppCatalogueEntry = {
|
|
|
35
42
|
releaseNotes?: string;
|
|
36
43
|
visibility?: string;
|
|
37
44
|
hostingMode?: string;
|
|
45
|
+
authMode?: string;
|
|
46
|
+
controllerMode?: string;
|
|
47
|
+
previewable?: boolean;
|
|
38
48
|
externalUrl?: string;
|
|
39
49
|
listing?: AppListingConfig;
|
|
40
50
|
username?: string;
|
|
51
|
+
achievements?: AppAchievementCatalogueDefinition[];
|
|
52
|
+
leaderboards?: AppLeaderboardCatalogueDefinition[];
|
|
41
53
|
embeddedAssets?: EmbeddedAssetCatalogueEntry[];
|
|
42
54
|
uses?: {
|
|
43
55
|
assets?: string[];
|
|
@@ -136,9 +148,14 @@ export type AppTask = {
|
|
|
136
148
|
releaseNotes?: string;
|
|
137
149
|
versionVisibility?: AppVersionVisibility;
|
|
138
150
|
hostingMode?: AppHostingMode;
|
|
151
|
+
authMode?: AppAuthMode;
|
|
152
|
+
controllerMode?: AppControllerMode;
|
|
153
|
+
previewable?: boolean;
|
|
139
154
|
externalUrl?: string;
|
|
140
155
|
listing?: ResolvedListingAssets;
|
|
141
156
|
username?: string;
|
|
157
|
+
achievements: ResolvedAppAchievementDefinition[];
|
|
158
|
+
leaderboards: ResolvedAppLeaderboardDefinition[];
|
|
142
159
|
remix?: string;
|
|
143
160
|
embeddedAssets: EmbeddedAssetTask[];
|
|
144
161
|
uses: {
|
package/dist/catalogue.js
CHANGED
|
@@ -442,6 +442,163 @@ function validateOptionalColor(value, hasColorField, errors) {
|
|
|
442
442
|
}
|
|
443
443
|
return color;
|
|
444
444
|
}
|
|
445
|
+
function validateAchievementDefinitions(value, catalogueDir, errors) {
|
|
446
|
+
if (value === undefined) {
|
|
447
|
+
return [];
|
|
448
|
+
}
|
|
449
|
+
if (!Array.isArray(value)) {
|
|
450
|
+
errors.push('achievements must be an array');
|
|
451
|
+
return [];
|
|
452
|
+
}
|
|
453
|
+
const seenKeys = new Set();
|
|
454
|
+
const definitions = [];
|
|
455
|
+
value.forEach((entry, index) => {
|
|
456
|
+
if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
|
|
457
|
+
errors.push(`achievements[${index}] must be an object`);
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
const key = typeof entry.key === 'string' ? entry.key.trim() : '';
|
|
461
|
+
if (!(0, types_1.isPlayerMetaKey)(key)) {
|
|
462
|
+
errors.push(`achievements[${index}].key must match the player meta key format`);
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
if (seenKeys.has(key)) {
|
|
466
|
+
errors.push(`achievements contains duplicate key "${key}"`);
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
seenKeys.add(key);
|
|
470
|
+
const displayName = typeof entry.displayName === 'string' ? entry.displayName.trim() : '';
|
|
471
|
+
if (!displayName) {
|
|
472
|
+
errors.push(`achievements[${index}].displayName must be a non-empty string`);
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
const description = typeof entry.description === 'string' ? entry.description.trim() : '';
|
|
476
|
+
if (!description) {
|
|
477
|
+
errors.push(`achievements[${index}].description must be a non-empty string`);
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
const kind = (0, types_1.parseAppAchievementKind)(entry.kind);
|
|
481
|
+
if (!kind) {
|
|
482
|
+
errors.push(`achievements[${index}].kind must be one of ${types_1.APP_ACHIEVEMENT_KIND_VALUES.join(', ')}`);
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
const hidden = Boolean(entry.hidden);
|
|
486
|
+
const status = (0, types_1.parsePlayerMetaDefinitionStatus)(entry.status ?? 'ACTIVE');
|
|
487
|
+
if (!status) {
|
|
488
|
+
errors.push(`achievements[${index}].status must be one of ${types_1.PLAYER_META_DEFINITION_STATUS_VALUES.join(', ')}`);
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
const icon = typeof entry.icon === 'string' ? entry.icon.trim() : '';
|
|
492
|
+
if (!icon) {
|
|
493
|
+
errors.push(`achievements[${index}].icon must be a non-empty PNG path`);
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
const iconExtensionError = validateListingAssetExtension(icon, `achievements[${index}].icon`, 'image');
|
|
497
|
+
if (iconExtensionError) {
|
|
498
|
+
errors.push(iconExtensionError);
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
const iconPathError = validateListingAssetPath(catalogueDir, icon, `achievements[${index}].icon`);
|
|
502
|
+
if (iconPathError) {
|
|
503
|
+
errors.push(iconPathError);
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
const iconPath = resolveListingAssetPath(catalogueDir, icon);
|
|
507
|
+
let maxProgress;
|
|
508
|
+
if (kind === 'INCREMENTAL') {
|
|
509
|
+
if (!Number.isSafeInteger(entry.maxProgress) || Number(entry.maxProgress) <= 0) {
|
|
510
|
+
errors.push(`achievements[${index}].maxProgress must be a positive safe integer`);
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
maxProgress = Number(entry.maxProgress);
|
|
514
|
+
}
|
|
515
|
+
else if (entry.maxProgress !== undefined) {
|
|
516
|
+
errors.push(`achievements[${index}].maxProgress is only valid for INCREMENTAL achievements`);
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
definitions.push({
|
|
520
|
+
key,
|
|
521
|
+
displayName,
|
|
522
|
+
description,
|
|
523
|
+
kind,
|
|
524
|
+
hidden,
|
|
525
|
+
...(maxProgress !== undefined ? { maxProgress } : {}),
|
|
526
|
+
icon,
|
|
527
|
+
iconPath,
|
|
528
|
+
status,
|
|
529
|
+
});
|
|
530
|
+
});
|
|
531
|
+
return definitions;
|
|
532
|
+
}
|
|
533
|
+
function validateLeaderboardDefinitions(value, errors) {
|
|
534
|
+
if (value === undefined) {
|
|
535
|
+
return [];
|
|
536
|
+
}
|
|
537
|
+
if (!Array.isArray(value)) {
|
|
538
|
+
errors.push('leaderboards must be an array');
|
|
539
|
+
return [];
|
|
540
|
+
}
|
|
541
|
+
const seenKeys = new Set();
|
|
542
|
+
const definitions = [];
|
|
543
|
+
value.forEach((entry, index) => {
|
|
544
|
+
if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
|
|
545
|
+
errors.push(`leaderboards[${index}] must be an object`);
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
const key = typeof entry.key === 'string' ? entry.key.trim() : '';
|
|
549
|
+
if (!(0, types_1.isPlayerMetaKey)(key)) {
|
|
550
|
+
errors.push(`leaderboards[${index}].key must match the player meta key format`);
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
if (seenKeys.has(key)) {
|
|
554
|
+
errors.push(`leaderboards contains duplicate key "${key}"`);
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
seenKeys.add(key);
|
|
558
|
+
const displayName = typeof entry.displayName === 'string' ? entry.displayName.trim() : '';
|
|
559
|
+
if (!displayName) {
|
|
560
|
+
errors.push(`leaderboards[${index}].displayName must be a non-empty string`);
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
const description = typeof entry.description === 'string' ? entry.description.trim() : '';
|
|
564
|
+
if (!description) {
|
|
565
|
+
errors.push(`leaderboards[${index}].description must be a non-empty string`);
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
const scoreType = (0, types_1.parseAppLeaderboardScoreType)(entry.scoreType);
|
|
569
|
+
if (!scoreType) {
|
|
570
|
+
errors.push(`leaderboards[${index}].scoreType must be one of ${types_1.APP_LEADERBOARD_SCORE_TYPE_VALUES.join(', ')}`);
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
const sort = (0, types_1.parseAppLeaderboardSort)(entry.sort);
|
|
574
|
+
if (!sort) {
|
|
575
|
+
errors.push(`leaderboards[${index}].sort must be one of ${types_1.APP_LEADERBOARD_SORT_VALUES.join(', ')}`);
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
if (!(0, types_1.isValidLeaderboardDefinitionCombination)(scoreType, sort)) {
|
|
579
|
+
errors.push(`leaderboards[${index}] must use INTEGER + DESC or TIME_MS + ASC`);
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
const status = (0, types_1.parsePlayerMetaDefinitionStatus)(entry.status ?? 'ACTIVE');
|
|
583
|
+
if (!status) {
|
|
584
|
+
errors.push(`leaderboards[${index}].status must be one of ${types_1.PLAYER_META_DEFINITION_STATUS_VALUES.join(', ')}`);
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
const unitLabel = typeof entry.unitLabel === 'string' && entry.unitLabel.trim().length > 0
|
|
588
|
+
? entry.unitLabel.trim()
|
|
589
|
+
: undefined;
|
|
590
|
+
definitions.push({
|
|
591
|
+
key,
|
|
592
|
+
displayName,
|
|
593
|
+
description,
|
|
594
|
+
scoreType,
|
|
595
|
+
sort,
|
|
596
|
+
...(unitLabel ? { unitLabel } : {}),
|
|
597
|
+
status,
|
|
598
|
+
});
|
|
599
|
+
});
|
|
600
|
+
return definitions;
|
|
601
|
+
}
|
|
445
602
|
function validateAppMetadata(entry) {
|
|
446
603
|
const warnings = [];
|
|
447
604
|
const errors = [];
|
|
@@ -592,6 +749,12 @@ function buildAppTasks(rootDir, catalogues, options) {
|
|
|
592
749
|
if (metadata.warnings.length > 0) {
|
|
593
750
|
warnings.push(`${label} (${metadata.warnings.join('; ')})`);
|
|
594
751
|
}
|
|
752
|
+
const errorCountBeforePlayerMeta = errors.length;
|
|
753
|
+
const achievements = validateAchievementDefinitions(entry.achievements, file.directory, errors);
|
|
754
|
+
const leaderboards = validateLeaderboardDefinitions(entry.leaderboards, errors);
|
|
755
|
+
if (errors.length > errorCountBeforePlayerMeta) {
|
|
756
|
+
continue;
|
|
757
|
+
}
|
|
595
758
|
if (isTypescriptProject && !hasValidateScript) {
|
|
596
759
|
warnings.push(`${label} (${rawName}: package.json missing "validate" script)`);
|
|
597
760
|
}
|
|
@@ -667,6 +830,32 @@ function buildAppTasks(rootDir, catalogues, options) {
|
|
|
667
830
|
}
|
|
668
831
|
hostingMode = rawHostingMode;
|
|
669
832
|
}
|
|
833
|
+
let authMode;
|
|
834
|
+
const rawAuthMode = typeof entry.authMode === 'string' ? entry.authMode.trim().toUpperCase() : '';
|
|
835
|
+
if (rawAuthMode) {
|
|
836
|
+
if (!types_1.APP_AUTH_MODE_VALUES.includes(rawAuthMode)) {
|
|
837
|
+
errors.push(`[${label}] Skipping ${rawName}: authMode must be one of ${types_1.APP_AUTH_MODE_VALUES.join(', ')}.`);
|
|
838
|
+
continue;
|
|
839
|
+
}
|
|
840
|
+
authMode = rawAuthMode;
|
|
841
|
+
}
|
|
842
|
+
let controllerMode;
|
|
843
|
+
const rawControllerMode = typeof entry.controllerMode === 'string' ? entry.controllerMode.trim().toUpperCase() : '';
|
|
844
|
+
if (rawControllerMode) {
|
|
845
|
+
if (!types_1.APP_CONTROLLER_MODE_VALUES.includes(rawControllerMode)) {
|
|
846
|
+
errors.push(`[${label}] Skipping ${rawName}: controllerMode must be one of ${types_1.APP_CONTROLLER_MODE_VALUES.join(', ')}.`);
|
|
847
|
+
continue;
|
|
848
|
+
}
|
|
849
|
+
controllerMode = rawControllerMode;
|
|
850
|
+
}
|
|
851
|
+
let previewable;
|
|
852
|
+
if (Object.prototype.hasOwnProperty.call(entry, 'previewable')) {
|
|
853
|
+
if (typeof entry.previewable !== 'boolean') {
|
|
854
|
+
errors.push(`[${label}] Skipping ${rawName}: previewable must be true or false.`);
|
|
855
|
+
continue;
|
|
856
|
+
}
|
|
857
|
+
previewable = Boolean(entry.previewable);
|
|
858
|
+
}
|
|
670
859
|
let externalUrl;
|
|
671
860
|
const rawExternalUrl = typeof entry.externalUrl === 'string' ? entry.externalUrl.trim() : '';
|
|
672
861
|
if (rawExternalUrl) {
|
|
@@ -685,6 +874,10 @@ function buildAppTasks(rootDir, catalogues, options) {
|
|
|
685
874
|
// Auto-set hostingMode to EXTERNAL if externalUrl is provided
|
|
686
875
|
hostingMode = 'EXTERNAL';
|
|
687
876
|
}
|
|
877
|
+
if ((hostingMode ?? 'HOSTED') === 'EXTERNAL' && previewable) {
|
|
878
|
+
errors.push(`[${label}] Skipping ${rawName}: previewable can only be true for HOSTED apps.`);
|
|
879
|
+
continue;
|
|
880
|
+
}
|
|
688
881
|
// Parse username field (admin-only)
|
|
689
882
|
let username;
|
|
690
883
|
const rawUsername = typeof entry.username === 'string' ? entry.username.trim() : '';
|
|
@@ -981,10 +1174,15 @@ function buildAppTasks(rootDir, catalogues, options) {
|
|
|
981
1174
|
releaseNotes,
|
|
982
1175
|
versionVisibility,
|
|
983
1176
|
hostingMode,
|
|
1177
|
+
authMode,
|
|
1178
|
+
controllerMode,
|
|
1179
|
+
previewable,
|
|
984
1180
|
externalUrl,
|
|
985
1181
|
listing,
|
|
986
1182
|
// Admin-only field
|
|
987
1183
|
username,
|
|
1184
|
+
achievements,
|
|
1185
|
+
leaderboards,
|
|
988
1186
|
remix,
|
|
989
1187
|
embeddedAssets,
|
|
990
1188
|
uses: {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function showChangelog(): Promise<void>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.showChangelog = showChangelog;
|
|
4
|
+
const publicPages_1 = require("./publicPages");
|
|
5
|
+
async function showChangelog() {
|
|
6
|
+
await (0, publicPages_1.showPublicPage)('changelog', 'app-pages/changelog.md');
|
|
7
|
+
}
|
package/dist/commands/create.js
CHANGED
|
@@ -537,6 +537,8 @@ function buildPendingExternalRemixTask(name, metadata, sourceInfo, cataloguePath
|
|
|
537
537
|
missingMetadata: [],
|
|
538
538
|
version: entry.version,
|
|
539
539
|
remix: typeof entry.remix === 'string' ? entry.remix : undefined,
|
|
540
|
+
achievements: [],
|
|
541
|
+
leaderboards: [],
|
|
540
542
|
embeddedAssets: [],
|
|
541
543
|
uses: { assets: [], packs: [] },
|
|
542
544
|
graph: { relations: [] },
|
|
@@ -22,6 +22,23 @@ function normalizeDocPath(value) {
|
|
|
22
22
|
}
|
|
23
23
|
return segments.join('/');
|
|
24
24
|
}
|
|
25
|
+
function toDisplayDocPath(entry) {
|
|
26
|
+
if (entry.endsWith('/README.md')) {
|
|
27
|
+
return entry.slice(0, -'/README.md'.length);
|
|
28
|
+
}
|
|
29
|
+
if (entry.endsWith('/AGENTS.md')) {
|
|
30
|
+
return `${entry.slice(0, -'/AGENTS.md'.length)}/agents`;
|
|
31
|
+
}
|
|
32
|
+
return entry;
|
|
33
|
+
}
|
|
34
|
+
function createDocAliasMap(entries) {
|
|
35
|
+
const aliases = new Map();
|
|
36
|
+
for (const entry of entries) {
|
|
37
|
+
aliases.set(entry, entry);
|
|
38
|
+
aliases.set(toDisplayDocPath(entry), entry);
|
|
39
|
+
}
|
|
40
|
+
return aliases;
|
|
41
|
+
}
|
|
25
42
|
function encodePathSegments(pathValue) {
|
|
26
43
|
return pathValue
|
|
27
44
|
.split('/')
|
|
@@ -68,7 +85,8 @@ async function browseDocumentation(options = {}) {
|
|
|
68
85
|
if (!entries) {
|
|
69
86
|
return;
|
|
70
87
|
}
|
|
71
|
-
const
|
|
88
|
+
const normalizedEntries = [...new Set(entries.map((entry) => normalizeDocPath(entry)).filter((entry) => Boolean(entry)))].sort((a, b) => a.localeCompare(b));
|
|
89
|
+
const items = [...new Set(normalizedEntries.map((entry) => toDisplayDocPath(entry)))].sort((a, b) => a.localeCompare(b));
|
|
72
90
|
if (options.json) {
|
|
73
91
|
(0, output_1.printJson)({ items });
|
|
74
92
|
return;
|
|
@@ -87,7 +105,7 @@ async function browseDocumentation(options = {}) {
|
|
|
87
105
|
async function readDocumentation(pathArg) {
|
|
88
106
|
const normalizedTarget = normalizeDocPath(pathArg);
|
|
89
107
|
if (!normalizedTarget) {
|
|
90
|
-
(0, messages_1.printErrorWithHelp)('A documentation path is required.', ['Use a relative path such as
|
|
108
|
+
(0, messages_1.printErrorWithHelp)('A documentation path is required.', ['Use a relative path such as runtime.', 'Run "playdrop documentation browse" to list available entries.'], { command: 'documentation read' });
|
|
91
109
|
process.exitCode = 1;
|
|
92
110
|
return;
|
|
93
111
|
}
|
|
@@ -102,12 +120,14 @@ async function readDocumentation(pathArg) {
|
|
|
102
120
|
return;
|
|
103
121
|
}
|
|
104
122
|
const normalizedEntries = [...new Set(entries.map((entry) => normalizeDocPath(entry)).filter((entry) => Boolean(entry)))];
|
|
105
|
-
|
|
123
|
+
const aliases = createDocAliasMap(normalizedEntries);
|
|
124
|
+
const resolvedTarget = aliases.get(normalizedTarget);
|
|
125
|
+
if (!resolvedTarget) {
|
|
106
126
|
(0, messages_1.printErrorWithHelp)(`Documentation entry "${normalizedTarget}" was not found.`, ['Run "playdrop documentation browse" to list available entries.'], { command: 'documentation read' });
|
|
107
127
|
process.exitCode = 1;
|
|
108
128
|
return;
|
|
109
129
|
}
|
|
110
|
-
const fileUrl = buildDocumentationFileUrl(envConfig.cdnBase,
|
|
130
|
+
const fileUrl = buildDocumentationFileUrl(envConfig.cdnBase, resolvedTarget);
|
|
111
131
|
let response;
|
|
112
132
|
try {
|
|
113
133
|
response = await fetch(fileUrl);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function showPublicPage(command: string, relativePath: string): Promise<void>;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.showPublicPage = showPublicPage;
|
|
4
|
+
const commandContext_1 = require("../commandContext");
|
|
5
|
+
const messages_1 = require("../messages");
|
|
6
|
+
function buildPublicPageUrl(base, relativePath) {
|
|
7
|
+
return `${base.replace(/\/$/, '')}/${relativePath}`;
|
|
8
|
+
}
|
|
9
|
+
async function showPublicPage(command, relativePath) {
|
|
10
|
+
await (0, commandContext_1.withPublicEnvironment)(command, async ({ envConfig }) => {
|
|
11
|
+
if (!envConfig.cdnBase) {
|
|
12
|
+
(0, messages_1.printErrorWithHelp)(`Environment "${envConfig.name}" does not include a public content base URL.`, ['Set PLAYDROP_CDN_BASE or use an environment that publishes public content assets.'], { command });
|
|
13
|
+
process.exitCode = 1;
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
let response;
|
|
17
|
+
try {
|
|
18
|
+
response = await fetch(buildPublicPageUrl(envConfig.cdnBase, relativePath));
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
(0, messages_1.printErrorWithHelp)(`Fetching ${command} failed.`, [error instanceof Error ? error.message : String(error)], { command });
|
|
22
|
+
process.exitCode = 1;
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (!response.ok) {
|
|
26
|
+
(0, messages_1.printErrorWithHelp)(`Failed to fetch ${command}.`, [`HTTP ${response.status} ${response.statusText}`], { command });
|
|
27
|
+
process.exitCode = 1;
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
process.stdout.write(await response.text());
|
|
31
|
+
});
|
|
32
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -30,7 +30,8 @@ const format_1 = require("./commands/format");
|
|
|
30
30
|
const upload_1 = require("./commands/upload");
|
|
31
31
|
const documentation_1 = require("./commands/documentation");
|
|
32
32
|
const feedback_1 = require("./commands/feedback");
|
|
33
|
-
const
|
|
33
|
+
const changelog_1 = require("./commands/changelog");
|
|
34
|
+
const publicPages_1 = require("./commands/publicPages");
|
|
34
35
|
const upgrade_1 = require("./commands/upgrade");
|
|
35
36
|
const messages_1 = require("./messages");
|
|
36
37
|
function resolveInitTarget(pathArg) {
|
|
@@ -83,7 +84,7 @@ program
|
|
|
83
84
|
.addHelpCommand('help [command]', 'Show help for a command');
|
|
84
85
|
program.showHelpAfterError();
|
|
85
86
|
program.showSuggestionAfterError();
|
|
86
|
-
program.addHelpText('afterAll', '\nStart here: run `playdrop getting-started`.\n');
|
|
87
|
+
program.addHelpText('afterAll', '\nStart here: run `playdrop getting-started`, `playdrop faq`, or `playdrop changelog`.\n');
|
|
87
88
|
program.on('command:*', (unknownCommands) => {
|
|
88
89
|
const [unknownCommand] = unknownCommands;
|
|
89
90
|
if (unknownCommand) {
|
|
@@ -648,14 +649,26 @@ feedback
|
|
|
648
649
|
program
|
|
649
650
|
.command('getting-started')
|
|
650
651
|
.description('Show the recommended first steps')
|
|
651
|
-
.action(() => {
|
|
652
|
-
(0,
|
|
652
|
+
.action(async () => {
|
|
653
|
+
await (0, publicPages_1.showPublicPage)('getting-started', 'app-pages/getting-started.md');
|
|
654
|
+
});
|
|
655
|
+
program
|
|
656
|
+
.command('faq')
|
|
657
|
+
.description('Show creator-focused platform questions and answers')
|
|
658
|
+
.action(async () => {
|
|
659
|
+
await (0, publicPages_1.showPublicPage)('faq', 'app-pages/faq.md');
|
|
660
|
+
});
|
|
661
|
+
program
|
|
662
|
+
.command('changelog')
|
|
663
|
+
.description('Read the latest public Playdrop changelog')
|
|
664
|
+
.action(async () => {
|
|
665
|
+
await (0, changelog_1.showChangelog)();
|
|
653
666
|
});
|
|
654
667
|
program
|
|
655
668
|
.command('start')
|
|
656
669
|
.description('Alias of getting-started')
|
|
657
|
-
.action(() => {
|
|
658
|
-
(0,
|
|
670
|
+
.action(async () => {
|
|
671
|
+
await (0, publicPages_1.showPublicPage)('getting-started', 'app-pages/getting-started.md');
|
|
659
672
|
});
|
|
660
673
|
program
|
|
661
674
|
.command('update')
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare function resolveCaptureSessionCookieDomain(hostname: string): string | null;
|
|
2
|
+
export declare function shouldUseSecureCaptureSessionCookie(hostname: string): boolean;
|
|
3
|
+
export declare function buildCaptureAccessTokenCookies(targetUrl: string, accessToken: string): {
|
|
4
|
+
name: string;
|
|
5
|
+
value: string;
|
|
6
|
+
url: string;
|
|
7
|
+
httpOnly: boolean;
|
|
8
|
+
sameSite: "Lax";
|
|
9
|
+
secure: boolean;
|
|
10
|
+
}[] | {
|
|
11
|
+
name: string;
|
|
12
|
+
value: string;
|
|
13
|
+
domain: string;
|
|
14
|
+
path: string;
|
|
15
|
+
httpOnly: boolean;
|
|
16
|
+
sameSite: "Lax";
|
|
17
|
+
secure: boolean;
|
|
18
|
+
}[];
|