neon-testing 2.6.2 → 3.0.0-beta.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.
package/dist/index.d.ts DELETED
@@ -1,113 +0,0 @@
1
- /**
2
- * https://neon.com/docs/reference/typescript-sdk
3
- */
4
- import { type Branch } from "@neondatabase/api-client";
5
- export interface NeonTestingOptions {
6
- /**
7
- * The Neon API key, this is used to create and teardown test branches (required)
8
- *
9
- * https://neon.com/docs/manage/api-keys#creating-api-keys
10
- */
11
- apiKey: string;
12
- /**
13
- * The Neon project ID to operate on (required)
14
- *
15
- * https://console.neon.tech/app/projects
16
- */
17
- projectId: string;
18
- /**
19
- * The parent branch ID for the new branch (default: undefined)
20
- *
21
- * If omitted or undefined, test branches will be created from the project's
22
- * default branch.
23
- */
24
- parentBranchId?: string;
25
- /**
26
- * Whether to create a schema-only branch (default: false)
27
- */
28
- schemaOnly?: boolean;
29
- /**
30
- * The type of connection to create (default: "pooler")
31
- */
32
- endpoint?: "pooler" | "direct";
33
- /**
34
- * Delete the test branch in afterAll (default: true)
35
- *
36
- * Disabling this will leave each test branch in the Neon project after the
37
- * test suite runs
38
- */
39
- deleteBranch?: boolean;
40
- /**
41
- * Automatically close Neon WebSocket connections opened during tests before
42
- * deleting the branch (default: false)
43
- *
44
- * Suppresses the specific Neon WebSocket "Connection terminated unexpectedly"
45
- * error that may surface when deleting a branch with open WebSocket
46
- * connections
47
- */
48
- autoCloseWebSockets?: boolean;
49
- /**
50
- * Time in seconds until the branch expires and is automatically deleted
51
- * (default: 600 = 10 minutes)
52
- *
53
- * This provides automatic cleanup for dangling branches from interrupted or
54
- * failed test runs. Set to `null` to disable automatic expiration.
55
- *
56
- * Must be a positive integer. Maximum 30 days (2,592,000 seconds).
57
- *
58
- * https://neon.com/docs/guides/branch-expiration
59
- */
60
- expiresIn?: number | null;
61
- /**
62
- * The database role to connect as (default: project owner role)
63
- *
64
- * The role must exist in the parent branch. Roles are automatically
65
- * copied to test branches when branching.
66
- */
67
- roleName?: string;
68
- /**
69
- * The database to connect to (default: project default database)
70
- */
71
- databaseName?: string;
72
- }
73
- /** Options for overriding test database setup (excludes apiKey) */
74
- export type NeonTestingOverrides = Omit<Partial<NeonTestingOptions>, "apiKey">;
75
- /**
76
- * Factory function that creates a Neon test database setup/teardown function
77
- * for Vitest test suites.
78
- *
79
- * @param factoryOptions - Configuration options (see {@link NeonTestingOptions})
80
- * @returns A setup/teardown function with attached utilities:
81
- * - `deleteAllTestBranches()` - Cleanup method to delete all test branches
82
- * - `api` - Direct access to the Neon API client
83
- *
84
- * @example
85
- * ```ts
86
- * // neon-testing.ts
87
- * import { makeNeonTesting } from "neon-testing";
88
- *
89
- * export const neonTesting = makeNeonTesting({
90
- * apiKey: "apiKey",
91
- * projectId: "projectId",
92
- * });
93
- * ```
94
- *
95
- * @example
96
- * ```ts
97
- * // my-test.test.ts
98
- * import { neonTesting } from "./neon-testing";
99
- *
100
- * const getBranch = neonTesting();
101
- *
102
- * test("my test", () => {
103
- * const branch = getBranch();
104
- * console.log(branch.id);
105
- * });
106
- * ```
107
- */
108
- export declare function makeNeonTesting(factoryOptions: NeonTestingOptions): {
109
- (overrides?: NeonTestingOverrides): () => Branch | undefined;
110
- deleteAllTestBranches: () => Promise<void>;
111
- api: import("@neondatabase/api-client").Api<unknown>;
112
- };
113
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAGL,KAAK,MAAM,EACZ,MAAM,0BAA0B,CAAC;AAsBlC,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;OAEG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;OAEG;IACH,QAAQ,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC/B;;;;;OAKG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;;;;OAOG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B;;;;;;;;;;OAUG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,mEAAmE;AACnE,MAAM,MAAM,oBAAoB,GAAG,IAAI,CAAC,OAAO,CAAC,kBAAkB,CAAC,EAAE,QAAQ,CAAC,CAAC;AAE/E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,eAAe,CAAC,cAAc,EAAE,kBAAkB;iBAyClD,oBAAoB;;;EAyKnC"}
package/dist/index.js DELETED
@@ -1,272 +0,0 @@
1
- /**
2
- * https://neon.com/docs/reference/typescript-sdk
3
- */
4
- import { createApiClient, EndpointType, } from "@neondatabase/api-client";
5
- import { afterAll, beforeAll } from "vitest";
6
- /**
7
- * Validates the expiresIn option
8
- */
9
- function validateExpiresIn(expiresIn) {
10
- if (expiresIn !== null && expiresIn !== undefined) {
11
- if (!Number.isInteger(expiresIn)) {
12
- throw new Error("expiresIn must be an integer");
13
- }
14
- if (expiresIn <= 0) {
15
- throw new Error("expiresIn must be a positive integer");
16
- }
17
- if (expiresIn > 2592000) {
18
- throw new Error("expiresIn must not exceed 30 days (2,592,000 seconds)");
19
- }
20
- }
21
- }
22
- /**
23
- * Factory function that creates a Neon test database setup/teardown function
24
- * for Vitest test suites.
25
- *
26
- * @param factoryOptions - Configuration options (see {@link NeonTestingOptions})
27
- * @returns A setup/teardown function with attached utilities:
28
- * - `deleteAllTestBranches()` - Cleanup method to delete all test branches
29
- * - `api` - Direct access to the Neon API client
30
- *
31
- * @example
32
- * ```ts
33
- * // neon-testing.ts
34
- * import { makeNeonTesting } from "neon-testing";
35
- *
36
- * export const neonTesting = makeNeonTesting({
37
- * apiKey: "apiKey",
38
- * projectId: "projectId",
39
- * });
40
- * ```
41
- *
42
- * @example
43
- * ```ts
44
- * // my-test.test.ts
45
- * import { neonTesting } from "./neon-testing";
46
- *
47
- * const getBranch = neonTesting();
48
- *
49
- * test("my test", () => {
50
- * const branch = getBranch();
51
- * console.log(branch.id);
52
- * });
53
- * ```
54
- */
55
- export function makeNeonTesting(factoryOptions) {
56
- // Validate factory options
57
- validateExpiresIn(factoryOptions.expiresIn);
58
- const apiClient = createApiClient({ apiKey: factoryOptions.apiKey });
59
- /**
60
- * Delete all test branches (branches with the "integration-test: true" annotation)
61
- */
62
- async function deleteAllTestBranches() {
63
- const { data } = await apiClient.listProjectBranches({
64
- projectId: factoryOptions.projectId,
65
- });
66
- for (const branch of data.branches) {
67
- const isTestBranch = data.annotations[branch.id]?.value["integration-test"] === "true";
68
- if (isTestBranch) {
69
- await apiClient.deleteProjectBranch(factoryOptions.projectId, branch.id);
70
- }
71
- }
72
- }
73
- /**
74
- * Setup/teardown function for Vitest test suites
75
- *
76
- * Registers Vitest lifecycle hooks that:
77
- * - Create an isolated test branch from your parent branch
78
- * - Set `DATABASE_URL` environment variable to the test branch connection URI
79
- * - Automatically delete the test branch after tests complete (unless `deleteBranch: false`)
80
- * - Automatically expire branches after 10 minutes for cleanup (unless `expiresIn: null`)
81
- *
82
- * @param overrides - Optional overrides for the factory options
83
- * @returns A function that provides access to the current Neon branch object
84
- */
85
- const neonTesting = (
86
- /** Override any factory options except apiKey */
87
- overrides) => {
88
- // Validate overrides
89
- if (overrides?.expiresIn !== undefined) {
90
- validateExpiresIn(overrides.expiresIn);
91
- }
92
- // Merge factory options with overrides
93
- const options = { ...factoryOptions, ...overrides };
94
- // Each test file gets its own branch and database client
95
- let branch;
96
- // List of tracked Neon WebSocket connections
97
- // Lazily initialized when autoCloseWebSockets is enabled (avoids
98
- // referencing the WebSocket global on runtimes that lack it, e.g. Node 20)
99
- let neonSockets;
100
- /**
101
- * Create a new test branch
102
- *
103
- * @returns The connection URI for the new branch
104
- */
105
- async function createBranch() {
106
- // Calculate expiration timestamp if expiresIn is set
107
- const expiresIn = options.expiresIn === undefined ? 600 : options.expiresIn; // Default: 10 minutes
108
- const expiresAt = expiresIn !== null
109
- ? new Date(Date.now() + expiresIn * 1000).toISOString()
110
- : undefined;
111
- const { data } = await apiClient.createProjectBranch(options.projectId, {
112
- branch: {
113
- name: `test/${crypto.randomUUID()}`,
114
- parent_id: options.parentBranchId,
115
- init_source: options.schemaOnly ? "schema-only" : undefined,
116
- expires_at: expiresAt,
117
- },
118
- endpoints: [{ type: EndpointType.ReadWrite }],
119
- annotation_value: {
120
- "integration-test": "true",
121
- },
122
- });
123
- branch = data.branch;
124
- // Determine role and database (handles multi-role/database projects)
125
- const targetRole = options.roleName ??
126
- data.roles?.find((r) => r.name === "neondb_owner")?.name ??
127
- data.roles?.[0]?.name;
128
- const targetDatabase = options.databaseName ??
129
- data.databases?.find((d) => d.name === "neondb")?.name ??
130
- data.databases?.[0]?.name;
131
- if (!targetRole) {
132
- throw new Error("No role available in branch");
133
- }
134
- if (!targetDatabase) {
135
- throw new Error("No database available in branch");
136
- }
137
- // Validate specified role exists
138
- if (options.roleName &&
139
- !data.roles?.some((r) => r.name === options.roleName)) {
140
- throw new Error(`Role not found: ${options.roleName}`);
141
- }
142
- // Validate specified database exists
143
- if (options.databaseName &&
144
- !data.databases?.some((d) => d.name === options.databaseName)) {
145
- throw new Error(`Database not found: ${options.databaseName}`);
146
- }
147
- // Use getConnectionUri API (works for all cases, including multi-role projects)
148
- const { data: uriData } = await apiClient.getConnectionUri({
149
- projectId: options.projectId,
150
- branch_id: branch.id,
151
- role_name: targetRole,
152
- database_name: targetDatabase,
153
- pooled: options.endpoint !== "direct",
154
- });
155
- return uriData.uri;
156
- }
157
- /**
158
- * Delete the test branch
159
- */
160
- async function deleteBranch() {
161
- if (!branch?.id) {
162
- throw new Error("No branch to delete");
163
- }
164
- await apiClient.deleteProjectBranch(options.projectId, branch.id);
165
- branch = undefined;
166
- }
167
- beforeAll(async () => {
168
- process.env.DATABASE_URL = await withRetry(createBranch, {
169
- maxRetries: 8,
170
- baseDelayMs: 1000,
171
- });
172
- if (options.autoCloseWebSockets) {
173
- neonSockets = new Set();
174
- // Custom WebSocket constructor that tracks Neon WebSocket connections
175
- class TrackingWebSocket extends WebSocket {
176
- constructor(url) {
177
- super(url);
178
- // Only track Neon WebSocket connections
179
- if (!url.includes(".neon.tech/"))
180
- return;
181
- neonSockets.add(this);
182
- }
183
- }
184
- // Install a custom WebSocket constructor that tracks Neon WebSocket
185
- // connections and closes them before deleting the branch
186
- try {
187
- const { neonConfig } = await import("@neondatabase/serverless");
188
- neonConfig.webSocketConstructor = TrackingWebSocket;
189
- }
190
- catch {
191
- throw new Error("autoCloseWebSockets requires @neondatabase/serverless to be installed. Run: npm install @neondatabase/serverless");
192
- }
193
- }
194
- });
195
- afterAll(async () => {
196
- process.env.DATABASE_URL = undefined;
197
- // Close all tracked Neon WebSocket connections before deleting the branch
198
- if (options.autoCloseWebSockets && neonSockets) {
199
- // Suppress Neon WebSocket "Connection terminated unexpectedly" error
200
- process.prependListener("uncaughtException", neonWsErrorHandler);
201
- // Close tracked Neon WebSocket connections before deleting the branch
202
- neonSockets.forEach((ws) => ws.close());
203
- }
204
- if (options.deleteBranch !== false) {
205
- await deleteBranch();
206
- }
207
- });
208
- /**
209
- * Return the Neon branch object
210
- *
211
- * @returns The Neon branch object
212
- */
213
- return () => branch;
214
- };
215
- // Attach utilities
216
- neonTesting.deleteAllTestBranches = deleteAllTestBranches;
217
- neonTesting.api = apiClient;
218
- return neonTesting;
219
- }
220
- /**
221
- * Error handler: Suppress Neon WebSocket "Connection terminated unexpectedly"
222
- * error
223
- */
224
- const neonWsErrorHandler = (error) => {
225
- const isNeonWsClose = error.message.includes("Connection terminated unexpectedly") &&
226
- error.stack?.includes("@neondatabase/serverless");
227
- if (isNeonWsClose) {
228
- // Swallow this specific Neon WS termination error
229
- return;
230
- }
231
- // For any other error, detach and rethrow
232
- throw error;
233
- };
234
- /**
235
- * Reusable API call wrapper with automatic retry on 423 errors with exponential
236
- * backoff
237
- *
238
- * https://neon.com/docs/reference/typescript-sdk#error-handling
239
- * https://neon.com/docs/changelog/2022-07-20
240
- */
241
- async function withRetry(fn, options) {
242
- if (!Number.isInteger(options.maxRetries) || options.maxRetries <= 0) {
243
- throw new Error("maxRetries must be a positive integer");
244
- }
245
- if (!Number.isInteger(options.baseDelayMs) || options.baseDelayMs <= 0) {
246
- throw new Error("baseDelayMs must be a positive integer");
247
- }
248
- for (let attempt = 1; attempt <= options.maxRetries; attempt++) {
249
- try {
250
- return await fn();
251
- }
252
- catch (error) {
253
- const status = error?.response?.status;
254
- // Retry on 423 (resource locked) with exponential backoff
255
- if (status === 423 && attempt < options.maxRetries) {
256
- const delay = options.baseDelayMs * Math.pow(2, attempt - 1);
257
- console.log(`API call failed with 423, retrying in ${delay}ms (attempt ${attempt}/${options.maxRetries})`);
258
- await new Promise((resolve) => setTimeout(resolve, delay));
259
- continue;
260
- }
261
- // Surface Neon API error details that are otherwise buried in the Axios error
262
- if (error?.response?.data?.code) {
263
- const { code, message } = error.response.data;
264
- throw new Error(`Neon API error - HTTP ${status} - ${code} - ${message}`, { cause: error });
265
- }
266
- // Non-API errors (network, timeouts, etc.) pass through as-is
267
- throw error;
268
- }
269
- }
270
- throw new Error("apiCallWithRetry reached unexpected end");
271
- }
272
- //# sourceMappingURL=index.js.map
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * https://neon.com/docs/reference/typescript-sdk\n */\nimport {\n createApiClient,\n EndpointType,\n type Branch,\n} from \"@neondatabase/api-client\";\nimport { afterAll, beforeAll } from \"vitest\";\n\n/**\n * Validates the expiresIn option\n */\nfunction validateExpiresIn(expiresIn: number | null | undefined) {\n if (expiresIn !== null && expiresIn !== undefined) {\n if (!Number.isInteger(expiresIn)) {\n throw new Error(\"expiresIn must be an integer\");\n }\n\n if (expiresIn <= 0) {\n throw new Error(\"expiresIn must be a positive integer\");\n }\n\n if (expiresIn > 2592000) {\n throw new Error(\"expiresIn must not exceed 30 days (2,592,000 seconds)\");\n }\n }\n}\n\nexport interface NeonTestingOptions {\n /**\n * The Neon API key, this is used to create and teardown test branches (required)\n *\n * https://neon.com/docs/manage/api-keys#creating-api-keys\n */\n apiKey: string;\n /**\n * The Neon project ID to operate on (required)\n *\n * https://console.neon.tech/app/projects\n */\n projectId: string;\n /**\n * The parent branch ID for the new branch (default: undefined)\n *\n * If omitted or undefined, test branches will be created from the project's\n * default branch.\n */\n parentBranchId?: string;\n /**\n * Whether to create a schema-only branch (default: false)\n */\n schemaOnly?: boolean;\n /**\n * The type of connection to create (default: \"pooler\")\n */\n endpoint?: \"pooler\" | \"direct\";\n /**\n * Delete the test branch in afterAll (default: true)\n *\n * Disabling this will leave each test branch in the Neon project after the\n * test suite runs\n */\n deleteBranch?: boolean;\n /**\n * Automatically close Neon WebSocket connections opened during tests before\n * deleting the branch (default: false)\n *\n * Suppresses the specific Neon WebSocket \"Connection terminated unexpectedly\"\n * error that may surface when deleting a branch with open WebSocket\n * connections\n */\n autoCloseWebSockets?: boolean;\n /**\n * Time in seconds until the branch expires and is automatically deleted\n * (default: 600 = 10 minutes)\n *\n * This provides automatic cleanup for dangling branches from interrupted or\n * failed test runs. Set to `null` to disable automatic expiration.\n *\n * Must be a positive integer. Maximum 30 days (2,592,000 seconds).\n *\n * https://neon.com/docs/guides/branch-expiration\n */\n expiresIn?: number | null;\n /**\n * The database role to connect as (default: project owner role)\n *\n * The role must exist in the parent branch. Roles are automatically\n * copied to test branches when branching.\n */\n roleName?: string;\n /**\n * The database to connect to (default: project default database)\n */\n databaseName?: string;\n}\n\n/** Options for overriding test database setup (excludes apiKey) */\nexport type NeonTestingOverrides = Omit<Partial<NeonTestingOptions>, \"apiKey\">;\n\n/**\n * Factory function that creates a Neon test database setup/teardown function\n * for Vitest test suites.\n *\n * @param factoryOptions - Configuration options (see {@link NeonTestingOptions})\n * @returns A setup/teardown function with attached utilities:\n * - `deleteAllTestBranches()` - Cleanup method to delete all test branches\n * - `api` - Direct access to the Neon API client\n *\n * @example\n * ```ts\n * // neon-testing.ts\n * import { makeNeonTesting } from \"neon-testing\";\n *\n * export const neonTesting = makeNeonTesting({\n * apiKey: \"apiKey\",\n * projectId: \"projectId\",\n * });\n * ```\n *\n * @example\n * ```ts\n * // my-test.test.ts\n * import { neonTesting } from \"./neon-testing\";\n *\n * const getBranch = neonTesting();\n *\n * test(\"my test\", () => {\n * const branch = getBranch();\n * console.log(branch.id);\n * });\n * ```\n */\nexport function makeNeonTesting(factoryOptions: NeonTestingOptions) {\n // Validate factory options\n validateExpiresIn(factoryOptions.expiresIn);\n\n const apiClient = createApiClient({ apiKey: factoryOptions.apiKey });\n\n /**\n * Delete all test branches (branches with the \"integration-test: true\" annotation)\n */\n async function deleteAllTestBranches() {\n const { data } = await apiClient.listProjectBranches({\n projectId: factoryOptions.projectId,\n });\n\n for (const branch of data.branches) {\n const isTestBranch =\n data.annotations[branch.id]?.value[\"integration-test\"] === \"true\";\n\n if (isTestBranch) {\n await apiClient.deleteProjectBranch(\n factoryOptions.projectId,\n branch.id,\n );\n }\n }\n }\n\n /**\n * Setup/teardown function for Vitest test suites\n *\n * Registers Vitest lifecycle hooks that:\n * - Create an isolated test branch from your parent branch\n * - Set `DATABASE_URL` environment variable to the test branch connection URI\n * - Automatically delete the test branch after tests complete (unless `deleteBranch: false`)\n * - Automatically expire branches after 10 minutes for cleanup (unless `expiresIn: null`)\n *\n * @param overrides - Optional overrides for the factory options\n * @returns A function that provides access to the current Neon branch object\n */\n const neonTesting = (\n /** Override any factory options except apiKey */\n overrides?: NeonTestingOverrides,\n ) => {\n // Validate overrides\n if (overrides?.expiresIn !== undefined) {\n validateExpiresIn(overrides.expiresIn);\n }\n\n // Merge factory options with overrides\n const options = { ...factoryOptions, ...overrides };\n\n // Each test file gets its own branch and database client\n let branch: Branch | undefined;\n\n // List of tracked Neon WebSocket connections\n // Lazily initialized when autoCloseWebSockets is enabled (avoids\n // referencing the WebSocket global on runtimes that lack it, e.g. Node 20)\n let neonSockets: Set<WebSocket> | undefined;\n\n /**\n * Create a new test branch\n *\n * @returns The connection URI for the new branch\n */\n async function createBranch() {\n // Calculate expiration timestamp if expiresIn is set\n const expiresIn =\n options.expiresIn === undefined ? 600 : options.expiresIn; // Default: 10 minutes\n\n const expiresAt =\n expiresIn !== null\n ? new Date(Date.now() + expiresIn * 1000).toISOString()\n : undefined;\n\n const { data } = await apiClient.createProjectBranch(options.projectId, {\n branch: {\n name: `test/${crypto.randomUUID()}`,\n parent_id: options.parentBranchId,\n init_source: options.schemaOnly ? \"schema-only\" : undefined,\n expires_at: expiresAt,\n },\n endpoints: [{ type: EndpointType.ReadWrite }],\n annotation_value: {\n \"integration-test\": \"true\",\n },\n });\n\n branch = data.branch;\n\n // Determine role and database (handles multi-role/database projects)\n const targetRole =\n options.roleName ??\n data.roles?.find((r) => r.name === \"neondb_owner\")?.name ??\n data.roles?.[0]?.name;\n const targetDatabase =\n options.databaseName ??\n data.databases?.find((d) => d.name === \"neondb\")?.name ??\n data.databases?.[0]?.name;\n\n if (!targetRole) {\n throw new Error(\"No role available in branch\");\n }\n if (!targetDatabase) {\n throw new Error(\"No database available in branch\");\n }\n\n // Validate specified role exists\n if (\n options.roleName &&\n !data.roles?.some((r) => r.name === options.roleName)\n ) {\n throw new Error(`Role not found: ${options.roleName}`);\n }\n\n // Validate specified database exists\n if (\n options.databaseName &&\n !data.databases?.some((d) => d.name === options.databaseName)\n ) {\n throw new Error(`Database not found: ${options.databaseName}`);\n }\n\n // Use getConnectionUri API (works for all cases, including multi-role projects)\n const { data: uriData } = await apiClient.getConnectionUri({\n projectId: options.projectId,\n branch_id: branch.id,\n role_name: targetRole,\n database_name: targetDatabase,\n pooled: options.endpoint !== \"direct\",\n });\n\n return uriData.uri;\n }\n\n /**\n * Delete the test branch\n */\n async function deleteBranch() {\n if (!branch?.id) {\n throw new Error(\"No branch to delete\");\n }\n\n await apiClient.deleteProjectBranch(options.projectId, branch.id);\n branch = undefined;\n }\n\n beforeAll(async () => {\n process.env.DATABASE_URL = await withRetry(createBranch, {\n maxRetries: 8,\n baseDelayMs: 1000,\n });\n\n if (options.autoCloseWebSockets) {\n neonSockets = new Set<WebSocket>();\n\n // Custom WebSocket constructor that tracks Neon WebSocket connections\n class TrackingWebSocket extends WebSocket {\n constructor(url: string) {\n super(url);\n\n // Only track Neon WebSocket connections\n if (!url.includes(\".neon.tech/\")) return;\n\n neonSockets!.add(this);\n }\n }\n\n // Install a custom WebSocket constructor that tracks Neon WebSocket\n // connections and closes them before deleting the branch\n try {\n const { neonConfig } = await import(\"@neondatabase/serverless\");\n neonConfig.webSocketConstructor = TrackingWebSocket;\n } catch {\n throw new Error(\n \"autoCloseWebSockets requires @neondatabase/serverless to be installed. Run: npm install @neondatabase/serverless\",\n );\n }\n }\n });\n\n afterAll(async () => {\n process.env.DATABASE_URL = undefined;\n\n // Close all tracked Neon WebSocket connections before deleting the branch\n if (options.autoCloseWebSockets && neonSockets) {\n // Suppress Neon WebSocket \"Connection terminated unexpectedly\" error\n process.prependListener(\"uncaughtException\", neonWsErrorHandler);\n\n // Close tracked Neon WebSocket connections before deleting the branch\n neonSockets.forEach((ws) => ws.close());\n }\n\n if (options.deleteBranch !== false) {\n await deleteBranch();\n }\n });\n\n /**\n * Return the Neon branch object\n *\n * @returns The Neon branch object\n */\n return () => branch;\n };\n\n // Attach utilities\n neonTesting.deleteAllTestBranches = deleteAllTestBranches;\n neonTesting.api = apiClient;\n\n return neonTesting;\n}\n\n/**\n * Error handler: Suppress Neon WebSocket \"Connection terminated unexpectedly\"\n * error\n */\nconst neonWsErrorHandler = (error: Error) => {\n const isNeonWsClose =\n error.message.includes(\"Connection terminated unexpectedly\") &&\n error.stack?.includes(\"@neondatabase/serverless\");\n\n if (isNeonWsClose) {\n // Swallow this specific Neon WS termination error\n return;\n }\n\n // For any other error, detach and rethrow\n throw error;\n};\n\n/**\n * Reusable API call wrapper with automatic retry on 423 errors with exponential\n * backoff\n *\n * https://neon.com/docs/reference/typescript-sdk#error-handling\n * https://neon.com/docs/changelog/2022-07-20\n */\nasync function withRetry<T>(\n fn: () => Promise<T>,\n options: {\n maxRetries: number;\n baseDelayMs: number;\n },\n): Promise<T> {\n if (!Number.isInteger(options.maxRetries) || options.maxRetries <= 0) {\n throw new Error(\"maxRetries must be a positive integer\");\n }\n\n if (!Number.isInteger(options.baseDelayMs) || options.baseDelayMs <= 0) {\n throw new Error(\"baseDelayMs must be a positive integer\");\n }\n\n for (let attempt = 1; attempt <= options.maxRetries; attempt++) {\n try {\n return await fn();\n } catch (error: any) {\n const status = error?.response?.status;\n\n // Retry on 423 (resource locked) with exponential backoff\n if (status === 423 && attempt < options.maxRetries) {\n const delay = options.baseDelayMs * Math.pow(2, attempt - 1);\n\n console.log(\n `API call failed with 423, retrying in ${delay}ms (attempt ${attempt}/${options.maxRetries})`,\n );\n await new Promise((resolve) => setTimeout(resolve, delay));\n\n continue;\n }\n\n // Surface Neon API error details that are otherwise buried in the Axios error\n if (error?.response?.data?.code) {\n const { code, message } = error.response.data;\n throw new Error(\n `Neon API error - HTTP ${status} - ${code} - ${message}`,\n { cause: error },\n );\n }\n\n // Non-API errors (network, timeouts, etc.) pass through as-is\n throw error;\n }\n }\n throw new Error(\"apiCallWithRetry reached unexpected end\");\n}\n"]}
@@ -1,5 +0,0 @@
1
- /**
2
- * Create a lazy singleton from a factory function
3
- */
4
- export declare function lazySingleton<T>(factory: () => T): () => T;
5
- //# sourceMappingURL=singleton.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"singleton.d.ts","sourceRoot":"","sources":["../src/singleton.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,CAM1D"}
package/dist/singleton.js DELETED
@@ -1,11 +0,0 @@
1
- /**
2
- * Create a lazy singleton from a factory function
3
- */
4
- export function lazySingleton(factory) {
5
- let instance;
6
- return () => {
7
- instance ??= factory();
8
- return instance;
9
- };
10
- }
11
- //# sourceMappingURL=singleton.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"singleton.js","sourceRoot":"","sources":["../src/singleton.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,UAAU,aAAa,CAAI,OAAgB;IAC/C,IAAI,QAAuB,CAAC;IAC5B,OAAO,GAAG,EAAE;QACV,QAAQ,KAAK,OAAO,EAAE,CAAC;QACvB,OAAO,QAAQ,CAAC;IAClB,CAAC,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Create a lazy singleton from a factory function\n */\nexport function lazySingleton<T>(factory: () => T): () => T {\n let instance: T | undefined;\n return () => {\n instance ??= factory();\n return instance;\n };\n}\n"]}
package/dist/utils.d.ts DELETED
@@ -1,9 +0,0 @@
1
- import { neonTesting as neonTestingVite } from "./vite-plugin.js";
2
- export { lazySingleton } from "./singleton.js";
3
- export { createBarrier } from "./barrier.js";
4
- /**
5
- * @deprecated Import the Vite plugin from "neon-testing/vite" instead. This
6
- * export will be removed in the next major version.
7
- */
8
- export declare const neonTesting: typeof neonTestingVite;
9
- //# sourceMappingURL=utils.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,IAAI,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C;;;GAGG;AACH,eAAO,MAAM,WAAW,wBAAkB,CAAC"}
package/dist/utils.js DELETED
@@ -1,9 +0,0 @@
1
- import { neonTesting as neonTestingVite } from "./vite-plugin.js";
2
- export { lazySingleton } from "./singleton.js";
3
- export { createBarrier } from "./barrier.js";
4
- /**
5
- * @deprecated Import the Vite plugin from "neon-testing/vite" instead. This
6
- * export will be removed in the next major version.
7
- */
8
- export const neonTesting = neonTestingVite;
9
- //# sourceMappingURL=utils.js.map
package/dist/utils.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,IAAI,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C;;;GAGG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,eAAe,CAAC","sourcesContent":["import { neonTesting as neonTestingVite } from \"./vite-plugin.js\";\nexport { lazySingleton } from \"./singleton.js\";\nexport { createBarrier } from \"./barrier.js\";\n\n/**\n * @deprecated Import the Vite plugin from \"neon-testing/vite\" instead. This\n * export will be removed in the next major version.\n */\nexport const neonTesting = neonTestingVite;\n"]}
@@ -1,13 +0,0 @@
1
- import type { Plugin } from "vitest/config";
2
- /**
3
- * Neon Testing Vite plugin
4
- */
5
- export declare function neonTesting(options?: {
6
- /**
7
- * Enable debug logging.
8
- *
9
- * @default false
10
- */
11
- debug?: boolean;
12
- }): Plugin;
13
- //# sourceMappingURL=vite-plugin.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"vite-plugin.d.ts","sourceRoot":"","sources":["../src/vite-plugin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAE5C;;GAEG;AACH,wBAAgB,WAAW,CACzB,OAAO,GAAE;IACP;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACZ,GACL,MAAM,CAyBR"}
@@ -1,26 +0,0 @@
1
- import { fileURLToPath } from "node:url";
2
- /**
3
- * Neon Testing Vite plugin
4
- */
5
- export function neonTesting(options = {}) {
6
- return {
7
- name: "neon-testing-plugin",
8
- // Run as late as possible to reduce the risk of other plugins restoring
9
- // DATABASE_URL after we clear it
10
- enforce: "post",
11
- config(user) {
12
- const setupPath = fileURLToPath(new URL("./vitest-setup.js", import.meta.url));
13
- return {
14
- test: {
15
- // Register the vitest-setup.js file to run after other setup files
16
- setupFiles: Array.from(new Set([...(user.test?.setupFiles ?? []), setupPath])),
17
- env: {
18
- ...user.test?.env,
19
- NEON_TESTING_DEBUG: options.debug ? "true" : "false",
20
- },
21
- },
22
- };
23
- },
24
- };
25
- }
26
- //# sourceMappingURL=vite-plugin.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"vite-plugin.js","sourceRoot":"","sources":["../src/vite-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAGzC;;GAEG;AACH,MAAM,UAAU,WAAW,CACzB,UAOI,EAAE;IAEN,OAAO;QACL,IAAI,EAAE,qBAAqB;QAC3B,wEAAwE;QACxE,iCAAiC;QACjC,OAAO,EAAE,MAAM;QACf,MAAM,CAAC,IAAI;YACT,MAAM,SAAS,GAAG,aAAa,CAC7B,IAAI,GAAG,CAAC,mBAAmB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAC9C,CAAC;YAEF,OAAO;gBACL,IAAI,EAAE;oBACJ,mEAAmE;oBACnE,UAAU,EAAE,KAAK,CAAC,IAAI,CACpB,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,IAAI,EAAE,CAAC,EAAE,SAAS,CAAC,CAAC,CACvD;oBACD,GAAG,EAAE;wBACH,GAAG,IAAI,CAAC,IAAI,EAAE,GAAG;wBACjB,kBAAkB,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO;qBACrD;iBACF;aACF,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["import { fileURLToPath } from \"node:url\";\nimport type { Plugin } from \"vitest/config\";\n\n/**\n * Neon Testing Vite plugin\n */\nexport function neonTesting(\n options: {\n /**\n * Enable debug logging.\n *\n * @default false\n */\n debug?: boolean;\n } = {},\n): Plugin {\n return {\n name: \"neon-testing-plugin\",\n // Run as late as possible to reduce the risk of other plugins restoring\n // DATABASE_URL after we clear it\n enforce: \"post\",\n config(user) {\n const setupPath = fileURLToPath(\n new URL(\"./vitest-setup.js\", import.meta.url),\n );\n\n return {\n test: {\n // Register the vitest-setup.js file to run after other setup files\n setupFiles: Array.from(\n new Set([...(user.test?.setupFiles ?? []), setupPath]),\n ),\n env: {\n ...user.test?.env,\n NEON_TESTING_DEBUG: options.debug ? \"true\" : \"false\",\n },\n },\n };\n },\n };\n}\n"]}
@@ -1,3 +0,0 @@
1
- declare const isVitest: string | undefined;
2
- declare const isDebug: boolean;
3
- //# sourceMappingURL=vitest-setup.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"vitest-setup.d.ts","sourceRoot":"","sources":["../src/vitest-setup.ts"],"names":[],"mappings":"AAAA,QAAA,MAAM,QAAQ,oBAAqB,CAAC;AACpC,QAAA,MAAM,OAAO,SAA4C,CAAC"}
@@ -1,10 +0,0 @@
1
- "use strict";
2
- const isVitest = process.env.VITEST;
3
- const isDebug = process.env.NEON_TESTING_DEBUG === "true";
4
- if (isVitest) {
5
- if (process.env.DATABASE_URL && isDebug) {
6
- console.debug("[neon-testing] Clearing existing DATABASE_URL in test environment");
7
- }
8
- delete process.env.DATABASE_URL;
9
- }
10
- //# sourceMappingURL=vitest-setup.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"vitest-setup.js","sourceRoot":"","sources":["../src/vitest-setup.ts"],"names":[],"mappings":";AAAA,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;AACpC,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,KAAK,MAAM,CAAC;AAE1D,IAAI,QAAQ,EAAE,CAAC;IACb,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,OAAO,EAAE,CAAC;QACxC,OAAO,CAAC,KAAK,CACX,mEAAmE,CACpE,CAAC;IACJ,CAAC;IAED,OAAO,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;AAClC,CAAC","sourcesContent":["const isVitest = process.env.VITEST;\nconst isDebug = process.env.NEON_TESTING_DEBUG === \"true\";\n\nif (isVitest) {\n if (process.env.DATABASE_URL && isDebug) {\n console.debug(\n \"[neon-testing] Clearing existing DATABASE_URL in test environment\",\n );\n }\n\n delete process.env.DATABASE_URL;\n}\n"]}