@playdrop/playdrop-cli 0.12.27 → 0.12.28

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": "0.12.8",
3
- "build": 28,
3
+ "build": 31,
4
4
  "runtimeSdkVersion": "0.12.5",
5
5
  "runtimeSdkBuild": 2,
6
6
  "clients": {
@@ -37,6 +37,7 @@ const CONTENT_TYPE_BY_EXTENSION = {
37
37
  '.png': 'image/png',
38
38
  '.jpg': 'image/jpeg',
39
39
  '.jpeg': 'image/jpeg',
40
+ '.gif': 'image/gif',
40
41
  '.svg': 'image/svg+xml',
41
42
  '.webp': 'image/webp',
42
43
  '.glb': 'model/gltf-binary',
@@ -52,25 +53,6 @@ function inferContentType(filePath) {
52
53
  const extension = (0, node_path_1.extname)(filePath).toLowerCase();
53
54
  return CONTENT_TYPE_BY_EXTENSION[extension] ?? null;
54
55
  }
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
56
  function buildSyntheticLocalAssetRef(creatorUsername, appName, ownedAssetName) {
75
57
  return (0, types_1.formatContentVersionRef)({
76
58
  kind: 'asset',
@@ -87,86 +69,6 @@ function buildLocalDevRuntimeAssetFileUrl(appBaseUrl, ownedAssetName, role) {
87
69
  const base = appBaseUrl.endsWith('/') ? appBaseUrl.slice(0, -1) : appBaseUrl;
88
70
  return `${base}/_playdrop/runtime-assets/files/${encodeURIComponent(ownedAssetName)}?role=${encodeURIComponent(role)}`;
89
71
  }
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
72
  function createLocalOwnedAssetSummary(task, creatorUsername, appBaseUrl, ownedAsset) {
171
73
  const files = {};
172
74
  const runtimeFiles = [];
@@ -260,41 +162,41 @@ async function buildDevRuntimeAssetManifest(input) {
260
162
  assets.push(localAsset.asset);
261
163
  ownedAssetFiles[ownedAsset.name] = localAsset.files;
262
164
  }
263
- for (const dependency of input.task.uses.assets) {
165
+ const directDependencies = input.task.uses.assets.map((dependency) => {
264
166
  assertRuntimeKeyAvailable(seenRuntimeKeys, dependency.runtimeKey, dependency.ref);
265
167
  const parsedDependencyRef = (0, types_1.parseContentVersionRef)(dependency.ref);
266
168
  if (!parsedDependencyRef || parsedDependencyRef.kind !== 'asset') {
267
169
  throw new Error(`dev_runtime_asset_ref_invalid:${dependency.ref}`);
268
170
  }
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) {
171
+ return {
172
+ ref: (0, types_1.formatContentVersionRef)(parsedDependencyRef),
173
+ runtimeKey: dependency.runtimeKey?.trim() || null,
174
+ };
175
+ });
176
+ const packDependencies = input.task.uses.packs.map((packRef) => {
277
177
  const parsedPackRef = (0, types_1.parseContentVersionRef)(packRef);
278
178
  if (!parsedPackRef || parsedPackRef.kind !== 'pack') {
279
179
  throw new Error(`dev_runtime_pack_ref_invalid:${packRef}`);
280
180
  }
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,
181
+ return (0, types_1.formatContentVersionRef)(parsedPackRef);
182
+ });
183
+ if (directDependencies.length > 0 || packDependencies.length > 0) {
184
+ const resolved = await input.client.resolveRuntimeAssets({
185
+ assets: directDependencies,
186
+ packs: packDependencies,
187
+ });
188
+ for (const entry of resolved.assets) {
189
+ if (entry.sourceType !== 'DIRECT' && entry.sourceType !== 'PACK') {
190
+ throw new Error(`dev_runtime_asset_source_type_invalid:${entry.assetRef}:${entry.sourceType}`);
191
+ }
192
+ assertCustomAssetVersionSupported(input.task, entry.assetRef, entry);
193
+ assets.push({
194
+ assetRef: entry.assetRef,
195
+ runtimeKey: entry.runtimeKey,
196
+ sourceType: entry.sourceType,
197
+ ...(entry.sourcePackRef ? { sourcePackRef: entry.sourcePackRef } : {}),
198
+ files: entry.files,
291
199
  });
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
200
  }
299
201
  }
300
202
  return {
@@ -0,0 +1,7 @@
1
+ import type { AppTask } from '../catalogue';
2
+ export declare function validateOwnedRuntimeImageFile(input: {
3
+ assetName: string;
4
+ filePath: string;
5
+ declaredFormat?: string;
6
+ }): Promise<void>;
7
+ export declare function assertOwnedRuntimeImagesAreValid(task: AppTask): Promise<void>;
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.validateOwnedRuntimeImageFile = validateOwnedRuntimeImageFile;
7
+ exports.assertOwnedRuntimeImagesAreValid = assertOwnedRuntimeImagesAreValid;
8
+ const node_fs_1 = require("node:fs");
9
+ const node_path_1 = require("node:path");
10
+ const sharp_1 = __importDefault(require("sharp"));
11
+ const IMAGE_BY_EXTENSION = {
12
+ '.png': { format: 'png', contentType: 'image/png' },
13
+ '.jpg': { format: 'jpeg', contentType: 'image/jpeg' },
14
+ '.jpeg': { format: 'jpeg', contentType: 'image/jpeg' },
15
+ '.webp': { format: 'webp', contentType: 'image/webp' },
16
+ '.gif': { format: 'gif', contentType: 'image/gif' },
17
+ '.svg': { format: 'svg', contentType: 'image/svg+xml' },
18
+ };
19
+ const SHARP_FORMAT_BY_DECLARED_FORMAT = {
20
+ PNG: 'png',
21
+ JPEG: 'jpeg',
22
+ WEBP: 'webp',
23
+ GIF: 'gif',
24
+ };
25
+ async function validateOwnedRuntimeImageFile(input) {
26
+ let stats;
27
+ try {
28
+ stats = (0, node_fs_1.statSync)(input.filePath);
29
+ }
30
+ catch {
31
+ throw new Error(`agent_task_owned_image_file_missing:${input.assetName}:${input.filePath}`);
32
+ }
33
+ if (!stats.isFile()) {
34
+ throw new Error(`agent_task_owned_image_file_missing:${input.assetName}:${input.filePath}`);
35
+ }
36
+ const extension = (0, node_path_1.extname)(input.filePath).toLowerCase();
37
+ const expected = IMAGE_BY_EXTENSION[extension];
38
+ if (!expected) {
39
+ throw new Error(`agent_task_owned_image_format_unsupported:${input.assetName}:${extension || 'missing_extension'}`);
40
+ }
41
+ let metadata;
42
+ try {
43
+ metadata = await (0, sharp_1.default)(input.filePath, { failOn: 'error' }).metadata();
44
+ }
45
+ catch {
46
+ throw new Error(`agent_task_owned_image_decode_failed:${input.assetName}:${input.filePath}`);
47
+ }
48
+ if (!Number.isInteger(metadata.width) || Number(metadata.width) <= 0
49
+ || !Number.isInteger(metadata.height) || Number(metadata.height) <= 0) {
50
+ throw new Error(`agent_task_owned_image_dimensions_invalid:${input.assetName}:${input.filePath}`);
51
+ }
52
+ if (metadata.format !== expected.format) {
53
+ throw new Error(`agent_task_owned_image_format_mismatch:${input.assetName}:extension=${extension}:contentType=${expected.contentType}:decoded=${metadata.format ?? 'unknown'}`);
54
+ }
55
+ const declaredFormat = input.declaredFormat?.trim().toUpperCase() ?? '';
56
+ const declaredSharpFormat = SHARP_FORMAT_BY_DECLARED_FORMAT[declaredFormat];
57
+ if (declaredSharpFormat && declaredSharpFormat !== metadata.format) {
58
+ throw new Error(`agent_task_owned_image_format_mismatch:${input.assetName}:declared=${declaredFormat}:decoded=${metadata.format}`);
59
+ }
60
+ }
61
+ async function assertOwnedRuntimeImagesAreValid(task) {
62
+ for (const ownedAsset of task.ownedAssets) {
63
+ if (!ownedAsset.runtimeKey || ownedAsset.category !== 'IMAGE') {
64
+ continue;
65
+ }
66
+ const primaryPath = ownedAsset.filePaths?.primary;
67
+ if (!primaryPath) {
68
+ throw new Error(`agent_task_owned_image_file_missing:${ownedAsset.name}:primary`);
69
+ }
70
+ await validateOwnedRuntimeImageFile({
71
+ assetName: ownedAsset.name,
72
+ filePath: primaryPath,
73
+ declaredFormat: ownedAsset.format,
74
+ });
75
+ }
76
+ }
@@ -6,6 +6,8 @@ export type UploadCommandOptions = {
6
6
  skipReview?: boolean;
7
7
  clearTags?: boolean;
8
8
  };
9
+ export declare function resolveUploadOwner(actorUsername: string | null | undefined, workspaceOwnerUsername: string | null | undefined): string;
10
+ export declare function resolveDefaultUploadCreator(owner: string | null | undefined, actorUsername: string | null | undefined): string;
9
11
  export declare function upload(pathOrName: string, options?: UploadCommandOptions): Promise<void>;
10
12
  export type WorkerAppPublishInput = {
11
13
  client: ApiClient;