@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.js +113 -3
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +113 -3
- package/dist/cli.mjs.map +1 -1
- package/package.json +1 -1
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(
|
|
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:
|
|
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(
|
|
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');
|