@playdrop/playdrop-cli 0.3.8-build.3 → 0.3.10
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 +43 -1
- package/config/client-meta.json +5 -6
- package/dist/apps/build.js +43 -38
- package/dist/catalogue-utils.js +30 -18
- package/dist/catalogue.d.ts +0 -3
- package/dist/catalogue.js +80 -49
- package/dist/clientInfo.js +16 -2
- package/dist/commands/browse.js +10 -1
- package/dist/commands/capture.js +3 -2
- package/dist/commands/create.js +153 -54
- package/dist/commands/createRemixContent.js +61 -46
- package/dist/commands/creations.js +10 -1
- 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/init.js +30 -2
- package/dist/commands/search.js +10 -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/dist/taskSelection.js +2 -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 +20 -12
- 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 +1 -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 +127 -128
- 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 +1 -0
- package/node_modules/@playdrop/api-client/dist/domains/assets.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/assets.js +150 -115
- 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/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 +34 -31
- package/node_modules/@playdrop/api-client/dist/index.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/index.js +19 -9
- 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/config/client-meta.json +5 -6
- package/node_modules/@playdrop/config/dist/src/index.js +6 -6
- package/node_modules/@playdrop/config/dist/test/validateClientEnvironment.test.js +15 -2
- package/node_modules/@playdrop/config/dist/tsconfig.tsbuildinfo +1 -1
- package/node_modules/@playdrop/types/dist/api.d.ts +54 -9
- package/node_modules/@playdrop/types/dist/api.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/api.js +15 -8
- package/node_modules/@playdrop/types/dist/asset-pack.d.ts +105 -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 +18 -50
- package/node_modules/@playdrop/types/dist/asset.d.ts.map +1 -1
- 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/package.json +23 -2
|
@@ -0,0 +1,627 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildAssetKey = buildAssetKey;
|
|
4
|
+
exports.buildPackKey = buildPackKey;
|
|
5
|
+
exports.getTaskCreatorResult = getTaskCreatorResult;
|
|
6
|
+
exports.parseUnversionedAssetTaskRef = parseUnversionedAssetTaskRef;
|
|
7
|
+
exports.buildAssetPackUploadPlans = buildAssetPackUploadPlans;
|
|
8
|
+
exports.uploadAssetTask = uploadAssetTask;
|
|
9
|
+
exports.uploadAssetPackTask = uploadAssetPackTask;
|
|
10
|
+
const node_crypto_1 = require("node:crypto");
|
|
11
|
+
const node_fs_1 = require("node:fs");
|
|
12
|
+
const node_path_1 = require("node:path");
|
|
13
|
+
const types_1 = require("@playdrop/types");
|
|
14
|
+
const model_artifacts_1 = require("../assets/model-artifacts");
|
|
15
|
+
const EXTENSION_TO_FORMAT = {
|
|
16
|
+
'.png': 'PNG',
|
|
17
|
+
'.jpg': 'JPEG',
|
|
18
|
+
'.jpeg': 'JPEG',
|
|
19
|
+
'.webp': 'WEBP',
|
|
20
|
+
'.gif': 'GIF',
|
|
21
|
+
'.aseprite': 'ASEPRITE',
|
|
22
|
+
'.mp4': 'MP4',
|
|
23
|
+
'.webm': 'WEBM',
|
|
24
|
+
'.mp3': 'MP3',
|
|
25
|
+
'.wav': 'WAV',
|
|
26
|
+
'.ogg': 'OGG',
|
|
27
|
+
'.vox': 'VOX',
|
|
28
|
+
'.gltf': 'GLTF',
|
|
29
|
+
'.glb': 'GLB',
|
|
30
|
+
};
|
|
31
|
+
const EXTENSION_TO_MIME = {
|
|
32
|
+
'.png': 'image/png',
|
|
33
|
+
'.jpg': 'image/jpeg',
|
|
34
|
+
'.jpeg': 'image/jpeg',
|
|
35
|
+
'.webp': 'image/webp',
|
|
36
|
+
'.gif': 'image/gif',
|
|
37
|
+
'.aseprite': 'application/octet-stream',
|
|
38
|
+
'.mp4': 'video/mp4',
|
|
39
|
+
'.webm': 'video/webm',
|
|
40
|
+
'.mp3': 'audio/mpeg',
|
|
41
|
+
'.wav': 'audio/wav',
|
|
42
|
+
'.ogg': 'audio/ogg',
|
|
43
|
+
'.vox': 'application/octet-stream',
|
|
44
|
+
'.gltf': 'model/gltf+json',
|
|
45
|
+
'.glb': 'model/gltf-binary',
|
|
46
|
+
'.json': 'application/json',
|
|
47
|
+
};
|
|
48
|
+
function isPngSignature(buffer) {
|
|
49
|
+
if (buffer.length < 8) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
return buffer[0] === 0x89
|
|
53
|
+
&& buffer[1] === 0x50
|
|
54
|
+
&& buffer[2] === 0x4e
|
|
55
|
+
&& buffer[3] === 0x47
|
|
56
|
+
&& buffer[4] === 0x0d
|
|
57
|
+
&& buffer[5] === 0x0a
|
|
58
|
+
&& buffer[6] === 0x1a
|
|
59
|
+
&& buffer[7] === 0x0a;
|
|
60
|
+
}
|
|
61
|
+
function isMp4Signature(buffer) {
|
|
62
|
+
return buffer.length >= 12
|
|
63
|
+
&& buffer[4] === 0x66
|
|
64
|
+
&& buffer[5] === 0x74
|
|
65
|
+
&& buffer[6] === 0x79
|
|
66
|
+
&& buffer[7] === 0x70;
|
|
67
|
+
}
|
|
68
|
+
function createListingFileFromPath(filePath, expectedMime) {
|
|
69
|
+
const data = (0, node_fs_1.readFileSync)(filePath);
|
|
70
|
+
if (expectedMime === 'image/png' && !isPngSignature(data)) {
|
|
71
|
+
throw new Error(`Invalid listing file signature for ${filePath}. Expected image/png.`);
|
|
72
|
+
}
|
|
73
|
+
if (expectedMime === 'video/mp4' && !isMp4Signature(data)) {
|
|
74
|
+
throw new Error(`Invalid listing file signature for ${filePath}. Expected video/mp4.`);
|
|
75
|
+
}
|
|
76
|
+
return new File([data], (0, node_path_1.basename)(filePath), { type: expectedMime });
|
|
77
|
+
}
|
|
78
|
+
function computeSha256Hex(buffer) {
|
|
79
|
+
return (0, node_crypto_1.createHash)('sha256').update(buffer).digest('hex');
|
|
80
|
+
}
|
|
81
|
+
function resolveMimeType(filePath) {
|
|
82
|
+
return EXTENSION_TO_MIME[(0, node_path_1.extname)(filePath).toLowerCase()] ?? 'application/octet-stream';
|
|
83
|
+
}
|
|
84
|
+
function prepareUploadFile(filePath, fieldName, explicitMimeType) {
|
|
85
|
+
const data = (0, node_fs_1.readFileSync)(filePath);
|
|
86
|
+
const filename = (0, node_path_1.basename)(filePath);
|
|
87
|
+
const contentType = explicitMimeType ?? resolveMimeType(filePath);
|
|
88
|
+
return {
|
|
89
|
+
fieldName,
|
|
90
|
+
filename,
|
|
91
|
+
filePath,
|
|
92
|
+
file: new File([data], filename, { type: contentType }),
|
|
93
|
+
contentType,
|
|
94
|
+
sizeBytes: data.length,
|
|
95
|
+
sha256: computeSha256Hex(data),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function buildListingMediaRelativePath(mediaKey) {
|
|
99
|
+
if (mediaKey === 'icon')
|
|
100
|
+
return 'listing/icon.png';
|
|
101
|
+
if (mediaKey === 'heroPortrait')
|
|
102
|
+
return 'listing/hero-portrait.png';
|
|
103
|
+
if (mediaKey === 'heroLandscape')
|
|
104
|
+
return 'listing/hero-landscape.png';
|
|
105
|
+
const portraitScreenshot = /^screenshotsPortrait:(\d+)$/.exec(mediaKey);
|
|
106
|
+
if (portraitScreenshot) {
|
|
107
|
+
return `listing/screenshots/portrait/${Number.parseInt(portraitScreenshot[1], 10) + 1}.png`;
|
|
108
|
+
}
|
|
109
|
+
const landscapeScreenshot = /^screenshotsLandscape:(\d+)$/.exec(mediaKey);
|
|
110
|
+
if (landscapeScreenshot) {
|
|
111
|
+
return `listing/screenshots/landscape/${Number.parseInt(landscapeScreenshot[1], 10) + 1}.png`;
|
|
112
|
+
}
|
|
113
|
+
const portraitVideo = /^videosPortrait:(\d+)$/.exec(mediaKey);
|
|
114
|
+
if (portraitVideo) {
|
|
115
|
+
return `listing/videos/portrait/${Number.parseInt(portraitVideo[1], 10) + 1}.mp4`;
|
|
116
|
+
}
|
|
117
|
+
const landscapeVideo = /^videosLandscape:(\d+)$/.exec(mediaKey);
|
|
118
|
+
if (landscapeVideo) {
|
|
119
|
+
return `listing/videos/landscape/${Number.parseInt(landscapeVideo[1], 10) + 1}.mp4`;
|
|
120
|
+
}
|
|
121
|
+
throw new Error(`Unsupported pack listing media key "${mediaKey}".`);
|
|
122
|
+
}
|
|
123
|
+
function prepareListingMediaUpload(mediaKey, filePath, expectedMime) {
|
|
124
|
+
const file = createListingFileFromPath(filePath, expectedMime);
|
|
125
|
+
const buffer = (0, node_fs_1.readFileSync)(filePath);
|
|
126
|
+
return {
|
|
127
|
+
mediaKey,
|
|
128
|
+
file,
|
|
129
|
+
filename: (0, node_path_1.basename)(filePath),
|
|
130
|
+
entry: {
|
|
131
|
+
mediaKey,
|
|
132
|
+
relativePath: buildListingMediaRelativePath(mediaKey),
|
|
133
|
+
contentType: expectedMime,
|
|
134
|
+
sizeBytes: buffer.length,
|
|
135
|
+
sha256: computeSha256Hex(buffer),
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function buildAssetKey(creatorUsername, name) {
|
|
140
|
+
return `${creatorUsername}/${name}`;
|
|
141
|
+
}
|
|
142
|
+
function buildPackKey(creatorUsername, name, version) {
|
|
143
|
+
return `${creatorUsername}/${name}@${version}`;
|
|
144
|
+
}
|
|
145
|
+
function getTaskCreatorResult(task, defaultCreator, currentUserRole) {
|
|
146
|
+
const requestedTaskOwner = 'username' in task && typeof task.username === 'string'
|
|
147
|
+
? String(task.username).trim()
|
|
148
|
+
: '';
|
|
149
|
+
const creatorTargetError = requestedTaskOwner && currentUserRole !== 'ADMIN'
|
|
150
|
+
? `Task "${task.name}" sets username="${requestedTaskOwner}", but only admins can target another creator.`
|
|
151
|
+
: null;
|
|
152
|
+
return {
|
|
153
|
+
requestedTaskOwner,
|
|
154
|
+
taskCreator: requestedTaskOwner && !creatorTargetError ? requestedTaskOwner : defaultCreator,
|
|
155
|
+
creatorTargetError,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
function parseUnversionedAssetTaskRef(rawRef, fallbackCreator) {
|
|
159
|
+
const trimmed = rawRef.trim();
|
|
160
|
+
if (!trimmed || trimmed.includes('@')) {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
const slashIndex = trimmed.indexOf('/');
|
|
164
|
+
if (slashIndex > 0) {
|
|
165
|
+
return {
|
|
166
|
+
creatorUsername: trimmed.slice(0, slashIndex),
|
|
167
|
+
name: trimmed.slice(slashIndex + 1),
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
creatorUsername: fallbackCreator,
|
|
172
|
+
name: trimmed,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function buildAssetPackUploadPlans(tasks, defaultCreator, currentUserRole) {
|
|
176
|
+
const localAssetTasksByKey = new Map();
|
|
177
|
+
for (const task of tasks) {
|
|
178
|
+
if (task.kind !== 'asset') {
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
const creator = getTaskCreatorResult(task, defaultCreator, currentUserRole);
|
|
182
|
+
if (creator.creatorTargetError) {
|
|
183
|
+
return {
|
|
184
|
+
ok: false,
|
|
185
|
+
task,
|
|
186
|
+
message: creator.creatorTargetError,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
localAssetTasksByKey.set(buildAssetKey(creator.taskCreator, task.name), task);
|
|
190
|
+
}
|
|
191
|
+
const packPlansByKey = new Map();
|
|
192
|
+
const ownedPackByAssetKey = new Map();
|
|
193
|
+
for (const task of tasks) {
|
|
194
|
+
if (task.kind !== 'asset-pack') {
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
const creator = getTaskCreatorResult(task, defaultCreator, currentUserRole);
|
|
198
|
+
if (creator.creatorTargetError) {
|
|
199
|
+
return {
|
|
200
|
+
ok: false,
|
|
201
|
+
task,
|
|
202
|
+
message: creator.creatorTargetError,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
const packKey = buildPackKey(creator.taskCreator, task.name, task.version);
|
|
206
|
+
const plan = {
|
|
207
|
+
packKey,
|
|
208
|
+
packTask: task,
|
|
209
|
+
packCreator: creator.taskCreator,
|
|
210
|
+
ownedAssetKeys: [],
|
|
211
|
+
uploadKeyByAssetKey: new Map(),
|
|
212
|
+
};
|
|
213
|
+
for (const assetRef of task.assets) {
|
|
214
|
+
const candidate = parseUnversionedAssetTaskRef(assetRef, creator.taskCreator);
|
|
215
|
+
if (!candidate || candidate.creatorUsername !== creator.taskCreator) {
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
const assetKey = buildAssetKey(candidate.creatorUsername, candidate.name);
|
|
219
|
+
if (!localAssetTasksByKey.has(assetKey)) {
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
const existingOwner = ownedPackByAssetKey.get(assetKey);
|
|
223
|
+
if (existingOwner && existingOwner.packKey !== packKey) {
|
|
224
|
+
return {
|
|
225
|
+
ok: false,
|
|
226
|
+
task,
|
|
227
|
+
message: `Local asset "${candidate.name}" is referenced by multiple local asset packs in this upload run.`,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
if (!plan.uploadKeyByAssetKey.has(assetKey)) {
|
|
231
|
+
plan.uploadKeyByAssetKey.set(assetKey, candidate.name);
|
|
232
|
+
plan.ownedAssetKeys.push(assetKey);
|
|
233
|
+
}
|
|
234
|
+
ownedPackByAssetKey.set(assetKey, plan);
|
|
235
|
+
}
|
|
236
|
+
packPlansByKey.set(packKey, plan);
|
|
237
|
+
}
|
|
238
|
+
return {
|
|
239
|
+
ok: true,
|
|
240
|
+
packPlansByKey,
|
|
241
|
+
ownedPackByAssetKey,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
function resolveMutationCreatorUsername(task, creatorUsername, targetCreatorUsername) {
|
|
245
|
+
const mutationCreatorUsername = typeof targetCreatorUsername === 'string' && targetCreatorUsername.trim().length > 0
|
|
246
|
+
? targetCreatorUsername.trim()
|
|
247
|
+
: creatorUsername.trim();
|
|
248
|
+
if (!mutationCreatorUsername) {
|
|
249
|
+
throw new Error(`Asset pack "${task.name}@${task.version}" upload is missing creator username.`);
|
|
250
|
+
}
|
|
251
|
+
return mutationCreatorUsername;
|
|
252
|
+
}
|
|
253
|
+
function resolveAssetFormat(task) {
|
|
254
|
+
if (typeof task.format === 'string' && task.format.trim().length > 0) {
|
|
255
|
+
return task.format.trim().toUpperCase();
|
|
256
|
+
}
|
|
257
|
+
if (task.category === 'MODEL_3D') {
|
|
258
|
+
const primaryFile = task.filePaths.primary;
|
|
259
|
+
if (typeof primaryFile === 'string') {
|
|
260
|
+
const normalizedPrimary = primaryFile.trim().toLowerCase();
|
|
261
|
+
if (normalizedPrimary.endsWith('.boxel.json')) {
|
|
262
|
+
return 'PLAYDROP_BOXEL_JSON';
|
|
263
|
+
}
|
|
264
|
+
if (normalizedPrimary.endsWith('.vox')) {
|
|
265
|
+
return 'VOX';
|
|
266
|
+
}
|
|
267
|
+
const primaryExtension = (0, node_path_1.extname)(normalizedPrimary);
|
|
268
|
+
if (primaryExtension === '.glb') {
|
|
269
|
+
return 'GLB';
|
|
270
|
+
}
|
|
271
|
+
if (primaryExtension === '.gltf') {
|
|
272
|
+
return 'GLTF';
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
const boxelFile = task.filePaths.boxel;
|
|
276
|
+
if (typeof boxelFile === 'string' && boxelFile.trim().toLowerCase().endsWith('.boxel.json')) {
|
|
277
|
+
return 'PLAYDROP_BOXEL_JSON';
|
|
278
|
+
}
|
|
279
|
+
const meshFile = task.filePaths.mesh;
|
|
280
|
+
if (typeof meshFile === 'string') {
|
|
281
|
+
const meshExtension = (0, node_path_1.extname)(meshFile).toLowerCase();
|
|
282
|
+
if (meshExtension === '.glb') {
|
|
283
|
+
return 'GLB';
|
|
284
|
+
}
|
|
285
|
+
if (meshExtension === '.gltf') {
|
|
286
|
+
return 'GLTF';
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
throw new Error(`Asset "${task.name}" is missing files.primary or uses an unsupported MODEL_3D primary extension.`);
|
|
290
|
+
}
|
|
291
|
+
const filePaths = Object.values(task.filePaths);
|
|
292
|
+
if (filePaths.length === 0) {
|
|
293
|
+
throw new Error(`Asset "${task.name}" has no files.`);
|
|
294
|
+
}
|
|
295
|
+
if (filePaths.length > 1) {
|
|
296
|
+
throw new Error(`Asset "${task.name}" is missing format and has multiple files. Set "format" explicitly in catalogue.json.`);
|
|
297
|
+
}
|
|
298
|
+
const extension = (0, node_path_1.extname)(filePaths[0]).toLowerCase();
|
|
299
|
+
const inferred = EXTENSION_TO_FORMAT[extension];
|
|
300
|
+
if (!inferred) {
|
|
301
|
+
throw new Error(`Asset "${task.name}" uses unsupported file extension "${extension}". Set "format" explicitly.`);
|
|
302
|
+
}
|
|
303
|
+
return inferred;
|
|
304
|
+
}
|
|
305
|
+
function validateAssetSubcategoryCompatibility(task, format) {
|
|
306
|
+
const category = typeof task.category === 'string' ? task.category.trim().toUpperCase() : '';
|
|
307
|
+
const subcategory = typeof task.subcategory === 'string' ? task.subcategory.trim().toLowerCase() : '';
|
|
308
|
+
const definition = types_1.ASSET_SUBCATEGORY_DEFINITIONS_BY_KEY[`${category}:${subcategory}`];
|
|
309
|
+
if (!definition) {
|
|
310
|
+
throw new Error(`Asset "${task.name}" uses unsupported subcategory "${task.subcategory}" for category "${task.category}".`);
|
|
311
|
+
}
|
|
312
|
+
if (!definition.formats.includes(format)) {
|
|
313
|
+
throw new Error(`Asset "${task.name}" uses format "${format}" not allowed for subcategory "${subcategory}".`);
|
|
314
|
+
}
|
|
315
|
+
(0, model_artifacts_1.assertGeneratedModel3DAssetRoles)(task, format);
|
|
316
|
+
return subcategory;
|
|
317
|
+
}
|
|
318
|
+
async function prepareAssetTaskUploadData(task) {
|
|
319
|
+
const format = resolveAssetFormat(task);
|
|
320
|
+
const subcategory = validateAssetSubcategoryCompatibility(task, format);
|
|
321
|
+
await (0, model_artifacts_1.prepareModel3DAssetArtifacts)(task);
|
|
322
|
+
const uploadFiles = Object.entries(task.filePaths)
|
|
323
|
+
.sort(([leftRole], [rightRole]) => leftRole.localeCompare(rightRole))
|
|
324
|
+
.map(([role, filePath]) => prepareUploadFile(filePath, role));
|
|
325
|
+
return {
|
|
326
|
+
format,
|
|
327
|
+
subcategory,
|
|
328
|
+
uploadFiles,
|
|
329
|
+
manifestFiles: uploadFiles.map((entry) => ({
|
|
330
|
+
role: entry.fieldName,
|
|
331
|
+
filename: entry.filename,
|
|
332
|
+
contentType: entry.contentType,
|
|
333
|
+
sizeBytes: entry.sizeBytes,
|
|
334
|
+
sha256: entry.sha256,
|
|
335
|
+
})),
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
async function uploadAssetTask(client, task, sourceAppVersionId, creatorUsername) {
|
|
339
|
+
const prepared = await prepareAssetTaskUploadData(task);
|
|
340
|
+
const visibility = task.visibility;
|
|
341
|
+
const files = prepared.uploadFiles.map((entry) => ({
|
|
342
|
+
file: entry.file,
|
|
343
|
+
fieldName: entry.fieldName,
|
|
344
|
+
filename: entry.filename,
|
|
345
|
+
}));
|
|
346
|
+
const targetCreatorUsername = typeof creatorUsername === 'string' ? creatorUsername.trim() : '';
|
|
347
|
+
if (!targetCreatorUsername) {
|
|
348
|
+
throw new Error(`Asset "${task.name}" upload is missing creator username.`);
|
|
349
|
+
}
|
|
350
|
+
const response = await client.createAssetVersion(targetCreatorUsername, task.name, {
|
|
351
|
+
displayName: task.kind === 'asset' ? task.displayName : undefined,
|
|
352
|
+
category: task.category,
|
|
353
|
+
subcategory: prepared.subcategory,
|
|
354
|
+
format: prepared.format,
|
|
355
|
+
remixRef: task.kind === 'asset' ? task.remix : undefined,
|
|
356
|
+
visibility: visibility,
|
|
357
|
+
sourceKind: sourceAppVersionId ? 'APP_EMBEDDED' : 'UPLOAD',
|
|
358
|
+
sourceAppVersionId,
|
|
359
|
+
shopListed: task.shopListed,
|
|
360
|
+
shopPriceCredits: task.shopPriceCredits,
|
|
361
|
+
files,
|
|
362
|
+
});
|
|
363
|
+
const uploadedCreatorUsername = response.asset.creatorUsername;
|
|
364
|
+
const name = response.asset.name;
|
|
365
|
+
const revision = response.version.revision;
|
|
366
|
+
const versionNodeId = typeof response.versionNodeId === 'string' ? response.versionNodeId.trim() : '';
|
|
367
|
+
if (typeof uploadedCreatorUsername !== 'string' || uploadedCreatorUsername.trim().length === 0) {
|
|
368
|
+
throw new Error(`Asset "${task.name}" upload did not return creatorUsername.`);
|
|
369
|
+
}
|
|
370
|
+
if (!versionNodeId) {
|
|
371
|
+
throw new Error(`Asset "${task.name}" upload did not return versionNodeId.`);
|
|
372
|
+
}
|
|
373
|
+
return {
|
|
374
|
+
creatorUsername: uploadedCreatorUsername,
|
|
375
|
+
name,
|
|
376
|
+
revision,
|
|
377
|
+
ref: `asset:${uploadedCreatorUsername}/${name}@r${revision}`,
|
|
378
|
+
versionNodeId,
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
function parseAssetRef(raw) {
|
|
382
|
+
const trimmed = raw.trim();
|
|
383
|
+
const canonicalMatch = /^asset:([^/]+)\/([^@]+)@r(\d+)$/i.exec(trimmed);
|
|
384
|
+
if (canonicalMatch) {
|
|
385
|
+
return {
|
|
386
|
+
creatorUsername: canonicalMatch[1],
|
|
387
|
+
name: canonicalMatch[2],
|
|
388
|
+
revision: Number.parseInt(canonicalMatch[3], 10),
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
const plainMatch = /^([^/]+)\/([^@]+)@r(\d+)$/i.exec(trimmed);
|
|
392
|
+
if (plainMatch) {
|
|
393
|
+
return {
|
|
394
|
+
creatorUsername: plainMatch[1],
|
|
395
|
+
name: plainMatch[2],
|
|
396
|
+
revision: Number.parseInt(plainMatch[3], 10),
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
return null;
|
|
400
|
+
}
|
|
401
|
+
async function resolveAssetReference(client, rawRef, fallbackCreator, uploadedAssets) {
|
|
402
|
+
const trimmed = rawRef.trim();
|
|
403
|
+
if (!trimmed) {
|
|
404
|
+
throw new Error('Asset pack has an empty asset reference.');
|
|
405
|
+
}
|
|
406
|
+
const parsed = parseAssetRef(trimmed);
|
|
407
|
+
if (parsed) {
|
|
408
|
+
return `asset:${parsed.creatorUsername}/${parsed.name}@r${parsed.revision}`;
|
|
409
|
+
}
|
|
410
|
+
if (trimmed.includes('@')) {
|
|
411
|
+
throw new Error(`Invalid asset ref "${trimmed}". Use "asset:creator/name@rN" format.`);
|
|
412
|
+
}
|
|
413
|
+
const slashIndex = trimmed.indexOf('/');
|
|
414
|
+
const creatorUsername = slashIndex > 0 ? trimmed.slice(0, slashIndex) : fallbackCreator;
|
|
415
|
+
const name = slashIndex > 0 ? trimmed.slice(slashIndex + 1) : trimmed;
|
|
416
|
+
if (!creatorUsername || !name) {
|
|
417
|
+
throw new Error(`Invalid asset reference "${trimmed}".`);
|
|
418
|
+
}
|
|
419
|
+
const key = `${creatorUsername}/${name}`;
|
|
420
|
+
const uploaded = uploadedAssets.get(key);
|
|
421
|
+
if (uploaded) {
|
|
422
|
+
return uploaded.ref;
|
|
423
|
+
}
|
|
424
|
+
const detail = await client.fetchAssetBySlug(creatorUsername, name);
|
|
425
|
+
const revision = detail?.asset?.currentVersion?.revision;
|
|
426
|
+
if (typeof revision !== 'number' || !Number.isInteger(revision) || revision <= 0) {
|
|
427
|
+
throw new Error(`Asset "${creatorUsername}/${name}" has no current version to reference from asset pack.`);
|
|
428
|
+
}
|
|
429
|
+
return `asset:${creatorUsername}/${name}@r${revision}`;
|
|
430
|
+
}
|
|
431
|
+
function shouldAbortPackUploadOnError(error) {
|
|
432
|
+
return error instanceof types_1.ApiError && error.status >= 400 && error.status < 500 && error.status !== 408 && error.status !== 429;
|
|
433
|
+
}
|
|
434
|
+
function describeError(error) {
|
|
435
|
+
if (error instanceof Error && error.message.trim().length > 0) {
|
|
436
|
+
return error.message;
|
|
437
|
+
}
|
|
438
|
+
return String(error);
|
|
439
|
+
}
|
|
440
|
+
async function prepareLocalPackAssets(task, mutationCreatorUsername, localAssetTasks, uploadKeyByAssetKey) {
|
|
441
|
+
const preparedLocalAssets = new Map();
|
|
442
|
+
for (const localAssetTask of localAssetTasks) {
|
|
443
|
+
const assetKey = buildAssetKey(mutationCreatorUsername, localAssetTask.name);
|
|
444
|
+
const uploadKey = uploadKeyByAssetKey.get(assetKey);
|
|
445
|
+
if (!uploadKey) {
|
|
446
|
+
throw new Error(`Asset pack "${task.name}@${task.version}" is missing upload key for local asset "${localAssetTask.name}".`);
|
|
447
|
+
}
|
|
448
|
+
preparedLocalAssets.set(uploadKey, {
|
|
449
|
+
task: localAssetTask,
|
|
450
|
+
prepared: await prepareAssetTaskUploadData(localAssetTask),
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
return preparedLocalAssets;
|
|
454
|
+
}
|
|
455
|
+
function buildLocalAssetManifestEntries(preparedLocalAssets) {
|
|
456
|
+
const localAssets = [];
|
|
457
|
+
for (const [uploadKey, localAsset] of preparedLocalAssets) {
|
|
458
|
+
localAssets.push({
|
|
459
|
+
uploadKey,
|
|
460
|
+
name: localAsset.task.name,
|
|
461
|
+
displayName: localAsset.task.displayName,
|
|
462
|
+
category: localAsset.task.category,
|
|
463
|
+
subcategory: localAsset.prepared.subcategory,
|
|
464
|
+
format: localAsset.prepared.format,
|
|
465
|
+
visibility: localAsset.task.visibility,
|
|
466
|
+
remixRef: localAsset.task.remix,
|
|
467
|
+
shopListed: localAsset.task.shopListed,
|
|
468
|
+
shopPriceCredits: localAsset.task.shopPriceCredits,
|
|
469
|
+
files: localAsset.prepared.manifestFiles,
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
return localAssets;
|
|
473
|
+
}
|
|
474
|
+
async function buildPackMembersAndExistingAssets(client, task, mutationCreatorUsername, uploadedAssets, uploadKeyByAssetKey) {
|
|
475
|
+
const existingAssetsByRef = new Map();
|
|
476
|
+
const members = [];
|
|
477
|
+
for (const rawRef of task.assets) {
|
|
478
|
+
const localCandidate = parseUnversionedAssetTaskRef(rawRef, mutationCreatorUsername);
|
|
479
|
+
if (localCandidate && localCandidate.creatorUsername === mutationCreatorUsername) {
|
|
480
|
+
const localAssetKey = buildAssetKey(localCandidate.creatorUsername, localCandidate.name);
|
|
481
|
+
const localUploadKey = uploadKeyByAssetKey.get(localAssetKey);
|
|
482
|
+
if (localUploadKey) {
|
|
483
|
+
members.push({ uploadKey: localUploadKey });
|
|
484
|
+
continue;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
const assetRef = await resolveAssetReference(client, rawRef, mutationCreatorUsername, uploadedAssets);
|
|
488
|
+
if (!existingAssetsByRef.has(assetRef)) {
|
|
489
|
+
existingAssetsByRef.set(assetRef, { assetRef });
|
|
490
|
+
}
|
|
491
|
+
members.push({ assetRef });
|
|
492
|
+
}
|
|
493
|
+
return {
|
|
494
|
+
existingAssets: Array.from(existingAssetsByRef.values()),
|
|
495
|
+
members,
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
function buildPackListingUploads(task) {
|
|
499
|
+
const listingUploads = [];
|
|
500
|
+
if (task.listing?.iconPath) {
|
|
501
|
+
listingUploads.push(prepareListingMediaUpload('icon', task.listing.iconPath, 'image/png'));
|
|
502
|
+
}
|
|
503
|
+
if (task.listing?.heroPortraitPath) {
|
|
504
|
+
listingUploads.push(prepareListingMediaUpload('heroPortrait', task.listing.heroPortraitPath, 'image/png'));
|
|
505
|
+
}
|
|
506
|
+
if (task.listing?.heroLandscapePath) {
|
|
507
|
+
listingUploads.push(prepareListingMediaUpload('heroLandscape', task.listing.heroLandscapePath, 'image/png'));
|
|
508
|
+
}
|
|
509
|
+
task.listing?.screenshotPortraitPaths.forEach((filePath, index) => {
|
|
510
|
+
listingUploads.push(prepareListingMediaUpload(`screenshotsPortrait:${index}`, filePath, 'image/png'));
|
|
511
|
+
});
|
|
512
|
+
task.listing?.screenshotLandscapePaths.forEach((filePath, index) => {
|
|
513
|
+
listingUploads.push(prepareListingMediaUpload(`screenshotsLandscape:${index}`, filePath, 'image/png'));
|
|
514
|
+
});
|
|
515
|
+
task.listing?.videoPortraitPaths.forEach((filePath, index) => {
|
|
516
|
+
listingUploads.push(prepareListingMediaUpload(`videosPortrait:${index}`, filePath, 'video/mp4'));
|
|
517
|
+
});
|
|
518
|
+
task.listing?.videoLandscapePaths.forEach((filePath, index) => {
|
|
519
|
+
listingUploads.push(prepareListingMediaUpload(`videosLandscape:${index}`, filePath, 'video/mp4'));
|
|
520
|
+
});
|
|
521
|
+
return listingUploads;
|
|
522
|
+
}
|
|
523
|
+
function buildPackListingPayload(listingUploads) {
|
|
524
|
+
if (listingUploads.length === 0) {
|
|
525
|
+
return undefined;
|
|
526
|
+
}
|
|
527
|
+
return {
|
|
528
|
+
icon: listingUploads.find((entry) => entry.mediaKey === 'icon')?.entry,
|
|
529
|
+
heroPortrait: listingUploads.find((entry) => entry.mediaKey === 'heroPortrait')?.entry,
|
|
530
|
+
heroLandscape: listingUploads.find((entry) => entry.mediaKey === 'heroLandscape')?.entry,
|
|
531
|
+
screenshotsPortrait: listingUploads.filter((entry) => entry.mediaKey.startsWith('screenshotsPortrait:')).map((entry) => entry.entry),
|
|
532
|
+
screenshotsLandscape: listingUploads.filter((entry) => entry.mediaKey.startsWith('screenshotsLandscape:')).map((entry) => entry.entry),
|
|
533
|
+
videosPortrait: listingUploads.filter((entry) => entry.mediaKey.startsWith('videosPortrait:')).map((entry) => entry.entry),
|
|
534
|
+
videosLandscape: listingUploads.filter((entry) => entry.mediaKey.startsWith('videosLandscape:')).map((entry) => entry.entry),
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
function buildPackInitializeRequest(task, localAssets, existingAssets, members, listingUploads) {
|
|
538
|
+
return {
|
|
539
|
+
version: task.version,
|
|
540
|
+
displayName: task.displayName,
|
|
541
|
+
remixRef: task.remix,
|
|
542
|
+
visibility: task.visibility,
|
|
543
|
+
hostingMode: task.hostingMode,
|
|
544
|
+
externalUrl: task.externalUrl,
|
|
545
|
+
downloadUrl: task.downloadUrl,
|
|
546
|
+
releaseNotes: task.releaseNotes,
|
|
547
|
+
localAssets,
|
|
548
|
+
existingAssets,
|
|
549
|
+
members,
|
|
550
|
+
listing: buildPackListingPayload(listingUploads),
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
async function uploadPendingPackSessionEntries(client, task, mutationCreatorUsername, sessionId, preparedLocalAssets, uploadedAssetKeys, listingUploads, uploadedMediaKeys) {
|
|
554
|
+
for (const [uploadKey, localAsset] of preparedLocalAssets) {
|
|
555
|
+
if (uploadedAssetKeys.has(uploadKey)) {
|
|
556
|
+
continue;
|
|
557
|
+
}
|
|
558
|
+
await client.uploadAssetPackSessionAsset(mutationCreatorUsername, task.name, sessionId, uploadKey, {
|
|
559
|
+
files: localAsset.prepared.uploadFiles.map((entry) => ({
|
|
560
|
+
file: entry.file,
|
|
561
|
+
fieldName: entry.fieldName,
|
|
562
|
+
filename: entry.filename,
|
|
563
|
+
})),
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
for (const media of listingUploads) {
|
|
567
|
+
if (uploadedMediaKeys.has(media.mediaKey)) {
|
|
568
|
+
continue;
|
|
569
|
+
}
|
|
570
|
+
await client.uploadAssetPackSessionMedia(mutationCreatorUsername, task.name, sessionId, media.mediaKey, {
|
|
571
|
+
file: media.file,
|
|
572
|
+
filename: media.filename,
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
function buildUploadedPackInfo(creatorUsername, task, versionNodeId, versionAssets, uploadedLocalAssets) {
|
|
577
|
+
return {
|
|
578
|
+
creatorUsername,
|
|
579
|
+
name: task.name,
|
|
580
|
+
version: task.version,
|
|
581
|
+
versionNodeId,
|
|
582
|
+
ref: `pack:${creatorUsername}/${task.name}@${task.version}`,
|
|
583
|
+
assetRefs: Array.isArray(versionAssets)
|
|
584
|
+
? versionAssets.map((asset) => `asset:${asset.creatorUsername}/${asset.name}@r${asset.revision}`)
|
|
585
|
+
: [],
|
|
586
|
+
uploadedLocalAssets: uploadedLocalAssets ?? [],
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
async function uploadAssetPackTask(client, task, creatorUsername, uploadedAssets, localAssetTasks, uploadKeyByAssetKey, targetCreatorUsername) {
|
|
590
|
+
const mutationCreatorUsername = resolveMutationCreatorUsername(task, creatorUsername, targetCreatorUsername);
|
|
591
|
+
const preparedLocalAssets = await prepareLocalPackAssets(task, mutationCreatorUsername, localAssetTasks, uploadKeyByAssetKey);
|
|
592
|
+
const localAssets = buildLocalAssetManifestEntries(preparedLocalAssets);
|
|
593
|
+
const { existingAssets, members } = await buildPackMembersAndExistingAssets(client, task, mutationCreatorUsername, uploadedAssets, uploadKeyByAssetKey);
|
|
594
|
+
const listingUploads = buildPackListingUploads(task);
|
|
595
|
+
const initializeBody = buildPackInitializeRequest(task, localAssets, existingAssets, members, listingUploads);
|
|
596
|
+
const initializeResponse = await client.initializeAssetPackUpload(mutationCreatorUsername, task.name, initializeBody);
|
|
597
|
+
if (initializeResponse.status === 'finalized') {
|
|
598
|
+
if (!initializeResponse.versionNodeId || !initializeResponse.version) {
|
|
599
|
+
throw new Error(`Asset pack "${task.name}" finalize response is missing version details.`);
|
|
600
|
+
}
|
|
601
|
+
return buildUploadedPackInfo(mutationCreatorUsername, task, initializeResponse.versionNodeId, initializeResponse.version.assets, initializeResponse.uploadedLocalAssets);
|
|
602
|
+
}
|
|
603
|
+
const sessionId = initializeResponse.session.id;
|
|
604
|
+
try {
|
|
605
|
+
await uploadPendingPackSessionEntries(client, task, mutationCreatorUsername, sessionId, preparedLocalAssets, new Set(initializeResponse.session.uploadedAssetKeys), listingUploads, new Set(initializeResponse.session.uploadedMediaKeys));
|
|
606
|
+
const finalizedResponse = await client.finalizeAssetPackUpload(mutationCreatorUsername, task.name, sessionId);
|
|
607
|
+
const versionNodeId = typeof finalizedResponse.versionNodeId === 'string' ? finalizedResponse.versionNodeId.trim() : '';
|
|
608
|
+
if (!versionNodeId) {
|
|
609
|
+
throw new Error(`Asset pack "${task.name}@${task.version}" upload did not return versionNodeId.`);
|
|
610
|
+
}
|
|
611
|
+
if (!finalizedResponse.version) {
|
|
612
|
+
throw new Error(`Asset pack "${task.name}@${task.version}" upload did not return version details.`);
|
|
613
|
+
}
|
|
614
|
+
return buildUploadedPackInfo(mutationCreatorUsername, task, versionNodeId, finalizedResponse.version.assets, finalizedResponse.uploadedLocalAssets);
|
|
615
|
+
}
|
|
616
|
+
catch (error) {
|
|
617
|
+
if (shouldAbortPackUploadOnError(error)) {
|
|
618
|
+
try {
|
|
619
|
+
await client.abortAssetPackUpload(mutationCreatorUsername, task.name, sessionId);
|
|
620
|
+
}
|
|
621
|
+
catch (abortError) {
|
|
622
|
+
throw new Error(`Asset pack "${task.name}@${task.version}" failed and aborting session "${sessionId}" also failed: ${describeError(abortError)}. Original error: ${describeError(error)}`);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
throw error;
|
|
626
|
+
}
|
|
627
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ApiClient } from '@playdrop/api-client';
|
|
2
|
+
type PendingRelationRow = {
|
|
3
|
+
fromNodeId: string;
|
|
4
|
+
type: string;
|
|
5
|
+
toRef: string;
|
|
6
|
+
context: string;
|
|
7
|
+
};
|
|
8
|
+
export type UploadGraphState = {
|
|
9
|
+
canonicalNodeByRef: Map<string, string>;
|
|
10
|
+
localAppNodeByName: Map<string, string>;
|
|
11
|
+
localAssetNodeByName: Map<string, string>;
|
|
12
|
+
localPackNodeByNameVersion: Map<string, string>;
|
|
13
|
+
ambiguousAppNames: Set<string>;
|
|
14
|
+
ambiguousAssetNames: Set<string>;
|
|
15
|
+
ambiguousPackNameVersions: Set<string>;
|
|
16
|
+
pendingRelations: PendingRelationRow[];
|
|
17
|
+
};
|
|
18
|
+
export declare function buildEmptyGraphState(): UploadGraphState;
|
|
19
|
+
export declare function registerLocalRef(map: Map<string, string>, ambiguous: Set<string>, key: string, nodeId: string): void;
|
|
20
|
+
export declare function registerCanonicalNode(state: UploadGraphState, ref: string, nodeId: string): void;
|
|
21
|
+
export declare function appendPendingRelation(state: UploadGraphState, fromNodeId: string, type: string, toRef: string, context: string): void;
|
|
22
|
+
export declare function applyGraphOperations(client: ApiClient, state: UploadGraphState): Promise<void>;
|
|
23
|
+
export {};
|