@playdrop/playdrop-cli 0.5.1 → 0.5.3

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.
Files changed (155) hide show
  1. package/config/client-meta.json +7 -7
  2. package/dist/apps/build.js +49 -6
  3. package/dist/apps/index.d.ts +2 -0
  4. package/dist/apps/index.js +2 -0
  5. package/dist/apps/upload.d.ts +2 -0
  6. package/dist/apps/upload.js +132 -28
  7. package/dist/assetSpecs.d.ts +16 -0
  8. package/dist/assetSpecs.js +263 -0
  9. package/dist/assets/model-artifacts.js +3 -0
  10. package/dist/catalogue.d.ts +57 -3
  11. package/dist/catalogue.js +342 -16
  12. package/dist/clientInfo.js +19 -3
  13. package/dist/commands/ads.d.ts +8 -0
  14. package/dist/commands/ads.js +124 -0
  15. package/dist/commands/boosts.d.ts +25 -0
  16. package/dist/commands/boosts.js +209 -0
  17. package/dist/commands/browse.d.ts +6 -1
  18. package/dist/commands/browse.js +365 -124
  19. package/dist/commands/captureListing.d.ts +53 -0
  20. package/dist/commands/captureListing.js +804 -0
  21. package/dist/commands/captureRemote.js +33 -0
  22. package/dist/commands/create.d.ts +1 -0
  23. package/dist/commands/create.js +183 -3
  24. package/dist/commands/credits.d.ts +6 -0
  25. package/dist/commands/credits.js +47 -1
  26. package/dist/commands/detail.js +38 -4
  27. package/dist/commands/devServer.js +10 -5
  28. package/dist/commands/generation.d.ts +2 -0
  29. package/dist/commands/generation.js +1 -0
  30. package/dist/commands/search.d.ts +5 -0
  31. package/dist/commands/search.js +139 -17
  32. package/dist/commands/tags.d.ts +7 -0
  33. package/dist/commands/tags.js +63 -0
  34. package/dist/commands/upload-content.d.ts +13 -3
  35. package/dist/commands/upload-content.js +86 -20
  36. package/dist/commands/upload.d.ts +2 -0
  37. package/dist/commands/upload.js +187 -11
  38. package/dist/commands/validate.js +163 -2
  39. package/dist/commands/versionsBrowse.js +128 -91
  40. package/dist/index.js +145 -3
  41. package/dist/refs.d.ts +2 -2
  42. package/dist/refs.js +13 -1
  43. package/dist/taskSelection.js +6 -3
  44. package/dist/taskUtils.d.ts +2 -2
  45. package/dist/taskUtils.js +1 -0
  46. package/dist/uploadLog.d.ts +1 -1
  47. package/dist/uploadLog.js +2 -2
  48. package/node_modules/@playdrop/ai-client/package.json +1 -1
  49. package/node_modules/@playdrop/api-client/dist/client.d.ts +131 -10
  50. package/node_modules/@playdrop/api-client/dist/client.d.ts.map +1 -1
  51. package/node_modules/@playdrop/api-client/dist/client.js +6 -0
  52. package/node_modules/@playdrop/api-client/dist/domains/admin.d.ts +9 -1
  53. package/node_modules/@playdrop/api-client/dist/domains/admin.d.ts.map +1 -1
  54. package/node_modules/@playdrop/api-client/dist/domains/admin.js +45 -0
  55. package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts +3 -0
  56. package/node_modules/@playdrop/api-client/dist/domains/apps.d.ts.map +1 -1
  57. package/node_modules/@playdrop/api-client/dist/domains/apps.js +27 -0
  58. package/node_modules/@playdrop/api-client/dist/domains/asset-packs.d.ts +2 -0
  59. package/node_modules/@playdrop/api-client/dist/domains/asset-packs.d.ts.map +1 -1
  60. package/node_modules/@playdrop/api-client/dist/domains/asset-packs.js +16 -0
  61. package/node_modules/@playdrop/api-client/dist/domains/assets.d.ts +44 -2
  62. package/node_modules/@playdrop/api-client/dist/domains/assets.d.ts.map +1 -1
  63. package/node_modules/@playdrop/api-client/dist/domains/assets.js +260 -3
  64. package/node_modules/@playdrop/api-client/dist/domains/payments.d.ts +17 -1
  65. package/node_modules/@playdrop/api-client/dist/domains/payments.d.ts.map +1 -1
  66. package/node_modules/@playdrop/api-client/dist/domains/payments.js +173 -0
  67. package/node_modules/@playdrop/api-client/dist/domains/search.d.ts.map +1 -1
  68. package/node_modules/@playdrop/api-client/dist/domains/search.js +39 -11
  69. package/node_modules/@playdrop/api-client/dist/domains/tags.d.ts +34 -0
  70. package/node_modules/@playdrop/api-client/dist/domains/tags.d.ts.map +1 -0
  71. package/node_modules/@playdrop/api-client/dist/domains/tags.js +111 -0
  72. package/node_modules/@playdrop/api-client/dist/index.d.ts +61 -1
  73. package/node_modules/@playdrop/api-client/dist/index.d.ts.map +1 -1
  74. package/node_modules/@playdrop/api-client/dist/index.js +50 -0
  75. package/node_modules/@playdrop/api-client/package.json +1 -1
  76. package/node_modules/@playdrop/boxel-core/dist/test/entity-utils.test.d.ts +1 -0
  77. package/node_modules/@playdrop/boxel-core/dist/test/entity-utils.test.js +92 -0
  78. package/node_modules/@playdrop/boxel-core/dist/test/entity-utils.test.js.map +1 -0
  79. package/node_modules/@playdrop/boxel-core/dist/test/greedy-mesher.test.d.ts +1 -0
  80. package/node_modules/@playdrop/boxel-core/dist/test/greedy-mesher.test.js +48 -0
  81. package/node_modules/@playdrop/boxel-core/dist/test/greedy-mesher.test.js.map +1 -0
  82. package/node_modules/@playdrop/boxel-core/dist/test/humanoid/humanoid-builders.test.d.ts +1 -0
  83. package/node_modules/@playdrop/boxel-core/dist/test/humanoid/humanoid-builders.test.js +270 -0
  84. package/node_modules/@playdrop/boxel-core/dist/test/humanoid/humanoid-builders.test.js.map +1 -0
  85. package/node_modules/@playdrop/boxel-core/dist/test/index.test.d.ts +1 -0
  86. package/node_modules/@playdrop/boxel-core/dist/test/index.test.js +48 -0
  87. package/node_modules/@playdrop/boxel-core/dist/test/index.test.js.map +1 -0
  88. package/node_modules/@playdrop/boxel-core/dist/test/layer-mode.test.d.ts +1 -0
  89. package/node_modules/@playdrop/boxel-core/dist/test/layer-mode.test.js +67 -0
  90. package/node_modules/@playdrop/boxel-core/dist/test/layer-mode.test.js.map +1 -0
  91. package/node_modules/@playdrop/boxel-core/dist/test/materials.test.d.ts +1 -0
  92. package/node_modules/@playdrop/boxel-core/dist/test/materials.test.js +55 -0
  93. package/node_modules/@playdrop/boxel-core/dist/test/materials.test.js.map +1 -0
  94. package/node_modules/@playdrop/boxel-core/dist/test/palette-tools.test.d.ts +1 -0
  95. package/node_modules/@playdrop/boxel-core/dist/test/palette-tools.test.js +124 -0
  96. package/node_modules/@playdrop/boxel-core/dist/test/palette-tools.test.js.map +1 -0
  97. package/node_modules/@playdrop/boxel-core/dist/test/serialization.test.d.ts +1 -0
  98. package/node_modules/@playdrop/boxel-core/dist/test/serialization.test.js +35 -0
  99. package/node_modules/@playdrop/boxel-core/dist/test/serialization.test.js.map +1 -0
  100. package/node_modules/@playdrop/boxel-core/dist/test/textures.test.d.ts +1 -0
  101. package/node_modules/@playdrop/boxel-core/dist/test/textures.test.js +120 -0
  102. package/node_modules/@playdrop/boxel-core/dist/test/textures.test.js.map +1 -0
  103. package/node_modules/@playdrop/boxel-core/dist/test/types.test.d.ts +1 -0
  104. package/node_modules/@playdrop/boxel-core/dist/test/types.test.js +32 -0
  105. package/node_modules/@playdrop/boxel-core/dist/test/types.test.js.map +1 -0
  106. package/node_modules/@playdrop/boxel-core/dist/test/upscale.test.d.ts +1 -0
  107. package/node_modules/@playdrop/boxel-core/dist/test/upscale.test.js +100 -0
  108. package/node_modules/@playdrop/boxel-core/dist/test/upscale.test.js.map +1 -0
  109. package/node_modules/@playdrop/boxel-core/dist/test/validation.test.d.ts +1 -0
  110. package/node_modules/@playdrop/boxel-core/dist/test/validation.test.js +61 -0
  111. package/node_modules/@playdrop/boxel-core/dist/test/validation.test.js.map +1 -0
  112. package/node_modules/@playdrop/boxel-core/dist/test/voxels.test.d.ts +1 -0
  113. package/node_modules/@playdrop/boxel-core/dist/test/voxels.test.js +51 -0
  114. package/node_modules/@playdrop/boxel-core/dist/test/voxels.test.js.map +1 -0
  115. package/node_modules/@playdrop/boxel-core/package.json +1 -1
  116. package/node_modules/@playdrop/boxel-three/package.json +1 -1
  117. package/node_modules/@playdrop/config/client-meta.json +7 -7
  118. package/node_modules/@playdrop/config/dist/src/constants.d.ts +11 -0
  119. package/node_modules/@playdrop/config/dist/src/constants.d.ts.map +1 -1
  120. package/node_modules/@playdrop/config/dist/src/constants.js +12 -1
  121. package/node_modules/@playdrop/config/dist/src/creator-docs.d.ts +24 -0
  122. package/node_modules/@playdrop/config/dist/src/creator-docs.d.ts.map +1 -0
  123. package/node_modules/@playdrop/config/dist/src/creator-docs.js +253 -0
  124. package/node_modules/@playdrop/config/dist/src/creator-faq.d.ts +17 -0
  125. package/node_modules/@playdrop/config/dist/src/creator-faq.d.ts.map +1 -0
  126. package/node_modules/@playdrop/config/dist/src/creator-faq.js +141 -0
  127. package/node_modules/@playdrop/config/dist/test/creator-docs.test.d.ts +2 -0
  128. package/node_modules/@playdrop/config/dist/test/creator-docs.test.d.ts.map +1 -0
  129. package/node_modules/@playdrop/config/dist/test/creator-docs.test.js +36 -0
  130. package/node_modules/@playdrop/config/dist/tsconfig.tsbuildinfo +1 -1
  131. package/node_modules/@playdrop/config/package.json +1 -1
  132. package/node_modules/@playdrop/types/dist/api.d.ts +346 -6
  133. package/node_modules/@playdrop/types/dist/api.d.ts.map +1 -1
  134. package/node_modules/@playdrop/types/dist/api.js +52 -1
  135. package/node_modules/@playdrop/types/dist/asset-pack.d.ts +7 -1
  136. package/node_modules/@playdrop/types/dist/asset-pack.d.ts.map +1 -1
  137. package/node_modules/@playdrop/types/dist/asset-spec-contract-meta-schema.json +86 -0
  138. package/node_modules/@playdrop/types/dist/asset-spec.d.ts +163 -0
  139. package/node_modules/@playdrop/types/dist/asset-spec.d.ts.map +1 -0
  140. package/node_modules/@playdrop/types/dist/asset-spec.js +101 -0
  141. package/node_modules/@playdrop/types/dist/asset.d.ts +23 -6
  142. package/node_modules/@playdrop/types/dist/asset.d.ts.map +1 -1
  143. package/node_modules/@playdrop/types/dist/asset.js +4 -1
  144. package/node_modules/@playdrop/types/dist/graph.d.ts +4 -2
  145. package/node_modules/@playdrop/types/dist/graph.d.ts.map +1 -1
  146. package/node_modules/@playdrop/types/dist/graph.js +9 -2
  147. package/node_modules/@playdrop/types/dist/index.d.ts +1 -0
  148. package/node_modules/@playdrop/types/dist/index.d.ts.map +1 -1
  149. package/node_modules/@playdrop/types/dist/index.js +1 -0
  150. package/node_modules/@playdrop/types/dist/version.d.ts +13 -0
  151. package/node_modules/@playdrop/types/dist/version.d.ts.map +1 -1
  152. package/node_modules/@playdrop/types/dist/version.js +21 -0
  153. package/node_modules/@playdrop/types/package.json +6 -1
  154. package/node_modules/@playdrop/vox-three/package.json +1 -1
  155. package/package.json +3 -1
@@ -0,0 +1,263 @@
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.normalizeAssetSpecSupportEntries = normalizeAssetSpecSupportEntries;
7
+ exports.loadAndValidateAssetSpecContract = loadAndValidateAssetSpecContract;
8
+ exports.validateCustomAssetFilesAgainstContract = validateCustomAssetFilesAgainstContract;
9
+ exports.normalizeAssetSpecVersionRef = normalizeAssetSpecVersionRef;
10
+ exports.validateAssetSpecName = validateAssetSpecName;
11
+ const node_fs_1 = require("node:fs");
12
+ const node_path_1 = require("node:path");
13
+ const ajv_1 = __importDefault(require("ajv"));
14
+ const semver_1 = require("semver");
15
+ const types_1 = require("@playdrop/types");
16
+ const LOCAL_EXTENSION_TO_MIME = {
17
+ '.png': 'image/png',
18
+ '.jpg': 'image/jpeg',
19
+ '.jpeg': 'image/jpeg',
20
+ '.webp': 'image/webp',
21
+ '.gif': 'image/gif',
22
+ '.aseprite': 'application/octet-stream',
23
+ '.mp4': 'video/mp4',
24
+ '.webm': 'video/webm',
25
+ '.mp3': 'audio/mpeg',
26
+ '.wav': 'audio/wav',
27
+ '.ogg': 'audio/ogg',
28
+ '.vox': 'application/octet-stream',
29
+ '.gltf': 'model/gltf+json',
30
+ '.glb': 'model/gltf-binary',
31
+ '.json': 'application/json',
32
+ '.md': 'text/markdown',
33
+ '.txt': 'text/plain',
34
+ };
35
+ const ajv = new ajv_1.default({
36
+ allErrors: true,
37
+ strict: false,
38
+ allowUnionTypes: true,
39
+ });
40
+ const validateContractMetaSchema = ajv.compile(types_1.ASSET_SPEC_CONTRACT_META_SCHEMA);
41
+ function formatAjvErrors(prefix) {
42
+ return (validateContractMetaSchema.errors ?? []).map((issue) => {
43
+ const path = issue.instancePath || issue.schemaPath || '/';
44
+ return `${prefix} contract is invalid at ${path}: ${issue.message ?? 'schema validation failed'}.`;
45
+ });
46
+ }
47
+ function createContractValidator() {
48
+ return new ajv_1.default({
49
+ allErrors: true,
50
+ strict: false,
51
+ allowUnionTypes: true,
52
+ });
53
+ }
54
+ function resolveMimeTypeFromPath(filePath) {
55
+ return LOCAL_EXTENSION_TO_MIME[(0, node_path_1.extname)(filePath).toLowerCase()] ?? 'application/octet-stream';
56
+ }
57
+ // eslint-disable-next-line complexity
58
+ function normalizeAssetSpecSupportEntries(raw, errors, context) {
59
+ if (raw === undefined || raw === null) {
60
+ return [];
61
+ }
62
+ if (!Array.isArray(raw)) {
63
+ errors.push(`${context} assetSpecSupport must be an array.`);
64
+ return [];
65
+ }
66
+ const rows = [];
67
+ for (let index = 0; index < raw.length; index += 1) {
68
+ const entry = raw[index];
69
+ if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
70
+ errors.push(`${context} assetSpecSupport[${index}] must be an object.`);
71
+ continue;
72
+ }
73
+ const assetSpec = typeof entry.assetSpec === 'string' ? entry.assetSpec.trim() : '';
74
+ const parsedAssetSpec = (0, types_1.parseAssetSpecFamilyRef)(assetSpec);
75
+ if (!assetSpec || !parsedAssetSpec) {
76
+ errors.push(`${context} assetSpecSupport[${index}].assetSpec must be an asset spec family ref.`);
77
+ continue;
78
+ }
79
+ const versionRange = typeof entry.versionRange === 'string' ? entry.versionRange.trim() : '';
80
+ if (!versionRange || !(0, semver_1.validRange)(versionRange)) {
81
+ errors.push(`${context} assetSpecSupport[${index}].versionRange must be a valid semver range.`);
82
+ continue;
83
+ }
84
+ const capabilitiesRaw = Array.isArray(entry.capabilities) ? entry.capabilities : null;
85
+ if (!capabilitiesRaw || capabilitiesRaw.length === 0) {
86
+ errors.push(`${context} assetSpecSupport[${index}].capabilities must be a non-empty array.`);
87
+ continue;
88
+ }
89
+ const capabilities = [];
90
+ let capabilitiesValid = true;
91
+ for (const capability of capabilitiesRaw) {
92
+ if (!(0, types_1.isAssetSpecCapability)(capability)) {
93
+ capabilitiesValid = false;
94
+ break;
95
+ }
96
+ if (!capabilities.includes(capability)) {
97
+ capabilities.push(capability);
98
+ }
99
+ }
100
+ if (!capabilitiesValid) {
101
+ errors.push(`${context} assetSpecSupport[${index}].capabilities contains an unsupported capability.`);
102
+ continue;
103
+ }
104
+ rows.push({
105
+ assetSpec,
106
+ versionRange,
107
+ capabilities,
108
+ });
109
+ }
110
+ return rows;
111
+ }
112
+ // eslint-disable-next-line complexity
113
+ function loadAndValidateAssetSpecContract(catalogueDir, relativeContractPath, errors, context) {
114
+ const contractPath = (0, node_path_1.resolve)(catalogueDir, relativeContractPath);
115
+ let rawText;
116
+ try {
117
+ const stat = (0, node_fs_1.statSync)(contractPath);
118
+ if (!stat.isFile()) {
119
+ errors.push(`${context} contract must be a file: ${contractPath}.`);
120
+ return null;
121
+ }
122
+ if (stat.size > types_1.ASSET_SPEC_CONTRACT_MAX_BYTES) {
123
+ errors.push(`${context} contract exceeds the ${types_1.ASSET_SPEC_CONTRACT_MAX_BYTES} byte limit.`);
124
+ return null;
125
+ }
126
+ rawText = (0, node_fs_1.readFileSync)(contractPath, 'utf8');
127
+ }
128
+ catch {
129
+ errors.push(`${context} contract file not found at ${contractPath}.`);
130
+ return null;
131
+ }
132
+ let parsed;
133
+ try {
134
+ parsed = JSON.parse(rawText);
135
+ }
136
+ catch (error) {
137
+ errors.push(`${context} contract must be valid JSON (${error?.message ?? 'parse failed'}).`);
138
+ return null;
139
+ }
140
+ if (!validateContractMetaSchema(parsed)) {
141
+ errors.push(...formatAjvErrors(context));
142
+ return null;
143
+ }
144
+ const contract = parsed;
145
+ const roleNames = new Set();
146
+ let hasContractError = false;
147
+ for (const role of contract.roles) {
148
+ const normalizedRoleName = role.name.trim().toLowerCase();
149
+ if (roleNames.has(normalizedRoleName)) {
150
+ errors.push(`${context} contract declares duplicate role "${role.name}".`);
151
+ hasContractError = true;
152
+ continue;
153
+ }
154
+ roleNames.add(normalizedRoleName);
155
+ if (role.jsonSchema) {
156
+ const allowsJsonSchema = role.contentTypes.some((contentType) => (0, types_1.isJsonLikeAssetSpecContentType)(contentType));
157
+ if (!allowsJsonSchema) {
158
+ errors.push(`${context} contract role "${role.name}" can only declare jsonSchema for JSON content types.`);
159
+ hasContractError = true;
160
+ continue;
161
+ }
162
+ const schemaAjv = createContractValidator();
163
+ try {
164
+ schemaAjv.compile(role.jsonSchema);
165
+ }
166
+ catch (error) {
167
+ errors.push(`${context} contract role "${role.name}" jsonSchema is invalid (${error?.message ?? 'compile failed'}).`);
168
+ hasContractError = true;
169
+ }
170
+ }
171
+ }
172
+ if (!roleNames.has(contract.primaryRole.trim().toLowerCase())) {
173
+ errors.push(`${context} contract primaryRole "${contract.primaryRole}" must match one declared role.`);
174
+ hasContractError = true;
175
+ }
176
+ const referenceNames = new Set();
177
+ for (const reference of contract.references ?? []) {
178
+ const normalizedReferenceName = reference.name.trim().toLowerCase();
179
+ if (referenceNames.has(normalizedReferenceName)) {
180
+ errors.push(`${context} contract declares duplicate reference "${reference.name}".`);
181
+ hasContractError = true;
182
+ continue;
183
+ }
184
+ referenceNames.add(normalizedReferenceName);
185
+ }
186
+ if (hasContractError) {
187
+ return null;
188
+ }
189
+ return {
190
+ contract,
191
+ validationKind: contract.roles.some((role) => role.jsonSchema) ? 'JSON_SCHEMA' : 'NONE',
192
+ contractPath,
193
+ };
194
+ }
195
+ // eslint-disable-next-line complexity
196
+ function validateCustomAssetFilesAgainstContract(contract, filePaths, errors, context) {
197
+ const contractRoles = new Map(contract.roles.map((role) => [role.name.trim().toLowerCase(), role]));
198
+ const providedRoles = new Set();
199
+ for (const [rawRole, filePath] of Object.entries(filePaths)) {
200
+ const normalizedRole = rawRole.trim().toLowerCase();
201
+ providedRoles.add(normalizedRole);
202
+ const roleContract = contractRoles.get(normalizedRole);
203
+ if (!roleContract) {
204
+ errors.push(`${context} declares file role "${rawRole}" that is not present in the asset spec contract.`);
205
+ continue;
206
+ }
207
+ const extension = (0, node_path_1.extname)(filePath).toLowerCase();
208
+ if (roleContract.extensions?.length) {
209
+ const allowedExtensions = roleContract.extensions.map((value) => value.trim().toLowerCase());
210
+ if (!allowedExtensions.includes(extension)) {
211
+ errors.push(`${context} role "${rawRole}" must use one of ${allowedExtensions.join(', ')}.`);
212
+ }
213
+ }
214
+ const resolvedContentType = resolveMimeTypeFromPath(filePath);
215
+ const normalizedContentTypes = roleContract.contentTypes.map((value) => value.trim().toLowerCase());
216
+ if (!normalizedContentTypes.includes(resolvedContentType.toLowerCase())) {
217
+ errors.push(`${context} role "${rawRole}" must use one of ${normalizedContentTypes.join(', ')}.`);
218
+ }
219
+ if (roleContract.jsonSchema) {
220
+ try {
221
+ const rawText = (0, node_fs_1.readFileSync)(filePath, 'utf8');
222
+ const parsedJson = JSON.parse(rawText);
223
+ const schemaAjv = createContractValidator();
224
+ const validateJson = schemaAjv.compile(roleContract.jsonSchema);
225
+ if (!validateJson(parsedJson)) {
226
+ const firstIssue = validateJson.errors?.[0];
227
+ const issuePath = firstIssue?.instancePath || firstIssue?.schemaPath || '/';
228
+ errors.push(`${context} role "${rawRole}" JSON content failed schema validation at ${issuePath}: ${firstIssue?.message ?? 'schema validation failed'}.`);
229
+ }
230
+ }
231
+ catch (error) {
232
+ errors.push(`${context} role "${rawRole}" JSON content is invalid (${error?.message ?? 'parse failed'}).`);
233
+ }
234
+ }
235
+ }
236
+ for (const role of contract.roles) {
237
+ if (role.required && !providedRoles.has(role.name.trim().toLowerCase())) {
238
+ errors.push(`${context} must define required file role "${role.name}".`);
239
+ }
240
+ }
241
+ }
242
+ function normalizeAssetSpecVersionRef(raw, errors, context) {
243
+ if (raw === undefined || raw === null) {
244
+ return undefined;
245
+ }
246
+ if (typeof raw !== 'string' || raw.trim().length === 0) {
247
+ errors.push(`${context} assetSpec must be an exact asset spec version ref.`);
248
+ return undefined;
249
+ }
250
+ const parsed = (0, types_1.parseAssetSpecVersionRef)(raw.trim());
251
+ if (!parsed) {
252
+ errors.push(`${context} assetSpec must use canonical asset spec version ref format.`);
253
+ return undefined;
254
+ }
255
+ return (0, types_1.formatAssetSpecVersionRef)(parsed);
256
+ }
257
+ function validateAssetSpecName(name, errors, context) {
258
+ if (!(0, types_1.isValidAssetSpecName)(name)) {
259
+ errors.push(`${context} name must use lowercase kebab-case.`);
260
+ return false;
261
+ }
262
+ return true;
263
+ }
@@ -344,6 +344,9 @@ async function prepareModel3DAssetArtifacts(task, options = {}) {
344
344
  if (options.dryRun) {
345
345
  return true;
346
346
  }
347
+ if (typeof task.subcategory !== 'string' || task.subcategory.trim().length === 0) {
348
+ throw new Error(`Asset "${label}" is missing a model subcategory.`);
349
+ }
347
350
  const preset = resolvePreset(task.subcategory);
348
351
  const [meshBuffer, previewBuffer] = await Promise.all([
349
352
  exportBoxelWithWorker(boxel, preset.meshMode),
@@ -1,6 +1,9 @@
1
- import { type AppSurface, type AppType, type AppHostingMode, type AppAuthMode, type AppControllerMode, type AppVersionVisibility, type AppAchievementCatalogueDefinition, type AppLeaderboardCatalogueDefinition, type PlayerMetaDefinitionStatus } from '@playdrop/types';
1
+ import { type AppSurface, type AppType, type AppHostingMode, type AppAuthMode, type AppControllerMode, type AppVersionVisibility, type AppAchievementCatalogueDefinition, type AppLeaderboardCatalogueDefinition, type PlayerMetaDefinitionStatus, type CatalogueTagGroupDefinition, type AppMetadataAssetSpecSupport, type AssetSpecContract, type AssetSpecStatus, type AssetSpecValidationKind } from '@playdrop/types';
2
+ import { type AssetSpecSupportDeclaration } from './assetSpecs';
2
3
  export type CatalogueJson = {
3
4
  apps?: Array<Record<string, unknown>>;
5
+ tagGroups?: CatalogueTagGroupDefinition[];
6
+ assetSpecs?: Array<Record<string, unknown>>;
4
7
  assets?: Array<Record<string, unknown>>;
5
8
  assetPacks?: Array<Record<string, unknown>>;
6
9
  } & Record<string, unknown>;
@@ -51,10 +54,12 @@ export type AppCatalogueEntry = {
51
54
  achievements?: AppAchievementCatalogueDefinition[];
52
55
  leaderboards?: AppLeaderboardCatalogueDefinition[];
53
56
  embeddedAssets?: EmbeddedAssetCatalogueEntry[];
57
+ assetSpecSupport?: AppMetadataAssetSpecSupport[];
54
58
  uses?: {
55
59
  assets?: string[];
56
60
  packs?: string[];
57
61
  };
62
+ tags?: string[];
58
63
  relations?: Array<{
59
64
  type: string;
60
65
  to: string;
@@ -66,10 +71,12 @@ export type AssetCatalogueEntry = {
66
71
  category?: string;
67
72
  subcategory?: string;
68
73
  format?: string;
74
+ assetSpec?: string;
69
75
  remix?: string;
70
76
  username?: string;
71
77
  files?: Record<string, string>;
72
78
  visibility?: string;
79
+ tags?: string[];
73
80
  shopListed?: boolean;
74
81
  shopPriceCredits?: number;
75
82
  relations?: Array<{
@@ -77,6 +84,21 @@ export type AssetCatalogueEntry = {
77
84
  to: string;
78
85
  }>;
79
86
  };
87
+ export type AssetSpecCatalogueEntry = {
88
+ name: string;
89
+ version?: string;
90
+ displayName?: string;
91
+ description?: string;
92
+ symbol?: string;
93
+ contract?: string;
94
+ documentation?: string;
95
+ visibility?: string;
96
+ validationKind?: string;
97
+ status?: string;
98
+ releaseNotes?: string;
99
+ successorVersion?: string;
100
+ username?: string;
101
+ };
80
102
  export type EmbeddedAssetCatalogueEntry = {
81
103
  name: string;
82
104
  category?: string;
@@ -99,6 +121,7 @@ export type AssetPackCatalogueEntry = {
99
121
  visibility?: string;
100
122
  releaseNotes?: string;
101
123
  listing?: AppListingConfig;
124
+ tags?: string[];
102
125
  relations?: Array<{
103
126
  type: string;
104
127
  to: string;
@@ -147,6 +170,7 @@ export type AppTask = {
147
170
  version?: string;
148
171
  releaseNotes?: string;
149
172
  versionVisibility?: AppVersionVisibility;
173
+ tags: string[];
150
174
  hostingMode?: AppHostingMode;
151
175
  authMode?: AppAuthMode;
152
176
  controllerMode?: AppControllerMode;
@@ -158,6 +182,7 @@ export type AppTask = {
158
182
  leaderboards: ResolvedAppLeaderboardDefinition[];
159
183
  remix?: string;
160
184
  embeddedAssets: EmbeddedAssetTask[];
185
+ assetSpecSupport: AssetSpecSupportDeclaration[];
161
186
  uses: {
162
187
  assets: string[];
163
188
  packs: string[];
@@ -174,12 +199,14 @@ export type AssetTask = {
174
199
  name: string;
175
200
  displayName?: string;
176
201
  cataloguePath: string;
177
- category: string;
178
- subcategory: string;
202
+ category?: string;
203
+ subcategory?: string | null;
204
+ assetSpec?: string;
179
205
  username?: string;
180
206
  remix?: string;
181
207
  format?: string;
182
208
  visibility?: string;
209
+ tags: string[];
183
210
  shopListed?: boolean;
184
211
  shopPriceCredits?: number;
185
212
  files: Record<string, string>;
@@ -189,6 +216,26 @@ export type AssetTask = {
189
216
  to: string;
190
217
  }>;
191
218
  };
219
+ export type AssetSpecTask = {
220
+ kind: 'asset-spec';
221
+ name: string;
222
+ version: string;
223
+ displayName: string;
224
+ description?: string;
225
+ cataloguePath: string;
226
+ catalogueAbsolutePath: string;
227
+ catalogueDir: string;
228
+ username?: string;
229
+ visibility?: 'PUBLIC' | 'PRIVATE';
230
+ validationKind: AssetSpecValidationKind;
231
+ status: AssetSpecStatus;
232
+ releaseNotes?: string;
233
+ successorVersion?: string;
234
+ symbolPath: string;
235
+ contractPath: string;
236
+ documentationPath?: string;
237
+ contract: AssetSpecContract;
238
+ };
192
239
  export type EmbeddedAssetTask = {
193
240
  kind: 'embedded-asset';
194
241
  appName: string;
@@ -212,6 +259,7 @@ export type AssetPackTask = {
212
259
  username?: string;
213
260
  remix?: string;
214
261
  assets: string[];
262
+ tags: string[];
215
263
  hostingMode?: AppHostingMode;
216
264
  externalUrl?: string;
217
265
  downloadUrl?: string;
@@ -229,12 +277,17 @@ export type CatalogueLoadOptions = {
229
277
  };
230
278
  export type CatalogueLoadResult = {
231
279
  apps: AppTask[];
280
+ assetSpecs: AssetSpecTask[];
232
281
  assets: AssetTask[];
233
282
  embeddedAssets: EmbeddedAssetTask[];
234
283
  assetPacks: AssetPackTask[];
235
284
  warnings: string[];
236
285
  errors: string[];
237
286
  };
287
+ export type CatalogueTagGroupsLoadResult = {
288
+ groups: CatalogueTagGroupDefinition[];
289
+ errors: string[];
290
+ };
238
291
  export type SurfaceTargetsMap = {
239
292
  desktop: boolean;
240
293
  mobileLandscape: boolean;
@@ -274,6 +327,7 @@ export type WorkspaceAppLookup = {
274
327
  appName?: string | null;
275
328
  };
276
329
  export declare function validateAppMetadata(entry: AppCatalogueEntry): ValidatedAppMetadata;
330
+ export declare function resolveCatalogueTagGroups(rootDir: string): CatalogueTagGroupsLoadResult;
277
331
  export declare function resolveCatalogueEntries(rootDir: string, options?: CatalogueLoadOptions): CatalogueLoadResult;
278
332
  export declare function resolveWorkspaceApp(options: WorkspaceAppLookup): WorkspaceAppResolution;
279
333
  export declare function formatMetadataWarnings(warnings: string[]): Set<string>;