run402 4.129.1 → 4.130.1

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.
Files changed (36) hide show
  1. package/lib/assets.mjs +36 -18
  2. package/lib/auth.mjs +48 -1
  3. package/lib/command-manifest.mjs +2 -1
  4. package/lib/deploy-v2.mjs +15 -8
  5. package/lib/dev.mjs +28 -7
  6. package/lib/init-astro.mjs +2 -2
  7. package/package.json +1 -1
  8. package/sdk/dist/errors.d.ts +8 -0
  9. package/sdk/dist/errors.d.ts.map +1 -1
  10. package/sdk/dist/errors.js +16 -0
  11. package/sdk/dist/errors.js.map +1 -1
  12. package/sdk/dist/namespaces/assets.d.ts +7 -1
  13. package/sdk/dist/namespaces/assets.d.ts.map +1 -1
  14. package/sdk/dist/namespaces/assets.js +39 -1
  15. package/sdk/dist/namespaces/assets.js.map +1 -1
  16. package/sdk/dist/namespaces/assets.types.d.ts +37 -0
  17. package/sdk/dist/namespaces/assets.types.d.ts.map +1 -1
  18. package/sdk/dist/namespaces/assets.types.js +2 -0
  19. package/sdk/dist/namespaces/assets.types.js.map +1 -1
  20. package/sdk/dist/namespaces/auth.d.ts +20 -0
  21. package/sdk/dist/namespaces/auth.d.ts.map +1 -1
  22. package/sdk/dist/namespaces/auth.js +20 -0
  23. package/sdk/dist/namespaces/auth.js.map +1 -1
  24. package/sdk/dist/namespaces/deploy.d.ts.map +1 -1
  25. package/sdk/dist/namespaces/deploy.js +117 -18
  26. package/sdk/dist/namespaces/deploy.js.map +1 -1
  27. package/sdk/dist/node/paid-fetch.js +2 -2
  28. package/sdk/dist/node/paid-fetch.js.map +1 -1
  29. package/sdk/dist/node/vault-degraded-read.d.ts.map +1 -1
  30. package/sdk/dist/node/vault-degraded-read.js +4 -8
  31. package/sdk/dist/node/vault-degraded-read.js.map +1 -1
  32. package/sdk/dist/scoped.d.ts +4 -2
  33. package/sdk/dist/scoped.d.ts.map +1 -1
  34. package/sdk/dist/scoped.js +6 -0
  35. package/sdk/dist/scoped.js.map +1 -1
  36. package/vault-surface.json +1 -1
@@ -24,7 +24,7 @@ import { isGrantKeyCredentials } from "../grant-key-credentials.js";
24
24
  import { isKnownBinaryContent, isUtf8StringSource } from "../validation.js";
25
25
  import { assertCiDeployableSpec } from "./ci.js";
26
26
  import { ROUTE_PRICING_NETWORKS, ROUTE_HTTP_METHODS, isModernPlanMigrationDiff, normalizeDeployResolveRequest, } from "./deploy.types.js";
27
- import { ApiError, LocalError, NetworkError, PaymentRequired, Run402DeployError, Unauthorized, isRun402Error, isTransferFreezeError, } from "../errors.js";
27
+ import { ApiError, LocalError, PaymentRequired, Run402DeployError, Unauthorized, isNoResponseError, isRun402Error, isTransferFreezeError, } from "../errors.js";
28
28
  import { assertAssetMetadata, assertExifPolicy, } from "./assets-validation.js";
29
29
  import { withSiteRemovalSummaries } from "./site-removal-summary.js";
30
30
  // ─── Constants ───────────────────────────────────────────────────────────────
@@ -85,18 +85,22 @@ const STATIC_FUNCTION_LIMITS_BY_TIER = {
85
85
  prototype: {
86
86
  maxTimeoutSeconds: 10,
87
87
  maxMemoryMb: 128,
88
+ maxSsrMemoryMb: 128,
88
89
  maxScheduledFunctions: 1,
89
90
  minCronIntervalMinutes: 15,
90
91
  },
91
92
  hobby: {
92
93
  maxTimeoutSeconds: 30,
93
94
  maxMemoryMb: 256,
95
+ maxSsrMemoryMb: 256,
94
96
  maxScheduledFunctions: 3,
95
97
  minCronIntervalMinutes: 5,
96
98
  },
97
99
  team: {
98
100
  maxTimeoutSeconds: 60,
99
101
  maxMemoryMb: 512,
102
+ // An SSR-class function (the Astro `ssr` function) may use one vCPU.
103
+ maxSsrMemoryMb: 1769,
100
104
  maxScheduledFunctions: 10,
101
105
  minCronIntervalMinutes: 1,
102
106
  },
@@ -1338,7 +1342,7 @@ async function preflightTierFunctionLimits(client, spec, ciCredentials) {
1338
1342
  return;
1339
1343
  if (!hasFunctionTierPreflightInputs(spec.functions))
1340
1344
  return;
1341
- const limits = await readTierFunctionLimits(client);
1345
+ const limits = await readTierFunctionLimits(client, spec.project_id);
1342
1346
  if (!limits)
1343
1347
  return;
1344
1348
  const entries = collectFunctionPreflightEntries(spec.functions);
@@ -1353,12 +1357,19 @@ async function preflightTierFunctionLimits(client, spec, ciCredentials) {
1353
1357
  });
1354
1358
  }
1355
1359
  const memory = entry.fn.config?.memoryMb;
1360
+ // An SSR-class function has its own cap. With no class declared the
1361
+ // function keeps its stored one, which only the gateway knows.
1362
+ const memoryLimit = entry.fn.class === "ssr"
1363
+ ? limits.maxSsrMemoryMb ?? limits.maxMemoryMb
1364
+ : entry.fn.class === undefined
1365
+ ? largerLimit(limits.maxMemoryMb, limits.maxSsrMemoryMb)
1366
+ : limits.maxMemoryMb;
1356
1367
  if (memory !== undefined &&
1357
- limits.maxMemoryMb &&
1358
- memory > limits.maxMemoryMb.value) {
1359
- throw tierLimitError(`Function ${entry.name} memoryMb ${memory} exceeds the ${limits.tier} tier maximum of ${limits.maxMemoryMb.value}.`, `${entry.fieldPrefix}.config.memoryMb`, memory, limits, limits.maxMemoryMb, {
1360
- tier_max: limits.maxMemoryMb.value,
1361
- max_function_memory_mb: limits.maxMemoryMb.value,
1368
+ memoryLimit &&
1369
+ memory > memoryLimit.value) {
1370
+ throw tierLimitError(`Function ${entry.name} memoryMb ${memory} exceeds the ${limits.tier} tier maximum of ${memoryLimit.value}.`, `${entry.fieldPrefix}.config.memoryMb`, memory, limits, memoryLimit, {
1371
+ tier_max: memoryLimit.value,
1372
+ max_function_memory_mb: memoryLimit.value,
1362
1373
  });
1363
1374
  }
1364
1375
  for (const trigger of scheduleTriggersForFunction(entry.fn)) {
@@ -1387,6 +1398,13 @@ async function preflightTierFunctionLimits(client, spec, ciCredentials) {
1387
1398
  }
1388
1399
  }
1389
1400
  }
1401
+ function largerLimit(a, b) {
1402
+ if (!a)
1403
+ return b;
1404
+ if (!b)
1405
+ return a;
1406
+ return b.value > a.value ? b : a;
1407
+ }
1390
1408
  function hasFunctionTierPreflightInputs(functions) {
1391
1409
  if (!functions)
1392
1410
  return false;
@@ -1406,7 +1424,14 @@ function collectFunctionPreflightEntries(functions) {
1406
1424
  }
1407
1425
  return entries;
1408
1426
  }
1409
- async function readTierFunctionLimits(client) {
1427
+ /**
1428
+ * The function caps of the tier the deploy is judged by: the tier of the
1429
+ * organization that owns the project. `/tiers/v1/status` describes the
1430
+ * wallet's own organization, so its caps count only when the project's tier
1431
+ * is the same; otherwise the static table for the project's tier applies.
1432
+ * When the project cannot be read, the wallet's own tier is used.
1433
+ */
1434
+ async function readTierFunctionLimits(client, projectId) {
1410
1435
  let status;
1411
1436
  try {
1412
1437
  status = await client.request("/tiers/v1/status", {
@@ -1416,6 +1441,10 @@ async function readTierFunctionLimits(client) {
1416
1441
  catch {
1417
1442
  return null;
1418
1443
  }
1444
+ const projectTier = await readProjectTier(client, projectId);
1445
+ if (projectTier && projectTier.toLowerCase() !== String(status.tier ?? "").toLowerCase()) {
1446
+ status = { tier: projectTier };
1447
+ }
1419
1448
  if (typeof status.tier !== "string" || status.tier.length === 0) {
1420
1449
  return null;
1421
1450
  }
@@ -1434,6 +1463,9 @@ async function readTierFunctionLimits(client) {
1434
1463
  "memory_mb_max",
1435
1464
  "function_memory_mb_max",
1436
1465
  ], fallback?.maxMemoryMb);
1466
+ // tier status names no SSR memory cap, so it comes from the static table.
1467
+ if (fallback)
1468
+ limits.maxSsrMemoryMb = { value: fallback.maxSsrMemoryMb, source: "local_static_fallback" };
1437
1469
  limits.maxScheduledFunctions = tierStatusLimitOrFallback(status, [
1438
1470
  "max_scheduled_functions",
1439
1471
  "scheduled_functions_limit",
@@ -1454,6 +1486,18 @@ async function readTierFunctionLimits(client) {
1454
1486
  ]);
1455
1487
  return hasAnyTierFunctionLimit(limits) ? limits : null;
1456
1488
  }
1489
+ async function readProjectTier(client, projectId) {
1490
+ try {
1491
+ const project = await client.request(`/projects/v1/${encodeURIComponent(projectId)}`, {
1492
+ method: "GET",
1493
+ context: "checking the project's tier for deploy preflight",
1494
+ });
1495
+ return typeof project.tier === "string" && project.tier.length > 0 ? project.tier : null;
1496
+ }
1497
+ catch {
1498
+ return null;
1499
+ }
1500
+ }
1457
1501
  function tierStatusLimitOrFallback(status, keys, fallback) {
1458
1502
  return tierStatusLimit(status, keys) ?? (fallback === undefined
1459
1503
  ? undefined
@@ -1895,7 +1939,7 @@ async function requestWithTransientRetry(send) {
1895
1939
  function isTransientGatewayFailure(err) {
1896
1940
  if (!isRun402Error(err))
1897
1941
  return false;
1898
- if (err.kind === "network_error")
1942
+ if (isNoResponseError(err))
1899
1943
  return true;
1900
1944
  return err.status === 502 || err.status === 503 || err.status === 504;
1901
1945
  }
@@ -4544,15 +4588,8 @@ function translateDeployError(err, phase, planId, operationId) {
4544
4588
  context: err.context,
4545
4589
  });
4546
4590
  }
4547
- if (err instanceof NetworkError) {
4548
- return new Run402DeployError(err.message, {
4549
- code: "NETWORK_ERROR",
4550
- phase,
4551
- retryable: true,
4552
- operationId,
4553
- planId,
4554
- context: phase,
4555
- });
4591
+ if (isNoResponseError(err)) {
4592
+ return noResponseDeployError(err, phase, planId, operationId);
4556
4593
  }
4557
4594
  // Re-throw other Run402Error subclasses (PaymentRequired, Unauthorized, etc.)
4558
4595
  // as-is — the consumer handles them at a different layer than
@@ -4579,6 +4616,68 @@ function translateDeployError(err, phase, planId, operationId) {
4579
4616
  context: phase,
4580
4617
  });
4581
4618
  }
4619
+ /**
4620
+ * A deploy request that got no response: the connection failed or the client
4621
+ * gave up waiting (Node's fetch stops waiting for response headers after
4622
+ * 300 s). For a commit that is not a failed deploy: the gateway may still be
4623
+ * running the operation, and committing the plan again is safe (a plan commits
4624
+ * once), so the error says so and points at the operation.
4625
+ */
4626
+ function noResponseDeployError(err, phase, planId, operationId) {
4627
+ const transportCode = noResponseTransportCode(err);
4628
+ const cause = transportCode ? ` (${transportCode})` : "";
4629
+ const isCommit = phase === "commit";
4630
+ const message = isCommit
4631
+ ? `The deploy commit got no response from the API${cause}. The deploy may still be running on the gateway${operationId ? ` as operation ${operationId}` : ""}; nothing failed that is known. Check the operation, or commit the same plan again: a plan commits once, so a repeat follows the running deploy.`
4632
+ : `The deploy request (${phase}) got no response from the API${cause}. Repeating it is safe.`;
4633
+ const nextActions = [];
4634
+ if (operationId) {
4635
+ nextActions.push({
4636
+ type: "poll",
4637
+ method: "GET",
4638
+ path: `/apply/v1/operations/${operationId}`,
4639
+ command: `run402 deploy status ${operationId}`,
4640
+ safe_to_auto_execute: true,
4641
+ why: "Read the operation to learn whether the deploy is still running, finished, or failed.",
4642
+ });
4643
+ }
4644
+ nextActions.push({
4645
+ type: "retry",
4646
+ safe_to_auto_execute: true,
4647
+ why: isCommit
4648
+ ? "Committing the same plan again cannot run the deploy twice: it attaches to the running operation or returns its stored result."
4649
+ : "The request got no response; repeating it is safe.",
4650
+ });
4651
+ return new Run402DeployError(message, {
4652
+ code: "NETWORK_ERROR",
4653
+ phase,
4654
+ retryable: true,
4655
+ operationId,
4656
+ planId,
4657
+ context: phase,
4658
+ body: {
4659
+ code: "NETWORK_ERROR",
4660
+ category: "network",
4661
+ retryable: true,
4662
+ safe_to_retry: true,
4663
+ mutation_state: isCommit ? "unknown" : "none",
4664
+ details: {
4665
+ ...(transportCode ? { transport_code: transportCode } : {}),
4666
+ ...(operationId ? { operation_id: operationId } : {}),
4667
+ },
4668
+ next_actions: nextActions,
4669
+ },
4670
+ });
4671
+ }
4672
+ function noResponseTransportCode(err) {
4673
+ const details = err.details;
4674
+ if (details && typeof details === "object" && !Array.isArray(details)) {
4675
+ const code = details.transport_code;
4676
+ if (typeof code === "string")
4677
+ return code;
4678
+ }
4679
+ return null;
4680
+ }
4582
4681
  function extractGatewayError(body) {
4583
4682
  // Gateway returns the error in any of:
4584
4683
  // { error: { code, message?, phase?, ... } } — nested