@onexapis/cli 1.1.51 → 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.mjs CHANGED
@@ -4625,6 +4625,18 @@ var MIME_MAP = {
4625
4625
  ".json": "application/json"
4626
4626
  };
4627
4627
  var HASH_LEN = 8;
4628
+ var VIDEO_EXTENSIONS = [
4629
+ ".mp4",
4630
+ ".webm",
4631
+ ".ogg",
4632
+ ".mov",
4633
+ ".avi",
4634
+ ".mkv"
4635
+ ];
4636
+ function isVideoAsset(filePath) {
4637
+ const lower = filePath.toLowerCase();
4638
+ return VIDEO_EXTENSIONS.some((ext) => lower.endsWith(ext));
4639
+ }
4628
4640
  function mimeFor(filename) {
4629
4641
  const ext = path9.extname(filename).toLowerCase();
4630
4642
  return MIME_MAP[ext] || "application/octet-stream";
@@ -4826,8 +4838,30 @@ Or use the --bump flag:
4826
4838
  );
4827
4839
  process.exit(1);
4828
4840
  }
4841
+ const videoAssets = assetEntries.filter((a) => isVideoAsset(a.originalPath));
4842
+ const regularAssets = assetEntries.filter(
4843
+ (a) => !isVideoAsset(a.originalPath)
4844
+ );
4845
+ const videoUrls = {};
4846
+ if (videoAssets.length > 0) {
4847
+ logger.startSpinner(`Uploading ${videoAssets.length} video(s)...`);
4848
+ try {
4849
+ for (const video of videoAssets) {
4850
+ const url = await uploadVideoMultipart(apiUrl, themeId, video);
4851
+ videoUrls[video.originalPath] = url;
4852
+ }
4853
+ logger.stopSpinner(true, `Uploaded ${videoAssets.length} video(s)`);
4854
+ } catch (error) {
4855
+ logger.stopSpinner(false, "Video upload failed");
4856
+ logger.error(error instanceof Error ? error.message : "Upload error");
4857
+ process.exit(1);
4858
+ }
4859
+ }
4829
4860
  try {
4830
- const assetMap = buildAssetMap(assetEntries);
4861
+ const assetMap = buildAssetMap(regularAssets);
4862
+ for (const [originalPath, url] of Object.entries(videoUrls)) {
4863
+ assetMap[originalPath] = url;
4864
+ }
4831
4865
  const assetMapPath = path9.join(distDir, "asset-map.json");
4832
4866
  await fs.writeFile(assetMapPath, JSON.stringify(assetMap, null, 2));
4833
4867
  } catch (error) {
@@ -4848,7 +4882,7 @@ Or use the --bump flag:
4848
4882
  method: "POST",
4849
4883
  body: JSON.stringify({
4850
4884
  version: version2,
4851
- assets: assetEntries.map((a) => ({
4885
+ assets: regularAssets.map((a) => ({
4852
4886
  path: a.hashedPath,
4853
4887
  hash: a.hash,
4854
4888
  size: a.size,
@@ -4882,7 +4916,7 @@ Or use the --bump flag:
4882
4916
  if (assetUploads.length > 0) {
4883
4917
  logger.startSpinner(`Uploading ${assetUploads.length} asset(s) to S3...`);
4884
4918
  const CONCURRENCY = 8;
4885
- const byHashedPath = new Map(assetEntries.map((a) => [a.hashedPath, a]));
4919
+ const byHashedPath = new Map(regularAssets.map((a) => [a.hashedPath, a]));
4886
4920
  const queue = [...assetUploads];
4887
4921
  let uploaded = 0;
4888
4922
  let failed = 0;
@@ -5038,6 +5072,82 @@ Or use the --bump flag:
5038
5072
  logger.newLine();
5039
5073
  logger.success(`\u2713 Theme "${themeId}" v${version2} published!`);
5040
5074
  }
5075
+ async function uploadVideoMultipart(apiUrl, themeId, video) {
5076
+ const fileName = path9.basename(video.originalPath);
5077
+ const initRes = await authenticatedFetch(
5078
+ `${apiUrl}/media/videos/multipart/init`,
5079
+ {
5080
+ method: "POST",
5081
+ body: JSON.stringify({
5082
+ file_name: fileName,
5083
+ content_type: video.contentType,
5084
+ file_size: video.size,
5085
+ prefix: `themes/${themeId}/assets`
5086
+ })
5087
+ }
5088
+ );
5089
+ const initData = await initRes.json();
5090
+ const initBody = initData.statusCode ? initData.body : initData;
5091
+ if (!initRes.ok || !initBody.upload_id) {
5092
+ throw new Error(
5093
+ `Init multipart failed for ${fileName}: ${initBody.error || initRes.status}`
5094
+ );
5095
+ }
5096
+ const { upload_id, file_key, chunk_size, chunk_urls } = initBody;
5097
+ const fileBuffer = await fs.promises.readFile(video.absPath);
5098
+ const CHUNK_CONCURRENCY = 4;
5099
+ const queue = [...chunk_urls];
5100
+ const parts = [];
5101
+ async function chunkWorker() {
5102
+ while (queue.length > 0) {
5103
+ const chunk = queue.shift();
5104
+ if (!chunk) break;
5105
+ const start = (chunk.part_number - 1) * chunk_size;
5106
+ const end = Math.min(start + chunk_size, fileBuffer.length);
5107
+ const body = fileBuffer.subarray(start, end);
5108
+ const res = await fetch(chunk.upload_url, { method: "PUT", body });
5109
+ if (!res.ok) {
5110
+ throw new Error(
5111
+ `Chunk ${chunk.part_number} upload failed: HTTP ${res.status}`
5112
+ );
5113
+ }
5114
+ const etag = res.headers.get("etag") || res.headers.get("ETag");
5115
+ if (!etag) {
5116
+ throw new Error(`Chunk ${chunk.part_number}: missing ETag header`);
5117
+ }
5118
+ parts.push({ part_number: chunk.part_number, etag });
5119
+ }
5120
+ }
5121
+ await Promise.all(
5122
+ Array.from(
5123
+ { length: Math.min(CHUNK_CONCURRENCY, chunk_urls.length) },
5124
+ () => chunkWorker()
5125
+ )
5126
+ );
5127
+ parts.sort((a, b) => a.part_number - b.part_number);
5128
+ const completeRes = await authenticatedFetch(
5129
+ `${apiUrl}/media/videos/multipart/complete`,
5130
+ {
5131
+ method: "POST",
5132
+ body: JSON.stringify({ upload_id, file_key, parts })
5133
+ }
5134
+ );
5135
+ const completeData = await completeRes.json();
5136
+ const completeBody = completeData.statusCode ? completeData.body : completeData;
5137
+ if (!completeRes.ok || !completeBody.url) {
5138
+ try {
5139
+ await authenticatedFetch(`${apiUrl}/media/videos/multipart/abort`, {
5140
+ method: "POST",
5141
+ body: JSON.stringify({ upload_id, file_key })
5142
+ });
5143
+ } catch {
5144
+ }
5145
+ throw new Error(
5146
+ `Complete multipart failed for ${fileName}: ${completeBody.error || completeRes.status}`
5147
+ );
5148
+ }
5149
+ return completeBody.url;
5150
+ }
5041
5151
  async function createZip(sourceDir, outputPath, exclude) {
5042
5152
  const archiver2 = (await import('archiver')).default;
5043
5153
  const { createWriteStream } = await import('fs');