@playdrop/playdrop-cli 0.18.9 → 0.19.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 +27 -0
- package/config/client-meta.json +2 -2
- package/dist/apps/upload.d.ts +2 -0
- package/dist/apps/upload.js +133 -60
- package/dist/catalogue.d.ts +18 -0
- package/dist/catalogue.js +171 -56
- package/dist/commands/devServer.js +3 -0
- package/dist/commands/validate.js +1 -0
- package/dist/commands/worker/game-source-git.js +7 -0
- package/dist/commands/worker/generation.d.ts +16 -1
- package/dist/commands/worker/generation.js +8 -2
- package/dist/commands/worker/ugc-processing.d.ts +13 -0
- package/dist/commands/worker/ugc-processing.js +146 -0
- package/dist/commands/worker.js +86 -20
- package/dist/proxyFetch.d.ts +5 -0
- package/dist/proxyFetch.js +10 -35
- package/dist/uploadDispatchers.d.ts +3 -0
- package/dist/uploadDispatchers.js +10 -0
- package/node_modules/@playdrop/api-client/dist/acquisition.d.ts +3 -1
- package/node_modules/@playdrop/api-client/dist/acquisition.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/acquisition.js +2 -2
- package/node_modules/@playdrop/api-client/dist/chat.d.ts +13 -4
- package/node_modules/@playdrop/api-client/dist/chat.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/client.d.ts +31 -10
- package/node_modules/@playdrop/api-client/dist/client.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/core/request.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/core/request.js +11 -1
- package/node_modules/@playdrop/api-client/dist/domains/chat.d.ts +30 -9
- package/node_modules/@playdrop/api-client/dist/domains/chat.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/chat.js +24 -19
- package/node_modules/@playdrop/api-client/dist/domains/comments.d.ts +3 -1
- package/node_modules/@playdrop/api-client/dist/domains/comments.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/comments.js +2 -2
- package/node_modules/@playdrop/api-client/dist/index.d.ts +17 -6
- package/node_modules/@playdrop/api-client/dist/index.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/index.js +12 -10
- package/node_modules/@playdrop/api-client/dist/runtime.d.ts +3 -0
- package/node_modules/@playdrop/api-client/dist/runtime.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/runtime.js +10 -1
- package/node_modules/@playdrop/config/client-meta.json +2 -2
- package/node_modules/@playdrop/game-source-git/dist/src/repository.d.ts +8 -0
- package/node_modules/@playdrop/game-source-git/dist/src/repository.d.ts.map +1 -1
- package/node_modules/@playdrop/game-source-git/dist/src/repository.js +30 -17
- package/node_modules/@playdrop/game-source-git/dist/test/core.test.js +10 -0
- package/node_modules/@playdrop/game-source-git/dist/tsconfig.tsbuildinfo +1 -1
- package/node_modules/@playdrop/game-source-git/package.json +2 -1
- package/node_modules/@playdrop/types/dist/api.d.ts +30 -3
- package/node_modules/@playdrop/types/dist/api.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/api.js +1 -0
- package/node_modules/@playdrop/types/dist/app-listing-localization.d.ts +34 -0
- package/node_modules/@playdrop/types/dist/app-listing-localization.d.ts.map +1 -0
- package/node_modules/@playdrop/types/dist/app-listing-localization.js +146 -0
- package/node_modules/@playdrop/types/dist/chat.d.ts +7 -2
- package/node_modules/@playdrop/types/dist/chat.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/index.d.ts +3 -0
- package/node_modules/@playdrop/types/dist/index.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/index.js +6 -0
- package/node_modules/@playdrop/types/dist/ugc-processing.d.ts +211 -0
- package/node_modules/@playdrop/types/dist/ugc-processing.d.ts.map +1 -0
- package/node_modules/@playdrop/types/dist/ugc-processing.js +144 -0
- package/node_modules/@playdrop/types/dist/ugc.d.ts +39 -0
- package/node_modules/@playdrop/types/dist/ugc.d.ts.map +1 -0
- package/node_modules/@playdrop/types/dist/ugc.js +70 -0
- package/node_modules/@playdrop/types/dist/version.d.ts +4 -0
- package/node_modules/@playdrop/types/dist/version.d.ts.map +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -153,3 +153,30 @@ For direct first-party server uploads from this checkout, start `npm run dev:age
|
|
|
153
153
|
playdrop documentation browse
|
|
154
154
|
playdrop documentation read runtime
|
|
155
155
|
```
|
|
156
|
+
|
|
157
|
+
## Localized game listings
|
|
158
|
+
|
|
159
|
+
An app in `catalogue.json` can declare a BCP47 `sourceLocale` (including languages such as Italian or Arabic) and an inline `listingLocalizations` object keyed by a supported PlayDrop locale. The original app fields remain the source listing. Each localization is a complete presentation: `displayName`, optional `subtitle`, `description`, `controls`, `makingOf`, `releaseNotes`, and its own `listing` media paths and captions. Omitted optional fields and arrays stay empty; they do not inherit source values. Explicitly reuse a source path when the same artwork should appear in both languages. Social artwork accepts PNG or JPEG, other images use PNG, and trailers use MP4.
|
|
160
|
+
|
|
161
|
+
Run `playdrop project validate .` to obtain `listingSourceHash`. Each localization records that value as `sourceHash`. Source text or media changes make older localizations stale; runtime version changes do not. Stale localizations remain inspectable but are excluded from public presentation. Uploads replace the complete localization set, so omitting an entry removes that locale from the new version.
|
|
162
|
+
|
|
163
|
+
```json
|
|
164
|
+
{
|
|
165
|
+
"sourceLocale": "en",
|
|
166
|
+
"listingLocalizations": {
|
|
167
|
+
"fr": {
|
|
168
|
+
"sourceHash": "<64-character listingSourceHash from validation>",
|
|
169
|
+
"displayName": "Mon jeu",
|
|
170
|
+
"description": "Description du jeu.",
|
|
171
|
+
"listing": {
|
|
172
|
+
"heroLandscape": "listing/hero-landscape.png",
|
|
173
|
+
"screenshotsLandscape": [
|
|
174
|
+
{ "path": "listing/fr/screenshot.png", "alt": "Une partie en cours" }
|
|
175
|
+
]
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Cloud task checkout, upload, and publish preserve this contract and include localized listing files in the source sync and listing locks.
|
package/config/client-meta.json
CHANGED
package/dist/apps/upload.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ApiClient } from "@playdrop/api-client";
|
|
2
2
|
import type { AgentTaskPlaytestProof, FinalizeAppUploadRequest, UserResponse } from "@playdrop/types";
|
|
3
|
+
import { type AppListingLocalization } from "@playdrop/types";
|
|
3
4
|
import type { AppTask } from "../catalogue";
|
|
4
5
|
import type { TaskCaptureSession } from "../appUrls";
|
|
5
6
|
import type { AppBuildArtifacts } from "./build";
|
|
@@ -73,6 +74,7 @@ type PreparedAppUploadFiles = {
|
|
|
73
74
|
sessionFiles: PreparedSessionFile[];
|
|
74
75
|
ownedAssets: PreparedOwnedAssetUpload[];
|
|
75
76
|
gameServerBuild: GameServerBuild | null;
|
|
77
|
+
listingLocalizations: AppListingLocalization[];
|
|
76
78
|
};
|
|
77
79
|
export declare function prepareAppUploadFiles(task: AppTask, artifacts: AppBuildArtifacts | null, options?: {
|
|
78
80
|
apiBase?: string;
|
package/dist/apps/upload.js
CHANGED
|
@@ -7,6 +7,8 @@ const types_1 = require("@playdrop/types");
|
|
|
7
7
|
const node_crypto_1 = require("node:crypto");
|
|
8
8
|
const node_fs_1 = require("node:fs");
|
|
9
9
|
const node_path_1 = require("node:path");
|
|
10
|
+
const proxyFetch_1 = require("../proxyFetch");
|
|
11
|
+
const uploadDispatchers_1 = require("../uploadDispatchers");
|
|
10
12
|
const http_1 = require("../http");
|
|
11
13
|
const proceduralValidation_1 = require("../proceduralValidation");
|
|
12
14
|
const game_source_git_1 = require("../commands/worker/game-source-git");
|
|
@@ -178,7 +180,36 @@ function pushPreparedArtifact(task, currentTotal, file, maxBytes, code, label) {
|
|
|
178
180
|
const bytes = ensurePreparedArtifactSize(file, maxBytes, code, label);
|
|
179
181
|
return accumulateUploadBytes(task.name, currentTotal, bytes);
|
|
180
182
|
}
|
|
181
|
-
|
|
183
|
+
function objectUploadTransportCode(error) {
|
|
184
|
+
return error && typeof error === "object" && "code" in error &&
|
|
185
|
+
typeof error.code === "string" && /^[A-Z][A-Z0-9_]{0,63}$/.test(error.code)
|
|
186
|
+
? error.code
|
|
187
|
+
: null;
|
|
188
|
+
}
|
|
189
|
+
function objectUploadTransportDetail(error, signedUrl) {
|
|
190
|
+
const details = [];
|
|
191
|
+
const seen = new Set();
|
|
192
|
+
let current = error;
|
|
193
|
+
while (current && typeof current === "object" && !seen.has(current) && seen.size < 4) {
|
|
194
|
+
seen.add(current);
|
|
195
|
+
const value = current;
|
|
196
|
+
const code = objectUploadTransportCode(value);
|
|
197
|
+
// Error messages can echo the signed request. Keep the transport explanation,
|
|
198
|
+
// never its URL, authorization header or credentials, in task/operator logs.
|
|
199
|
+
const message = typeof value.message === "string" ? value.message
|
|
200
|
+
.split(signedUrl).join("[redacted-url]")
|
|
201
|
+
.replace(/\bhttps?:\/\/[^\s"'<>]+/gi, "[redacted-url]")
|
|
202
|
+
.replace(/\b(?:proxy-authorization|authorization)\s*:\s*[^\r\n]+/gi, "authorization: [redacted]")
|
|
203
|
+
.replace(/\b(?:Bearer|Basic)\s+[^\s,;]+/gi, "[redacted-authorization]")
|
|
204
|
+
.replace(/\b(?:X-Goog-(?:Signature|Credential|Security-Token)|token|signature|credential|authorization)\b["']?\s*[=:]\s*["']?[^\s"',;}]+/gi, "[redacted-credential]")
|
|
205
|
+
.replace(/[\x00-\x1f\x7f]+/g, " ").trim().slice(0, 200) : "";
|
|
206
|
+
if (message || code)
|
|
207
|
+
details.push([code, message].filter(Boolean).join(": "));
|
|
208
|
+
current = value.cause;
|
|
209
|
+
}
|
|
210
|
+
return details.join(" <- ").slice(0, 400);
|
|
211
|
+
}
|
|
212
|
+
async function uploadSignedObject(client, creatorUsername, appName, sessionId, file, descriptor, transport) {
|
|
182
213
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
183
214
|
let response;
|
|
184
215
|
try {
|
|
@@ -186,22 +217,23 @@ async function uploadSignedObject(client, creatorUsername, appName, sessionId, f
|
|
|
186
217
|
method: descriptor.method,
|
|
187
218
|
headers: descriptor.headers,
|
|
188
219
|
body: file.file,
|
|
220
|
+
...transport,
|
|
189
221
|
});
|
|
190
222
|
}
|
|
191
223
|
catch (error) {
|
|
192
224
|
const cause = error instanceof Error ? error.cause : null;
|
|
193
|
-
const code = cause
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
: "UNKNOWN";
|
|
197
|
-
const annotated = new Error(`direct_object_upload_network_failed:${file.relativePath}:${code}`);
|
|
225
|
+
const code = objectUploadTransportCode(cause) ?? objectUploadTransportCode(error) ?? "UNKNOWN";
|
|
226
|
+
const detail = objectUploadTransportDetail(cause ?? error, descriptor.url);
|
|
227
|
+
const annotated = new Error(`direct_object_upload_network_failed:${file.relativePath}:${code}${detail ? `: ${detail}` : ""}`);
|
|
198
228
|
Object.defineProperty(annotated, "cause", { value: error, configurable: true, writable: true });
|
|
199
229
|
throw annotated;
|
|
200
230
|
}
|
|
201
231
|
if (response.ok) {
|
|
232
|
+
await response.body?.cancel();
|
|
202
233
|
return;
|
|
203
234
|
}
|
|
204
235
|
if ((response.status === 401 || response.status === 403) && attempt === 0) {
|
|
236
|
+
await response.body?.cancel();
|
|
205
237
|
const signed = await client.signAppUploadObjects(creatorUsername, appName, sessionId, {
|
|
206
238
|
objectIds: [file.id],
|
|
207
239
|
});
|
|
@@ -217,23 +249,48 @@ async function uploadSignedObject(client, creatorUsername, appName, sessionId, f
|
|
|
217
249
|
}
|
|
218
250
|
}
|
|
219
251
|
async function uploadSignedObjects(client, creatorUsername, appName, sessionId, files) {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
252
|
+
if (files.length === 0)
|
|
253
|
+
return;
|
|
254
|
+
// Uploads run in the long-lived worker supervisor. Own this pool here so a
|
|
255
|
+
// later upload cannot inherit its connections or Node's changing H2 defaults.
|
|
256
|
+
const dispatcher = new uploadDispatchers_1.ObjectUploadAgent({
|
|
257
|
+
...(0, proxyFetch_1.resolveProxyEnvironment)(),
|
|
258
|
+
allowH2: false,
|
|
259
|
+
requestTls: { allowH2: false },
|
|
260
|
+
proxyTls: { allowH2: false },
|
|
261
|
+
});
|
|
262
|
+
const controller = new AbortController();
|
|
263
|
+
const transport = { dispatcher, signal: controller.signal };
|
|
264
|
+
try {
|
|
265
|
+
for (let offset = 0; offset < files.length; offset += 100) {
|
|
266
|
+
const batch = files.slice(offset, offset + 100);
|
|
267
|
+
const signed = await client.signAppUploadObjects(creatorUsername, appName, sessionId, {
|
|
268
|
+
objectIds: batch.map((file) => file.id),
|
|
269
|
+
});
|
|
270
|
+
const descriptors = new Map(signed.uploads.map((entry) => [entry.id, entry]));
|
|
271
|
+
let cursor = 0;
|
|
272
|
+
const workers = Array.from({ length: Math.min(8, batch.length) }, async () => {
|
|
273
|
+
try {
|
|
274
|
+
while (!controller.signal.aborted && cursor < batch.length) {
|
|
275
|
+
const file = batch[cursor++];
|
|
276
|
+
const descriptor = descriptors.get(file.id);
|
|
277
|
+
// A missing descriptor means the API verified this object is stored.
|
|
278
|
+
if (descriptor)
|
|
279
|
+
await uploadSignedObject(client, creatorUsername, appName, sessionId, file, descriptor, transport);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
catch (error) {
|
|
283
|
+
// Preserve the triggering failure, cancel peers and stop taking work.
|
|
284
|
+
controller.abort(error);
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
await Promise.all(workers);
|
|
288
|
+
controller.signal.throwIfAborted();
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
finally {
|
|
292
|
+
// Finish cancellation before the caller aborts the server upload session.
|
|
293
|
+
await dispatcher.destroy();
|
|
237
294
|
}
|
|
238
295
|
}
|
|
239
296
|
async function prepareAppUploadFiles(task, artifacts, options = {}) {
|
|
@@ -277,46 +334,59 @@ async function prepareAppUploadFiles(task, artifacts, options = {}) {
|
|
|
277
334
|
sessionFiles.push(await buildPreparedSessionFile("SERVER", "server/bundle.mjs", bundle.file));
|
|
278
335
|
sessionFiles.push(await buildPreparedSessionFile("SERVER", "server/manifest.json", manifest.file));
|
|
279
336
|
}
|
|
280
|
-
|
|
281
|
-
|
|
337
|
+
const preparedByHash = new Map();
|
|
338
|
+
const prepareListingFile = async (filePath, destination, slot, reuse) => {
|
|
339
|
+
const video = slot.startsWith("videos");
|
|
340
|
+
const socialJpeg = slot === "socialLandscapePath" && [".jpg", ".jpeg"].includes((0, node_path_1.extname)(filePath).toLowerCase());
|
|
341
|
+
if (socialJpeg)
|
|
342
|
+
destination = destination.replace(/\.png$/, ".jpg");
|
|
343
|
+
const maxBytes = video ? config_1.MAX_VERSION_VIDEO_BYTES : slot === "iconPath" ? config_1.MAX_VERSION_ICON_BYTES : slot.startsWith("screenshots") ? config_1.MAX_VERSION_SCREENSHOT_BYTES : config_1.MAX_VERSION_HERO_BYTES;
|
|
344
|
+
const prepared = createFileFromPath(filePath, video ? "video/mp4" : socialJpeg ? "image/jpeg" : "image/png", maxBytes, "listing_media_too_large", "invalid_listing_media_content_type", destination, video ? isMp4Signature : socialJpeg ? (bytes) => bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff : isPngSignature);
|
|
345
|
+
const file = await buildPreparedSessionFile("LISTING", destination, prepared.file);
|
|
346
|
+
const hashKey = `${file.contentType}:${file.sha256}`;
|
|
347
|
+
const existing = preparedByHash.get(hashKey);
|
|
348
|
+
if (reuse && existing)
|
|
349
|
+
return existing;
|
|
282
350
|
totalUploadBytes = pushPreparedFile(task.name, totalUploadBytes, prepared);
|
|
283
|
-
sessionFiles.push(
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
for (let index = 0; index < (listing?.screenshotLandscapePaths ?? []).length; index += 1) {
|
|
302
|
-
const filePath = listing.screenshotLandscapePaths[index];
|
|
303
|
-
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);
|
|
304
|
-
totalUploadBytes = pushPreparedFile(task.name, totalUploadBytes, prepared);
|
|
305
|
-
sessionFiles.push(await buildPreparedSessionFile("LISTING", `listing/screenshots/landscape/${index}.png`, prepared.file));
|
|
351
|
+
sessionFiles.push(file);
|
|
352
|
+
preparedByHash.set(hashKey, destination);
|
|
353
|
+
return destination;
|
|
354
|
+
};
|
|
355
|
+
const singletonSlots = [
|
|
356
|
+
["iconPath", "icon.png"], ["heroPortraitPath", "hero-portrait.png"],
|
|
357
|
+
["heroLandscapePath", "hero-landscape.png"], ["socialLandscapePath", "social-landscape.png"],
|
|
358
|
+
];
|
|
359
|
+
const arraySlots = [
|
|
360
|
+
["screenshotsPortrait", "screenshotPortraitPaths", "screenshots/portrait", "png"],
|
|
361
|
+
["screenshotsLandscape", "screenshotLandscapePaths", "screenshots/landscape", "png"],
|
|
362
|
+
["videosPortrait", "videoPortraitPaths", "videos/portrait", "mp4"],
|
|
363
|
+
["videosLandscape", "videoLandscapePaths", "videos/landscape", "mp4"],
|
|
364
|
+
];
|
|
365
|
+
for (const [slot, filename] of singletonSlots) {
|
|
366
|
+
const file = listing?.[slot];
|
|
367
|
+
if (file)
|
|
368
|
+
await prepareListingFile(file, `listing/${filename}`, slot, false);
|
|
306
369
|
}
|
|
307
|
-
for (
|
|
308
|
-
const
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
sessionFiles.push(await buildPreparedSessionFile("LISTING", `listing/videos/portrait/${index}.mp4`, prepared.file));
|
|
370
|
+
for (const [slot, field, directory, extension] of arraySlots) {
|
|
371
|
+
const files = listing?.[field] ?? [];
|
|
372
|
+
for (const [index, file] of files.entries())
|
|
373
|
+
await prepareListingFile(file, `listing/${directory}/${index}.${extension}`, slot, false);
|
|
312
374
|
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
const
|
|
316
|
-
|
|
317
|
-
|
|
375
|
+
const listingLocalizations = [];
|
|
376
|
+
for (const localization of task.listingLocalizations ?? []) {
|
|
377
|
+
const localized = { ...localization };
|
|
378
|
+
for (const [slot, filename] of singletonSlots) {
|
|
379
|
+
const file = localization[slot];
|
|
380
|
+
localized[slot] = file ? await prepareListingFile(file, `listing/locales/${localization.locale}/${filename}`, slot, true) : null;
|
|
381
|
+
}
|
|
382
|
+
for (const [slot, , directory, extension] of arraySlots) {
|
|
383
|
+
localized[slot] = [];
|
|
384
|
+
for (const [index, file] of localization[slot].entries())
|
|
385
|
+
localized[slot].push(await prepareListingFile(file, `listing/locales/${localization.locale}/${directory}/${index}.${extension}`, slot, true));
|
|
386
|
+
}
|
|
387
|
+
listingLocalizations.push(localized);
|
|
318
388
|
}
|
|
319
|
-
return { sessionFiles, ownedAssets, gameServerBuild: gameServer };
|
|
389
|
+
return { sessionFiles, ownedAssets, gameServerBuild: gameServer, listingLocalizations };
|
|
320
390
|
}
|
|
321
391
|
async function uploadApp(client, task, artifacts, options) {
|
|
322
392
|
// Version is required - no legacy fallback
|
|
@@ -349,7 +419,7 @@ async function uploadAppVersion(client, task, artifacts, options) {
|
|
|
349
419
|
"The prepared upload does not match the project; finish edits and checks before retrying.");
|
|
350
420
|
}
|
|
351
421
|
};
|
|
352
|
-
const { sessionFiles: preparedSessionFiles, ownedAssets: preparedOwnedAssets, gameServerBuild } = await prepareAppUploadFiles(task, artifacts, { apiBase: options?.apiBase, client });
|
|
422
|
+
const { sessionFiles: preparedSessionFiles, ownedAssets: preparedOwnedAssets, gameServerBuild, listingLocalizations } = await prepareAppUploadFiles(task, artifacts, { apiBase: options?.apiBase, client });
|
|
353
423
|
await assertPublicationUnchanged(inputFingerprint, false);
|
|
354
424
|
const publicationFingerprint = options?.prepareGitTransition
|
|
355
425
|
? await (0, game_source_git_1.fingerprintGameSourcePublication)(task.projectDir)
|
|
@@ -367,6 +437,9 @@ async function uploadAppVersion(client, task, artifacts, options) {
|
|
|
367
437
|
})));
|
|
368
438
|
const preparedObjects = [...preparedSessionFiles, ...preparedOwnedAssetFiles];
|
|
369
439
|
const initializeRequest = {
|
|
440
|
+
sourceLocale: task.sourceLocale,
|
|
441
|
+
listingSourceHash: task.listingSourceHash,
|
|
442
|
+
listingLocalizations,
|
|
370
443
|
version: task.version,
|
|
371
444
|
agentTaskId: options?.agentTaskId,
|
|
372
445
|
playtestProof: options?.playtestProof,
|
package/dist/catalogue.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type AppListingContent, type AppListingLocalization } from "@playdrop/types";
|
|
1
2
|
import { type AppAchievementCatalogueDefinition, type AppAuthMode, type AppControllerMode, type AppCreationMetadata, type AppHostingMode, type AppLeaderboardCatalogueDefinition, type AppListingMediaMetadata, type AppMetadataAssetSpecSupport, type AppPlaytestTapes, type AppPlayersConfig, type AppSurface, type AppType, type AppTweaksDeclaration, type AppVersionDesign, type AppVersionVisibility, type AssetSpecContract, type AssetSpecStatus, type AssetSpecValidationKind, type CatalogueTagGroupDefinition, type ContentLicense, type PlayerMetaDefinitionStatus } from "@playdrop/types";
|
|
2
3
|
import { type AssetSpecSupportDeclaration } from "./assetSpecs";
|
|
3
4
|
export type CatalogueJson = {
|
|
@@ -21,6 +22,7 @@ export type AppListingConfig = {
|
|
|
21
22
|
icon?: string;
|
|
22
23
|
heroPortrait?: string;
|
|
23
24
|
heroLandscape?: string;
|
|
25
|
+
socialLandscape?: string;
|
|
24
26
|
screenshotsPortrait?: AppListingMediaConfig[];
|
|
25
27
|
screenshotsLandscape?: AppListingMediaConfig[];
|
|
26
28
|
videosPortrait?: AppListingMediaConfig[];
|
|
@@ -177,6 +179,7 @@ export type ResolvedListingAssets = {
|
|
|
177
179
|
iconPath?: string;
|
|
178
180
|
heroPortraitPath?: string;
|
|
179
181
|
heroLandscapePath?: string;
|
|
182
|
+
socialLandscapePath?: string;
|
|
180
183
|
screenshotPortraitPaths: string[];
|
|
181
184
|
screenshotLandscapePaths: string[];
|
|
182
185
|
videoPortraitPaths: string[];
|
|
@@ -194,6 +197,9 @@ export type AppServerTask = {
|
|
|
194
197
|
rooms: string[];
|
|
195
198
|
};
|
|
196
199
|
export type AppTask = {
|
|
200
|
+
sourceLocale?: string | null;
|
|
201
|
+
listingSourceHash?: string;
|
|
202
|
+
listingLocalizations?: AppListingLocalization[];
|
|
197
203
|
kind: "app";
|
|
198
204
|
name: string;
|
|
199
205
|
cataloguePath: string;
|
|
@@ -437,4 +443,16 @@ export declare function findAppTaskByFile(htmlPath: string, options?: Pick<Catal
|
|
|
437
443
|
warnings: string[];
|
|
438
444
|
errors: string[];
|
|
439
445
|
};
|
|
446
|
+
export declare function catalogueListingContent(task: {
|
|
447
|
+
displayName?: string;
|
|
448
|
+
subtitle?: string;
|
|
449
|
+
description?: string;
|
|
450
|
+
controls?: string;
|
|
451
|
+
makingOf?: string;
|
|
452
|
+
releaseNotes?: string;
|
|
453
|
+
listing?: ResolvedListingAssets;
|
|
454
|
+
}): AppListingContent;
|
|
455
|
+
export declare function calculateCatalogueListingSourceHash(task: Parameters<typeof catalogueListingContent>[0] & {
|
|
456
|
+
sourceLocale?: string | null;
|
|
457
|
+
}): string;
|
|
440
458
|
export {};
|