@infracraft/pulumi 1.30.1 → 1.30.2

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.
@@ -2,10 +2,30 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require_chunk = require('../chunk-BVYJZCqc.cjs');
3
3
  const require_errors_api_not_found_error = require('../errors/api-not-found-error.cjs');
4
4
  const require_http_resilient_fetch = require('../http/resilient-fetch.cjs');
5
+ let _pulumi_pulumi = require("@pulumi/pulumi");
6
+ _pulumi_pulumi = require_chunk.__toESM(_pulumi_pulumi, 1);
5
7
 
6
8
  //#region src/neon/client.ts
7
9
  const NEON_API_URL = "https://console.neon.tech/api/v2";
8
10
  /**
11
+ * Neon runs project mutations as ASYNC operations and answers 423 ("project
12
+ * already has running conflicting operations, scheduling of new ones is
13
+ * prohibited") while earlier ones settle — its docs prescribe waiting for
14
+ * completion before the next request. A destroy immediately followed by an
15
+ * `up` on the same project hits this deterministically (live: branch/endpoint
16
+ * deletion still settling 30s later). Waiting until the lock clears is the
17
+ * documented protocol, bounded so a genuinely wedged project still fails.
18
+ */
19
+ const OPERATION_LOCK_WAIT = {
20
+ /** Delay between lock probes. */
21
+ intervalMs: 5e3,
22
+ /** Upper bound on probes (~2 minutes total). */
23
+ maxAttempts: 24
24
+ };
25
+ function sleep(ms) {
26
+ return new Promise((resolve) => setTimeout(resolve, ms));
27
+ }
28
+ /**
9
29
  * REST client for the Neon API.
10
30
  *
11
31
  * @example
@@ -69,14 +89,20 @@ var NeonClient = class {
69
89
  await this.request("DELETE", path);
70
90
  }
71
91
  async request(method, path, body) {
72
- const response = await require_http_resilient_fetch.resilientFetch(`${NEON_API_URL}${path}`, {
73
- method,
74
- headers: {
75
- "Content-Type": "application/json",
76
- Authorization: `Bearer ${this.apiKey}`
77
- },
78
- body: body ? JSON.stringify(body) : void 0
79
- });
92
+ let response;
93
+ for (let attempt = 1;; attempt++) {
94
+ response = await require_http_resilient_fetch.resilientFetch(`${NEON_API_URL}${path}`, {
95
+ method,
96
+ headers: {
97
+ "Content-Type": "application/json",
98
+ Authorization: `Bearer ${this.apiKey}`
99
+ },
100
+ body: body ? JSON.stringify(body) : void 0
101
+ });
102
+ if (response.status !== 423 || attempt >= OPERATION_LOCK_WAIT.maxAttempts) break;
103
+ _pulumi_pulumi.log.info(`[infracraft] Neon project operations still settling (423) — waiting ${OPERATION_LOCK_WAIT.intervalMs / 1e3}s (attempt ${attempt}/${OPERATION_LOCK_WAIT.maxAttempts})`);
104
+ await sleep(OPERATION_LOCK_WAIT.intervalMs);
105
+ }
80
106
  if (response.status === 404) throw new require_errors_api_not_found_error.ApiNotFoundError("neon", path);
81
107
  if (!response.ok) {
82
108
  const errorText = await response.text();
@@ -1 +1 @@
1
- {"version":3,"file":"client.cjs","names":["resilientFetch","ApiNotFoundError"],"sources":["../../src/neon/client.ts"],"sourcesContent":["import { ApiNotFoundError } from \"../errors/api-not-found-error\";\nimport { resilientFetch } from \"../http/resilient-fetch\";\n\nconst NEON_API_URL = \"https://console.neon.tech/api/v2\";\n\n/**\n * REST client for the Neon API.\n *\n * @example\n * ```typescript\n * const client = new NeonClient(apiKey);\n * const branch = await client.get<{ branch: { id: string } }>(\n * `/projects/abc/branches/br-xyz`\n * );\n * ```\n */\nexport class NeonClient {\n\t/** Neon API key for authentication. */\n\tprivate readonly apiKey: string;\n\n\t/**\n\t * @param apiKey Neon API key (project-scoped or account-scoped)\n\t */\n\tconstructor(apiKey: string) {\n\t\tthis.apiKey = apiKey;\n\t}\n\n\t/**\n\t * Performs a GET request against the Neon API.\n\t *\n\t * @param path API path (e.g. `/projects/abc/branches`)\n\t * @returns Typed JSON response body\n\t * @throws {ApiNotFoundError} On 404\n\t * @throws {Error} On any other non-2xx HTTP status\n\t */\n\tasync get<T>(path: string): Promise<T> {\n\t\treturn this.request<T>(\"GET\", path);\n\t}\n\n\t/**\n\t * Performs a POST request against the Neon API.\n\t *\n\t * @param path API path\n\t * @param body Request body (will be JSON-serialized)\n\t * @returns Typed JSON response body\n\t * @throws {ApiNotFoundError} On 404\n\t * @throws {Error} On any other non-2xx HTTP status\n\t */\n\tasync post<T>(path: string, body?: unknown): Promise<T> {\n\t\treturn this.request<T>(\"POST\", path, body);\n\t}\n\n\t/**\n\t * Performs a PATCH request against the Neon API.\n\t *\n\t * @param path API path\n\t * @param body Request body (will be JSON-serialized)\n\t * @returns Typed JSON response body\n\t * @throws {ApiNotFoundError} On 404\n\t * @throws {Error} On any other non-2xx HTTP status\n\t */\n\tasync patch<T>(path: string, body?: unknown): Promise<T> {\n\t\treturn this.request<T>(\"PATCH\", path, body);\n\t}\n\n\t/**\n\t * Performs a DELETE request against the Neon API.\n\t *\n\t * @param path API path\n\t * @throws {ApiNotFoundError} On 404\n\t * @throws {Error} On any other non-2xx HTTP status\n\t */\n\tasync delete(path: string): Promise<void> {\n\t\tawait this.request<void>(\"DELETE\", path);\n\t}\n\n\tprivate async request<T>(\n\t\tmethod: string,\n\t\tpath: string,\n\t\tbody?: unknown,\n\t): Promise<T> {\n\t\tconst response = await resilientFetch(`${NEON_API_URL}${path}`, {\n\t\t\tmethod,\n\t\t\theaders: {\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\tAuthorization: `Bearer ${this.apiKey}`,\n\t\t\t},\n\t\t\tbody: body ? JSON.stringify(body) : undefined,\n\t\t});\n\n\t\tif (response.status === 404) {\n\t\t\tthrow new ApiNotFoundError(\"neon\", path);\n\t\t}\n\n\t\tif (!response.ok) {\n\t\t\tconst errorText = await response.text();\n\n\t\t\tthrow new Error(`Neon API error (${response.status}): ${errorText}`);\n\t\t}\n\n\t\tif (method === \"DELETE\") {\n\t\t\treturn undefined as T;\n\t\t}\n\n\t\treturn (await response.json()) as T;\n\t}\n}\n"],"mappings":";;;;;;AAGA,MAAM,eAAe;;;;;;;;;;;;AAarB,IAAa,aAAb,MAAwB;;;;CAOvB,YAAY,QAAgB;EAC3B,KAAK,SAAS;CACf;;;;;;;;;CAUA,MAAM,IAAO,MAA0B;EACtC,OAAO,KAAK,QAAW,OAAO,IAAI;CACnC;;;;;;;;;;CAWA,MAAM,KAAQ,MAAc,MAA4B;EACvD,OAAO,KAAK,QAAW,QAAQ,MAAM,IAAI;CAC1C;;;;;;;;;;CAWA,MAAM,MAAS,MAAc,MAA4B;EACxD,OAAO,KAAK,QAAW,SAAS,MAAM,IAAI;CAC3C;;;;;;;;CASA,MAAM,OAAO,MAA6B;EACzC,MAAM,KAAK,QAAc,UAAU,IAAI;CACxC;CAEA,MAAc,QACb,QACA,MACA,MACa;EACb,MAAM,WAAW,MAAMA,4CAAe,GAAG,eAAe,QAAQ;GAC/D;GACA,SAAS;IACR,gBAAgB;IAChB,eAAe,UAAU,KAAK;GAC/B;GACA,MAAM,OAAO,KAAK,UAAU,IAAI,IAAI;EACrC,CAAC;EAED,IAAI,SAAS,WAAW,KACvB,MAAM,IAAIC,oDAAiB,QAAQ,IAAI;EAGxC,IAAI,CAAC,SAAS,IAAI;GACjB,MAAM,YAAY,MAAM,SAAS,KAAK;GAEtC,MAAM,IAAI,MAAM,mBAAmB,SAAS,OAAO,KAAK,WAAW;EACpE;EAEA,IAAI,WAAW,UACd;EAGD,OAAQ,MAAM,SAAS,KAAK;CAC7B;AACD"}
1
+ {"version":3,"file":"client.cjs","names":["resilientFetch","ApiNotFoundError"],"sources":["../../src/neon/client.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\nimport { ApiNotFoundError } from \"../errors/api-not-found-error\";\nimport { resilientFetch } from \"../http/resilient-fetch\";\n\nconst NEON_API_URL = \"https://console.neon.tech/api/v2\";\n\n/**\n * Neon runs project mutations as ASYNC operations and answers 423 (\"project\n * already has running conflicting operations, scheduling of new ones is\n * prohibited\") while earlier ones settle — its docs prescribe waiting for\n * completion before the next request. A destroy immediately followed by an\n * `up` on the same project hits this deterministically (live: branch/endpoint\n * deletion still settling 30s later). Waiting until the lock clears is the\n * documented protocol, bounded so a genuinely wedged project still fails.\n */\nconst OPERATION_LOCK_WAIT = {\n\t/** Delay between lock probes. */\n\tintervalMs: 5_000,\n\t/** Upper bound on probes (~2 minutes total). */\n\tmaxAttempts: 24,\n} as const;\n\nfunction sleep(ms: number): Promise<void> {\n\treturn new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * REST client for the Neon API.\n *\n * @example\n * ```typescript\n * const client = new NeonClient(apiKey);\n * const branch = await client.get<{ branch: { id: string } }>(\n * `/projects/abc/branches/br-xyz`\n * );\n * ```\n */\nexport class NeonClient {\n\t/** Neon API key for authentication. */\n\tprivate readonly apiKey: string;\n\n\t/**\n\t * @param apiKey Neon API key (project-scoped or account-scoped)\n\t */\n\tconstructor(apiKey: string) {\n\t\tthis.apiKey = apiKey;\n\t}\n\n\t/**\n\t * Performs a GET request against the Neon API.\n\t *\n\t * @param path API path (e.g. `/projects/abc/branches`)\n\t * @returns Typed JSON response body\n\t * @throws {ApiNotFoundError} On 404\n\t * @throws {Error} On any other non-2xx HTTP status\n\t */\n\tasync get<T>(path: string): Promise<T> {\n\t\treturn this.request<T>(\"GET\", path);\n\t}\n\n\t/**\n\t * Performs a POST request against the Neon API.\n\t *\n\t * @param path API path\n\t * @param body Request body (will be JSON-serialized)\n\t * @returns Typed JSON response body\n\t * @throws {ApiNotFoundError} On 404\n\t * @throws {Error} On any other non-2xx HTTP status\n\t */\n\tasync post<T>(path: string, body?: unknown): Promise<T> {\n\t\treturn this.request<T>(\"POST\", path, body);\n\t}\n\n\t/**\n\t * Performs a PATCH request against the Neon API.\n\t *\n\t * @param path API path\n\t * @param body Request body (will be JSON-serialized)\n\t * @returns Typed JSON response body\n\t * @throws {ApiNotFoundError} On 404\n\t * @throws {Error} On any other non-2xx HTTP status\n\t */\n\tasync patch<T>(path: string, body?: unknown): Promise<T> {\n\t\treturn this.request<T>(\"PATCH\", path, body);\n\t}\n\n\t/**\n\t * Performs a DELETE request against the Neon API.\n\t *\n\t * @param path API path\n\t * @throws {ApiNotFoundError} On 404\n\t * @throws {Error} On any other non-2xx HTTP status\n\t */\n\tasync delete(path: string): Promise<void> {\n\t\tawait this.request<void>(\"DELETE\", path);\n\t}\n\n\tprivate async request<T>(\n\t\tmethod: string,\n\t\tpath: string,\n\t\tbody?: unknown,\n\t): Promise<T> {\n\t\tlet response: Response;\n\n\t\tfor (let attempt = 1; ; attempt++) {\n\t\t\tresponse = await resilientFetch(`${NEON_API_URL}${path}`, {\n\t\t\t\tmethod,\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\tAuthorization: `Bearer ${this.apiKey}`,\n\t\t\t\t},\n\t\t\t\tbody: body ? JSON.stringify(body) : undefined,\n\t\t\t});\n\n\t\t\tif (\n\t\t\t\tresponse.status !== 423 ||\n\t\t\t\tattempt >= OPERATION_LOCK_WAIT.maxAttempts\n\t\t\t) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tpulumi.log.info(\n\t\t\t\t`[infracraft] Neon project operations still settling (423) — waiting ${OPERATION_LOCK_WAIT.intervalMs / 1000}s (attempt ${attempt}/${OPERATION_LOCK_WAIT.maxAttempts})`,\n\t\t\t);\n\n\t\t\tawait sleep(OPERATION_LOCK_WAIT.intervalMs);\n\t\t}\n\n\t\tif (response.status === 404) {\n\t\t\tthrow new ApiNotFoundError(\"neon\", path);\n\t\t}\n\n\t\tif (!response.ok) {\n\t\t\tconst errorText = await response.text();\n\n\t\t\tthrow new Error(`Neon API error (${response.status}): ${errorText}`);\n\t\t}\n\n\t\tif (method === \"DELETE\") {\n\t\t\treturn undefined as T;\n\t\t}\n\n\t\treturn (await response.json()) as T;\n\t}\n}\n"],"mappings":";;;;;;;;AAIA,MAAM,eAAe;;;;;;;;;;AAWrB,MAAM,sBAAsB;;CAE3B,YAAY;;CAEZ,aAAa;AACd;AAEA,SAAS,MAAM,IAA2B;CACzC,OAAO,IAAI,SAAS,YAAY,WAAW,SAAS,EAAE,CAAC;AACxD;;;;;;;;;;;;AAaA,IAAa,aAAb,MAAwB;;;;CAOvB,YAAY,QAAgB;EAC3B,KAAK,SAAS;CACf;;;;;;;;;CAUA,MAAM,IAAO,MAA0B;EACtC,OAAO,KAAK,QAAW,OAAO,IAAI;CACnC;;;;;;;;;;CAWA,MAAM,KAAQ,MAAc,MAA4B;EACvD,OAAO,KAAK,QAAW,QAAQ,MAAM,IAAI;CAC1C;;;;;;;;;;CAWA,MAAM,MAAS,MAAc,MAA4B;EACxD,OAAO,KAAK,QAAW,SAAS,MAAM,IAAI;CAC3C;;;;;;;;CASA,MAAM,OAAO,MAA6B;EACzC,MAAM,KAAK,QAAc,UAAU,IAAI;CACxC;CAEA,MAAc,QACb,QACA,MACA,MACa;EACb,IAAI;EAEJ,KAAK,IAAI,UAAU,IAAK,WAAW;GAClC,WAAW,MAAMA,4CAAe,GAAG,eAAe,QAAQ;IACzD;IACA,SAAS;KACR,gBAAgB;KAChB,eAAe,UAAU,KAAK;IAC/B;IACA,MAAM,OAAO,KAAK,UAAU,IAAI,IAAI;GACrC,CAAC;GAED,IACC,SAAS,WAAW,OACpB,WAAW,oBAAoB,aAE/B;GAGD,eAAO,IAAI,KACV,uEAAuE,oBAAoB,aAAa,IAAK,aAAa,QAAQ,GAAG,oBAAoB,YAAY,EACtK;GAEA,MAAM,MAAM,oBAAoB,UAAU;EAC3C;EAEA,IAAI,SAAS,WAAW,KACvB,MAAM,IAAIC,oDAAiB,QAAQ,IAAI;EAGxC,IAAI,CAAC,SAAS,IAAI;GACjB,MAAM,YAAY,MAAM,SAAS,KAAK;GAEtC,MAAM,IAAI,MAAM,mBAAmB,SAAS,OAAO,KAAK,WAAW;EACpE;EAEA,IAAI,WAAW,UACd;EAGD,OAAQ,MAAM,SAAS,KAAK;CAC7B;AACD"}
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.cts","names":[],"sources":["../../src/neon/client.ts"],"mappings":";;;;;;AAgBA;;;;;;;;cAAa,UAAA;EAwDgB;EAAA,iBAtDX,MAAA;EAsDkB;;;cAjDvB,MAAA;EAYN;;;;;;;;EAAA,GAAA,GAAA,CAAO,IAAA,WAAe,OAAA,CAAQ,CAAA;EAaS;;;;;;;;;EAAvC,IAAA,GAAA,CAAQ,IAAA,UAAc,IAAA,aAAiB,OAAA,CAAQ,CAAA;EAwBzB;;;AAIP;;;;;;EAff,KAAA,GAAA,CAAS,IAAA,UAAc,IAAA,aAAiB,OAAA,CAAQ,CAAA;;;;;;;;EAWhD,MAAA,CAAO,IAAA,WAAe,OAAA;EAAA,QAId,OAAA;AAAA"}
1
+ {"version":3,"file":"client.d.cts","names":[],"sources":["../../src/neon/client.ts"],"mappings":";;;;;;AAqCA;;;;;;;;cAAa,UAAA;EAwDgB;EAAA,iBAtDX,MAAA;EAsDkB;;;cAjDvB,MAAA;EAYN;;;;;;;;EAAA,GAAA,GAAA,CAAO,IAAA,WAAe,OAAA,CAAQ,CAAA;EAaS;;;;;;;;;EAAvC,IAAA,GAAA,CAAQ,IAAA,UAAc,IAAA,aAAiB,OAAA,CAAQ,CAAA;EAwBzB;;;AAIP;;;;;;EAff,KAAA,GAAA,CAAS,IAAA,UAAc,IAAA,aAAiB,OAAA,CAAQ,CAAA;;;;;;;;EAWhD,MAAA,CAAO,IAAA,WAAe,OAAA;EAAA,QAId,OAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.mts","names":[],"sources":["../../src/neon/client.ts"],"mappings":";;;;;;AAgBA;;;;;;;;cAAa,UAAA;EAwDgB;EAAA,iBAtDX,MAAA;EAsDkB;;;cAjDvB,MAAA;EAYN;;;;;;;;EAAA,GAAA,GAAA,CAAO,IAAA,WAAe,OAAA,CAAQ,CAAA;EAaS;;;;;;;;;EAAvC,IAAA,GAAA,CAAQ,IAAA,UAAc,IAAA,aAAiB,OAAA,CAAQ,CAAA;EAwBzB;;;AAIP;;;;;;EAff,KAAA,GAAA,CAAS,IAAA,UAAc,IAAA,aAAiB,OAAA,CAAQ,CAAA;;;;;;;;EAWhD,MAAA,CAAO,IAAA,WAAe,OAAA;EAAA,QAId,OAAA;AAAA"}
1
+ {"version":3,"file":"client.d.mts","names":[],"sources":["../../src/neon/client.ts"],"mappings":";;;;;;AAqCA;;;;;;;;cAAa,UAAA;EAwDgB;EAAA,iBAtDX,MAAA;EAsDkB;;;cAjDvB,MAAA;EAYN;;;;;;;;EAAA,GAAA,GAAA,CAAO,IAAA,WAAe,OAAA,CAAQ,CAAA;EAaS;;;;;;;;;EAAvC,IAAA,GAAA,CAAQ,IAAA,UAAc,IAAA,aAAiB,OAAA,CAAQ,CAAA;EAwBzB;;;AAIP;;;;;;EAff,KAAA,GAAA,CAAS,IAAA,UAAc,IAAA,aAAiB,OAAA,CAAQ,CAAA;;;;;;;;EAWhD,MAAA,CAAO,IAAA,WAAe,OAAA;EAAA,QAId,OAAA;AAAA"}
@@ -1,10 +1,29 @@
1
1
  import { t as __name } from "../chunk-OPjESj5l.mjs";
2
2
  import { ApiNotFoundError } from "../errors/api-not-found-error.mjs";
3
3
  import { resilientFetch } from "../http/resilient-fetch.mjs";
4
+ import * as pulumi from "@pulumi/pulumi";
4
5
 
5
6
  //#region src/neon/client.ts
6
7
  const NEON_API_URL = "https://console.neon.tech/api/v2";
7
8
  /**
9
+ * Neon runs project mutations as ASYNC operations and answers 423 ("project
10
+ * already has running conflicting operations, scheduling of new ones is
11
+ * prohibited") while earlier ones settle — its docs prescribe waiting for
12
+ * completion before the next request. A destroy immediately followed by an
13
+ * `up` on the same project hits this deterministically (live: branch/endpoint
14
+ * deletion still settling 30s later). Waiting until the lock clears is the
15
+ * documented protocol, bounded so a genuinely wedged project still fails.
16
+ */
17
+ const OPERATION_LOCK_WAIT = {
18
+ /** Delay between lock probes. */
19
+ intervalMs: 5e3,
20
+ /** Upper bound on probes (~2 minutes total). */
21
+ maxAttempts: 24
22
+ };
23
+ function sleep(ms) {
24
+ return new Promise((resolve) => setTimeout(resolve, ms));
25
+ }
26
+ /**
8
27
  * REST client for the Neon API.
9
28
  *
10
29
  * @example
@@ -68,14 +87,20 @@ var NeonClient = class {
68
87
  await this.request("DELETE", path);
69
88
  }
70
89
  async request(method, path, body) {
71
- const response = await resilientFetch(`${NEON_API_URL}${path}`, {
72
- method,
73
- headers: {
74
- "Content-Type": "application/json",
75
- Authorization: `Bearer ${this.apiKey}`
76
- },
77
- body: body ? JSON.stringify(body) : void 0
78
- });
90
+ let response;
91
+ for (let attempt = 1;; attempt++) {
92
+ response = await resilientFetch(`${NEON_API_URL}${path}`, {
93
+ method,
94
+ headers: {
95
+ "Content-Type": "application/json",
96
+ Authorization: `Bearer ${this.apiKey}`
97
+ },
98
+ body: body ? JSON.stringify(body) : void 0
99
+ });
100
+ if (response.status !== 423 || attempt >= OPERATION_LOCK_WAIT.maxAttempts) break;
101
+ pulumi.log.info(`[infracraft] Neon project operations still settling (423) — waiting ${OPERATION_LOCK_WAIT.intervalMs / 1e3}s (attempt ${attempt}/${OPERATION_LOCK_WAIT.maxAttempts})`);
102
+ await sleep(OPERATION_LOCK_WAIT.intervalMs);
103
+ }
79
104
  if (response.status === 404) throw new ApiNotFoundError("neon", path);
80
105
  if (!response.ok) {
81
106
  const errorText = await response.text();
@@ -1 +1 @@
1
- {"version":3,"file":"client.mjs","names":[],"sources":["../../src/neon/client.ts"],"sourcesContent":["import { ApiNotFoundError } from \"../errors/api-not-found-error\";\nimport { resilientFetch } from \"../http/resilient-fetch\";\n\nconst NEON_API_URL = \"https://console.neon.tech/api/v2\";\n\n/**\n * REST client for the Neon API.\n *\n * @example\n * ```typescript\n * const client = new NeonClient(apiKey);\n * const branch = await client.get<{ branch: { id: string } }>(\n * `/projects/abc/branches/br-xyz`\n * );\n * ```\n */\nexport class NeonClient {\n\t/** Neon API key for authentication. */\n\tprivate readonly apiKey: string;\n\n\t/**\n\t * @param apiKey Neon API key (project-scoped or account-scoped)\n\t */\n\tconstructor(apiKey: string) {\n\t\tthis.apiKey = apiKey;\n\t}\n\n\t/**\n\t * Performs a GET request against the Neon API.\n\t *\n\t * @param path API path (e.g. `/projects/abc/branches`)\n\t * @returns Typed JSON response body\n\t * @throws {ApiNotFoundError} On 404\n\t * @throws {Error} On any other non-2xx HTTP status\n\t */\n\tasync get<T>(path: string): Promise<T> {\n\t\treturn this.request<T>(\"GET\", path);\n\t}\n\n\t/**\n\t * Performs a POST request against the Neon API.\n\t *\n\t * @param path API path\n\t * @param body Request body (will be JSON-serialized)\n\t * @returns Typed JSON response body\n\t * @throws {ApiNotFoundError} On 404\n\t * @throws {Error} On any other non-2xx HTTP status\n\t */\n\tasync post<T>(path: string, body?: unknown): Promise<T> {\n\t\treturn this.request<T>(\"POST\", path, body);\n\t}\n\n\t/**\n\t * Performs a PATCH request against the Neon API.\n\t *\n\t * @param path API path\n\t * @param body Request body (will be JSON-serialized)\n\t * @returns Typed JSON response body\n\t * @throws {ApiNotFoundError} On 404\n\t * @throws {Error} On any other non-2xx HTTP status\n\t */\n\tasync patch<T>(path: string, body?: unknown): Promise<T> {\n\t\treturn this.request<T>(\"PATCH\", path, body);\n\t}\n\n\t/**\n\t * Performs a DELETE request against the Neon API.\n\t *\n\t * @param path API path\n\t * @throws {ApiNotFoundError} On 404\n\t * @throws {Error} On any other non-2xx HTTP status\n\t */\n\tasync delete(path: string): Promise<void> {\n\t\tawait this.request<void>(\"DELETE\", path);\n\t}\n\n\tprivate async request<T>(\n\t\tmethod: string,\n\t\tpath: string,\n\t\tbody?: unknown,\n\t): Promise<T> {\n\t\tconst response = await resilientFetch(`${NEON_API_URL}${path}`, {\n\t\t\tmethod,\n\t\t\theaders: {\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\tAuthorization: `Bearer ${this.apiKey}`,\n\t\t\t},\n\t\t\tbody: body ? JSON.stringify(body) : undefined,\n\t\t});\n\n\t\tif (response.status === 404) {\n\t\t\tthrow new ApiNotFoundError(\"neon\", path);\n\t\t}\n\n\t\tif (!response.ok) {\n\t\t\tconst errorText = await response.text();\n\n\t\t\tthrow new Error(`Neon API error (${response.status}): ${errorText}`);\n\t\t}\n\n\t\tif (method === \"DELETE\") {\n\t\t\treturn undefined as T;\n\t\t}\n\n\t\treturn (await response.json()) as T;\n\t}\n}\n"],"mappings":";;;;;AAGA,MAAM,eAAe;;;;;;;;;;;;AAarB,IAAa,aAAb,MAAwB;;;;CAOvB,YAAY,QAAgB;EAC3B,KAAK,SAAS;CACf;;;;;;;;;CAUA,MAAM,IAAO,MAA0B;EACtC,OAAO,KAAK,QAAW,OAAO,IAAI;CACnC;;;;;;;;;;CAWA,MAAM,KAAQ,MAAc,MAA4B;EACvD,OAAO,KAAK,QAAW,QAAQ,MAAM,IAAI;CAC1C;;;;;;;;;;CAWA,MAAM,MAAS,MAAc,MAA4B;EACxD,OAAO,KAAK,QAAW,SAAS,MAAM,IAAI;CAC3C;;;;;;;;CASA,MAAM,OAAO,MAA6B;EACzC,MAAM,KAAK,QAAc,UAAU,IAAI;CACxC;CAEA,MAAc,QACb,QACA,MACA,MACa;EACb,MAAM,WAAW,MAAM,eAAe,GAAG,eAAe,QAAQ;GAC/D;GACA,SAAS;IACR,gBAAgB;IAChB,eAAe,UAAU,KAAK;GAC/B;GACA,MAAM,OAAO,KAAK,UAAU,IAAI,IAAI;EACrC,CAAC;EAED,IAAI,SAAS,WAAW,KACvB,MAAM,IAAI,iBAAiB,QAAQ,IAAI;EAGxC,IAAI,CAAC,SAAS,IAAI;GACjB,MAAM,YAAY,MAAM,SAAS,KAAK;GAEtC,MAAM,IAAI,MAAM,mBAAmB,SAAS,OAAO,KAAK,WAAW;EACpE;EAEA,IAAI,WAAW,UACd;EAGD,OAAQ,MAAM,SAAS,KAAK;CAC7B;AACD"}
1
+ {"version":3,"file":"client.mjs","names":[],"sources":["../../src/neon/client.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\nimport { ApiNotFoundError } from \"../errors/api-not-found-error\";\nimport { resilientFetch } from \"../http/resilient-fetch\";\n\nconst NEON_API_URL = \"https://console.neon.tech/api/v2\";\n\n/**\n * Neon runs project mutations as ASYNC operations and answers 423 (\"project\n * already has running conflicting operations, scheduling of new ones is\n * prohibited\") while earlier ones settle — its docs prescribe waiting for\n * completion before the next request. A destroy immediately followed by an\n * `up` on the same project hits this deterministically (live: branch/endpoint\n * deletion still settling 30s later). Waiting until the lock clears is the\n * documented protocol, bounded so a genuinely wedged project still fails.\n */\nconst OPERATION_LOCK_WAIT = {\n\t/** Delay between lock probes. */\n\tintervalMs: 5_000,\n\t/** Upper bound on probes (~2 minutes total). */\n\tmaxAttempts: 24,\n} as const;\n\nfunction sleep(ms: number): Promise<void> {\n\treturn new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * REST client for the Neon API.\n *\n * @example\n * ```typescript\n * const client = new NeonClient(apiKey);\n * const branch = await client.get<{ branch: { id: string } }>(\n * `/projects/abc/branches/br-xyz`\n * );\n * ```\n */\nexport class NeonClient {\n\t/** Neon API key for authentication. */\n\tprivate readonly apiKey: string;\n\n\t/**\n\t * @param apiKey Neon API key (project-scoped or account-scoped)\n\t */\n\tconstructor(apiKey: string) {\n\t\tthis.apiKey = apiKey;\n\t}\n\n\t/**\n\t * Performs a GET request against the Neon API.\n\t *\n\t * @param path API path (e.g. `/projects/abc/branches`)\n\t * @returns Typed JSON response body\n\t * @throws {ApiNotFoundError} On 404\n\t * @throws {Error} On any other non-2xx HTTP status\n\t */\n\tasync get<T>(path: string): Promise<T> {\n\t\treturn this.request<T>(\"GET\", path);\n\t}\n\n\t/**\n\t * Performs a POST request against the Neon API.\n\t *\n\t * @param path API path\n\t * @param body Request body (will be JSON-serialized)\n\t * @returns Typed JSON response body\n\t * @throws {ApiNotFoundError} On 404\n\t * @throws {Error} On any other non-2xx HTTP status\n\t */\n\tasync post<T>(path: string, body?: unknown): Promise<T> {\n\t\treturn this.request<T>(\"POST\", path, body);\n\t}\n\n\t/**\n\t * Performs a PATCH request against the Neon API.\n\t *\n\t * @param path API path\n\t * @param body Request body (will be JSON-serialized)\n\t * @returns Typed JSON response body\n\t * @throws {ApiNotFoundError} On 404\n\t * @throws {Error} On any other non-2xx HTTP status\n\t */\n\tasync patch<T>(path: string, body?: unknown): Promise<T> {\n\t\treturn this.request<T>(\"PATCH\", path, body);\n\t}\n\n\t/**\n\t * Performs a DELETE request against the Neon API.\n\t *\n\t * @param path API path\n\t * @throws {ApiNotFoundError} On 404\n\t * @throws {Error} On any other non-2xx HTTP status\n\t */\n\tasync delete(path: string): Promise<void> {\n\t\tawait this.request<void>(\"DELETE\", path);\n\t}\n\n\tprivate async request<T>(\n\t\tmethod: string,\n\t\tpath: string,\n\t\tbody?: unknown,\n\t): Promise<T> {\n\t\tlet response: Response;\n\n\t\tfor (let attempt = 1; ; attempt++) {\n\t\t\tresponse = await resilientFetch(`${NEON_API_URL}${path}`, {\n\t\t\t\tmethod,\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\tAuthorization: `Bearer ${this.apiKey}`,\n\t\t\t\t},\n\t\t\t\tbody: body ? JSON.stringify(body) : undefined,\n\t\t\t});\n\n\t\t\tif (\n\t\t\t\tresponse.status !== 423 ||\n\t\t\t\tattempt >= OPERATION_LOCK_WAIT.maxAttempts\n\t\t\t) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tpulumi.log.info(\n\t\t\t\t`[infracraft] Neon project operations still settling (423) — waiting ${OPERATION_LOCK_WAIT.intervalMs / 1000}s (attempt ${attempt}/${OPERATION_LOCK_WAIT.maxAttempts})`,\n\t\t\t);\n\n\t\t\tawait sleep(OPERATION_LOCK_WAIT.intervalMs);\n\t\t}\n\n\t\tif (response.status === 404) {\n\t\t\tthrow new ApiNotFoundError(\"neon\", path);\n\t\t}\n\n\t\tif (!response.ok) {\n\t\t\tconst errorText = await response.text();\n\n\t\t\tthrow new Error(`Neon API error (${response.status}): ${errorText}`);\n\t\t}\n\n\t\tif (method === \"DELETE\") {\n\t\t\treturn undefined as T;\n\t\t}\n\n\t\treturn (await response.json()) as T;\n\t}\n}\n"],"mappings":";;;;;;AAIA,MAAM,eAAe;;;;;;;;;;AAWrB,MAAM,sBAAsB;;CAE3B,YAAY;;CAEZ,aAAa;AACd;AAEA,SAAS,MAAM,IAA2B;CACzC,OAAO,IAAI,SAAS,YAAY,WAAW,SAAS,EAAE,CAAC;AACxD;;;;;;;;;;;;AAaA,IAAa,aAAb,MAAwB;;;;CAOvB,YAAY,QAAgB;EAC3B,KAAK,SAAS;CACf;;;;;;;;;CAUA,MAAM,IAAO,MAA0B;EACtC,OAAO,KAAK,QAAW,OAAO,IAAI;CACnC;;;;;;;;;;CAWA,MAAM,KAAQ,MAAc,MAA4B;EACvD,OAAO,KAAK,QAAW,QAAQ,MAAM,IAAI;CAC1C;;;;;;;;;;CAWA,MAAM,MAAS,MAAc,MAA4B;EACxD,OAAO,KAAK,QAAW,SAAS,MAAM,IAAI;CAC3C;;;;;;;;CASA,MAAM,OAAO,MAA6B;EACzC,MAAM,KAAK,QAAc,UAAU,IAAI;CACxC;CAEA,MAAc,QACb,QACA,MACA,MACa;EACb,IAAI;EAEJ,KAAK,IAAI,UAAU,IAAK,WAAW;GAClC,WAAW,MAAM,eAAe,GAAG,eAAe,QAAQ;IACzD;IACA,SAAS;KACR,gBAAgB;KAChB,eAAe,UAAU,KAAK;IAC/B;IACA,MAAM,OAAO,KAAK,UAAU,IAAI,IAAI;GACrC,CAAC;GAED,IACC,SAAS,WAAW,OACpB,WAAW,oBAAoB,aAE/B;GAGD,OAAO,IAAI,KACV,uEAAuE,oBAAoB,aAAa,IAAK,aAAa,QAAQ,GAAG,oBAAoB,YAAY,EACtK;GAEA,MAAM,MAAM,oBAAoB,UAAU;EAC3C;EAEA,IAAI,SAAS,WAAW,KACvB,MAAM,IAAI,iBAAiB,QAAQ,IAAI;EAGxC,IAAI,CAAC,SAAS,IAAI;GACjB,MAAM,YAAY,MAAM,SAAS,KAAK;GAEtC,MAAM,IAAI,MAAM,mBAAmB,SAAS,OAAO,KAAK,WAAW;EACpE;EAEA,IAAI,WAAW,UACd;EAGD,OAAQ,MAAM,SAAS,KAAK;CAC7B;AACD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@infracraft/pulumi",
3
- "version": "1.30.1",
3
+ "version": "1.30.2",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Native Pulumi providers for Railway, Neon, Vercel, and Fly.io with adopt-or-create semantics and deploy orchestration. No Terraform bridge.",