@glash/cli 0.2.33 → 0.2.34

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/bin/glash.mjs CHANGED
@@ -102,6 +102,7 @@ ${color.bold('FLAGS')}
102
102
  --asset-threshold <size> Auto-offload threshold (default: 100mb)
103
103
  --source-upload <mode> Source upload: auto, direct, chunked
104
104
  --source-threshold <size> Auto-chunk source threshold (default: 90mb)
105
+ --source-concurrency <n> Parallel chunk uploads for large source bundles (default: 4)
105
106
  --framework <name> Override framework detection (glashjs, next, vite, etc.)
106
107
  --help, -h Show this help
107
108
  --version, -v Show CLI version
@@ -218,6 +218,7 @@ export async function deploy({ flags }) {
218
218
  gitBranch: gitBranch || '',
219
219
  target,
220
220
  framework: projectProfile.framework,
221
+ flags,
221
222
  };
222
223
 
223
224
  if (shouldUseChunkedSourceUpload(zipStat.size, flags)) {
@@ -519,10 +520,19 @@ async function uploadSourceBundleStaged(projectId, zipPath, zipStat, meta, indic
519
520
 
520
521
  const partSize = sourceUploadPartSize(prepared.partSize);
521
522
  const blob = await openAsBlob(zipPath, { type: 'application/zip' });
523
+ const chunks = [];
524
+ for (let offset = 0, partNumber = 1; offset < sizeBytes; offset += partSize, partNumber += 1) {
525
+ chunks.push({
526
+ partNumber,
527
+ offset,
528
+ end: Math.min(offset + partSize, sizeBytes),
529
+ });
530
+ }
531
+
522
532
  const parts = [];
523
- let partNumber = 1;
524
- for (let offset = 0; offset < sizeBytes; offset += partSize) {
525
- const end = Math.min(offset + partSize, sizeBytes);
533
+ let uploadedBytes = 0;
534
+ const concurrency = sourceUploadConcurrency(meta.flags);
535
+ await mapWithConcurrency(chunks, concurrency, async ({ partNumber, offset, end }) => {
526
536
  const partUrl = withQueryParam(prepared.uploadPartUrl, 'partNumber', String(partNumber));
527
537
  const res = await fetchWithRetries(partUrl, {
528
538
  method: 'PUT',
@@ -540,10 +550,11 @@ async function uploadSourceBundleStaged(projectId, zipPath, zipStat, meta, indic
540
550
  throw new Error(`Chunked source upload part ${partNumber} did not return a multipart ETag`);
541
551
  }
542
552
  parts.push({ partNumber: Number(part.partNumber), etag: String(part.etag) });
543
- const percent = 28 + Math.round((end / sizeBytes) * 14);
553
+ uploadedBytes += (end - offset);
554
+ const percent = 28 + Math.round((uploadedBytes / sizeBytes) * 14);
544
555
  indicator?.setProgress?.(percent, 'uploading');
545
- partNumber += 1;
546
- }
556
+ });
557
+ parts.sort((a, b) => a.partNumber - b.partNumber);
547
558
 
548
559
  const completed = await fetchWithRetries(prepared.completeUrl, {
549
560
  method: 'POST',
@@ -575,6 +586,19 @@ async function uploadSourceBundleStaged(projectId, zipPath, zipStat, meta, indic
575
586
  return { deployment, partsUploaded: parts.length, sha256 };
576
587
  }
577
588
 
589
+ async function mapWithConcurrency(items, concurrency, worker) {
590
+ const limit = Math.max(1, Math.min(Number(concurrency) || 1, items.length || 1));
591
+ let next = 0;
592
+ const runners = Array.from({ length: limit }, async () => {
593
+ while (next < items.length) {
594
+ const item = items[next];
595
+ next += 1;
596
+ await worker(item);
597
+ }
598
+ });
599
+ await Promise.all(runners);
600
+ }
601
+
578
602
  function sourceUploadFailure(error) {
579
603
  const message = error?.message || String(error);
580
604
  if (error?.status === 404 && /source-upload|Cannot POST/i.test(message)) {
@@ -629,7 +653,7 @@ function uploadTimeoutMsFor(bytes) {
629
653
  function publicAssetPartSize(value) {
630
654
  const parsed = Number(value);
631
655
  if (Number.isFinite(parsed) && parsed >= 5 * 1024 * 1024) return Math.round(parsed);
632
- return 16 * 1024 * 1024;
656
+ return 80 * 1024 * 1024;
633
657
  }
634
658
 
635
659
  function assetOffloadFailure(error, assetSummary) {
@@ -678,7 +702,14 @@ function sourceUploadThresholdBytes(flags) {
678
702
  function sourceUploadPartSize(value) {
679
703
  const parsed = Number(value);
680
704
  if (Number.isFinite(parsed) && parsed >= 5 * 1024 * 1024) return Math.round(parsed);
681
- return 16 * 1024 * 1024;
705
+ return 80 * 1024 * 1024;
706
+ }
707
+
708
+ function sourceUploadConcurrency(flags = {}) {
709
+ const raw = flags['source-concurrency'] ?? process.env.GLASH_SOURCE_UPLOAD_CONCURRENCY ?? '4';
710
+ const parsed = Number(raw);
711
+ if (!Number.isFinite(parsed) || parsed < 1) return 4;
712
+ return Math.max(1, Math.min(8, Math.floor(parsed)));
682
713
  }
683
714
 
684
715
  async function detectProjectProfile(cwd, flags = {}) {
@@ -686,9 +717,12 @@ async function detectProjectProfile(cwd, flags = {}) {
686
717
  if (forced) return { framework: forced, label: forced };
687
718
  const pkg = await readPackageJson(cwd);
688
719
  const deps = { ...(pkg?.dependencies ?? {}), ...(pkg?.devDependencies ?? {}) };
720
+ const scripts = pkg?.scripts ?? {};
721
+ const hasNextConfig = await existsAny(cwd, ['next.config.js', 'next.config.mjs', 'next.config.cjs', 'next.config.ts']);
689
722
  const hasGlashConfig = await existsAny(cwd, ['glash.config.mjs', 'glash.config.js', 'glash.config.cjs']);
690
- if (deps.glashjs || hasGlashConfig) return { framework: 'glashjs', label: 'glashjs' };
691
- if (deps.next) return { framework: 'next', label: 'Next.js' };
723
+ const scriptText = Object.values(scripts).join('\n');
724
+ if (deps.next || hasNextConfig || /\bnext\s+(build|dev|start)\b/i.test(scriptText)) return { framework: 'next', label: 'Next.js' };
725
+ if (deps.glashjs || hasGlashConfig || /\bglash\s+(build|serve|dev)\b/i.test(scriptText)) return { framework: 'glashjs', label: 'glashjs' };
692
726
  if (deps.nuxt || deps.nuxt3) return { framework: 'nuxt', label: 'Nuxt' };
693
727
  if (deps.vite) return { framework: 'vite', label: 'Vite' };
694
728
  if (deps['@remix-run/dev'] || deps.remix) return { framework: 'remix', label: 'Remix' };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glash/cli",
3
- "version": "0.2.33",
3
+ "version": "0.2.34",
4
4
  "description": "glashDB command-line interface — deploy projects, pull source + env, manage env vars.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://glashdb.com",