@onexapis/cli 1.1.50 → 1.1.52

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/dist/cli.js CHANGED
@@ -4668,6 +4668,18 @@ var MIME_MAP = {
4668
4668
  ".json": "application/json"
4669
4669
  };
4670
4670
  var HASH_LEN = 8;
4671
+ var VIDEO_EXTENSIONS = [
4672
+ ".mp4",
4673
+ ".webm",
4674
+ ".ogg",
4675
+ ".mov",
4676
+ ".avi",
4677
+ ".mkv"
4678
+ ];
4679
+ function isVideoAsset(filePath) {
4680
+ const lower = filePath.toLowerCase();
4681
+ return VIDEO_EXTENSIONS.some((ext) => lower.endsWith(ext));
4682
+ }
4671
4683
  function mimeFor(filename) {
4672
4684
  const ext = path9__default.default.extname(filename).toLowerCase();
4673
4685
  return MIME_MAP[ext] || "application/octet-stream";
@@ -4869,8 +4881,30 @@ Or use the --bump flag:
4869
4881
  );
4870
4882
  process.exit(1);
4871
4883
  }
4884
+ const videoAssets = assetEntries.filter((a) => isVideoAsset(a.originalPath));
4885
+ const regularAssets = assetEntries.filter(
4886
+ (a) => !isVideoAsset(a.originalPath)
4887
+ );
4888
+ const videoUrls = {};
4889
+ if (videoAssets.length > 0) {
4890
+ logger.startSpinner(`Uploading ${videoAssets.length} video(s)...`);
4891
+ try {
4892
+ for (const video of videoAssets) {
4893
+ const url = await uploadVideoMultipart(apiUrl, themeId, video);
4894
+ videoUrls[video.originalPath] = url;
4895
+ }
4896
+ logger.stopSpinner(true, `Uploaded ${videoAssets.length} video(s)`);
4897
+ } catch (error) {
4898
+ logger.stopSpinner(false, "Video upload failed");
4899
+ logger.error(error instanceof Error ? error.message : "Upload error");
4900
+ process.exit(1);
4901
+ }
4902
+ }
4872
4903
  try {
4873
- const assetMap = buildAssetMap(assetEntries);
4904
+ const assetMap = buildAssetMap(regularAssets);
4905
+ for (const [originalPath, url] of Object.entries(videoUrls)) {
4906
+ assetMap[originalPath] = url;
4907
+ }
4874
4908
  const assetMapPath = path9__default.default.join(distDir, "asset-map.json");
4875
4909
  await fs__default.default.writeFile(assetMapPath, JSON.stringify(assetMap, null, 2));
4876
4910
  } catch (error) {
@@ -4891,7 +4925,7 @@ Or use the --bump flag:
4891
4925
  method: "POST",
4892
4926
  body: JSON.stringify({
4893
4927
  version: version2,
4894
- assets: assetEntries.map((a) => ({
4928
+ assets: regularAssets.map((a) => ({
4895
4929
  path: a.hashedPath,
4896
4930
  hash: a.hash,
4897
4931
  size: a.size,
@@ -4925,7 +4959,7 @@ Or use the --bump flag:
4925
4959
  if (assetUploads.length > 0) {
4926
4960
  logger.startSpinner(`Uploading ${assetUploads.length} asset(s) to S3...`);
4927
4961
  const CONCURRENCY = 8;
4928
- const byHashedPath = new Map(assetEntries.map((a) => [a.hashedPath, a]));
4962
+ const byHashedPath = new Map(regularAssets.map((a) => [a.hashedPath, a]));
4929
4963
  const queue = [...assetUploads];
4930
4964
  let uploaded = 0;
4931
4965
  let failed = 0;
@@ -5081,6 +5115,82 @@ Or use the --bump flag:
5081
5115
  logger.newLine();
5082
5116
  logger.success(`\u2713 Theme "${themeId}" v${version2} published!`);
5083
5117
  }
5118
+ async function uploadVideoMultipart(apiUrl, themeId, video) {
5119
+ const fileName = path9__default.default.basename(video.originalPath);
5120
+ const initRes = await authenticatedFetch(
5121
+ `${apiUrl}/media/videos/multipart/init`,
5122
+ {
5123
+ method: "POST",
5124
+ body: JSON.stringify({
5125
+ file_name: fileName,
5126
+ content_type: video.contentType,
5127
+ file_size: video.size,
5128
+ prefix: `themes/${themeId}/assets`
5129
+ })
5130
+ }
5131
+ );
5132
+ const initData = await initRes.json();
5133
+ const initBody = initData.statusCode ? initData.body : initData;
5134
+ if (!initRes.ok || !initBody.upload_id) {
5135
+ throw new Error(
5136
+ `Init multipart failed for ${fileName}: ${initBody.error || initRes.status}`
5137
+ );
5138
+ }
5139
+ const { upload_id, file_key, chunk_size, chunk_urls } = initBody;
5140
+ const fileBuffer = await fs__default.default.promises.readFile(video.absPath);
5141
+ const CHUNK_CONCURRENCY = 4;
5142
+ const queue = [...chunk_urls];
5143
+ const parts = [];
5144
+ async function chunkWorker() {
5145
+ while (queue.length > 0) {
5146
+ const chunk = queue.shift();
5147
+ if (!chunk) break;
5148
+ const start = (chunk.part_number - 1) * chunk_size;
5149
+ const end = Math.min(start + chunk_size, fileBuffer.length);
5150
+ const body = fileBuffer.subarray(start, end);
5151
+ const res = await fetch(chunk.upload_url, { method: "PUT", body });
5152
+ if (!res.ok) {
5153
+ throw new Error(
5154
+ `Chunk ${chunk.part_number} upload failed: HTTP ${res.status}`
5155
+ );
5156
+ }
5157
+ const etag = res.headers.get("etag") || res.headers.get("ETag");
5158
+ if (!etag) {
5159
+ throw new Error(`Chunk ${chunk.part_number}: missing ETag header`);
5160
+ }
5161
+ parts.push({ part_number: chunk.part_number, etag });
5162
+ }
5163
+ }
5164
+ await Promise.all(
5165
+ Array.from(
5166
+ { length: Math.min(CHUNK_CONCURRENCY, chunk_urls.length) },
5167
+ () => chunkWorker()
5168
+ )
5169
+ );
5170
+ parts.sort((a, b) => a.part_number - b.part_number);
5171
+ const completeRes = await authenticatedFetch(
5172
+ `${apiUrl}/media/videos/multipart/complete`,
5173
+ {
5174
+ method: "POST",
5175
+ body: JSON.stringify({ upload_id, file_key, parts })
5176
+ }
5177
+ );
5178
+ const completeData = await completeRes.json();
5179
+ const completeBody = completeData.statusCode ? completeData.body : completeData;
5180
+ if (!completeRes.ok || !completeBody.url) {
5181
+ try {
5182
+ await authenticatedFetch(`${apiUrl}/media/videos/multipart/abort`, {
5183
+ method: "POST",
5184
+ body: JSON.stringify({ upload_id, file_key })
5185
+ });
5186
+ } catch {
5187
+ }
5188
+ throw new Error(
5189
+ `Complete multipart failed for ${fileName}: ${completeBody.error || completeRes.status}`
5190
+ );
5191
+ }
5192
+ return completeBody.url;
5193
+ }
5084
5194
  async function createZip(sourceDir, outputPath, exclude) {
5085
5195
  const archiver2 = (await import('archiver')).default;
5086
5196
  const { createWriteStream } = await import('fs');