@playdrop/playdrop-cli 0.11.6 → 0.11.8
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 +1 -1
- package/dist/apps/build.d.ts +0 -4
- package/dist/apps/build.js +1 -23
- package/dist/apps/index.d.ts +0 -1
- package/dist/apps/index.js +0 -1
- package/dist/apps/upload.d.ts +0 -3
- package/dist/apps/upload.js +0 -10
- package/dist/catalogue-utils.d.ts +0 -2
- package/dist/catalogue-utils.js +0 -8
- package/dist/catalogue.d.ts +0 -4
- package/dist/catalogue.js +0 -41
- package/dist/commands/dev.js +0 -43
- package/dist/commands/devShared.d.ts +0 -2
- package/dist/commands/devShared.js +0 -6
- package/dist/commands/upload.d.ts +0 -1
- package/dist/commands/upload.js +0 -1
- package/dist/index.js +0 -2
- package/dist/sessionCookie.js +1 -1
- package/node_modules/@playdrop/config/client-meta.json +1 -1
- package/node_modules/@playdrop/types/dist/api.d.ts +0 -2
- package/node_modules/@playdrop/types/dist/api.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/index.d.ts +0 -2
- package/node_modules/@playdrop/types/dist/index.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/index.js +1 -5
- package/node_modules/@playdrop/types/dist/version.d.ts +0 -2
- package/node_modules/@playdrop/types/dist/version.d.ts.map +1 -1
- package/node_modules/@playdrop/types/package.json +0 -5
- package/package.json +1 -1
- package/node_modules/@playdrop/types/dist/ecs.d.ts +0 -39
- package/node_modules/@playdrop/types/dist/ecs.d.ts.map +0 -1
- package/node_modules/@playdrop/types/dist/ecs.js +0 -117
- package/node_modules/@playdrop/types/dist/engine-builtins.d.ts +0 -17
- package/node_modules/@playdrop/types/dist/engine-builtins.d.ts.map +0 -1
- package/node_modules/@playdrop/types/dist/engine-builtins.js +0 -607
package/config/client-meta.json
CHANGED
package/dist/apps/build.d.ts
CHANGED
|
@@ -15,10 +15,6 @@ export type AppBuildArtifacts = {
|
|
|
15
15
|
sourceFile: File;
|
|
16
16
|
sourceHash: string;
|
|
17
17
|
metadata: AppBuildMetadata;
|
|
18
|
-
ecsFile?: File;
|
|
19
|
-
ecsHash?: string;
|
|
20
|
-
serverFile?: File;
|
|
21
|
-
serverHash?: string;
|
|
22
18
|
};
|
|
23
19
|
export declare function runNpmScript(projectDir: string, script: string): Promise<void>;
|
|
24
20
|
export declare function buildApp(task: AppTask): Promise<AppBuildArtifacts>;
|
package/dist/apps/build.js
CHANGED
|
@@ -303,7 +303,7 @@ function isExcludedRelativePath(relativePath, excludedRelativeFiles) {
|
|
|
303
303
|
return false;
|
|
304
304
|
}
|
|
305
305
|
// Files that are uploaded separately and should not be included in the bundle zip.
|
|
306
|
-
const RESERVED_BUNDLE_FILES = new Set(['source.zip'
|
|
306
|
+
const RESERVED_BUNDLE_FILES = new Set(['source.zip']);
|
|
307
307
|
function isReservedBundlePath(relativePath) {
|
|
308
308
|
const normalized = relativePath.replace(/\\/g, '/');
|
|
309
309
|
const basenameValue = normalized.split('/').pop()?.toLowerCase() || '';
|
|
@@ -566,33 +566,11 @@ async function buildApp(task) {
|
|
|
566
566
|
metadata.surfaceTargets = task.surfaceTargets;
|
|
567
567
|
}
|
|
568
568
|
metadata.entryPoint = bundleArchive.entryPoint;
|
|
569
|
-
// Build ecs file if path is defined
|
|
570
|
-
let ecsFile;
|
|
571
|
-
let ecsHash;
|
|
572
|
-
if (task.ecsPath) {
|
|
573
|
-
const ecsBuffer = (0, node_fs_1.readFileSync)(task.ecsPath);
|
|
574
|
-
ecsHash = (0, node_crypto_1.createHash)('sha256').update(ecsBuffer).digest('hex');
|
|
575
|
-
const ecsBlobPart = Uint8Array.from(ecsBuffer);
|
|
576
|
-
ecsFile = new File([ecsBlobPart], 'ecs.json', { type: 'application/json' });
|
|
577
|
-
}
|
|
578
|
-
// Build server file if path is defined
|
|
579
|
-
let serverFile;
|
|
580
|
-
let serverHash;
|
|
581
|
-
if (task.serverPath) {
|
|
582
|
-
const serverBuffer = (0, node_fs_1.readFileSync)(task.serverPath);
|
|
583
|
-
serverHash = (0, node_crypto_1.createHash)('sha256').update(serverBuffer).digest('hex');
|
|
584
|
-
const serverBlobPart = Uint8Array.from(serverBuffer);
|
|
585
|
-
serverFile = new File([serverBlobPart], 'server.js', { type: 'application/javascript' });
|
|
586
|
-
}
|
|
587
569
|
return {
|
|
588
570
|
bundleFile,
|
|
589
571
|
bundleHash,
|
|
590
572
|
sourceFile,
|
|
591
573
|
sourceHash: sourceArchive.hash,
|
|
592
574
|
metadata,
|
|
593
|
-
ecsFile,
|
|
594
|
-
ecsHash,
|
|
595
|
-
serverFile,
|
|
596
|
-
serverHash,
|
|
597
575
|
};
|
|
598
576
|
}
|
package/dist/apps/index.d.ts
CHANGED
package/dist/apps/index.js
CHANGED
|
@@ -58,7 +58,6 @@ async function runAppPipeline(client, task, options) {
|
|
|
58
58
|
const artifacts = isExternal ? null : await (0, build_1.buildApp)(task);
|
|
59
59
|
const warnings = (0, validate_1.collectAppValidationWarnings)(task, isExternal ? 'source' : 'bundle');
|
|
60
60
|
const uploadOptions = {
|
|
61
|
-
skipEcs: options?.skipEcs,
|
|
62
61
|
skipReview: options?.skipReview,
|
|
63
62
|
clearTags: options?.clearTags,
|
|
64
63
|
creatorUsername: options?.creatorUsername,
|
package/dist/apps/upload.d.ts
CHANGED
|
@@ -6,15 +6,12 @@ export type AppUploadResult = {
|
|
|
6
6
|
fileUnchanged?: boolean;
|
|
7
7
|
metadataUnchanged?: boolean;
|
|
8
8
|
sourceUnchanged?: boolean;
|
|
9
|
-
ecsUnchanged?: boolean;
|
|
10
|
-
serverUnchanged?: boolean;
|
|
11
9
|
versionCreated?: boolean;
|
|
12
10
|
version?: string;
|
|
13
11
|
versionId?: number;
|
|
14
12
|
versionNodeId?: string;
|
|
15
13
|
};
|
|
16
14
|
export type AppUploadOptions = {
|
|
17
|
-
skipEcs?: boolean;
|
|
18
15
|
skipReview?: boolean;
|
|
19
16
|
clearTags?: boolean;
|
|
20
17
|
creatorUsername?: string;
|
package/dist/apps/upload.js
CHANGED
|
@@ -189,16 +189,6 @@ async function uploadAppVersion(client, task, artifacts, options) {
|
|
|
189
189
|
totalUploadBytes = pushPreparedArtifact(task, totalUploadBytes, artifacts.sourceFile, config_1.MAX_APP_SOURCE_BYTES, 'source_too_large', 'Source archive');
|
|
190
190
|
preparedSessionFiles.push(await buildPreparedSessionFile('bundle', artifacts.bundleFile));
|
|
191
191
|
preparedSessionFiles.push(await buildPreparedSessionFile('source', artifacts.sourceFile));
|
|
192
|
-
if (!options?.skipEcs) {
|
|
193
|
-
if (artifacts.ecsFile) {
|
|
194
|
-
totalUploadBytes = pushPreparedArtifact(task, totalUploadBytes, artifacts.ecsFile, config_1.MAX_APP_ECS_BYTES, 'ecs_too_large', 'ECS config');
|
|
195
|
-
preparedSessionFiles.push(await buildPreparedSessionFile('ecs', artifacts.ecsFile));
|
|
196
|
-
}
|
|
197
|
-
if (artifacts.serverFile) {
|
|
198
|
-
totalUploadBytes = pushPreparedArtifact(task, totalUploadBytes, artifacts.serverFile, config_1.MAX_APP_SERVER_BYTES, 'server_too_large', 'Server.js');
|
|
199
|
-
preparedSessionFiles.push(await buildPreparedSessionFile('server', artifacts.serverFile));
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
192
|
}
|
|
203
193
|
if (listing?.iconPath) {
|
|
204
194
|
const prepared = createFileFromPath(listing.iconPath, 'image/png', config_1.MAX_VERSION_ICON_BYTES, 'icon_too_large', 'invalid_icon_content_type', 'Icon', isPngSignature);
|
|
@@ -14,8 +14,6 @@ export type AppCatalogueInfo = {
|
|
|
14
14
|
type: string;
|
|
15
15
|
surfaceTargets: ParsedSurfaceTargets;
|
|
16
16
|
cataloguePath: string;
|
|
17
|
-
ecsPath?: string | null;
|
|
18
|
-
serverScriptPath?: string | null;
|
|
19
17
|
};
|
|
20
18
|
export declare function findAppCatalogueInfo(htmlPath: string): AppCatalogueInfo;
|
|
21
19
|
export declare function findAppDefinition(htmlPath: string): {
|
package/dist/catalogue-utils.js
CHANGED
|
@@ -98,12 +98,6 @@ function findAppCatalogueInfo(htmlPath) {
|
|
|
98
98
|
const name = typeof match.name === 'string' && match.name.trim().length > 0
|
|
99
99
|
? match.name.trim()
|
|
100
100
|
: fileName.replace(/\.html$/i, '');
|
|
101
|
-
const ecsPath = typeof match.ecs === 'string' && match.ecs.trim().length > 0
|
|
102
|
-
? (0, node_path_1.join)(dir, match.ecs.trim())
|
|
103
|
-
: null;
|
|
104
|
-
const serverScriptPath = typeof match.server === 'string' && match.server.trim().length > 0
|
|
105
|
-
? (0, node_path_1.join)(dir, match.server.trim())
|
|
106
|
-
: null;
|
|
107
101
|
let surfaceTargets;
|
|
108
102
|
try {
|
|
109
103
|
surfaceTargets = parseSurfaceTargets(match.surfaceTargets);
|
|
@@ -116,8 +110,6 @@ function findAppCatalogueInfo(htmlPath) {
|
|
|
116
110
|
type: parsedType,
|
|
117
111
|
surfaceTargets: cloneSurfaceTargets(surfaceTargets),
|
|
118
112
|
cataloguePath,
|
|
119
|
-
ecsPath,
|
|
120
|
-
serverScriptPath,
|
|
121
113
|
};
|
|
122
114
|
}
|
|
123
115
|
}
|
package/dist/catalogue.d.ts
CHANGED
|
@@ -32,8 +32,6 @@ export type AppCatalogueEntry = {
|
|
|
32
32
|
displayName?: string;
|
|
33
33
|
description?: string;
|
|
34
34
|
file?: string;
|
|
35
|
-
ecs?: string;
|
|
36
|
-
server?: string;
|
|
37
35
|
type?: string;
|
|
38
36
|
emoji?: string | null;
|
|
39
37
|
color?: string | null;
|
|
@@ -180,8 +178,6 @@ export type AppTask = {
|
|
|
180
178
|
surfaceTargets: AppSurface[];
|
|
181
179
|
design?: AppVersionDesign;
|
|
182
180
|
missingMetadata: string[];
|
|
183
|
-
ecsPath?: string;
|
|
184
|
-
serverPath?: string;
|
|
185
181
|
version?: string;
|
|
186
182
|
releaseNotes?: string;
|
|
187
183
|
versionVisibility?: AppVersionVisibility;
|
package/dist/catalogue.js
CHANGED
|
@@ -1002,45 +1002,6 @@ function buildAppTasks(rootDir, catalogues, options) {
|
|
|
1002
1002
|
if (isTypescriptProject && !hasFormatScript) {
|
|
1003
1003
|
warnings.push(`${label} (${rawName}: package.json missing "format" script)`);
|
|
1004
1004
|
}
|
|
1005
|
-
// Handle optional ecs and server paths
|
|
1006
|
-
let ecsPath;
|
|
1007
|
-
let serverPath;
|
|
1008
|
-
const ecsField = typeof entry.ecs === 'string' ? entry.ecs.trim() : '';
|
|
1009
|
-
if (ecsField) {
|
|
1010
|
-
const absoluteEcsPath = (0, node_path_1.resolve)(file.directory, ecsField);
|
|
1011
|
-
const relativeToProject = (0, node_path_1.relative)(file.directory, absoluteEcsPath);
|
|
1012
|
-
if (relativeToProject.startsWith('..')) {
|
|
1013
|
-
errors.push(`[${label}] Skipping ${rawName}: ecs path escapes project directory (${ecsField}).`);
|
|
1014
|
-
continue;
|
|
1015
|
-
}
|
|
1016
|
-
if (!(0, node_fs_1.existsSync)(absoluteEcsPath)) {
|
|
1017
|
-
errors.push(`[${label}] Skipping ${rawName}: ecs file not found at ${absoluteEcsPath}`);
|
|
1018
|
-
continue;
|
|
1019
|
-
}
|
|
1020
|
-
if (!(0, node_fs_1.statSync)(absoluteEcsPath).isFile()) {
|
|
1021
|
-
errors.push(`[${label}] Skipping ${rawName}: expected a file at ${absoluteEcsPath}`);
|
|
1022
|
-
continue;
|
|
1023
|
-
}
|
|
1024
|
-
ecsPath = absoluteEcsPath;
|
|
1025
|
-
}
|
|
1026
|
-
const serverField = typeof entry.server === 'string' ? entry.server.trim() : '';
|
|
1027
|
-
if (serverField) {
|
|
1028
|
-
const absoluteServerPath = (0, node_path_1.resolve)(file.directory, serverField);
|
|
1029
|
-
const relativeToProject = (0, node_path_1.relative)(file.directory, absoluteServerPath);
|
|
1030
|
-
if (relativeToProject.startsWith('..')) {
|
|
1031
|
-
errors.push(`[${label}] Skipping ${rawName}: server path escapes project directory (${serverField}).`);
|
|
1032
|
-
continue;
|
|
1033
|
-
}
|
|
1034
|
-
if (!(0, node_fs_1.existsSync)(absoluteServerPath)) {
|
|
1035
|
-
errors.push(`[${label}] Skipping ${rawName}: server file not found at ${absoluteServerPath}`);
|
|
1036
|
-
continue;
|
|
1037
|
-
}
|
|
1038
|
-
if (!(0, node_fs_1.statSync)(absoluteServerPath).isFile()) {
|
|
1039
|
-
errors.push(`[${label}] Skipping ${rawName}: expected a file at ${absoluteServerPath}`);
|
|
1040
|
-
continue;
|
|
1041
|
-
}
|
|
1042
|
-
serverPath = absoluteServerPath;
|
|
1043
|
-
}
|
|
1044
1005
|
// Parse versioning fields.
|
|
1045
1006
|
const rawVersion = typeof entry.version === 'string' ? entry.version.trim() : '';
|
|
1046
1007
|
if (!rawVersion) {
|
|
@@ -1513,8 +1474,6 @@ function buildAppTasks(rootDir, catalogues, options) {
|
|
|
1513
1474
|
surfaceTargets: metadata.surfaceTargets,
|
|
1514
1475
|
design,
|
|
1515
1476
|
missingMetadata: metadata.missingFields,
|
|
1516
|
-
ecsPath,
|
|
1517
|
-
serverPath,
|
|
1518
1477
|
// Versioning fields
|
|
1519
1478
|
version,
|
|
1520
1479
|
releaseNotes,
|
package/dist/commands/dev.js
CHANGED
|
@@ -17,7 +17,6 @@ const appUrls_1 = require("../appUrls");
|
|
|
17
17
|
const devServer_1 = require("./devServer");
|
|
18
18
|
const devRuntimeAssets_1 = require("./devRuntimeAssets");
|
|
19
19
|
const devAuth_1 = require("../devAuth");
|
|
20
|
-
const DEFAULT_ROOM_BASE = 'http://localhost:3001';
|
|
21
20
|
function formatDevRuntimeAssetManifestFailure(error) {
|
|
22
21
|
const rawMessage = error instanceof Error ? error.message : String(error ?? 'unknown_error');
|
|
23
22
|
if (rawMessage.startsWith('dev_runtime_asset_ref_invalid:')) {
|
|
@@ -321,48 +320,6 @@ async function dev(targetArg, appOption, devOptions = {}) {
|
|
|
321
320
|
return;
|
|
322
321
|
}
|
|
323
322
|
}
|
|
324
|
-
if (resolvedTarget.ecsPath && resolvedTarget.serverScriptPath && appResponse?.id) {
|
|
325
|
-
try {
|
|
326
|
-
if (!(0, node_fs_1.existsSync)(resolvedTarget.ecsPath) || !(0, node_fs_1.existsSync)(resolvedTarget.serverScriptPath)) {
|
|
327
|
-
throw new Error('ecs_or_server_missing');
|
|
328
|
-
}
|
|
329
|
-
const ecsRaw = (0, node_fs_1.readFileSync)(resolvedTarget.ecsPath, 'utf8');
|
|
330
|
-
const ecsJson = JSON.parse(ecsRaw);
|
|
331
|
-
const serverJs = (0, node_fs_1.readFileSync)(resolvedTarget.serverScriptPath, 'utf8');
|
|
332
|
-
const roomBase = (process.env.PLAYDROP_ROOM_BASE || DEFAULT_ROOM_BASE).replace(/\/$/, '');
|
|
333
|
-
const response = await fetch(`${roomBase}/rooms/dev_config`, {
|
|
334
|
-
method: 'POST',
|
|
335
|
-
headers: { 'Content-Type': 'application/json' },
|
|
336
|
-
body: JSON.stringify({
|
|
337
|
-
app_id: appResponse.id,
|
|
338
|
-
ecs: ecsJson,
|
|
339
|
-
server_js: serverJs,
|
|
340
|
-
}),
|
|
341
|
-
});
|
|
342
|
-
if (!response.ok) {
|
|
343
|
-
let reason = `${response.status}`;
|
|
344
|
-
try {
|
|
345
|
-
const payload = await response.json();
|
|
346
|
-
if (payload?.error) {
|
|
347
|
-
reason = payload.error;
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
catch {
|
|
351
|
-
// ignore parse errors
|
|
352
|
-
}
|
|
353
|
-
throw new Error(`room_dev_config_failed:${reason}`);
|
|
354
|
-
}
|
|
355
|
-
console.log(`Uploaded dev ECS config to engine-server for app ${appName}.`);
|
|
356
|
-
}
|
|
357
|
-
catch (error) {
|
|
358
|
-
(0, messages_1.printErrorWithHelp)(error?.message || 'Failed to upload dev ECS config to engine-server.', [
|
|
359
|
-
'Ensure catalogue entry includes valid "ecs" and "server" paths.',
|
|
360
|
-
'Check PLAYDROP_ROOM_BASE or the default http://localhost:3001 is reachable.',
|
|
361
|
-
], { command: 'project dev' });
|
|
362
|
-
process.exitCode = 1;
|
|
363
|
-
return;
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
323
|
let handle;
|
|
367
324
|
try {
|
|
368
325
|
handle = await (0, devServer_1.startDevServer)({
|
|
@@ -120,8 +120,6 @@ function resolveAppFromCatalogue(cataloguePath, appName) {
|
|
|
120
120
|
htmlPath,
|
|
121
121
|
cataloguePath: info.cataloguePath,
|
|
122
122
|
surfaceTargets: cloneSurfaceTargets(info.surfaceTargets),
|
|
123
|
-
ecsPath: info.ecsPath,
|
|
124
|
-
serverScriptPath: info.serverScriptPath,
|
|
125
123
|
};
|
|
126
124
|
}
|
|
127
125
|
function resolveDevTarget(targetArg, appOption) {
|
|
@@ -161,8 +159,6 @@ function resolveDevTarget(targetArg, appOption) {
|
|
|
161
159
|
htmlPath: absoluteTarget,
|
|
162
160
|
cataloguePath: info.cataloguePath,
|
|
163
161
|
surfaceTargets,
|
|
164
|
-
ecsPath: info.ecsPath,
|
|
165
|
-
serverScriptPath: info.serverScriptPath,
|
|
166
162
|
};
|
|
167
163
|
}
|
|
168
164
|
catch {
|
|
@@ -173,8 +169,6 @@ function resolveDevTarget(targetArg, appOption) {
|
|
|
173
169
|
htmlPath: absoluteTarget,
|
|
174
170
|
cataloguePath: derivedCataloguePath,
|
|
175
171
|
surfaceTargets,
|
|
176
|
-
ecsPath: null,
|
|
177
|
-
serverScriptPath: null,
|
|
178
172
|
};
|
|
179
173
|
}
|
|
180
174
|
const cataloguePath = findNearestCatalogue(process.cwd());
|
package/dist/commands/upload.js
CHANGED
|
@@ -849,7 +849,6 @@ function appendTaskRelations(graphState, fromNodeId, relations, contextLabel) {
|
|
|
849
849
|
}
|
|
850
850
|
async function uploadAppTask(state, task, taskCreator, options) {
|
|
851
851
|
const { upload, warnings } = await (0, apps_1.runAppPipeline)(state.client, task, {
|
|
852
|
-
skipEcs: options?.skipEcs,
|
|
853
852
|
skipReview: options?.skipReview,
|
|
854
853
|
clearTags: options?.clearTags,
|
|
855
854
|
creatorUsername: taskCreator,
|
package/dist/index.js
CHANGED
|
@@ -1132,13 +1132,11 @@ project
|
|
|
1132
1132
|
.command('publish [target]')
|
|
1133
1133
|
.description('Publish local content to Playdrop')
|
|
1134
1134
|
.option('--env <env>', 'Resolve this command against one logged-in environment')
|
|
1135
|
-
.option('--skip-ecs', 'Skip ecs.json and server.js during publish')
|
|
1136
1135
|
.option('--clear-tags', 'Confirm clearing existing live tags when this publish removes them')
|
|
1137
1136
|
.addOption(new commander_1.Option('--skip-review', 'Mark uploaded app versions as NOT_REQUIRED review. Admin-only.').hideHelp())
|
|
1138
1137
|
.action(async (target, opts) => {
|
|
1139
1138
|
await (0, upload_1.upload)(target ?? '.', {
|
|
1140
1139
|
env: opts.env,
|
|
1141
|
-
skipEcs: opts.skipEcs,
|
|
1142
1140
|
skipReview: opts.skipReview,
|
|
1143
1141
|
clearTags: opts.clearTags,
|
|
1144
1142
|
});
|
package/dist/sessionCookie.js
CHANGED
|
@@ -5,7 +5,7 @@ exports.shouldUseSecureCaptureSessionCookie = shouldUseSecureCaptureSessionCooki
|
|
|
5
5
|
exports.buildCaptureAccessTokenCookies = buildCaptureAccessTokenCookies;
|
|
6
6
|
const node_net_1 = require("node:net");
|
|
7
7
|
const ACCESS_TOKEN_COOKIE_NAME = 'playdrop_access_token';
|
|
8
|
-
const DOMAIN_PREFIXES = ['api.', 'www.', 'admin.', 'ai.', 'play.', 'assets.'
|
|
8
|
+
const DOMAIN_PREFIXES = ['api.', 'www.', 'admin.', 'ai.', 'play.', 'assets.'];
|
|
9
9
|
function normalizeHostname(hostname) {
|
|
10
10
|
return hostname.trim().toLowerCase();
|
|
11
11
|
}
|
|
@@ -1059,8 +1059,6 @@ export interface AppResponse {
|
|
|
1059
1059
|
assetSpecSupport?: AppMetadataAssetSpecSupport[];
|
|
1060
1060
|
bundleSize?: number | null;
|
|
1061
1061
|
sourceUrl?: string | null;
|
|
1062
|
-
hasEcs?: boolean;
|
|
1063
|
-
hasServer?: boolean;
|
|
1064
1062
|
iconUrl?: string | null;
|
|
1065
1063
|
heroPortraitUrl?: string | null;
|
|
1066
1064
|
heroLandscapeUrl?: string | null;
|