neon 7.0.6 → 7.0.7

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/api.js CHANGED
@@ -208,7 +208,8 @@ function validateRequestTimeout(ms) {
208
208
  }
209
209
  const makeTimedFetch = (requestTimeoutMs) => async (input, init) => {
210
210
  const timeout = AbortSignal.timeout(requestTimeoutMs);
211
- const signal = init?.signal ? AbortSignal.any([init.signal, timeout]) : timeout;
211
+ const callerSignal = init?.signal ?? (input instanceof Request ? input.signal : void 0);
212
+ const signal = callerSignal ? AbortSignal.any([callerSignal, timeout]) : timeout;
212
213
  const method = init?.method ?? (input instanceof Request ? input.method : "GET");
213
214
  const url = input instanceof Request ? input.url : String(input);
214
215
  log.debug("%s %s", method.toUpperCase(), url);
@@ -220,7 +221,7 @@ const makeTimedFetch = (requestTimeoutMs) => async (input, init) => {
220
221
  log.debug("%d %s", response.status, response.statusText);
221
222
  return response;
222
223
  } catch (err) {
223
- if (timeout.aborted && !init?.signal?.aborted) throw new RequestTimeoutError(requestTimeoutMs, err);
224
+ if (timeout.aborted && !callerSignal?.aborted) throw new RequestTimeoutError(requestTimeoutMs, err);
224
225
  throw err;
225
226
  }
226
227
  };
@@ -365,8 +366,9 @@ const getApiClient = ({ apiKey, apiHost, requestTimeoutMs = REQUEST_TIMEOUT_MS }
365
366
  client,
366
367
  query
367
368
  })),
368
- listSharedProjects: (query = {}) => call(() => raw.listSharedProjects({
369
+ listSharedProjects: (query = {}, options = {}) => call(() => raw.listSharedProjects({
369
370
  client,
371
+ signal: options.signal,
370
372
  query: {
371
373
  ...query.cursor !== void 0 ? { cursor: query.cursor } : {},
372
374
  ...query.limit !== void 0 ? { limit: query.limit } : {},
@@ -2,12 +2,13 @@ import { t as __exportAll } from "../_chunks/rolldown-runtime-8H4AJuhK.js";
2
2
  import { isCi } from "../env.js";
3
3
  import { log } from "../log.js";
4
4
  import { isNeonApiError, messageFromBody } from "../api.js";
5
- import { updateContextFile } from "../context.js";
5
+ import { readContextFile, updateContextFile } from "../context.js";
6
6
  import { getCliName } from "../utils/cli_name.js";
7
7
  import { projectCreateRequest, projectUpdateRequest } from "../parameters.gen.js";
8
8
  import { writer } from "../writer.js";
9
9
  import { getComputeUnits } from "../utils/compute_units.js";
10
10
  import { psql } from "../utils/psql.js";
11
+ import chalk from "chalk";
11
12
  import prompts from "prompts";
12
13
  //#region src/commands/projects.ts
13
14
  var projects_exports = /* @__PURE__ */ __exportAll({
@@ -18,6 +19,7 @@ var projects_exports = /* @__PURE__ */ __exportAll({
18
19
  builder: () => builder,
19
20
  command: () => command,
20
21
  describe: () => describe,
22
+ formatDuration: () => formatDuration,
21
23
  handler: () => handler
22
24
  });
23
25
  const PROJECT_FIELDS = [
@@ -176,37 +178,56 @@ const builder = (argv) => {
176
178
  const handler = (args) => {
177
179
  return args;
178
180
  };
181
+ const listAll = async (fetchPage) => {
182
+ const result = [];
183
+ let cursor;
184
+ let end = false;
185
+ while (!end) {
186
+ const { data } = await fetchPage(cursor);
187
+ result.push(...data.projects);
188
+ cursor = data.pagination?.cursor;
189
+ log.debug("Got %d projects, with cursor: %s", data.projects.length, cursor);
190
+ if (data.projects.length < PROJECTS_LIST_LIMIT) end = true;
191
+ }
192
+ return result;
193
+ };
194
+ const projectColumns = (props) => {
195
+ const current = readContextFile(props.contextFile).projectId;
196
+ return { name: (project) => project.id === current ? `${chalk.green("[current]")} ${project.name}` : project.name };
197
+ };
179
198
  const list = async (props) => {
180
- const getList = async (fn) => {
181
- const result = [];
182
- let cursor;
183
- let end = false;
184
- while (!end) {
185
- const { data } = await fn({
186
- limit: PROJECTS_LIST_LIMIT,
187
- org_id: props.orgId,
188
- recoverable: props.recoverableOnly,
189
- cursor
190
- });
191
- result.push(...data.projects);
192
- cursor = data.pagination?.cursor;
193
- log.debug("Got %d projects, with cursor: %s", data.projects.length, cursor);
194
- if (data.projects.length < PROJECTS_LIST_LIMIT) end = true;
195
- }
196
- return result;
197
- };
198
- const ownedProjects = await getList(props.apiClient.listProjects);
199
- const sharedProjects = props.orgId || props.recoverableOnly ? [] : await getList(props.apiClient.listSharedProjects);
199
+ const query = (cursor) => ({
200
+ limit: PROJECTS_LIST_LIMIT,
201
+ org_id: props.orgId,
202
+ recoverable: props.recoverableOnly,
203
+ cursor
204
+ });
205
+ const includeShared = !props.orgId && !props.recoverableOnly;
206
+ const ownedRequest = listAll((cursor) => props.apiClient.listProjects(query(cursor)));
207
+ const sharedAbort = new AbortController();
208
+ const sharedRequest = includeShared ? listAll((cursor) => props.apiClient.listSharedProjects(query(cursor), { signal: sharedAbort.signal })) : void 0;
209
+ sharedRequest?.catch(() => void 0);
210
+ let ownedProjects;
211
+ try {
212
+ ownedProjects = await ownedRequest;
213
+ } catch (err) {
214
+ sharedAbort.abort();
215
+ throw err;
216
+ }
217
+ const sharedProjects = await sharedRequest ?? [];
200
218
  const out = writer(props);
219
+ const renderColumns = projectColumns(props);
201
220
  out.write(ownedProjects, {
202
221
  fields: props.recoverableOnly ? RECOVERABLE_PROJECT_FIELDS : PROJECT_FIELDS,
203
222
  title: "Projects",
204
- emptyMessage: props.recoverableOnly ? "You don't have any recoverable projects." : "You don't have any projects yet. See how to create a new project:\n> neon projects create --help"
223
+ emptyMessage: props.recoverableOnly ? "You don't have any recoverable projects." : "You don't have any projects yet. See how to create a new project:\n> neon projects create --help",
224
+ renderColumns
205
225
  });
206
- if (!props.orgId && !props.recoverableOnly) out.write(sharedProjects, {
226
+ if (includeShared) out.write(sharedProjects, {
207
227
  fields: PROJECT_FIELDS,
208
228
  title: "Shared with you",
209
- emptyMessage: "No projects have been shared with you"
229
+ emptyMessage: "No projects have been shared with you",
230
+ renderColumns
210
231
  });
211
232
  out.end();
212
233
  };
@@ -303,7 +324,55 @@ const recover = async (props) => {
303
324
  };
304
325
  const get = async (props) => {
305
326
  const { data } = await props.apiClient.getProject(props.id);
306
- writer(props).end(data.project, { fields: PROJECT_FIELDS });
327
+ if (props.output === "json" || props.output === "yaml") {
328
+ writer(props).end(data.project, { fields: PROJECT_FIELDS });
329
+ return;
330
+ }
331
+ writer(props).end(projectDetails(data.project), {
332
+ fields: PROJECT_DETAIL_FIELDS,
333
+ humanTitle: "Project"
334
+ });
335
+ };
336
+ const PROJECT_DETAIL_FIELDS = [
337
+ "name",
338
+ "id",
339
+ "region_id",
340
+ "postgres_version",
341
+ "organization",
342
+ "default_compute",
343
+ "history_retention",
344
+ "created_at",
345
+ "updated_at"
346
+ ];
347
+ /** Table-only view of a project; the field names become the row labels. */
348
+ const projectDetails = (project) => ({
349
+ name: project.name,
350
+ id: project.id,
351
+ region_id: project.region_id,
352
+ postgres_version: project.pg_version,
353
+ organization: project.org_id,
354
+ default_compute: formatComputeRange(project.default_endpoint_settings),
355
+ history_retention: project.history_retention_seconds === void 0 ? void 0 : formatDuration(project.history_retention_seconds),
356
+ created_at: project.created_at,
357
+ updated_at: project.updated_at
358
+ });
359
+ const formatComputeRange = (settings) => {
360
+ const min = settings?.autoscaling_limit_min_cu;
361
+ const max = settings?.autoscaling_limit_max_cu;
362
+ if (min === void 0 || max === void 0) return;
363
+ return min === max ? `${min} CU` : `${min}-${max} CU`;
364
+ };
365
+ const DURATION_UNITS = [
366
+ ["day", 86400],
367
+ ["hour", 3600],
368
+ ["minute", 60]
369
+ ];
370
+ const formatDuration = (seconds) => {
371
+ for (const [unit, size] of DURATION_UNITS) if (seconds > 0 && seconds % size === 0) {
372
+ const count = seconds / size;
373
+ return `${count} ${unit}${count === 1 ? "" : "s"}`;
374
+ }
375
+ return `${seconds} second${seconds === 1 ? "" : "s"}`;
307
376
  };
308
377
  const handleMissingOrgId = async (args, cmd) => {
309
378
  try {
@@ -367,4 +436,4 @@ const onPromptState = (state) => {
367
436
  }
368
437
  };
369
438
  //#endregion
370
- export { PROJECT_FIELDS, RECOVERABLE_PROJECT_FIELDS, REGIONS, aliases, builder, command, describe, handler, projects_exports as t };
439
+ export { PROJECT_FIELDS, RECOVERABLE_PROJECT_FIELDS, REGIONS, aliases, builder, command, describe, formatDuration, handler, projects_exports as t };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neon",
3
- "version": "7.0.6",
3
+ "version": "7.0.7",
4
4
  "description": "CLI tool for Neon, the cloud backend primitives built around Lakebase Postgres",
5
5
  "keywords": [
6
6
  "neon",
@@ -64,8 +64,8 @@
64
64
  "yaml": "^2.9.0",
65
65
  "yargs": "17.7.2",
66
66
  "yoctocolors": "^2.1.2",
67
- "@neon/config": "1.8.3",
68
67
  "@neon/config-runtime": "1.7.3",
68
+ "@neon/config": "1.8.3",
69
69
  "@neon/sdk": "7.0.0"
70
70
  },
71
71
  "optionalDependencies": {