deepspace 0.4.0 → 0.4.2

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
@@ -187,10 +187,12 @@ var DEV_VARS_DIVIDER = "# --- not managed by the SDK; preserved across dev/test
187
187
  function extractCustomDevVars(content) {
188
188
  const lines = content.split("\n");
189
189
  const out = [];
190
+ let belowDivider = false;
190
191
  let i = 0;
191
192
  while (i < lines.length) {
192
193
  const line = lines[i];
193
194
  if (line.trim() === DEV_VARS_DIVIDER.trim()) {
195
+ belowDivider = true;
194
196
  i++;
195
197
  continue;
196
198
  }
@@ -215,7 +217,7 @@ function extractCustomDevVars(content) {
215
217
  } else {
216
218
  i++;
217
219
  }
218
- if (!SDK_MANAGED_KEYS.has(key)) {
220
+ if (belowDivider || !SDK_MANAGED_KEYS.has(key)) {
219
221
  out.push(...block);
220
222
  }
221
223
  }
@@ -1346,6 +1348,7 @@ var test_accounts_default = defineCommand7({
1346
1348
  // src/cli/commands/deploy.ts
1347
1349
  import { defineCommand as defineCommand8 } from "citty";
1348
1350
  import { execSync as execSync2 } from "child_process";
1351
+ import { randomUUID } from "crypto";
1349
1352
  import { readFileSync as readFileSync6, readdirSync, existsSync as existsSync9 } from "fs";
1350
1353
  import { parse as parseToml2 } from "smol-toml";
1351
1354
  import { join as join9, resolve as resolve5, dirname } from "path";
@@ -1599,6 +1602,59 @@ function formatDeployWorkerError(status2, error) {
1599
1602
 
1600
1603
  Underlying error: ${detail}`;
1601
1604
  }
1605
+ var MAX_GROUP_BYTES = 40 * 1024 * 1024;
1606
+ var GROUP_BYTES = (() => {
1607
+ const raw = Number(process.env.DEEPSPACE_DEPLOY_GROUP_BYTES);
1608
+ const v = Number.isInteger(raw) && raw > 0 ? raw : 3 * 1024 * 1024;
1609
+ return Math.min(v, MAX_GROUP_BYTES);
1610
+ })();
1611
+ function packAssetGroups(assets, maxBytes) {
1612
+ const groups = [];
1613
+ let current = [];
1614
+ let currentBytes = 2;
1615
+ for (const a of assets) {
1616
+ const entryBytes = Buffer.byteLength(JSON.stringify(a), "utf-8") + 1;
1617
+ if (current.length > 0 && currentBytes + entryBytes > maxBytes) {
1618
+ groups.push(current);
1619
+ current = [];
1620
+ currentBytes = 2;
1621
+ }
1622
+ current.push(a);
1623
+ currentBytes += entryBytes;
1624
+ }
1625
+ if (current.length > 0) groups.push(current);
1626
+ return groups;
1627
+ }
1628
+ async function postWithRetry(url, makeInit, { attempts = 4, retryServerErrors = true } = {}) {
1629
+ let lastRes;
1630
+ let lastErr;
1631
+ for (let attempt = 1; attempt <= attempts; attempt++) {
1632
+ try {
1633
+ const res = await fetch(url, makeInit());
1634
+ const isTransient = retryServerErrors && (res.status >= 500 || res.status === 408 || res.status === 429);
1635
+ if (!isTransient) {
1636
+ return res;
1637
+ }
1638
+ lastRes = res;
1639
+ } catch (err) {
1640
+ lastErr = err;
1641
+ }
1642
+ if (attempt < attempts) {
1643
+ await new Promise((r) => setTimeout(r, Math.min(8e3, 500 * 2 ** (attempt - 1))));
1644
+ }
1645
+ }
1646
+ if (lastRes) return lastRes;
1647
+ throw lastErr instanceof Error ? lastErr : new Error(String(lastErr));
1648
+ }
1649
+ async function abortStagedUpload(appName, uploadId, totalGroups, token2) {
1650
+ try {
1651
+ await fetch(
1652
+ `${DEPLOY_URL}/api/deploy/${appName}/assets?uploadId=${uploadId}&totalGroups=${totalGroups}`,
1653
+ { method: "DELETE", headers: { Authorization: `Bearer ${token2}` } }
1654
+ );
1655
+ } catch {
1656
+ }
1657
+ }
1602
1658
  var deploy_default = defineCommand8({
1603
1659
  meta: {
1604
1660
  name: "deploy",
@@ -1647,7 +1703,8 @@ var deploy_default = defineCommand8({
1647
1703
  execSync2("npx vite build", { cwd: appDir, stdio: "pipe" });
1648
1704
  } catch (err) {
1649
1705
  s.stop("Build failed");
1650
- console.error(err.stderr?.toString() ?? err.message);
1706
+ const detail = [err.stdout?.toString(), err.stderr?.toString()].filter(Boolean).join("\n").trim();
1707
+ console.error(detail || err.message);
1651
1708
  process.exit(1);
1652
1709
  }
1653
1710
  s.stop("Built");
@@ -1740,30 +1797,98 @@ var deploy_default = defineCommand8({
1740
1797
  p4.log.info(`App secrets (from .dev.vars): ${userSecretNames.join(", ")}`);
1741
1798
  }
1742
1799
  s.start(`Deploying to ${appName}.app.space...`);
1743
- const form = new FormData();
1744
- form.append("worker", new Blob([workerJs], { type: "application/javascript" }), "worker.js");
1745
- form.append("assets", JSON.stringify(assets));
1746
- if (doManifest) {
1747
- form.append("doManifest", JSON.stringify(doManifest));
1748
- }
1749
- if (customBindings.length) {
1750
- form.append("bindingManifest", JSON.stringify(customBindings));
1751
- }
1752
- if (userSecretNames.length) {
1753
- form.append("userSecrets", JSON.stringify(userSecrets));
1754
- }
1755
1800
  const extraRoutes = extractRunWorkerFirst(wranglerConfig);
1756
1801
  if (extraRoutes.length) {
1757
- form.append("extraRunWorkerFirst", JSON.stringify(extraRoutes));
1758
1802
  p4.log.info(`Custom worker-first routes: ${extraRoutes.join(", ")}`);
1759
1803
  }
1760
- const res = await fetch(`${DEPLOY_URL}/api/deploy/${appName}`, {
1761
- method: "POST",
1762
- headers: { Authorization: `Bearer ${token2}` },
1763
- body: form
1764
- });
1804
+ const assetGroups = packAssetGroups(assets, GROUP_BYTES);
1805
+ let inlineAssets = null;
1806
+ let uploadId = null;
1807
+ let totalGroups = 0;
1808
+ if (assetGroups.length <= 1) {
1809
+ inlineAssets = JSON.stringify(assets);
1810
+ } else {
1811
+ uploadId = randomUUID();
1812
+ totalGroups = assetGroups.length;
1813
+ try {
1814
+ for (let i = 0; i < totalGroups; i++) {
1815
+ const groupJson = JSON.stringify(assetGroups[i]);
1816
+ s.message(`Uploading assets \u2014 group ${i + 1}/${totalGroups}...`);
1817
+ const groupRes = await postWithRetry(
1818
+ `${DEPLOY_URL}/api/deploy/${appName}/assets?uploadId=${uploadId}&groupIndex=${i}&totalGroups=${totalGroups}`,
1819
+ () => ({
1820
+ method: "POST",
1821
+ headers: {
1822
+ Authorization: `Bearer ${token2}`,
1823
+ "Content-Type": "application/json"
1824
+ },
1825
+ body: groupJson
1826
+ })
1827
+ );
1828
+ if (!groupRes.ok) {
1829
+ const errBody = await groupRes.json().catch(() => ({}));
1830
+ await abortStagedUpload(appName, uploadId, totalGroups, token2);
1831
+ s.stop("Deploy failed");
1832
+ p4.cancel(
1833
+ `Asset upload failed on group ${i + 1}/${totalGroups}: ${errBody.error ?? `HTTP ${groupRes.status}`}`
1834
+ );
1835
+ process.exit(1);
1836
+ }
1837
+ }
1838
+ } catch (err) {
1839
+ await abortStagedUpload(appName, uploadId, totalGroups, token2);
1840
+ s.stop("Deploy failed");
1841
+ p4.cancel(`Asset upload failed (network): ${err?.message ?? err}`);
1842
+ process.exit(1);
1843
+ }
1844
+ s.message(`Deploying to ${appName}.app.space...`);
1845
+ }
1846
+ const makeForm = () => {
1847
+ const form = new FormData();
1848
+ form.append("worker", new Blob([workerJs], { type: "application/javascript" }), "worker.js");
1849
+ if (inlineAssets !== null) {
1850
+ form.append("assets", inlineAssets);
1851
+ } else {
1852
+ form.append("uploadId", uploadId);
1853
+ form.append("totalGroups", String(totalGroups));
1854
+ }
1855
+ if (doManifest) {
1856
+ form.append("doManifest", JSON.stringify(doManifest));
1857
+ }
1858
+ if (customBindings.length) {
1859
+ form.append("bindingManifest", JSON.stringify(customBindings));
1860
+ }
1861
+ if (userSecretNames.length) {
1862
+ form.append("userSecrets", JSON.stringify(userSecrets));
1863
+ }
1864
+ if (extraRoutes.length) {
1865
+ form.append("extraRunWorkerFirst", JSON.stringify(extraRoutes));
1866
+ }
1867
+ return form;
1868
+ };
1869
+ let res;
1870
+ try {
1871
+ res = await postWithRetry(
1872
+ `${DEPLOY_URL}/api/deploy/${appName}`,
1873
+ () => ({
1874
+ method: "POST",
1875
+ headers: { Authorization: `Bearer ${token2}` },
1876
+ body: makeForm()
1877
+ }),
1878
+ // Commit runs a real deploy — don't retry its 5xx (could double-deploy);
1879
+ // only a thrown fetch (dropped connection) is retried, and the staged
1880
+ // groups survive on the worker until this request succeeds.
1881
+ { retryServerErrors: false }
1882
+ );
1883
+ } catch (err) {
1884
+ if (uploadId) await abortStagedUpload(appName, uploadId, totalGroups, token2);
1885
+ s.stop("Deploy failed");
1886
+ p4.cancel(`Deploy request failed: ${err?.message ?? err}`);
1887
+ process.exit(1);
1888
+ }
1765
1889
  const body = await res.json().catch(() => ({}));
1766
1890
  if (!res.ok || !body.success) {
1891
+ if (uploadId) await abortStagedUpload(appName, uploadId, totalGroups, token2);
1767
1892
  s.stop("Deploy failed");
1768
1893
  p4.cancel(formatDeployWorkerError(res.status, body.error));
1769
1894
  process.exit(1);