neonctl 2.27.1 → 2.29.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 (136) hide show
  1. package/README.md +35 -3
  2. package/dist/analytics.js +52 -34
  3. package/dist/api.js +643 -13
  4. package/dist/auth.js +50 -44
  5. package/dist/cli.js +8 -1
  6. package/dist/commands/auth.js +64 -51
  7. package/dist/commands/bootstrap.js +115 -157
  8. package/dist/commands/branches.js +160 -150
  9. package/dist/commands/bucket.js +183 -146
  10. package/dist/commands/checkout.js +51 -51
  11. package/dist/commands/config.js +228 -82
  12. package/dist/commands/connection_string.js +62 -62
  13. package/dist/commands/data_api.js +100 -101
  14. package/dist/commands/databases.js +29 -26
  15. package/dist/commands/deploy.js +12 -12
  16. package/dist/commands/dev.js +114 -114
  17. package/dist/commands/env.js +43 -43
  18. package/dist/commands/functions.js +101 -104
  19. package/dist/commands/index.js +27 -25
  20. package/dist/commands/init.js +23 -22
  21. package/dist/commands/ip_allow.js +29 -29
  22. package/dist/commands/link.js +232 -182
  23. package/dist/commands/neon_auth.js +385 -370
  24. package/dist/commands/operations.js +11 -11
  25. package/dist/commands/orgs.js +8 -8
  26. package/dist/commands/projects.js +103 -101
  27. package/dist/commands/psql.js +31 -31
  28. package/dist/commands/roles.js +27 -24
  29. package/dist/commands/schema_diff.js +25 -26
  30. package/dist/commands/set_context.js +17 -17
  31. package/dist/commands/status.js +40 -0
  32. package/dist/commands/user.js +5 -5
  33. package/dist/commands/vpc_endpoints.js +50 -50
  34. package/dist/config.js +7 -7
  35. package/dist/config_format.js +5 -5
  36. package/dist/context.js +37 -14
  37. package/dist/current_branch_fast_path.js +55 -0
  38. package/dist/dev/env.js +33 -33
  39. package/dist/dev/functions.js +4 -4
  40. package/dist/dev/inputs.js +6 -6
  41. package/dist/dev/runtime.js +25 -25
  42. package/dist/env.js +14 -14
  43. package/dist/env_file.js +13 -13
  44. package/dist/errors.js +68 -5
  45. package/dist/functions_api.js +10 -10
  46. package/dist/help.js +15 -15
  47. package/dist/index.js +110 -107
  48. package/dist/log.js +2 -2
  49. package/dist/parameters.gen.js +14 -14
  50. package/dist/pkg.js +5 -5
  51. package/dist/psql/cli.js +4 -2
  52. package/dist/psql/command/cmd_cond.js +61 -61
  53. package/dist/psql/command/cmd_connect.js +159 -154
  54. package/dist/psql/command/cmd_copy.js +107 -97
  55. package/dist/psql/command/cmd_describe.js +368 -363
  56. package/dist/psql/command/cmd_format.js +276 -263
  57. package/dist/psql/command/cmd_io.js +269 -263
  58. package/dist/psql/command/cmd_lo.js +74 -66
  59. package/dist/psql/command/cmd_meta.js +148 -148
  60. package/dist/psql/command/cmd_misc.js +17 -17
  61. package/dist/psql/command/cmd_pipeline.js +142 -135
  62. package/dist/psql/command/cmd_restrict.js +25 -25
  63. package/dist/psql/command/cmd_show.js +183 -168
  64. package/dist/psql/command/dispatch.js +26 -26
  65. package/dist/psql/command/shared.js +14 -14
  66. package/dist/psql/complete/filenames.js +16 -16
  67. package/dist/psql/complete/index.js +4 -4
  68. package/dist/psql/complete/matcher.js +33 -32
  69. package/dist/psql/complete/psqlVars.js +173 -173
  70. package/dist/psql/complete/queries.js +5 -3
  71. package/dist/psql/complete/rules.js +900 -863
  72. package/dist/psql/core/common.js +136 -133
  73. package/dist/psql/core/help.js +343 -343
  74. package/dist/psql/core/mainloop.js +160 -153
  75. package/dist/psql/core/prompt.js +126 -123
  76. package/dist/psql/core/settings.js +111 -111
  77. package/dist/psql/core/sqlHelp.js +150 -150
  78. package/dist/psql/core/startup.js +211 -205
  79. package/dist/psql/core/syncVars.js +14 -14
  80. package/dist/psql/core/variables.js +24 -24
  81. package/dist/psql/describe/formatters.js +302 -289
  82. package/dist/psql/describe/processNamePattern.js +28 -28
  83. package/dist/psql/describe/queries.js +656 -651
  84. package/dist/psql/index.js +436 -411
  85. package/dist/psql/io/history.js +36 -36
  86. package/dist/psql/io/input.js +15 -15
  87. package/dist/psql/io/lineEditor/buffer.js +27 -25
  88. package/dist/psql/io/lineEditor/complete.js +15 -15
  89. package/dist/psql/io/lineEditor/filename.js +22 -22
  90. package/dist/psql/io/lineEditor/index.js +65 -62
  91. package/dist/psql/io/lineEditor/keymap.js +325 -318
  92. package/dist/psql/io/lineEditor/vt100.js +60 -60
  93. package/dist/psql/io/pgpass.js +18 -18
  94. package/dist/psql/io/pgservice.js +14 -14
  95. package/dist/psql/io/psqlrc.js +46 -46
  96. package/dist/psql/print/aligned.js +175 -166
  97. package/dist/psql/print/asciidoc.js +51 -51
  98. package/dist/psql/print/crosstab.js +34 -31
  99. package/dist/psql/print/csv.js +25 -22
  100. package/dist/psql/print/html.js +54 -54
  101. package/dist/psql/print/json.js +12 -12
  102. package/dist/psql/print/latex.js +118 -118
  103. package/dist/psql/print/pager.js +28 -26
  104. package/dist/psql/print/troff.js +48 -48
  105. package/dist/psql/print/unaligned.js +15 -14
  106. package/dist/psql/print/units.js +17 -17
  107. package/dist/psql/scanner/slash.js +48 -46
  108. package/dist/psql/scanner/sql.js +88 -84
  109. package/dist/psql/scanner/stringutils.js +21 -17
  110. package/dist/psql/types/index.js +7 -7
  111. package/dist/psql/types/scanner.js +8 -8
  112. package/dist/psql/wire/connection.js +341 -327
  113. package/dist/psql/wire/copy.js +7 -7
  114. package/dist/psql/wire/pipeline.js +26 -24
  115. package/dist/psql/wire/protocol.js +102 -102
  116. package/dist/psql/wire/sasl.js +62 -62
  117. package/dist/psql/wire/tls.js +79 -73
  118. package/dist/storage_api.js +22 -23
  119. package/dist/test_utils/fixtures.js +74 -41
  120. package/dist/test_utils/oauth_server.js +5 -5
  121. package/dist/utils/api_enums.js +33 -0
  122. package/dist/utils/branch_notice.js +5 -5
  123. package/dist/utils/branch_picker.js +26 -26
  124. package/dist/utils/compute_units.js +4 -4
  125. package/dist/utils/enrichers.js +28 -16
  126. package/dist/utils/esbuild.js +28 -28
  127. package/dist/utils/formats.js +1 -1
  128. package/dist/utils/middlewares.js +3 -3
  129. package/dist/utils/package_manager.js +68 -0
  130. package/dist/utils/point_in_time.js +12 -12
  131. package/dist/utils/psql.js +30 -30
  132. package/dist/utils/string.js +2 -2
  133. package/dist/utils/ui.js +9 -9
  134. package/dist/utils/zip.js +1 -1
  135. package/dist/writer.js +17 -17
  136. package/package.json +10 -12
@@ -1,39 +1,39 @@
1
- import { EndpointType } from '@neondatabase/api-client';
2
- import { contextBranch, readContextFile } from '../context.js';
3
- import { writer } from '../writer.js';
4
- import { branchCreateRequest } from '../parameters.gen.js';
5
- import { retryOnLock } from '../api.js';
6
- import { branchIdFromProps, branchIdResolve, fillSingleProject, } from '../utils/enrichers.js';
7
- import { looksLikeBranchId, looksLikeLSN, looksLikeTimestamp, } from '../utils/formats.js';
8
- import { psql } from '../utils/psql.js';
9
- import { parsePointInTime } from '../utils/point_in_time.js';
10
- import { log } from '../log.js';
11
- import { parseSchemaDiffParams, schemaDiff } from './schema_diff.js';
12
- import { getComputeUnits } from '../utils/compute_units.js';
1
+ import { retryOnLock } from "../api.js";
2
+ import { contextBranch, readContextFile } from "../context.js";
3
+ import { log } from "../log.js";
4
+ import { branchCreateRequest } from "../parameters.gen.js";
5
+ import { EndpointType } from "../utils/api_enums.js";
6
+ import { getComputeUnits } from "../utils/compute_units.js";
7
+ import { branchIdFromProps, branchIdResolve, fillSingleProject, } from "../utils/enrichers.js";
8
+ import { looksLikeBranchId, looksLikeLSN, looksLikeTimestamp, } from "../utils/formats.js";
9
+ import { parsePointInTime } from "../utils/point_in_time.js";
10
+ import { psql } from "../utils/psql.js";
11
+ import { writer } from "../writer.js";
12
+ import { parseSchemaDiffParams, schemaDiff } from "./schema_diff.js";
13
13
  export const BRANCH_FIELDS = [
14
- 'name',
15
- 'id',
16
- 'current_state',
17
- 'created_at',
18
- 'expires_at',
14
+ "name",
15
+ "id",
16
+ "current_state",
17
+ "created_at",
18
+ "expires_at",
19
19
  ];
20
20
  const BRANCH_FIELDS_RESET = [
21
- 'name',
22
- 'id',
23
- 'default',
24
- 'current_state',
25
- 'created_at',
26
- 'last_reset_at',
21
+ "name",
22
+ "id",
23
+ "default",
24
+ "current_state",
25
+ "created_at",
26
+ "last_reset_at",
27
27
  ];
28
- export const command = 'branches';
29
- export const describe = 'Manage branches';
30
- export const aliases = ['branch'];
28
+ export const command = "branches";
29
+ export const describe = "Manage branches";
30
+ export const aliases = ["branch"];
31
31
  export const builder = (argv) => argv
32
- .usage('$0 branches <sub-command> [options]')
32
+ .usage("$0 branches <sub-command> [options]")
33
33
  .options({
34
- 'project-id': {
35
- describe: 'Project ID',
36
- type: 'string',
34
+ "project-id": {
35
+ describe: "Project ID",
36
+ type: "string",
37
37
  },
38
38
  })
39
39
  .middleware(fillSingleProject)
@@ -41,172 +41,173 @@ export const builder = (argv) => argv
41
41
  // Provide alias for analytics
42
42
  args.branchId ?? (args.branchId = args.id);
43
43
  })
44
- .command('list', 'List branches', (yargs) => yargs, (args) => list(args))
45
- .command('create', 'Create a branch', (yargs) => yargs.options({
46
- name: branchCreateRequest['branch.name'],
44
+ .command("list", "List branches", (yargs) => yargs, (args) => list(args))
45
+ .command("create", "Create a branch", (yargs) => yargs.options({
46
+ name: branchCreateRequest["branch.name"],
47
47
  parent: {
48
- describe: 'Parent branch name or id or timestamp or LSN. Defaults to the default branch',
49
- type: 'string',
48
+ describe: "Parent branch name or id or timestamp or LSN. Defaults to the default branch",
49
+ type: "string",
50
50
  },
51
51
  compute: {
52
- describe: 'Create a branch with or without a compute. By default branch is created with a read-write compute. To create a branch without compute use --no-compute',
53
- type: 'boolean',
52
+ describe: "Create a branch with or without a compute. By default branch is created with a read-write compute. To create a branch without compute use --no-compute",
53
+ type: "boolean",
54
54
  default: true,
55
55
  },
56
56
  type: {
57
- describe: 'Type of compute to add',
58
- type: 'string',
59
- implies: 'compute',
57
+ describe: "Type of compute to add",
58
+ type: "string",
59
+ implies: "compute",
60
60
  default: EndpointType.ReadWrite,
61
61
  choices: Object.values(EndpointType),
62
62
  },
63
- 'suspend-timeout': {
64
- describe: 'Duration of inactivity in seconds after which the compute endpoint is\nautomatically suspended. The value `0` means use the global default.\nThe value `-1` means never suspend. The default value is `300` seconds (5 minutes).\nThe maximum value is `604800` seconds (1 week).',
65
- type: 'number',
66
- implies: 'compute',
63
+ "suspend-timeout": {
64
+ describe: "Duration of inactivity in seconds after which the compute endpoint is\nautomatically suspended. The value `0` means use the global default.\nThe value `-1` means never suspend. The default value is `300` seconds (5 minutes).\nThe maximum value is `604800` seconds (1 week).",
65
+ type: "number",
66
+ implies: "compute",
67
67
  default: 0,
68
68
  },
69
69
  cu: {
70
70
  describe: 'The number of Compute Units. Could be a fixed size (e.g. "2") or a range delimited by a dash (e.g. "0.5-3").',
71
- type: 'string',
72
- implies: 'compute',
71
+ type: "string",
72
+ implies: "compute",
73
73
  },
74
74
  psql: {
75
- type: 'boolean',
76
- describe: 'Connect to a new branch via psql',
75
+ type: "boolean",
76
+ describe: "Connect to a new branch via psql",
77
77
  default: false,
78
78
  },
79
79
  fallback: {
80
- type: 'boolean',
81
- describe: 'Force the embedded TypeScript psql fallback (for testing)',
80
+ type: "boolean",
81
+ describe: "Force the embedded TypeScript psql fallback (for testing)",
82
82
  default: false,
83
83
  hidden: true,
84
84
  },
85
85
  annotation: {
86
- type: 'string',
86
+ type: "string",
87
87
  hidden: true,
88
- default: '{}',
88
+ default: "{}",
89
89
  },
90
- 'schema-only': {
91
- describe: 'Create a schema-only branch. Requires exactly one read-write compute.',
92
- type: 'boolean',
90
+ "schema-only": {
91
+ describe: "Create a schema-only branch. Requires exactly one read-write compute.",
92
+ type: "boolean",
93
93
  default: false,
94
94
  },
95
- 'expires-at': {
96
- describe: 'Set an expiration date for the branch. Accepts a date string (e.g., 2024-12-31T23:59:59Z).',
97
- type: 'string',
95
+ "expires-at": {
96
+ describe: "Set an expiration date for the branch. Accepts a date string (e.g., 2024-12-31T23:59:59Z).",
97
+ type: "string",
98
98
  requiresArg: true,
99
99
  },
100
100
  }), (args) => create(args))
101
- .command('reset <id|name>', 'Reset a branch', (yargs) => yargs.options({
101
+ .command("reset <id|name>", "Reset a branch", (yargs) => yargs.options({
102
102
  parent: {
103
- describe: 'Reset to a parent branch',
104
- type: 'boolean',
103
+ describe: "Reset to a parent branch",
104
+ type: "boolean",
105
105
  default: false,
106
106
  },
107
- 'preserve-under-name': {
108
- describe: 'Name under which to preserve the old branch',
107
+ "preserve-under-name": {
108
+ describe: "Name under which to preserve the old branch",
109
109
  },
110
110
  }), (args) => reset(args))
111
- .command('restore <target-id|name> <source>[@(timestamp|lsn)]', 'Restores a branch to a specific point in time\n<source> can be: ^self, ^parent, or <source-branch-id|name>', (yargs) => yargs
111
+ .command("restore <target-id|name> <source>[@(timestamp|lsn)]", "Restores a branch to a specific point in time\n<source> can be: ^self, ^parent, or <source-branch-id|name>", (yargs) => yargs
112
112
  // we want to show meaningful help for the command
113
113
  // but it makes yargs to fail on parsing the command
114
114
  // so we need to fill in the missing args manually
115
115
  .middleware((args) => {
116
116
  args.id = args.targetId;
117
- args.pointInTime = args['source@(timestamp'];
117
+ args.pointInTime = args["source@(timestamp"];
118
118
  args.branchId = args.id; // for analytics
119
119
  })
120
- .usage('$0 branches restore <target-id|name> <source>[@(timestamp|lsn)]')
120
+ .usage("$0 branches restore <target-id|name> <source>[@(timestamp|lsn)]")
121
121
  .options({
122
- 'preserve-under-name': {
123
- describe: 'Name under which to preserve the old branch',
122
+ "preserve-under-name": {
123
+ describe: "Name under which to preserve the old branch",
124
124
  },
125
125
  })
126
126
  .example([
127
127
  [
128
- '$0 branches restore main br-source-branch-123456',
129
- 'Restores main to the head of the branch with id br-source-branch-123456',
128
+ "$0 branches restore main br-source-branch-123456",
129
+ "Restores main to the head of the branch with id br-source-branch-123456",
130
130
  ],
131
131
  [
132
- '$0 branches restore main source@2021-01-01T00:00:00Z',
133
- 'Restores main to the timestamp 2021-01-01T00:00:00Z of the source branch',
132
+ "$0 branches restore main source@2021-01-01T00:00:00Z",
133
+ "Restores main to the timestamp 2021-01-01T00:00:00Z of the source branch",
134
134
  ],
135
135
  [
136
- '$0 branches restore my-branch ^self@0/123456',
137
- 'Restores my-branch to the LSN 0/123456 from its own history',
136
+ "$0 branches restore my-branch ^self@0/123456",
137
+ "Restores my-branch to the LSN 0/123456 from its own history",
138
138
  ],
139
139
  [
140
- '$0 branches restore my-branch ^parent',
141
- 'Restore my-branch to the head of its parent branch',
140
+ "$0 branches restore my-branch ^parent",
141
+ "Restore my-branch to the head of its parent branch",
142
142
  ],
143
143
  ]), (args) => restore(args))
144
- .command('rename <id|name> <new-name>', 'Rename a branch', (yargs) => yargs, (args) => rename(args))
145
- .command('set-default <id|name>', 'Set a branch as default', (yargs) => yargs, (args) => setDefault(args))
144
+ .command("rename <id|name> <new-name>", "Rename a branch", (yargs) => yargs, (args) => rename(args))
145
+ .command("set-default <id|name>", "Set a branch as default", (yargs) => yargs, (args) => setDefault(args))
146
146
  .command({
147
- command: 'set-expiration <id|name>',
148
- describe: 'Set an expiration date for the branch',
147
+ command: "set-expiration <id|name>",
148
+ describe: "Set an expiration date for the branch",
149
149
  builder: (yargs) => yargs.options({
150
- 'expires-at': {
151
- describe: 'Set a expiration date for the branch. If omitted, expiration will be removed. Format [RFC3339]: 2024-12-31T23:59:59Z',
152
- type: 'string',
150
+ "expires-at": {
151
+ describe: "Set a expiration date for the branch. If omitted, expiration will be removed. Format [RFC3339]: 2024-12-31T23:59:59Z",
152
+ type: "string",
153
153
  requiresArg: false,
154
154
  },
155
155
  }),
156
156
  handler: (args) => setExpiration(args),
157
157
  })
158
- .command('add-compute <id|name>', 'Add a compute to a branch', (yargs) => yargs.options({
158
+ .command("add-compute <id|name>", "Add a compute to a branch", (yargs) => yargs.options({
159
159
  type: {
160
- type: 'string',
160
+ type: "string",
161
161
  choices: Object.values(EndpointType),
162
- describe: 'Type of compute to add',
162
+ describe: "Type of compute to add",
163
163
  default: EndpointType.ReadOnly,
164
164
  },
165
165
  cu: {
166
166
  describe: 'The number of Compute Units. Could be a fixed size (e.g. "2") or a range delimited by a dash (e.g. "0.5-3").',
167
- type: 'string',
167
+ type: "string",
168
168
  },
169
169
  name: {
170
- type: 'string',
171
- describe: 'Optional name of the compute',
170
+ type: "string",
171
+ describe: "Optional name of the compute",
172
172
  },
173
173
  }), (args) => addCompute(args))
174
- .command('delete <id|name>', 'Delete a branch', (yargs) => yargs, (args) => deleteBranch(args))
175
- .command('get <id|name>', 'Get a branch', (yargs) => yargs, (args) => get(args))
174
+ .command("delete <id|name>", "Delete a branch", (yargs) => yargs, (args) => deleteBranch(args))
175
+ .command("get <id|name>", "Get a branch", (yargs) => yargs, (args) => get(args))
176
176
  .command({
177
- command: 'schema-diff [base-branch] [compare-source[@(timestamp|lsn)]]',
178
- aliases: ['sd'],
177
+ command: "schema-diff [base-branch] [compare-source[@(timestamp|lsn)]]",
178
+ aliases: ["sd"],
179
179
  describe: "Compare the latest schemas of any two branches, or compare a branch to its own or another branch's history.",
180
180
  builder: (yargs) => {
181
181
  return yargs
182
- .middleware((args) => (args.compareSource = args['compare-source@(timestamp']))
182
+ .middleware((args) => (args.compareSource =
183
+ args["compare-source@(timestamp"]))
183
184
  .middleware(parseSchemaDiffParams)
184
185
  .options({
185
186
  database: {
186
- alias: 'db',
187
- type: 'string',
188
- description: 'Name of the database for which the schema comparison is performed',
187
+ alias: "db",
188
+ type: "string",
189
+ description: "Name of the database for which the schema comparison is performed",
189
190
  },
190
191
  })
191
192
  .example([
192
193
  [
193
- '$0 branches schema-diff main br-compare-branch-123456',
194
- 'Compares the main branch to the head of the branch with ID br-compare-branch-123456',
194
+ "$0 branches schema-diff main br-compare-branch-123456",
195
+ "Compares the main branch to the head of the branch with ID br-compare-branch-123456",
195
196
  ],
196
197
  [
197
- '$0 branches schema-diff main compare@2024-06-01T00:00:00Z',
198
- 'Compares the main branch to the state of the compare branch at timestamp 2024-06-01T00:00:00.000Z',
198
+ "$0 branches schema-diff main compare@2024-06-01T00:00:00Z",
199
+ "Compares the main branch to the state of the compare branch at timestamp 2024-06-01T00:00:00.000Z",
199
200
  ],
200
201
  [
201
- '$0 branches schema-diff my-branch ^self@0/123456',
202
- 'Compares my-branch to LSN 0/123456 from its own history',
202
+ "$0 branches schema-diff my-branch ^self@0/123456",
203
+ "Compares my-branch to LSN 0/123456 from its own history",
203
204
  ],
204
205
  [
205
- '$0 branches schema-diff my-branch ^parent',
206
- 'Compares my-branch to the head of its parent branch',
206
+ "$0 branches schema-diff my-branch ^parent",
207
+ "Compares my-branch to the head of its parent branch",
207
208
  ],
208
209
  [
209
- '$0 branches schema-diff',
210
+ "$0 branches schema-diff",
210
211
  "If a branch is specified in 'set-context', compares this branch to its parent. Otherwise, compares the default branch to its parent.",
211
212
  ],
212
213
  ]);
@@ -227,27 +228,27 @@ const list = async (props) => {
227
228
  writer(props).end(branches, {
228
229
  fields: BRANCH_FIELDS,
229
230
  renderColumns: {
230
- expires_at: (br) => br.expires_at || 'never',
231
+ expires_at: (br) => br.expires_at || "never",
231
232
  // Word labels (not symbols) so they read clearly and match the existing `[anon]`.
232
233
  name: (br) => {
233
234
  const annotation = annotations[br.id];
234
235
  const isAnon = annotation?.value.anonymized;
235
236
  const labels = [];
236
237
  if (br.default) {
237
- labels.push('[default]');
238
+ labels.push("[default]");
238
239
  }
239
240
  if (br.protected) {
240
- labels.push('[protected]');
241
+ labels.push("[protected]");
241
242
  }
242
243
  if (isAnon) {
243
- labels.push('[anon]');
244
+ labels.push("[anon]");
244
245
  }
245
246
  if (currentBranch !== undefined &&
246
247
  (br.id === currentBranch || br.name === currentBranch)) {
247
- labels.push('[current]');
248
+ labels.push("[current]");
248
249
  }
249
250
  labels.push(br.name);
250
- return labels.join(' ');
251
+ return labels.join(" ");
251
252
  },
252
253
  },
253
254
  });
@@ -260,7 +261,7 @@ const create = async (props) => {
260
261
  if (!props.parent) {
261
262
  const branch = branches.find((b) => b.default);
262
263
  if (!branch) {
263
- throw new Error('No default branch found');
264
+ throw new Error("No default branch found");
264
265
  }
265
266
  return { parent_id: branch.id };
266
267
  }
@@ -282,27 +283,33 @@ const create = async (props) => {
282
283
  // Validate schema-only branch requirements
283
284
  if (props.schemaOnly) {
284
285
  if (!props.compute) {
285
- throw new Error('Schema-only branches require a compute endpoint');
286
+ throw new Error("Schema-only branches require a compute endpoint");
286
287
  }
287
288
  if (props.type !== EndpointType.ReadWrite) {
288
- throw new Error('Schema-only branches require a read-write compute endpoint');
289
+ throw new Error("Schema-only branches require a read-write compute endpoint");
289
290
  }
290
291
  }
291
292
  const { data } = await retryOnLock(() => props.apiClient.createProjectBranch(props.projectId, {
292
293
  branch: {
293
294
  name: props.name,
294
295
  ...parentProps,
295
- ...(props.schemaOnly ? { init_source: 'schema-only' } : {}),
296
- ...(props['expires-at']
297
- ? { expires_at: new Date(props['expires-at']).toISOString() }
296
+ ...(props.schemaOnly ? { init_source: "schema-only" } : {}),
297
+ ...(props["expires-at"]
298
+ ? {
299
+ expires_at: new Date(props["expires-at"]).toISOString(),
300
+ }
298
301
  : {}),
299
302
  },
300
303
  endpoints: props.compute
301
304
  ? [
302
305
  {
303
306
  type: props.type,
304
- suspend_timeout_seconds: props.suspendTimeout === 0 ? undefined : props.suspendTimeout,
305
- ...(props.cu ? getComputeUnits(props.cu) : undefined),
307
+ suspend_timeout_seconds: props.suspendTimeout === 0
308
+ ? undefined
309
+ : props.suspendTimeout,
310
+ ...(props.cu
311
+ ? getComputeUnits(props.cu)
312
+ : undefined),
306
313
  },
307
314
  ]
308
315
  : [],
@@ -312,26 +319,26 @@ const create = async (props) => {
312
319
  }));
313
320
  const parent = branches.find((b) => b.id === data.branch.parent_id);
314
321
  if (parent?.protected) {
315
- log.warning('The parent branch is protected; a unique role password has been generated for the new branch.');
322
+ log.warning("The parent branch is protected; a unique role password has been generated for the new branch.");
316
323
  }
317
324
  const out = writer(props);
318
325
  out.write(data.branch, {
319
326
  fields: BRANCH_FIELDS,
320
- title: 'branch',
321
- emptyMessage: 'No branches have been found.',
327
+ title: "branch",
328
+ emptyMessage: "No branches have been found.",
322
329
  });
323
330
  if (data.endpoints?.length > 0) {
324
331
  out.write(data.endpoints, {
325
- fields: ['id', 'created_at'],
326
- title: 'endpoints',
327
- emptyMessage: 'No endpoints have been found.',
332
+ fields: ["id", "created_at"],
333
+ title: "endpoints",
334
+ emptyMessage: "No endpoints have been found.",
328
335
  });
329
336
  }
330
337
  if (data.connection_uris?.length) {
331
338
  out.write(data.connection_uris, {
332
- fields: ['connection_uri'],
333
- title: 'connection_uris',
334
- emptyMessage: 'No connection uris have been found',
339
+ fields: ["connection_uri"],
340
+ title: "connection_uris",
341
+ emptyMessage: "No connection uris have been found",
335
342
  });
336
343
  }
337
344
  out.end();
@@ -340,9 +347,9 @@ const create = async (props) => {
340
347
  throw new Error(`Branch ${data.branch.id} doesn't have a connection uri`);
341
348
  }
342
349
  const connection_uri = data.connection_uris[0].connection_uri;
343
- const psqlArgs = props['--'];
350
+ const psqlArgs = props["--"];
344
351
  await psql(connection_uri, psqlArgs, {
345
- mode: props.fallback ? 'ts' : 'auto',
352
+ mode: props.fallback ? "ts" : "auto",
346
353
  });
347
354
  }
348
355
  };
@@ -367,9 +374,12 @@ const setDefault = async (props) => {
367
374
  const deleteBranch = async (props) => {
368
375
  const branchId = await branchIdFromProps(props);
369
376
  const { data } = await retryOnLock(() => props.apiClient.deleteProjectBranch(props.projectId, branchId));
370
- writer(props).end(data.branch, {
371
- fields: BRANCH_FIELDS,
372
- });
377
+ // A 204 (branch already gone) carries no body; only a 200 returns it.
378
+ if (data) {
379
+ writer(props).end(data.branch, {
380
+ fields: BRANCH_FIELDS,
381
+ });
382
+ }
373
383
  };
374
384
  const get = async (props) => {
375
385
  const branchId = await branchIdFromProps(props);
@@ -390,17 +400,17 @@ const addCompute = async (props) => {
390
400
  },
391
401
  }));
392
402
  writer(props).end(data.endpoint, {
393
- fields: ['id', 'host'],
403
+ fields: ["id", "host"],
394
404
  });
395
405
  };
396
406
  const reset = async (props) => {
397
407
  if (!props.parent) {
398
- throw new Error('Only resetting to parent is supported for now');
408
+ throw new Error("Only resetting to parent is supported for now");
399
409
  }
400
410
  const branchId = await branchIdFromProps(props);
401
411
  const { data: { branch: { parent_id }, }, } = await props.apiClient.getProjectBranch(props.projectId, branchId);
402
412
  if (!parent_id) {
403
- throw new Error('Branch has no parent');
413
+ throw new Error("Branch has no parent");
404
414
  }
405
415
  const { data } = await retryOnLock(() => props.apiClient.restoreProjectBranch(props.projectId, branchId, {
406
416
  source_branch_id: parent_id,
@@ -423,30 +433,30 @@ const restore = async (props) => {
423
433
  projectId: props.projectId,
424
434
  api: props.apiClient,
425
435
  });
426
- log.info(`Restoring branch ${targetBranchId} to the branch ${pointInTime.branchId} ${(pointInTime.tag === 'lsn' && 'LSN ' + pointInTime.lsn) ||
427
- (pointInTime.tag === 'timestamp' &&
428
- 'timestamp ' + pointInTime.timestamp) ||
429
- 'head'}`);
436
+ log.info(`Restoring branch ${targetBranchId} to the branch ${pointInTime.branchId} ${(pointInTime.tag === "lsn" && "LSN " + pointInTime.lsn) ||
437
+ (pointInTime.tag === "timestamp" &&
438
+ "timestamp " + pointInTime.timestamp) ||
439
+ "head"}`);
430
440
  const { data } = await retryOnLock(() => props.apiClient.restoreProjectBranch(props.projectId, targetBranchId, {
431
441
  source_branch_id: pointInTime.branchId,
432
442
  preserve_under_name: props.preserveUnderName || undefined,
433
- ...(pointInTime.tag === 'lsn' && { source_lsn: pointInTime.lsn }),
434
- ...(pointInTime.tag === 'timestamp' && {
443
+ ...(pointInTime.tag === "lsn" && { source_lsn: pointInTime.lsn }),
444
+ ...(pointInTime.tag === "timestamp" && {
435
445
  source_timestamp: pointInTime.timestamp,
436
446
  }),
437
447
  }));
438
448
  const writeInst = writer(props).write(data.branch, {
439
- title: 'Restored branch',
440
- fields: ['id', 'name', 'last_reset_at'],
441
- emptyMessage: 'No branches have been restored.',
449
+ title: "Restored branch",
450
+ fields: ["id", "name", "last_reset_at"],
451
+ emptyMessage: "No branches have been restored.",
442
452
  });
443
453
  const parentId = data.branch.parent_id;
444
454
  if (props.preserveUnderName && parentId) {
445
455
  const { data } = await props.apiClient.getProjectBranch(props.projectId, parentId);
446
456
  writeInst.write(data.branch, {
447
- title: 'Backup branch',
448
- fields: ['id', 'name'],
449
- emptyMessage: 'Backup branch has not been found.',
457
+ title: "Backup branch",
458
+ fields: ["id", "name"],
459
+ emptyMessage: "Backup branch has not been found.",
450
460
  });
451
461
  }
452
462
  writeInst.end();
@@ -457,7 +467,7 @@ const setExpiration = async (props) => {
457
467
  ...props,
458
468
  id: props.branchId,
459
469
  });
460
- const expiresAt = typeof props.expiresAt === 'string'
470
+ const expiresAt = typeof props.expiresAt === "string"
461
471
  ? new Date(props.expiresAt).toISOString()
462
472
  : null;
463
473
  const { data } = await retryOnLock(() => props.apiClient.updateProjectBranch(props.projectId, branchId, {