@supacloud/admin 0.16.1 → 0.18.0

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 (2) hide show
  1. package/dist/index.js +65 -22
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -26123,6 +26123,14 @@ function validatedPostTimeout(options) {
26123
26123
  }
26124
26124
  return timeoutMs;
26125
26125
  }
26126
+ function validatedResponseTimeout(timeoutMs) {
26127
+ if (timeoutMs === undefined)
26128
+ return;
26129
+ if (!Number.isSafeInteger(timeoutMs) || timeoutMs <= 0 || timeoutMs > MAX_POST_TIMEOUT_MS) {
26130
+ throw new RangeError(`HTTP response timeout must be between 1 and ${MAX_POST_TIMEOUT_MS} ms`);
26131
+ }
26132
+ return timeoutMs;
26133
+ }
26126
26134
  function validatedGetResponseLimit(options) {
26127
26135
  const maxBytes = options.maxResponseBytes;
26128
26136
  if (maxBytes === undefined)
@@ -26154,16 +26162,12 @@ function joinedResponseBytes(chunks, totalBytes) {
26154
26162
  }
26155
26163
  return responseBytes;
26156
26164
  }
26157
- async function boundedResponseBytes(response, maxBytes) {
26158
- if (responseExceedsDeclaredLimit(response, maxBytes)) {
26159
- response.body?.cancel().catch(() => {
26160
- return;
26161
- });
26162
- return null;
26163
- }
26164
- if (!response.body)
26165
- return new Uint8Array;
26166
- const reader = response.body.getReader();
26165
+ function cancelResponseReader(reader) {
26166
+ reader.cancel().catch(() => {
26167
+ return;
26168
+ });
26169
+ }
26170
+ async function responseBytesFromReader(reader, maxBytes) {
26167
26171
  const chunks = [];
26168
26172
  let totalBytes = 0;
26169
26173
  while (true) {
@@ -26172,14 +26176,41 @@ async function boundedResponseBytes(response, maxBytes) {
26172
26176
  return joinedResponseBytes(chunks, totalBytes);
26173
26177
  totalBytes += value.byteLength;
26174
26178
  if (totalBytes > maxBytes) {
26175
- reader.cancel().catch(() => {
26176
- return;
26177
- });
26179
+ cancelResponseReader(reader);
26178
26180
  return null;
26179
26181
  }
26180
26182
  chunks.push(value);
26181
26183
  }
26182
26184
  }
26185
+ async function responseBytesBeforeDeadline(reader, maxBytes, responseTimeoutMs) {
26186
+ let deadlineTimer;
26187
+ const deadline = new Promise((resolve2) => {
26188
+ deadlineTimer = setTimeout(() => {
26189
+ cancelResponseReader(reader);
26190
+ resolve2(null);
26191
+ }, responseTimeoutMs);
26192
+ });
26193
+ try {
26194
+ return await Promise.race([responseBytesFromReader(reader, maxBytes), deadline]);
26195
+ } catch {
26196
+ cancelResponseReader(reader);
26197
+ return null;
26198
+ } finally {
26199
+ clearTimeout(deadlineTimer);
26200
+ }
26201
+ }
26202
+ async function boundedResponseBytes(response, maxBytes, responseTimeoutMs) {
26203
+ if (responseExceedsDeclaredLimit(response, maxBytes)) {
26204
+ response.body?.cancel().catch(() => {
26205
+ return;
26206
+ });
26207
+ return null;
26208
+ }
26209
+ if (!response.body)
26210
+ return new Uint8Array;
26211
+ const reader = response.body.getReader();
26212
+ return responseTimeoutMs === undefined ? responseBytesFromReader(reader, maxBytes) : responseBytesBeforeDeadline(reader, maxBytes, responseTimeoutMs);
26213
+ }
26183
26214
  async function boundedResponseJson(response, maxBytes) {
26184
26215
  const responseBytes = await boundedResponseBytes(response, maxBytes);
26185
26216
  if (responseBytes === null)
@@ -26193,8 +26224,8 @@ async function boundedResponseJson(response, maxBytes) {
26193
26224
  throw error;
26194
26225
  }
26195
26226
  }
26196
- async function strictBoundedResponseJson(response, maxBytes) {
26197
- const responseBytes = await boundedResponseBytes(response, maxBytes);
26227
+ async function strictBoundedResponseJson(response, maxBytes, responseTimeoutMs) {
26228
+ const responseBytes = await boundedResponseBytes(response, maxBytes, responseTimeoutMs);
26198
26229
  if (responseBytes === null)
26199
26230
  throw new Error("HTTP JSON response exceeded its byte limit");
26200
26231
  const responseText = new TextDecoder("utf-8", { fatal: true }).decode(responseBytes);
@@ -26282,6 +26313,7 @@ class HttpTransport {
26282
26313
  async post(path, body, options) {
26283
26314
  const timeoutMs = validatedPostTimeout(options);
26284
26315
  const maxJsonBytes = validatedStrictJsonLimit({ maxJsonBytes: options?.maxJsonBytes });
26316
+ const responseTimeoutMs = validatedResponseTimeout(options?.responseTimeoutMs);
26285
26317
  let response;
26286
26318
  try {
26287
26319
  response = await fetchWithRetry(`${this.baseUrl}${path}`, {
@@ -26294,7 +26326,7 @@ class HttpTransport {
26294
26326
  }
26295
26327
  if (maxJsonBytes !== undefined) {
26296
26328
  try {
26297
- const data2 = await strictBoundedResponseJson(response, maxJsonBytes);
26329
+ const data2 = await strictBoundedResponseJson(response, maxJsonBytes, responseTimeoutMs);
26298
26330
  return { ok: response.ok, status: response.status, data: data2 };
26299
26331
  } catch {
26300
26332
  return responseBodyFailure(response.status);
@@ -26315,6 +26347,7 @@ class HttpTransport {
26315
26347
  }
26316
26348
  const maxJsonBytes = validatedStrictJsonLimit({ maxJsonBytes: options.maxJsonBytes });
26317
26349
  const timeoutMs = validatedPostTimeout(options.timeoutMs === undefined ? undefined : { timeoutMs: options.timeoutMs });
26350
+ const responseTimeoutMs = validatedResponseTimeout(options.responseTimeoutMs);
26318
26351
  try {
26319
26352
  const request = {
26320
26353
  method: "POST",
@@ -26329,7 +26362,7 @@ class HttpTransport {
26329
26362
  };
26330
26363
  const response = await fetchWithRetry(`${this.baseUrl}${path}`, request, timeoutMs);
26331
26364
  try {
26332
- const data = await strictBoundedResponseJson(response, maxJsonBytes);
26365
+ const data = await strictBoundedResponseJson(response, maxJsonBytes, responseTimeoutMs);
26333
26366
  return { ok: response.ok, status: response.status, data };
26334
26367
  } catch {
26335
26368
  return responseBodyFailure(response.status);
@@ -31817,6 +31850,7 @@ Actions: routes, upsert_route, update_route, delete_route, config, get_certifica
31817
31850
  managed_upstream: optional(stringEnum(["edge-functions"]), "[upsert_route/update_route] 托管上游(同步 Edge Function)"),
31818
31851
  upstream_tls_insecure_skip_verify: optional(Type.Boolean(), "[upsert_route/update_route] 上游 TLS 跳过校验"),
31819
31852
  static_root: optional(Type.String(), "[upsert_route/update_route] 静态站点根目录"),
31853
+ spa: optional(Type.Boolean(), "[upsert_route/update_route] 是否启用 SPA 单页回退"),
31820
31854
  protocol: optional(stringEnum(["http", "https"]), "[upsert_route/update_route] 可选请求协议匹配"),
31821
31855
  redirect_to: optional(Type.String(), "[upsert_route/update_route] 带固定 host 的绝对 http(s) 目标,可在末尾使用 {http.request.uri}"),
31822
31856
  redirect_status: withDescription(redirectStatus, "[upsert_route/update_route] 重定向状态码,默认 308"),
@@ -31863,6 +31897,7 @@ Actions: routes, upsert_route, update_route, delete_route, config, get_certifica
31863
31897
  managed_upstream,
31864
31898
  upstream_tls_insecure_skip_verify,
31865
31899
  static_root,
31900
+ spa,
31866
31901
  protocol,
31867
31902
  redirect_to,
31868
31903
  redirect_status,
@@ -31912,6 +31947,8 @@ Actions: routes, upsert_route, update_route, delete_route, config, get_certifica
31912
31947
  body.upstream_tls_insecure_skip_verify = upstream_tls_insecure_skip_verify;
31913
31948
  if (static_root !== undefined)
31914
31949
  body.static_root = static_root;
31950
+ if (spa !== undefined)
31951
+ body.spa = spa;
31915
31952
  if (protocol !== undefined)
31916
31953
  body.protocol = protocol;
31917
31954
  if (redirect_to !== undefined)
@@ -31949,6 +31986,8 @@ Actions: routes, upsert_route, update_route, delete_route, config, get_certifica
31949
31986
  body.upstream_tls_insecure_skip_verify = upstream_tls_insecure_skip_verify;
31950
31987
  if (static_root !== undefined)
31951
31988
  body.static_root = static_root;
31989
+ if (spa !== undefined)
31990
+ body.spa = spa;
31952
31991
  if (protocol !== undefined)
31953
31992
  body.protocol = protocol;
31954
31993
  if (redirect_to !== undefined)
@@ -32066,7 +32105,7 @@ Actions: routes, upsert_route, update_route, delete_route, config, get_certifica
32066
32105
  });
32067
32106
  }
32068
32107
 
32069
- // src/shared/tools/frontend-release-control.ts
32108
+ // ../cli/src/shared/tools/frontend-release-control.ts
32070
32109
  import { createHash as createHash3 } from "node:crypto";
32071
32110
  import { constants as fsConstants2 } from "node:fs";
32072
32111
  import { open as open2 } from "node:fs/promises";
@@ -32080,6 +32119,7 @@ var ARCHIVE_MAX_BYTES = 100 * 1024 * 1024;
32080
32119
  var ARCHIVE_CHUNK_BYTES = 64 * 1024;
32081
32120
  var RESPONSE_MAX_BYTES = 1024 * 1024;
32082
32121
  var UPLOAD_REQUEST_TIMEOUT_MS = 10 * 60000;
32122
+ var MUTATION_RESPONSE_TIMEOUT_MS = 5000;
32083
32123
  var RELEASE_LIST_LIMIT_MAX = 100;
32084
32124
  function toolResponse(payload) {
32085
32125
  return { content: [{ type: "text", text: JSON.stringify(payload, null, 2) }] };
@@ -32290,7 +32330,8 @@ async function uploadFrontendRelease(http, projectRef, deploymentId, archivePath
32290
32330
  contentLength: archive.sizeBytes,
32291
32331
  contentSha256: archive.sha256,
32292
32332
  maxJsonBytes: RESPONSE_MAX_BYTES,
32293
- timeoutMs: UPLOAD_REQUEST_TIMEOUT_MS
32333
+ timeoutMs: UPLOAD_REQUEST_TIMEOUT_MS,
32334
+ responseTimeoutMs: MUTATION_RESPONSE_TIMEOUT_MS
32294
32335
  });
32295
32336
  } finally {
32296
32337
  await archive.handle.close();
@@ -32423,7 +32464,10 @@ async function activateFrontendRelease(http, input) {
32423
32464
  expected_active_release_id: input.expectedActiveReleaseId,
32424
32465
  expected_activation_id: input.expectedActivationId,
32425
32466
  mutation_id: mutationId
32426
- }, { maxJsonBytes: RESPONSE_MAX_BYTES });
32467
+ }, {
32468
+ maxJsonBytes: RESPONSE_MAX_BYTES,
32469
+ responseTimeoutMs: MUTATION_RESPONSE_TIMEOUT_MS
32470
+ });
32427
32471
  const release = activationReceipt(response.data, { ...input, mutationId });
32428
32472
  if (!response.ok || !release) {
32429
32473
  if (response.status >= 400 && response.status < 500 && response.status !== 408 && !response.transportError) {
@@ -32467,7 +32511,6 @@ async function activationReadback(http, input, mutationId, activationStatus) {
32467
32511
  release: readback.release
32468
32512
  });
32469
32513
  }
32470
-
32471
32514
  // src/shared/tools/frontend-tools.ts
32472
32515
  function registerFrontendTools(server, http) {
32473
32516
  server.tool("frontend", `Immutable prebuilt frontend release control.
@@ -32542,7 +32585,7 @@ Actions: list_releases, get_release, upload_release, activate_release`, {
32542
32585
  // package.json
32543
32586
  var package_default = {
32544
32587
  name: "@supacloud/admin",
32545
- version: "0.16.1",
32588
+ version: "0.18.0",
32546
32589
  description: "Platform administration CLI for SupaCloud operators",
32547
32590
  type: "module",
32548
32591
  main: "./dist/index.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supacloud/admin",
3
- "version": "0.16.1",
3
+ "version": "0.18.0",
4
4
  "description": "Platform administration CLI for SupaCloud operators",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",