neonctl 2.22.2 → 2.23.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 (113) hide show
  1. package/README.md +84 -0
  2. package/analytics.js +5 -2
  3. package/commands/branches.js +9 -1
  4. package/commands/connection_string.js +9 -1
  5. package/commands/functions.js +277 -0
  6. package/commands/index.js +4 -0
  7. package/commands/neon_auth.js +1013 -0
  8. package/commands/projects.js +9 -1
  9. package/commands/psql.js +6 -1
  10. package/functions_api.js +44 -0
  11. package/package.json +15 -5
  12. package/psql/cli.js +51 -0
  13. package/psql/command/cmd_cond.js +437 -0
  14. package/psql/command/cmd_connect.js +815 -0
  15. package/psql/command/cmd_copy.js +1025 -0
  16. package/psql/command/cmd_describe.js +1810 -0
  17. package/psql/command/cmd_format.js +909 -0
  18. package/psql/command/cmd_io.js +2187 -0
  19. package/psql/command/cmd_lo.js +385 -0
  20. package/psql/command/cmd_meta.js +970 -0
  21. package/psql/command/cmd_misc.js +187 -0
  22. package/psql/command/cmd_pipeline.js +1141 -0
  23. package/psql/command/cmd_restrict.js +171 -0
  24. package/psql/command/cmd_show.js +751 -0
  25. package/psql/command/dispatch.js +343 -0
  26. package/psql/command/inputQueue.js +42 -0
  27. package/psql/command/shared.js +71 -0
  28. package/psql/complete/filenames.js +139 -0
  29. package/psql/complete/index.js +104 -0
  30. package/psql/complete/matcher.js +314 -0
  31. package/psql/complete/psqlVars.js +247 -0
  32. package/psql/complete/queries.js +491 -0
  33. package/psql/complete/rules.js +2387 -0
  34. package/psql/core/common.js +1250 -0
  35. package/psql/core/help.js +576 -0
  36. package/psql/core/mainloop.js +1353 -0
  37. package/psql/core/prompt.js +437 -0
  38. package/psql/core/settings.js +684 -0
  39. package/psql/core/sqlHelp.js +1066 -0
  40. package/psql/core/startup.js +840 -0
  41. package/psql/core/syncVars.js +116 -0
  42. package/psql/core/variables.js +287 -0
  43. package/psql/describe/formatters.js +1277 -0
  44. package/psql/describe/processNamePattern.js +270 -0
  45. package/psql/describe/queries.js +2373 -0
  46. package/psql/describe/versionGate.js +43 -0
  47. package/psql/index.js +2005 -0
  48. package/psql/io/history.js +299 -0
  49. package/psql/io/input.js +120 -0
  50. package/psql/io/lineEditor/buffer.js +323 -0
  51. package/psql/io/lineEditor/complete.js +227 -0
  52. package/psql/io/lineEditor/filename.js +159 -0
  53. package/psql/io/lineEditor/index.js +891 -0
  54. package/psql/io/lineEditor/keymap.js +738 -0
  55. package/psql/io/lineEditor/vt100.js +363 -0
  56. package/psql/io/pgpass.js +202 -0
  57. package/psql/io/pgservice.js +194 -0
  58. package/psql/io/psqlrc.js +422 -0
  59. package/psql/print/aligned.js +1756 -0
  60. package/psql/print/asciidoc.js +248 -0
  61. package/psql/print/crosstab.js +460 -0
  62. package/psql/print/csv.js +92 -0
  63. package/psql/print/html.js +258 -0
  64. package/psql/print/json.js +96 -0
  65. package/psql/print/latex.js +396 -0
  66. package/psql/print/pager.js +265 -0
  67. package/psql/print/troff.js +258 -0
  68. package/psql/print/unaligned.js +118 -0
  69. package/psql/print/units.js +135 -0
  70. package/psql/scanner/slash.js +513 -0
  71. package/psql/scanner/sql.js +910 -0
  72. package/psql/scanner/stringutils.js +390 -0
  73. package/psql/types/backslash.js +1 -0
  74. package/psql/types/connection.js +1 -0
  75. package/psql/types/index.js +7 -0
  76. package/psql/types/printer.js +1 -0
  77. package/psql/types/repl.js +1 -0
  78. package/psql/types/scanner.js +24 -0
  79. package/psql/types/settings.js +1 -0
  80. package/psql/types/variables.js +1 -0
  81. package/psql/wire/connection.js +2844 -0
  82. package/psql/wire/copy.js +108 -0
  83. package/psql/wire/notify.js +59 -0
  84. package/psql/wire/pipeline.js +519 -0
  85. package/psql/wire/protocol.js +466 -0
  86. package/psql/wire/sasl.js +296 -0
  87. package/psql/wire/tls.js +596 -0
  88. package/test_utils/fixtures.js +1 -0
  89. package/utils/esbuild.js +147 -0
  90. package/utils/psql.js +107 -11
  91. package/utils/zip.js +4 -0
  92. package/writer.js +1 -1
  93. package/commands/auth.test.js +0 -211
  94. package/commands/branches.test.js +0 -460
  95. package/commands/checkout.test.js +0 -170
  96. package/commands/connection_string.test.js +0 -196
  97. package/commands/data_api.test.js +0 -169
  98. package/commands/databases.test.js +0 -39
  99. package/commands/help.test.js +0 -9
  100. package/commands/init.test.js +0 -56
  101. package/commands/ip_allow.test.js +0 -59
  102. package/commands/link.test.js +0 -381
  103. package/commands/operations.test.js +0 -7
  104. package/commands/orgs.test.js +0 -7
  105. package/commands/projects.test.js +0 -144
  106. package/commands/psql.test.js +0 -49
  107. package/commands/roles.test.js +0 -37
  108. package/commands/set_context.test.js +0 -159
  109. package/commands/vpc_endpoints.test.js +0 -69
  110. package/context.test.js +0 -119
  111. package/env.test.js +0 -55
  112. package/utils/formats.test.js +0 -32
  113. package/writer.test.js +0 -104
@@ -1,196 +0,0 @@
1
- import { describe, expect } from 'vitest';
2
- import { test } from '../test_utils/fixtures';
3
- describe('connection_string', () => {
4
- test('connection_string', async ({ testCliCommand }) => {
5
- await testCliCommand([
6
- 'connection-string',
7
- 'test_branch',
8
- '--project-id',
9
- 'test',
10
- '--database-name',
11
- 'test_db',
12
- '--role-name',
13
- 'test_role',
14
- ]);
15
- });
16
- test('connection_string branch id', async ({ testCliCommand }) => {
17
- await testCliCommand([
18
- 'connection-string',
19
- 'br-sunny-branch-123456',
20
- '--project-id',
21
- 'test',
22
- '--database-name',
23
- 'test_db',
24
- '--role-name',
25
- 'test_role',
26
- ]);
27
- });
28
- test('connection_string branch id 8 digits', async ({ testCliCommand }) => {
29
- await testCliCommand([
30
- 'connection-string',
31
- 'br-cloudy-branch-12345678',
32
- '--project-id',
33
- 'test',
34
- '--database-name',
35
- 'test_db',
36
- '--role-name',
37
- 'test_role',
38
- ]);
39
- });
40
- test('connection_string pooled', async ({ testCliCommand }) => {
41
- await testCliCommand([
42
- 'connection-string',
43
- 'test_branch',
44
- '--project-id',
45
- 'test',
46
- '--database-name',
47
- 'test_db',
48
- '--role-name',
49
- 'test_role',
50
- '--pooled',
51
- ]);
52
- });
53
- test('connection_string prisma', async ({ testCliCommand }) => {
54
- await testCliCommand([
55
- 'connection-string',
56
- 'test_branch',
57
- '--project-id',
58
- 'test',
59
- '--database-name',
60
- 'test_db',
61
- '--role-name',
62
- 'test_role',
63
- '--prisma',
64
- ]);
65
- });
66
- test('connection_string prisma pooled', async ({ testCliCommand }) => {
67
- await testCliCommand([
68
- 'connection-string',
69
- 'test_branch',
70
- '--project-id',
71
- 'test',
72
- '--database-name',
73
- 'test_db',
74
- '--role-name',
75
- 'test_role',
76
- '--prisma',
77
- '--pooled',
78
- ]);
79
- });
80
- test('connection_string prisma pooled extended', async ({ testCliCommand, }) => {
81
- await testCliCommand([
82
- 'connection-string',
83
- 'test_branch',
84
- '--project-id',
85
- 'test',
86
- '--database-name',
87
- 'test_db',
88
- '--role-name',
89
- 'test_role',
90
- '--prisma',
91
- '--pooled',
92
- '--extended',
93
- ]);
94
- });
95
- test('connection_string without any args should pass', async ({ testCliCommand, }) => {
96
- await testCliCommand(['connection-string'], {
97
- mockDir: 'single_project',
98
- });
99
- });
100
- test('connection_string with psql', async ({ testCliCommand }) => {
101
- await testCliCommand([
102
- 'connection-string',
103
- 'test_branch',
104
- '--project-id',
105
- 'test',
106
- '--database-name',
107
- 'test_db',
108
- '--role-name',
109
- 'test_role',
110
- '--psql',
111
- ]);
112
- });
113
- test('connection_string with psql and psql args', async ({ testCliCommand, }) => {
114
- await testCliCommand([
115
- 'connection-string',
116
- 'test_branch',
117
- '--project-id',
118
- 'test',
119
- '--database-name',
120
- 'test_db',
121
- '--role-name',
122
- 'test_role',
123
- '--psql',
124
- '--',
125
- '-c',
126
- 'SELECT 1',
127
- ]);
128
- });
129
- test('connection_string without ssl', async ({ testCliCommand }) => {
130
- await testCliCommand([
131
- 'connection-string',
132
- 'test_branch',
133
- '--project-id',
134
- 'test',
135
- '--database-name',
136
- 'test_db',
137
- '--role-name',
138
- 'test_role',
139
- '--ssl',
140
- 'omit',
141
- ]);
142
- });
143
- test('connection_string with ssl verify full', async ({ testCliCommand }) => {
144
- await testCliCommand([
145
- 'connection-string',
146
- 'test_branch',
147
- '--project-id',
148
- 'test',
149
- '--database-name',
150
- 'test_db',
151
- '--role-name',
152
- 'test_role',
153
- '--ssl',
154
- 'verify-full',
155
- ]);
156
- });
157
- test('connection_string with lsn', async ({ testCliCommand }) => {
158
- await testCliCommand([
159
- 'connection-string',
160
- 'test_branch@0/123456',
161
- '--project-id',
162
- 'test',
163
- '--database-name',
164
- 'test_db',
165
- '--role-name',
166
- 'test_role',
167
- ]);
168
- });
169
- test('connection_string with timestamp', async ({ testCliCommand }) => {
170
- await testCliCommand([
171
- 'connection-string',
172
- 'test_branch@2021-01-01T00:00:00Z',
173
- '--project-id',
174
- 'test',
175
- '--database-name',
176
- 'test_db',
177
- '--role-name',
178
- 'test_role',
179
- ]);
180
- });
181
- test('connection_string fails for non-existing database', async ({ testCliCommand, }) => {
182
- await testCliCommand([
183
- 'connection-string',
184
- 'test_branch',
185
- '--project-id',
186
- 'test',
187
- '--database-name',
188
- 'non_existing_db',
189
- '--role-name',
190
- 'test_role',
191
- ], {
192
- code: 1,
193
- stderr: expect.stringMatching(/Database not found/),
194
- });
195
- });
196
- });
@@ -1,169 +0,0 @@
1
- import { describe } from 'vitest';
2
- import { test } from '../test_utils/fixtures';
3
- describe('data-api', () => {
4
- test('delete', async ({ testCliCommand }) => {
5
- await testCliCommand([
6
- 'data-api',
7
- 'delete',
8
- '--project-id',
9
- 'test-project-123456',
10
- '--branch',
11
- 'main',
12
- '--database',
13
- 'db1',
14
- ], {
15
- mockDir: 'single_org',
16
- stderr: 'INFO: Data API deleted for db1 on branch br-main-branch-123456',
17
- });
18
- });
19
- test('get', async ({ testCliCommand }) => {
20
- await testCliCommand([
21
- 'data-api',
22
- 'get',
23
- '--project-id',
24
- 'test-project-123456',
25
- '--branch',
26
- 'main',
27
- '--database',
28
- 'db1',
29
- ], { mockDir: 'single_org' });
30
- });
31
- test('get with table output', async ({ testCliCommand }) => {
32
- await testCliCommand([
33
- 'data-api',
34
- 'get',
35
- '--project-id',
36
- 'test-project-123456',
37
- '--branch',
38
- 'main',
39
- '--database',
40
- 'db1',
41
- ], { mockDir: 'single_org', outputTable: true });
42
- });
43
- test('create', async ({ testCliCommand }) => {
44
- await testCliCommand([
45
- 'data-api',
46
- 'create',
47
- '--project-id',
48
- 'test-project-123456',
49
- '--branch',
50
- 'main',
51
- '--database',
52
- 'db1',
53
- '--auth-provider',
54
- 'neon_auth',
55
- '--add-default-grants',
56
- '--db-schemas',
57
- 'public,analytics',
58
- '--db-max-rows',
59
- '500',
60
- ], { mockDir: 'single_org' });
61
- });
62
- test('update --replace', async ({ testCliCommand }) => {
63
- await testCliCommand([
64
- 'data-api',
65
- 'update',
66
- '--project-id',
67
- 'test-project-123456',
68
- '--branch',
69
- 'main',
70
- '--database',
71
- 'db1',
72
- '--replace',
73
- '--db-max-rows',
74
- '250',
75
- ], {
76
- mockDir: 'single_org',
77
- stderr: 'INFO: Data API settings updated for db1 on branch br-main-branch-123456',
78
- });
79
- });
80
- test('update merges with existing settings', async ({ testCliCommand }) => {
81
- await testCliCommand([
82
- 'data-api',
83
- 'update',
84
- '--project-id',
85
- 'test-project-123456',
86
- '--branch',
87
- 'main',
88
- '--database',
89
- 'db1',
90
- '--db-max-rows',
91
- '9999',
92
- ], {
93
- mockDir: 'single_org',
94
- stderr: 'INFO: Data API settings updated for db1 on branch br-main-branch-123456',
95
- });
96
- });
97
- test('update fails when no settings flags are passed', async ({ testCliCommand, }) => {
98
- await testCliCommand([
99
- 'data-api',
100
- 'update',
101
- '--project-id',
102
- 'test-project-123456',
103
- '--branch',
104
- 'main',
105
- '--database',
106
- 'db1',
107
- '--replace',
108
- ], {
109
- mockDir: 'single_org',
110
- code: 1,
111
- stderr: 'ERROR: No settings flags provided. Pass at least one setting flag to update, or use `data-api refresh-schema` to refresh the schema cache without changing settings.',
112
- });
113
- });
114
- test('refresh-schema sends empty PATCH', async ({ testCliCommand }) => {
115
- await testCliCommand([
116
- 'data-api',
117
- 'refresh-schema',
118
- '--project-id',
119
- 'test-project-123456',
120
- '--branch',
121
- 'main',
122
- '--database',
123
- 'db1',
124
- ], {
125
- mockDir: 'single_org',
126
- stderr: 'INFO: Data API schema cache refreshed for db1 on branch br-main-branch-123456',
127
- });
128
- });
129
- test('delete with implicit database', async ({ testCliCommand }) => {
130
- // The single_org fixture has exactly one database (db1) on the main branch,
131
- // so omitting --database should resolve it automatically.
132
- await testCliCommand([
133
- 'data-api',
134
- 'delete',
135
- '--project-id',
136
- 'test-project-123456',
137
- '--branch',
138
- 'main',
139
- ], {
140
- mockDir: 'single_org',
141
- stderr: 'INFO: Data API deleted for db1 on branch br-main-branch-123456',
142
- });
143
- });
144
- test('get with fully implicit resolution', async ({ testCliCommand }) => {
145
- // single_org has one org, one project, one default branch (main), one
146
- // database (db1) — all three should be auto-resolved when omitted.
147
- await testCliCommand(['data-api', 'get'], { mockDir: 'single_org' });
148
- });
149
- test('update with fully implicit resolution', async ({ testCliCommand }) => {
150
- await testCliCommand(['data-api', 'update', '--db-max-rows', '9999'], {
151
- mockDir: 'single_org',
152
- stderr: 'INFO: Data API settings updated for db1 on branch br-main-branch-123456',
153
- });
154
- });
155
- test('delete fails with helpful error when multiple databases', async ({ testCliCommand, }) => {
156
- await testCliCommand([
157
- 'data-api',
158
- 'delete',
159
- '--project-id',
160
- 'test-project-123456',
161
- '--branch',
162
- 'main',
163
- ], {
164
- mockDir: 'single_org_multidb',
165
- code: 1,
166
- stderr: 'ERROR: Multiple databases found for the branch, please provide one with the --database option: db1, db2',
167
- });
168
- });
169
- });
@@ -1,39 +0,0 @@
1
- import { describe } from 'vitest';
2
- import { test } from '../test_utils/fixtures';
3
- describe('databases', () => {
4
- test('list', async ({ testCliCommand }) => {
5
- await testCliCommand([
6
- 'databases',
7
- 'list',
8
- '--project-id',
9
- 'test',
10
- '--branch',
11
- 'test_branch',
12
- ]);
13
- });
14
- test('create', async ({ testCliCommand }) => {
15
- await testCliCommand([
16
- 'databases',
17
- 'create',
18
- '--project-id',
19
- 'test',
20
- '--branch',
21
- 'test_branch',
22
- '--name',
23
- 'test_db',
24
- '--owner-name',
25
- 'test_owner',
26
- ]);
27
- });
28
- test('delete', async ({ testCliCommand }) => {
29
- await testCliCommand([
30
- 'databases',
31
- 'delete',
32
- 'test_db',
33
- '--project-id',
34
- 'test',
35
- '--branch',
36
- 'test_branch',
37
- ]);
38
- });
39
- });
@@ -1,9 +0,0 @@
1
- import { describe, expect } from 'vitest';
2
- import { test } from '../test_utils/fixtures';
3
- describe('help', () => {
4
- test('without args', async ({ testCliCommand }) => {
5
- await testCliCommand([], {
6
- stderr: expect.stringContaining(`neonctl <command> [options]`),
7
- });
8
- });
9
- });
@@ -1,56 +0,0 @@
1
- import { afterEach, describe, expect, test, vi } from 'vitest';
2
- // Mock dependencies that require package.json
3
- vi.mock('../analytics.js', () => ({
4
- sendError: vi.fn(),
5
- trackEvent: vi.fn(),
6
- closeAnalytics: vi.fn(),
7
- }));
8
- vi.mock('../log.js', () => ({
9
- log: { error: vi.fn(), info: vi.fn(), warning: vi.fn(), debug: vi.fn() },
10
- }));
11
- // Mock neon-init
12
- vi.mock('neon-init', () => ({
13
- init: vi.fn().mockResolvedValue(undefined),
14
- }));
15
- describe('init', () => {
16
- const exitSpy = vi
17
- .spyOn(process, 'exit')
18
- .mockImplementation((() => undefined));
19
- afterEach(() => {
20
- vi.clearAllMocks();
21
- });
22
- test('should call neon-init with no options when agent is omitted', async () => {
23
- const { handler } = await import('./init.js');
24
- const { init } = await import('neon-init');
25
- await handler({});
26
- expect(init).toHaveBeenCalledTimes(1);
27
- expect(init).toHaveBeenCalledWith(undefined);
28
- });
29
- test('should call neon-init with { agent: "Cursor" } when --agent cursor', async () => {
30
- const { handler } = await import('./init.js');
31
- const { init } = await import('neon-init');
32
- await handler({ agent: 'cursor' });
33
- expect(init).toHaveBeenCalledWith({ agent: 'Cursor' });
34
- });
35
- test('should call neon-init with { agent: "VS Code" } when --agent copilot', async () => {
36
- const { handler } = await import('./init.js');
37
- const { init } = await import('neon-init');
38
- await handler({ agent: 'copilot' });
39
- expect(init).toHaveBeenCalledWith({ agent: 'VS Code' });
40
- });
41
- test('should call neon-init with { agent: "Claude CLI" } when --agent claude', async () => {
42
- const { handler } = await import('./init.js');
43
- const { init } = await import('neon-init');
44
- await handler({ agent: 'claude' });
45
- expect(init).toHaveBeenCalledWith({ agent: 'Claude CLI' });
46
- });
47
- test('should log error and exit 1 when --agent is invalid', async () => {
48
- const { handler } = await import('./init.js');
49
- const { init } = await import('neon-init');
50
- const { log } = await import('../log.js');
51
- await handler({ agent: 'invalid-agent' });
52
- expect(init).not.toHaveBeenCalled();
53
- expect(log.error).toHaveBeenCalledWith('Invalid --agent value: "invalid-agent". Supported: cursor, copilot, claude');
54
- expect(exitSpy).toHaveBeenCalledWith(1);
55
- });
56
- });
@@ -1,59 +0,0 @@
1
- import { describe } from 'vitest';
2
- import { test } from '../test_utils/fixtures';
3
- describe('ip-allow', () => {
4
- test('list IP allow', async ({ testCliCommand }) => {
5
- await testCliCommand(['ip-allow', 'list', '--project-id', 'test']);
6
- });
7
- test('list IP Allow with single-project', async ({ testCliCommand }) => {
8
- await testCliCommand(['ip-allow', 'list'], {
9
- mockDir: 'single_project',
10
- });
11
- });
12
- test('Add IP allow - Error', async ({ testCliCommand }) => {
13
- await testCliCommand(['ip-allow', 'add', '--projectId', 'test'], {
14
- code: 1,
15
- stderr: `ERROR: Enter individual IP addresses, define ranges with a dash, or use CIDR notation for more flexibility.
16
- Example: neonctl ip-allow add 192.168.1.1, 192.168.1.20-192.168.1.50, 192.168.1.0/24 --project-id <id>`,
17
- });
18
- });
19
- test('Add IP allow - Protected', async ({ testCliCommand }) => {
20
- await testCliCommand([
21
- 'ip-allow',
22
- 'add',
23
- '127.0.0.1',
24
- '192.168.10.1-192.168.10.15',
25
- '--protected-only',
26
- '--project-id',
27
- 'test',
28
- ]);
29
- });
30
- test('Remove IP allow - Error', async ({ testCliCommand }) => {
31
- await testCliCommand(['ip-allow', 'remove', '--project-id', 'test'], {
32
- code: 1,
33
- stderr: `ERROR: Remove individual IP addresses and ranges. Example: neonctl ip-allow remove 192.168.1.1 --project-id <id>`,
34
- });
35
- });
36
- test('Remove IP allow', async ({ testCliCommand }) => {
37
- await testCliCommand([
38
- 'ip-allow',
39
- 'remove',
40
- '192.168.1.1',
41
- '--project-id',
42
- 'test',
43
- ]);
44
- });
45
- test('Reset IP allow', async ({ testCliCommand }) => {
46
- await testCliCommand(['ip-allow', 'reset', '--project-id', 'test'], {
47
- stderr: `INFO: The IP allowlist has been reset. All databases on project "test_project" are now exposed to the internet`,
48
- });
49
- });
50
- test('Reset IP allow to new list', async ({ testCliCommand }) => {
51
- await testCliCommand([
52
- 'ip-allow',
53
- 'reset',
54
- '192.168.2.2',
55
- '--project-id',
56
- 'test',
57
- ]);
58
- });
59
- });