@playdrop/playdrop-cli 0.5.5 → 0.5.6
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 +2 -1
- package/config/client-meta.json +4 -4
- package/dist/apps/upload.js +226 -80
- package/dist/assetSpecs.d.ts +1 -0
- package/dist/assetSpecs.js +22 -1
- package/dist/assets/model-artifacts.d.ts +2 -2
- package/dist/assets/model-artifacts.js +1 -1
- package/dist/captureRuntime.d.ts +1 -0
- package/dist/captureRuntime.js +3 -2
- package/dist/catalogue.d.ts +33 -8
- package/dist/catalogue.js +364 -46
- package/dist/commandContext.d.ts +5 -1
- package/dist/commandContext.js +90 -29
- package/dist/commands/browse.d.ts +3 -0
- package/dist/commands/browse.js +90 -17
- package/dist/commands/build.js +1 -1
- package/dist/commands/capture.d.ts +3 -0
- package/dist/commands/capture.js +121 -9
- package/dist/commands/captureListing.d.ts +2 -0
- package/dist/commands/captureListing.js +157 -61
- package/dist/commands/create.js +6 -28
- package/dist/commands/createRemixContent.js +4 -26
- package/dist/commands/creations.js +2 -3
- package/dist/commands/detail.js +24 -2
- package/dist/commands/dev.d.ts +8 -1
- package/dist/commands/dev.js +180 -2
- package/dist/commands/devRuntimeAssets.d.ts +34 -0
- package/dist/commands/devRuntimeAssets.js +308 -0
- package/dist/commands/devServer.d.ts +11 -0
- package/dist/commands/devServer.js +196 -13
- package/dist/commands/init.js +6 -24
- package/dist/commands/search.d.ts +4 -0
- package/dist/commands/search.js +68 -11
- package/dist/commands/upload-content.d.ts +3 -3
- package/dist/commands/upload-content.js +19 -38
- package/dist/commands/upload.js +67 -77
- package/dist/commands/validate.js +13 -20
- package/dist/commands/whoami.js +23 -26
- package/dist/devAuth.d.ts +16 -0
- package/dist/devAuth.js +60 -0
- package/dist/index.js +22 -4
- package/dist/taskSelection.js +4 -3
- package/dist/taskUtils.d.ts +2 -2
- package/dist/taskUtils.js +1 -1
- package/dist/uploadLog.d.ts +1 -1
- package/node_modules/@playdrop/ai-client/package.json +1 -1
- package/node_modules/@playdrop/api-client/dist/client.d.ts +43 -114
- package/node_modules/@playdrop/api-client/dist/client.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/client.js +22 -0
- package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts +11 -19
- package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/apps.js +116 -106
- package/node_modules/@playdrop/api-client/dist/domains/assets.d.ts +2 -1
- package/node_modules/@playdrop/api-client/dist/domains/assets.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/assets.js +13 -0
- package/node_modules/@playdrop/api-client/dist/domains/payments.d.ts +5 -5
- package/node_modules/@playdrop/api-client/dist/domains/payments.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/payments.js +8 -8
- package/node_modules/@playdrop/api-client/dist/domains/search.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/search.js +24 -2
- package/node_modules/@playdrop/api-client/dist/domains/tags.d.ts +13 -1
- package/node_modules/@playdrop/api-client/dist/domains/tags.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/tags.js +52 -0
- package/node_modules/@playdrop/api-client/dist/index.d.ts +25 -29
- package/node_modules/@playdrop/api-client/dist/index.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/index.js +23 -8
- 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 +4 -4
- package/node_modules/@playdrop/config/package.json +1 -1
- package/node_modules/@playdrop/types/dist/api.d.ts +124 -3
- package/node_modules/@playdrop/types/dist/api.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/api.js +23 -0
- package/node_modules/@playdrop/types/dist/app-capability-filters.d.ts +24 -0
- package/node_modules/@playdrop/types/dist/app-capability-filters.d.ts.map +1 -0
- package/node_modules/@playdrop/types/dist/app-capability-filters.js +72 -0
- package/node_modules/@playdrop/types/dist/asset-pack.d.ts +3 -2
- package/node_modules/@playdrop/types/dist/asset-pack.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/asset.d.ts +2 -3
- package/node_modules/@playdrop/types/dist/asset.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/asset.js +1 -1
- package/node_modules/@playdrop/types/dist/index.d.ts +2 -0
- package/node_modules/@playdrop/types/dist/index.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/index.js +2 -0
- package/node_modules/@playdrop/types/dist/owned-assets.d.ts +21 -0
- package/node_modules/@playdrop/types/dist/owned-assets.d.ts.map +1 -0
- package/node_modules/@playdrop/types/dist/owned-assets.js +35 -0
- package/node_modules/@playdrop/types/dist/player-meta.d.ts +28 -0
- package/node_modules/@playdrop/types/dist/player-meta.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/version.d.ts +111 -1
- package/node_modules/@playdrop/types/dist/version.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/version.js +3 -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/dev.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.formatDevRuntimeAssetManifestFailure = formatDevRuntimeAssetManifestFailure;
|
|
3
4
|
exports.dev = dev;
|
|
4
5
|
const node_fs_1 = require("node:fs");
|
|
5
6
|
const node_path_1 = require("node:path");
|
|
@@ -7,12 +8,132 @@ const types_1 = require("@playdrop/types");
|
|
|
7
8
|
const commandContext_1 = require("../commandContext");
|
|
8
9
|
const http_1 = require("../http");
|
|
9
10
|
const messages_1 = require("../messages");
|
|
11
|
+
const catalogue_1 = require("../catalogue");
|
|
10
12
|
const catalogue_utils_1 = require("../catalogue-utils");
|
|
11
13
|
const devShared_1 = require("./devShared");
|
|
12
14
|
const appUrls_1 = require("../appUrls");
|
|
13
15
|
const devServer_1 = require("./devServer");
|
|
16
|
+
const devRuntimeAssets_1 = require("./devRuntimeAssets");
|
|
17
|
+
const devAuth_1 = require("../devAuth");
|
|
14
18
|
const DEFAULT_ROOM_BASE = 'http://localhost:3001';
|
|
15
|
-
|
|
19
|
+
function formatDevRuntimeAssetManifestFailure(error) {
|
|
20
|
+
const rawMessage = error instanceof Error ? error.message : String(error ?? 'unknown_error');
|
|
21
|
+
if (rawMessage.startsWith('dev_runtime_asset_ref_invalid:')) {
|
|
22
|
+
const ref = rawMessage.slice('dev_runtime_asset_ref_invalid:'.length);
|
|
23
|
+
return {
|
|
24
|
+
message: `Invalid uses.assets ref "${ref}" in catalogue.json.`,
|
|
25
|
+
suggestions: [
|
|
26
|
+
'Use an exact canonical asset ref like asset:creator/name@r1.',
|
|
27
|
+
],
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
if (rawMessage.startsWith('dev_runtime_asset_version_not_found:')) {
|
|
31
|
+
const ref = rawMessage.slice('dev_runtime_asset_version_not_found:'.length);
|
|
32
|
+
return {
|
|
33
|
+
message: `Exact direct asset dependency "${ref}" does not exist on the server.`,
|
|
34
|
+
suggestions: [
|
|
35
|
+
'Publish that exact asset revision first or update uses.assets to a valid exact ref.',
|
|
36
|
+
],
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
if (rawMessage.startsWith('dev_runtime_asset_not_public:')) {
|
|
40
|
+
const ref = rawMessage.slice('dev_runtime_asset_not_public:'.length);
|
|
41
|
+
return {
|
|
42
|
+
message: `Direct asset dependency "${ref}" is not publicly readable for project dev.`,
|
|
43
|
+
suggestions: [
|
|
44
|
+
'Make the asset public or remove it from uses.assets for this first-pass dev flow.',
|
|
45
|
+
],
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
if (rawMessage.startsWith('dev_runtime_pack_ref_invalid:')) {
|
|
49
|
+
const ref = rawMessage.slice('dev_runtime_pack_ref_invalid:'.length);
|
|
50
|
+
return {
|
|
51
|
+
message: `Invalid uses.packs ref "${ref}" in catalogue.json.`,
|
|
52
|
+
suggestions: [
|
|
53
|
+
'Use an exact canonical pack ref like pack:creator/name@1.2.3.',
|
|
54
|
+
],
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
if (rawMessage.startsWith('dev_runtime_pack_version_not_found:')) {
|
|
58
|
+
const ref = rawMessage.slice('dev_runtime_pack_version_not_found:'.length);
|
|
59
|
+
return {
|
|
60
|
+
message: `Exact pack dependency "${ref}" does not exist on the server.`,
|
|
61
|
+
suggestions: [
|
|
62
|
+
'Publish that exact pack version first or update uses.packs to a valid exact ref.',
|
|
63
|
+
],
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
if (rawMessage.startsWith('dev_runtime_pack_not_public:')) {
|
|
67
|
+
const ref = rawMessage.slice('dev_runtime_pack_not_public:'.length);
|
|
68
|
+
return {
|
|
69
|
+
message: `Pack dependency "${ref}" is not publicly readable for project dev.`,
|
|
70
|
+
suggestions: [
|
|
71
|
+
'Make the pack public or remove it from uses.packs for this first-pass dev flow.',
|
|
72
|
+
],
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
if (rawMessage.startsWith('dev_runtime_key_duplicate:')) {
|
|
76
|
+
const runtimeKey = rawMessage.split(':')[1] ?? 'unknown';
|
|
77
|
+
return {
|
|
78
|
+
message: `Duplicate runtimeKey "${runtimeKey}" detected while building dev runtime assets.`,
|
|
79
|
+
suggestions: [
|
|
80
|
+
'Give every owned asset and direct dependency a unique runtimeKey in catalogue.json.',
|
|
81
|
+
],
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
if (rawMessage.startsWith('dev_runtime_owned_asset_duplicate:')) {
|
|
85
|
+
const ownedAssetName = rawMessage.slice('dev_runtime_owned_asset_duplicate:'.length);
|
|
86
|
+
return {
|
|
87
|
+
message: `Duplicate owned asset name "${ownedAssetName}" detected in catalogue.json.`,
|
|
88
|
+
suggestions: [
|
|
89
|
+
'Owned asset names must be unique inside the app catalogue entry.',
|
|
90
|
+
],
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
if (rawMessage.startsWith('dev_runtime_asset_files_missing:')) {
|
|
94
|
+
const ref = rawMessage.slice('dev_runtime_asset_files_missing:'.length);
|
|
95
|
+
return {
|
|
96
|
+
message: `Dependency "${ref}" has no runtime file manifest on the server.`,
|
|
97
|
+
suggestions: [
|
|
98
|
+
'Republish the dependency so it has declared file roles before using it in project dev.',
|
|
99
|
+
],
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
if (rawMessage.startsWith('dev_runtime_asset_spec_invalid:')) {
|
|
103
|
+
const [, ref = 'unknown', assetSpecVersion = 'unknown'] = rawMessage.split(':');
|
|
104
|
+
return {
|
|
105
|
+
message: `Dependency "${ref}" returned an invalid asset spec version ref "${assetSpecVersion}".`,
|
|
106
|
+
suggestions: [
|
|
107
|
+
'Republish the dependency with a valid assetSpecVersionRef or remove it from uses.assets.',
|
|
108
|
+
],
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
if (rawMessage.startsWith('dev_runtime_asset_spec_unsupported:')) {
|
|
112
|
+
const [, ref = 'unknown', assetSpecVersion = 'unknown'] = rawMessage.split(':');
|
|
113
|
+
return {
|
|
114
|
+
message: `Dependency "${ref}" uses unsupported custom asset version "${assetSpecVersion}" for this app.`,
|
|
115
|
+
suggestions: [
|
|
116
|
+
'Update assetSpecSupport in catalogue.json or pin uses.assets to a supported custom asset revision.',
|
|
117
|
+
],
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
if (rawMessage.startsWith('dev_runtime_app_type_missing:')) {
|
|
121
|
+
const appName = rawMessage.slice('dev_runtime_app_type_missing:'.length);
|
|
122
|
+
return {
|
|
123
|
+
message: `App "${appName}" is missing a valid type in catalogue.json.`,
|
|
124
|
+
suggestions: [
|
|
125
|
+
'Set "type" in the app catalogue entry before running "playdrop project dev".',
|
|
126
|
+
],
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
message: 'Failed to build the local runtime asset manifest for project dev.',
|
|
131
|
+
suggestions: [
|
|
132
|
+
rawMessage,
|
|
133
|
+
],
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
async function dev(targetArg, _port, appOption, devOptions = {}) {
|
|
16
137
|
let resolvedTarget;
|
|
17
138
|
try {
|
|
18
139
|
resolvedTarget = (0, devShared_1.resolveDevTarget)(targetArg, appOption);
|
|
@@ -73,6 +194,26 @@ async function dev(targetArg, _port, appOption) {
|
|
|
73
194
|
return;
|
|
74
195
|
}
|
|
75
196
|
}
|
|
197
|
+
let devAuthSelection;
|
|
198
|
+
try {
|
|
199
|
+
devAuthSelection = (0, devAuth_1.parseHostedDevAuthSelection)({
|
|
200
|
+
devAuth: devOptions.devAuth,
|
|
201
|
+
player: devOptions.player,
|
|
202
|
+
defaultMode: 'prompt',
|
|
203
|
+
allowPrompt: true,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
const message = error instanceof Error ? error.message : 'dev_auth_invalid';
|
|
208
|
+
if (message === 'dev_auth_player_slot_required') {
|
|
209
|
+
(0, messages_1.printErrorWithHelp)('Use --player 1, 2, 3, or 4 when --dev-auth player is selected.', [], { command: 'project dev' });
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
(0, messages_1.printErrorWithHelp)('Use --dev-auth prompt, anonymous, viewer, or player.', ['If you choose --dev-auth player, also pass --player 1, 2, 3, or 4.'], { command: 'project dev' });
|
|
213
|
+
}
|
|
214
|
+
process.exitCode = 1;
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
76
217
|
const workspacePath = resolvedTarget.cataloguePath ?? (0, node_path_1.dirname)(filePath);
|
|
77
218
|
await (0, commandContext_1.withEnvironment)('project dev', 'Starting the dev server',
|
|
78
219
|
// eslint-disable-next-line complexity
|
|
@@ -147,6 +288,36 @@ async function dev(targetArg, _port, appOption) {
|
|
|
147
288
|
}
|
|
148
289
|
const projectInfo = (0, devShared_1.findProjectInfo)(filePath);
|
|
149
290
|
const devScriptAvailable = Boolean(projectInfo.projectDir && projectInfo.packageJson && typeof projectInfo.packageJson.scripts?.dev === 'string');
|
|
291
|
+
const taskLookup = (0, catalogue_1.findAppTaskByFile)(filePath);
|
|
292
|
+
if (taskLookup.errors.length > 0) {
|
|
293
|
+
(0, messages_1.printErrorWithHelp)(taskLookup.errors[0] || 'Failed to resolve the app task from catalogue.json.', taskLookup.errors.slice(1), { command: 'project dev' });
|
|
294
|
+
process.exitCode = 1;
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
const localDevAppUrl = (0, devServer_1.buildLocalDevAppUrl)({
|
|
298
|
+
creatorUsername: currentUsername,
|
|
299
|
+
appType: appTypeSlug,
|
|
300
|
+
appName,
|
|
301
|
+
port: devServer_1.DEV_ROUTER_PORT,
|
|
302
|
+
});
|
|
303
|
+
let runtimeAssetManifest = (0, devRuntimeAssets_1.createEmptyDevRuntimeAssetManifest)();
|
|
304
|
+
if (taskLookup.task) {
|
|
305
|
+
try {
|
|
306
|
+
runtimeAssetManifest = await (0, devRuntimeAssets_1.buildDevRuntimeAssetManifest)({
|
|
307
|
+
client,
|
|
308
|
+
apiBase: envConfig.apiBase,
|
|
309
|
+
task: taskLookup.task,
|
|
310
|
+
creatorUsername: currentUsername,
|
|
311
|
+
appBaseUrl: new URL('.', localDevAppUrl).toString(),
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
catch (error) {
|
|
315
|
+
const formatted = formatDevRuntimeAssetManifestFailure(error);
|
|
316
|
+
(0, messages_1.printErrorWithHelp)(formatted.message, formatted.suggestions, { command: 'project dev' });
|
|
317
|
+
process.exitCode = 1;
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
150
321
|
if (resolvedTarget.ecsPath && resolvedTarget.serverScriptPath && appResponse?.id) {
|
|
151
322
|
try {
|
|
152
323
|
if (!(0, node_fs_1.existsSync)(resolvedTarget.ecsPath) || !(0, node_fs_1.existsSync)(resolvedTarget.serverScriptPath)) {
|
|
@@ -198,6 +369,7 @@ async function dev(targetArg, _port, appOption) {
|
|
|
198
369
|
htmlPath: filePath,
|
|
199
370
|
port: devServer_1.DEV_ROUTER_PORT,
|
|
200
371
|
projectInfo,
|
|
372
|
+
runtimeAssetManifest,
|
|
201
373
|
});
|
|
202
374
|
}
|
|
203
375
|
catch (error) {
|
|
@@ -210,6 +382,12 @@ async function dev(targetArg, _port, appOption) {
|
|
|
210
382
|
`Mounted HTML path: ${mountConflict.htmlPath}.`,
|
|
211
383
|
], { command: 'project dev' });
|
|
212
384
|
}
|
|
385
|
+
else if (message.startsWith('dev_router_incompatible:')) {
|
|
386
|
+
(0, messages_1.printErrorWithHelp)(`An incompatible shared dev router is already running on port ${devServer_1.DEV_ROUTER_PORT}.`, [
|
|
387
|
+
'Stop the existing dev router process, then retry "playdrop project dev".',
|
|
388
|
+
`If needed, rebuild the local CLI first with "npm run build --workspace @playdrop/playdrop-cli" from /Users/oliviermichon/Documents/playdrop.`,
|
|
389
|
+
], { command: 'project dev' });
|
|
390
|
+
}
|
|
213
391
|
else {
|
|
214
392
|
(0, messages_1.printErrorWithHelp)(error?.message || `Failed to start the shared dev router on port ${devServer_1.DEV_ROUTER_PORT}.`, [
|
|
215
393
|
'Close the conflicting process or wait for the stale mount to exit.',
|
|
@@ -247,7 +425,7 @@ async function dev(targetArg, _port, appOption) {
|
|
|
247
425
|
}
|
|
248
426
|
}
|
|
249
427
|
if (webUrl) {
|
|
250
|
-
const iframeUrl = `${webUrl}/creators/${encodeURIComponent(currentUsername)}/apps/${appTypeSlug}/${encodeURIComponent(appName)}/dev
|
|
428
|
+
const iframeUrl = (0, devAuth_1.applyHostedDevAuthSelectionToUrl)(`${webUrl}/creators/${encodeURIComponent(currentUsername)}/apps/${appTypeSlug}/${encodeURIComponent(appName)}/dev`, devAuthSelection);
|
|
251
429
|
console.log('\nTest your app at:');
|
|
252
430
|
console.log(` ${iframeUrl}`);
|
|
253
431
|
console.log('\nMake something fun then share it with');
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { ApiClient } from '@playdrop/api-client';
|
|
2
|
+
import { type AppAuthMode, type AppControllerMode, type AppMetadataAssetSpecSupport, type AppRuntimeAssetsResponse, type AppSurface, type AppType } from '@playdrop/types';
|
|
3
|
+
import type { AppTask } from '../catalogue';
|
|
4
|
+
export type LocalOwnedRuntimeAssetFileRecord = {
|
|
5
|
+
absolutePath: string;
|
|
6
|
+
contentType: string | null;
|
|
7
|
+
sizeBytes: number | null;
|
|
8
|
+
};
|
|
9
|
+
export type LocalDevAppMetadata = {
|
|
10
|
+
type: AppType;
|
|
11
|
+
surfaceTargets: AppSurface[];
|
|
12
|
+
authMode: AppAuthMode;
|
|
13
|
+
controllerMode: AppControllerMode;
|
|
14
|
+
previewable: boolean;
|
|
15
|
+
assetSpecSupport: AppMetadataAssetSpecSupport[];
|
|
16
|
+
version: string | null;
|
|
17
|
+
};
|
|
18
|
+
export type DevRuntimeAssetsResponse = AppRuntimeAssetsResponse & {
|
|
19
|
+
localAppMetadata: LocalDevAppMetadata;
|
|
20
|
+
};
|
|
21
|
+
export type DevRuntimeAssetManifestRegistration = {
|
|
22
|
+
response: DevRuntimeAssetsResponse;
|
|
23
|
+
ownedAssetFiles: Record<string, Record<string, LocalOwnedRuntimeAssetFileRecord>>;
|
|
24
|
+
};
|
|
25
|
+
export declare function createEmptyDevRuntimeAssetManifest(): DevRuntimeAssetManifestRegistration;
|
|
26
|
+
export declare function buildLocalDevRuntimeAssetsUrl(appBaseUrl: string): string;
|
|
27
|
+
export declare function buildLocalDevRuntimeAssetFileUrl(appBaseUrl: string, ownedAssetName: string, role: string): string;
|
|
28
|
+
export declare function buildDevRuntimeAssetManifest(input: {
|
|
29
|
+
client: ApiClient;
|
|
30
|
+
apiBase: string;
|
|
31
|
+
task: AppTask;
|
|
32
|
+
creatorUsername: string;
|
|
33
|
+
appBaseUrl: string;
|
|
34
|
+
}): Promise<DevRuntimeAssetManifestRegistration>;
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createEmptyDevRuntimeAssetManifest = createEmptyDevRuntimeAssetManifest;
|
|
4
|
+
exports.buildLocalDevRuntimeAssetsUrl = buildLocalDevRuntimeAssetsUrl;
|
|
5
|
+
exports.buildLocalDevRuntimeAssetFileUrl = buildLocalDevRuntimeAssetFileUrl;
|
|
6
|
+
exports.buildDevRuntimeAssetManifest = buildDevRuntimeAssetManifest;
|
|
7
|
+
const node_fs_1 = require("node:fs");
|
|
8
|
+
const node_path_1 = require("node:path");
|
|
9
|
+
const semver_1 = require("semver");
|
|
10
|
+
const types_1 = require("@playdrop/types");
|
|
11
|
+
function createEmptyDevRuntimeAssetManifest() {
|
|
12
|
+
return {
|
|
13
|
+
response: {
|
|
14
|
+
appVersionId: 0,
|
|
15
|
+
appVersion: 'dev',
|
|
16
|
+
assets: [],
|
|
17
|
+
localAppMetadata: {
|
|
18
|
+
type: 'GAME',
|
|
19
|
+
surfaceTargets: Array.from(types_1.DEFAULT_APP_SURFACE_TARGETS),
|
|
20
|
+
authMode: types_1.DEFAULT_APP_AUTH_MODE,
|
|
21
|
+
controllerMode: types_1.DEFAULT_APP_CONTROLLER_MODE,
|
|
22
|
+
previewable: types_1.DEFAULT_APP_PREVIEWABLE,
|
|
23
|
+
assetSpecSupport: [],
|
|
24
|
+
version: null,
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
ownedAssetFiles: {},
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
const CONTENT_TYPE_BY_EXTENSION = {
|
|
31
|
+
'.html': 'text/html; charset=utf-8',
|
|
32
|
+
'.js': 'application/javascript; charset=utf-8',
|
|
33
|
+
'.mjs': 'application/javascript; charset=utf-8',
|
|
34
|
+
'.cjs': 'application/javascript; charset=utf-8',
|
|
35
|
+
'.css': 'text/css; charset=utf-8',
|
|
36
|
+
'.json': 'application/json; charset=utf-8',
|
|
37
|
+
'.png': 'image/png',
|
|
38
|
+
'.jpg': 'image/jpeg',
|
|
39
|
+
'.jpeg': 'image/jpeg',
|
|
40
|
+
'.svg': 'image/svg+xml',
|
|
41
|
+
'.webp': 'image/webp',
|
|
42
|
+
'.glb': 'model/gltf-binary',
|
|
43
|
+
'.gltf': 'model/gltf+json',
|
|
44
|
+
'.wasm': 'application/wasm',
|
|
45
|
+
'.mp4': 'video/mp4',
|
|
46
|
+
'.webm': 'video/webm',
|
|
47
|
+
'.ogg': 'audio/ogg',
|
|
48
|
+
'.mp3': 'audio/mpeg',
|
|
49
|
+
'.wav': 'audio/wav',
|
|
50
|
+
};
|
|
51
|
+
function inferContentType(filePath) {
|
|
52
|
+
const extension = (0, node_path_1.extname)(filePath).toLowerCase();
|
|
53
|
+
return CONTENT_TYPE_BY_EXTENSION[extension] ?? null;
|
|
54
|
+
}
|
|
55
|
+
function normalizeManifestFiles(fileManifest) {
|
|
56
|
+
const files = fileManifest && typeof fileManifest === 'object' && Array.isArray(fileManifest.files)
|
|
57
|
+
? fileManifest.files
|
|
58
|
+
: [];
|
|
59
|
+
return files
|
|
60
|
+
.filter((entry) => Boolean(entry && typeof entry === 'object'))
|
|
61
|
+
.map((entry) => ({
|
|
62
|
+
role: typeof entry.role === 'string' ? entry.role.trim().toLowerCase() : '',
|
|
63
|
+
key: typeof entry.key === 'string' ? entry.key.trim() : '',
|
|
64
|
+
contentType: typeof entry.contentType === 'string' ? entry.contentType : null,
|
|
65
|
+
sizeBytes: typeof entry.sizeBytes === 'number' && Number.isFinite(entry.sizeBytes) ? entry.sizeBytes : null,
|
|
66
|
+
sha256: typeof entry.sha256 === 'string' ? entry.sha256 : null,
|
|
67
|
+
}))
|
|
68
|
+
.filter((entry) => entry.role.length > 0);
|
|
69
|
+
}
|
|
70
|
+
function buildPublishedAssetFileUrl(apiBase, creatorUsername, assetName, revision, role) {
|
|
71
|
+
const path = `/assets/${encodeURIComponent(creatorUsername)}/${encodeURIComponent(assetName)}/versions/${encodeURIComponent(`r${revision}`)}/file?role=${encodeURIComponent(role)}`;
|
|
72
|
+
return new URL(path, apiBase).toString();
|
|
73
|
+
}
|
|
74
|
+
function buildSyntheticLocalAssetRef(creatorUsername, appName, ownedAssetName) {
|
|
75
|
+
return (0, types_1.formatContentVersionRef)({
|
|
76
|
+
kind: 'asset',
|
|
77
|
+
creatorUsername: `${creatorUsername}-dev`,
|
|
78
|
+
name: `${appName}-${ownedAssetName}`,
|
|
79
|
+
revision: 1,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
function buildLocalDevRuntimeAssetsUrl(appBaseUrl) {
|
|
83
|
+
const base = appBaseUrl.endsWith('/') ? appBaseUrl.slice(0, -1) : appBaseUrl;
|
|
84
|
+
return `${base}/_playdrop/runtime-assets`;
|
|
85
|
+
}
|
|
86
|
+
function buildLocalDevRuntimeAssetFileUrl(appBaseUrl, ownedAssetName, role) {
|
|
87
|
+
const base = appBaseUrl.endsWith('/') ? appBaseUrl.slice(0, -1) : appBaseUrl;
|
|
88
|
+
return `${base}/_playdrop/runtime-assets/files/${encodeURIComponent(ownedAssetName)}?role=${encodeURIComponent(role)}`;
|
|
89
|
+
}
|
|
90
|
+
async function findAssetVersionByRevision(client, creatorUsername, assetName, revision) {
|
|
91
|
+
let offset = 0;
|
|
92
|
+
for (let page = 0; page < 200; page += 1) {
|
|
93
|
+
const response = await client.listAssetVersions(creatorUsername, assetName, {
|
|
94
|
+
limit: 100,
|
|
95
|
+
offset,
|
|
96
|
+
});
|
|
97
|
+
const version = (response.versions ?? []).find((entry) => entry.revision === revision);
|
|
98
|
+
if (version) {
|
|
99
|
+
return version;
|
|
100
|
+
}
|
|
101
|
+
if (!response.pagination?.hasMore) {
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
offset += response.pagination.limit;
|
|
105
|
+
}
|
|
106
|
+
throw new Error(`dev_runtime_asset_version_not_found:${creatorUsername}/${assetName}@r${revision}`);
|
|
107
|
+
}
|
|
108
|
+
async function findPackVersion(client, creatorUsername, packName, versionString) {
|
|
109
|
+
let offset = 0;
|
|
110
|
+
for (let page = 0; page < 200; page += 1) {
|
|
111
|
+
const response = await client.listAssetPackVersions(creatorUsername, packName, {
|
|
112
|
+
limit: 100,
|
|
113
|
+
offset,
|
|
114
|
+
});
|
|
115
|
+
const version = (response.versions ?? []).find((entry) => entry.version === versionString);
|
|
116
|
+
if (version) {
|
|
117
|
+
return version;
|
|
118
|
+
}
|
|
119
|
+
if (!response.pagination?.hasMore) {
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
offset += response.pagination.limit;
|
|
123
|
+
}
|
|
124
|
+
throw new Error(`dev_runtime_pack_version_not_found:${creatorUsername}/${packName}@${versionString}`);
|
|
125
|
+
}
|
|
126
|
+
function createPublishedAssetSummary(input) {
|
|
127
|
+
const files = normalizeManifestFiles(input.version.fileManifest).map((file) => ({
|
|
128
|
+
role: file.role,
|
|
129
|
+
url: buildPublishedAssetFileUrl(input.apiBase, input.creatorUsername, input.assetName, input.revision, file.role),
|
|
130
|
+
contentType: file.contentType ?? null,
|
|
131
|
+
sizeBytes: file.sizeBytes ?? null,
|
|
132
|
+
sha256: file.sha256 ?? null,
|
|
133
|
+
}));
|
|
134
|
+
if (files.length === 0) {
|
|
135
|
+
throw new Error(`dev_runtime_asset_files_missing:${input.creatorUsername}/${input.assetName}@r${input.revision}`);
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
assetRef: (0, types_1.formatContentVersionRef)({
|
|
139
|
+
kind: 'asset',
|
|
140
|
+
creatorUsername: input.creatorUsername,
|
|
141
|
+
name: input.assetName,
|
|
142
|
+
revision: input.revision,
|
|
143
|
+
}),
|
|
144
|
+
runtimeKey: input.runtimeKey?.trim() || null,
|
|
145
|
+
sourceType: input.sourceType,
|
|
146
|
+
...(input.sourcePackRef ? { sourcePackRef: input.sourcePackRef } : {}),
|
|
147
|
+
files,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
async function resolvePublishedAssetSummary(client, apiBase, assetRef, options) {
|
|
151
|
+
const parsed = (0, types_1.parseContentVersionRef)(assetRef);
|
|
152
|
+
if (!parsed || parsed.kind !== 'asset') {
|
|
153
|
+
throw new Error(`dev_runtime_asset_ref_invalid:${assetRef}`);
|
|
154
|
+
}
|
|
155
|
+
const version = await findAssetVersionByRevision(client, parsed.creatorUsername, parsed.name, parsed.revision);
|
|
156
|
+
if (version.visibility !== 'PUBLIC') {
|
|
157
|
+
throw new Error(`dev_runtime_asset_not_public:${assetRef}`);
|
|
158
|
+
}
|
|
159
|
+
return createPublishedAssetSummary({
|
|
160
|
+
apiBase,
|
|
161
|
+
creatorUsername: parsed.creatorUsername,
|
|
162
|
+
assetName: parsed.name,
|
|
163
|
+
revision: parsed.revision,
|
|
164
|
+
version,
|
|
165
|
+
runtimeKey: options.runtimeKey,
|
|
166
|
+
sourceType: options.sourceType,
|
|
167
|
+
sourcePackRef: options.sourcePackRef,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
function createLocalOwnedAssetSummary(task, creatorUsername, appBaseUrl, ownedAsset) {
|
|
171
|
+
const files = {};
|
|
172
|
+
const runtimeFiles = [];
|
|
173
|
+
for (const [role, absolutePath] of Object.entries(ownedAsset.filePaths)) {
|
|
174
|
+
const stats = (0, node_fs_1.statSync)(absolutePath);
|
|
175
|
+
const contentType = inferContentType(absolutePath);
|
|
176
|
+
files[role] = {
|
|
177
|
+
absolutePath,
|
|
178
|
+
contentType,
|
|
179
|
+
sizeBytes: stats.size,
|
|
180
|
+
};
|
|
181
|
+
runtimeFiles.push({
|
|
182
|
+
role,
|
|
183
|
+
url: buildLocalDevRuntimeAssetFileUrl(appBaseUrl, ownedAsset.name, role),
|
|
184
|
+
contentType,
|
|
185
|
+
sizeBytes: stats.size,
|
|
186
|
+
sha256: null,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
asset: {
|
|
191
|
+
assetRef: buildSyntheticLocalAssetRef(creatorUsername, task.name, ownedAsset.name),
|
|
192
|
+
runtimeKey: ownedAsset.runtimeKey?.trim() || null,
|
|
193
|
+
sourceType: 'OWNED',
|
|
194
|
+
files: runtimeFiles,
|
|
195
|
+
},
|
|
196
|
+
files,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
function assertRuntimeKeyAvailable(seenRuntimeKeys, runtimeKey, context) {
|
|
200
|
+
const normalized = typeof runtimeKey === 'string' ? runtimeKey.trim() : '';
|
|
201
|
+
if (!normalized) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
if (seenRuntimeKeys.has(normalized)) {
|
|
205
|
+
throw new Error(`dev_runtime_key_duplicate:${normalized}:${context}`);
|
|
206
|
+
}
|
|
207
|
+
seenRuntimeKeys.add(normalized);
|
|
208
|
+
}
|
|
209
|
+
function buildLocalAppMetadata(task) {
|
|
210
|
+
if (!task.type) {
|
|
211
|
+
throw new Error(`dev_runtime_app_type_missing:${task.name}`);
|
|
212
|
+
}
|
|
213
|
+
return {
|
|
214
|
+
type: task.type,
|
|
215
|
+
surfaceTargets: task.surfaceTargets.length > 0 ? Array.from(task.surfaceTargets) : Array.from(types_1.DEFAULT_APP_SURFACE_TARGETS),
|
|
216
|
+
authMode: task.authMode ?? types_1.DEFAULT_APP_AUTH_MODE,
|
|
217
|
+
controllerMode: task.controllerMode ?? types_1.DEFAULT_APP_CONTROLLER_MODE,
|
|
218
|
+
previewable: task.previewable ?? types_1.DEFAULT_APP_PREVIEWABLE,
|
|
219
|
+
assetSpecSupport: task.assetSpecSupport.map((entry) => ({
|
|
220
|
+
assetSpecRef: entry.assetSpec,
|
|
221
|
+
versionRange: entry.versionRange,
|
|
222
|
+
capabilities: Array.from(entry.capabilities),
|
|
223
|
+
})),
|
|
224
|
+
version: typeof task.version === 'string' && task.version.trim().length > 0 ? task.version.trim() : null,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
function assertCustomAssetVersionSupported(task, assetRef, version) {
|
|
228
|
+
if (typeof version.assetSpecVersionRef !== 'string' || version.assetSpecVersionRef.trim().length === 0) {
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
const parsedAssetSpecVersion = (0, types_1.parseAssetSpecVersionRef)(version.assetSpecVersionRef);
|
|
232
|
+
if (!parsedAssetSpecVersion) {
|
|
233
|
+
throw new Error(`dev_runtime_asset_spec_invalid:${assetRef}:${version.assetSpecVersionRef}`);
|
|
234
|
+
}
|
|
235
|
+
const assetSpecFamilyRef = (0, types_1.formatAssetSpecFamilyRef)({
|
|
236
|
+
kind: 'asset-spec',
|
|
237
|
+
creatorUsername: parsedAssetSpecVersion.creatorUsername,
|
|
238
|
+
name: parsedAssetSpecVersion.name,
|
|
239
|
+
});
|
|
240
|
+
const supported = task.assetSpecSupport.some((entry) => (entry.assetSpec === assetSpecFamilyRef
|
|
241
|
+
&& entry.capabilities.includes('READ')
|
|
242
|
+
&& (0, semver_1.satisfies)(parsedAssetSpecVersion.version, entry.versionRange, { includePrerelease: false })));
|
|
243
|
+
if (!supported) {
|
|
244
|
+
throw new Error(`dev_runtime_asset_spec_unsupported:${assetRef}:${version.assetSpecVersionRef}`);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
async function buildDevRuntimeAssetManifest(input) {
|
|
248
|
+
const localAppMetadata = buildLocalAppMetadata(input.task);
|
|
249
|
+
const assets = [];
|
|
250
|
+
const ownedAssetFiles = {};
|
|
251
|
+
const seenRuntimeKeys = new Set();
|
|
252
|
+
const seenOwnedAssetNames = new Set();
|
|
253
|
+
for (const ownedAsset of input.task.ownedAssets) {
|
|
254
|
+
if (seenOwnedAssetNames.has(ownedAsset.name)) {
|
|
255
|
+
throw new Error(`dev_runtime_owned_asset_duplicate:${ownedAsset.name}`);
|
|
256
|
+
}
|
|
257
|
+
seenOwnedAssetNames.add(ownedAsset.name);
|
|
258
|
+
assertRuntimeKeyAvailable(seenRuntimeKeys, ownedAsset.runtimeKey, ownedAsset.name);
|
|
259
|
+
const localAsset = createLocalOwnedAssetSummary(input.task, input.creatorUsername, input.appBaseUrl, ownedAsset);
|
|
260
|
+
assets.push(localAsset.asset);
|
|
261
|
+
ownedAssetFiles[ownedAsset.name] = localAsset.files;
|
|
262
|
+
}
|
|
263
|
+
for (const dependency of input.task.uses.assets) {
|
|
264
|
+
assertRuntimeKeyAvailable(seenRuntimeKeys, dependency.runtimeKey, dependency.ref);
|
|
265
|
+
const parsedDependencyRef = (0, types_1.parseContentVersionRef)(dependency.ref);
|
|
266
|
+
if (!parsedDependencyRef || parsedDependencyRef.kind !== 'asset') {
|
|
267
|
+
throw new Error(`dev_runtime_asset_ref_invalid:${dependency.ref}`);
|
|
268
|
+
}
|
|
269
|
+
const dependencyVersion = await findAssetVersionByRevision(input.client, parsedDependencyRef.creatorUsername, parsedDependencyRef.name, parsedDependencyRef.revision);
|
|
270
|
+
assertCustomAssetVersionSupported(input.task, dependency.ref, dependencyVersion);
|
|
271
|
+
assets.push(await resolvePublishedAssetSummary(input.client, input.apiBase, dependency.ref, {
|
|
272
|
+
runtimeKey: dependency.runtimeKey,
|
|
273
|
+
sourceType: 'DIRECT',
|
|
274
|
+
}));
|
|
275
|
+
}
|
|
276
|
+
for (const packRef of input.task.uses.packs) {
|
|
277
|
+
const parsedPackRef = (0, types_1.parseContentVersionRef)(packRef);
|
|
278
|
+
if (!parsedPackRef || parsedPackRef.kind !== 'pack') {
|
|
279
|
+
throw new Error(`dev_runtime_pack_ref_invalid:${packRef}`);
|
|
280
|
+
}
|
|
281
|
+
const packVersion = await findPackVersion(input.client, parsedPackRef.creatorUsername, parsedPackRef.name, parsedPackRef.version);
|
|
282
|
+
if (packVersion.visibility !== 'PUBLIC') {
|
|
283
|
+
throw new Error(`dev_runtime_pack_not_public:${packRef}`);
|
|
284
|
+
}
|
|
285
|
+
for (const member of packVersion.assets) {
|
|
286
|
+
const memberRef = (0, types_1.formatContentVersionRef)({
|
|
287
|
+
kind: 'asset',
|
|
288
|
+
creatorUsername: member.creatorUsername,
|
|
289
|
+
name: member.name,
|
|
290
|
+
revision: member.revision,
|
|
291
|
+
});
|
|
292
|
+
const memberVersion = await findAssetVersionByRevision(input.client, member.creatorUsername, member.name, member.revision);
|
|
293
|
+
assertCustomAssetVersionSupported(input.task, memberRef, memberVersion);
|
|
294
|
+
assets.push(await resolvePublishedAssetSummary(input.client, input.apiBase, memberRef, {
|
|
295
|
+
sourceType: 'PACK',
|
|
296
|
+
sourcePackRef: packRef,
|
|
297
|
+
}));
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return {
|
|
301
|
+
response: {
|
|
302
|
+
...createEmptyDevRuntimeAssetManifest().response,
|
|
303
|
+
assets,
|
|
304
|
+
localAppMetadata,
|
|
305
|
+
},
|
|
306
|
+
ownedAssetFiles,
|
|
307
|
+
};
|
|
308
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import http from 'node:http';
|
|
1
2
|
import { type ChildProcess } from 'node:child_process';
|
|
2
3
|
import type { ProjectInfo } from './devShared';
|
|
4
|
+
import type { DevRuntimeAssetManifestRegistration } from './devRuntimeAssets';
|
|
3
5
|
export interface StartDevServerOptions {
|
|
4
6
|
appName: string;
|
|
5
7
|
appType: string;
|
|
@@ -7,6 +9,7 @@ export interface StartDevServerOptions {
|
|
|
7
9
|
htmlPath: string;
|
|
8
10
|
port: number;
|
|
9
11
|
projectInfo: ProjectInfo;
|
|
12
|
+
runtimeAssetManifest?: DevRuntimeAssetManifestRegistration | null;
|
|
10
13
|
}
|
|
11
14
|
export interface DevServerHandle {
|
|
12
15
|
server: null;
|
|
@@ -30,6 +33,12 @@ export declare function buildLocalDevAppUrl(input: {
|
|
|
30
33
|
port?: number;
|
|
31
34
|
}): string;
|
|
32
35
|
export declare function ensureDevRouterRunning(port?: number): Promise<void>;
|
|
36
|
+
export declare function updateMountedDevRuntimeAssetManifest(input: {
|
|
37
|
+
creatorUsername: string;
|
|
38
|
+
appName: string;
|
|
39
|
+
runtimeAssetManifest: DevRuntimeAssetManifestRegistration | null;
|
|
40
|
+
port?: number;
|
|
41
|
+
}): Promise<void>;
|
|
33
42
|
export declare function startDevServer(options: StartDevServerOptions): Promise<DevServerHandle>;
|
|
34
43
|
export declare function isDevServerAvailable(input: {
|
|
35
44
|
creatorUsername: string;
|
|
@@ -37,5 +46,7 @@ export declare function isDevServerAvailable(input: {
|
|
|
37
46
|
appName: string;
|
|
38
47
|
port?: number;
|
|
39
48
|
}, timeoutMs?: number): Promise<boolean>;
|
|
49
|
+
export declare function createDevRouterServer(initialPort?: number): http.Server;
|
|
50
|
+
export declare function listenDevRouterServer(server: http.Server, port?: number): Promise<number>;
|
|
40
51
|
export declare function runDevRouterServer(port?: number): Promise<void>;
|
|
41
52
|
export {};
|