@infracraft/pulumi 0.1.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.
Potentially problematic release.
This version of @infracraft/pulumi might be problematic. Click here for more details.
- package/dist/chunk-BVYJZCqc.cjs +44 -0
- package/dist/chunk-OPjESj5l.mjs +9 -0
- package/dist/git-guard.cjs +62 -0
- package/dist/git-guard.cjs.map +1 -0
- package/dist/git-guard.d.cts +13 -0
- package/dist/git-guard.d.cts.map +1 -0
- package/dist/git-guard.d.mts +13 -0
- package/dist/git-guard.d.mts.map +1 -0
- package/dist/git-guard.mjs +56 -0
- package/dist/git-guard.mjs.map +1 -0
- package/dist/hash.cjs +40 -0
- package/dist/hash.cjs.map +1 -0
- package/dist/hash.d.cts +10 -0
- package/dist/hash.d.cts.map +1 -0
- package/dist/hash.d.mts +10 -0
- package/dist/hash.d.mts.map +1 -0
- package/dist/hash.mjs +36 -0
- package/dist/hash.mjs.map +1 -0
- package/dist/neon/branch.cjs +113 -0
- package/dist/neon/branch.cjs.map +1 -0
- package/dist/neon/branch.d.cts +40 -0
- package/dist/neon/branch.d.cts.map +1 -0
- package/dist/neon/branch.d.mts +40 -0
- package/dist/neon/branch.d.mts.map +1 -0
- package/dist/neon/branch.mjs +111 -0
- package/dist/neon/branch.mjs.map +1 -0
- package/dist/neon/client.cjs +85 -0
- package/dist/neon/client.cjs.map +1 -0
- package/dist/neon/client.d.cts +59 -0
- package/dist/neon/client.d.cts.map +1 -0
- package/dist/neon/client.d.mts +59 -0
- package/dist/neon/client.d.mts.map +1 -0
- package/dist/neon/client.mjs +84 -0
- package/dist/neon/client.mjs.map +1 -0
- package/dist/neon/database.cjs +117 -0
- package/dist/neon/database.cjs.map +1 -0
- package/dist/neon/database.d.cts +48 -0
- package/dist/neon/database.d.cts.map +1 -0
- package/dist/neon/database.d.mts +48 -0
- package/dist/neon/database.d.mts.map +1 -0
- package/dist/neon/database.mjs +115 -0
- package/dist/neon/database.mjs.map +1 -0
- package/dist/neon/endpoint.cjs +167 -0
- package/dist/neon/endpoint.cjs.map +1 -0
- package/dist/neon/endpoint.d.cts +57 -0
- package/dist/neon/endpoint.d.cts.map +1 -0
- package/dist/neon/endpoint.d.mts +57 -0
- package/dist/neon/endpoint.d.mts.map +1 -0
- package/dist/neon/endpoint.mjs +165 -0
- package/dist/neon/endpoint.mjs.map +1 -0
- package/dist/neon/index.cjs +14 -0
- package/dist/neon/index.d.cts +7 -0
- package/dist/neon/index.d.mts +7 -0
- package/dist/neon/index.mjs +8 -0
- package/dist/neon/project.cjs +120 -0
- package/dist/neon/project.cjs.map +1 -0
- package/dist/neon/project.d.cts +52 -0
- package/dist/neon/project.d.cts.map +1 -0
- package/dist/neon/project.d.mts +52 -0
- package/dist/neon/project.d.mts.map +1 -0
- package/dist/neon/project.mjs +118 -0
- package/dist/neon/project.mjs.map +1 -0
- package/dist/neon/role.cjs +135 -0
- package/dist/neon/role.cjs.map +1 -0
- package/dist/neon/role.d.cts +49 -0
- package/dist/neon/role.d.cts.map +1 -0
- package/dist/neon/role.d.mts +49 -0
- package/dist/neon/role.d.mts.map +1 -0
- package/dist/neon/role.mjs +133 -0
- package/dist/neon/role.mjs.map +1 -0
- package/dist/railway/client.cjs +59 -0
- package/dist/railway/client.cjs.map +1 -0
- package/dist/railway/client.d.cts +39 -0
- package/dist/railway/client.d.cts.map +1 -0
- package/dist/railway/client.d.mts +39 -0
- package/dist/railway/client.d.mts.map +1 -0
- package/dist/railway/client.mjs +58 -0
- package/dist/railway/client.mjs.map +1 -0
- package/dist/railway/deploy.cjs +40 -0
- package/dist/railway/deploy.cjs.map +1 -0
- package/dist/railway/deploy.d.cts +47 -0
- package/dist/railway/deploy.d.cts.map +1 -0
- package/dist/railway/deploy.d.mts +47 -0
- package/dist/railway/deploy.d.mts.map +1 -0
- package/dist/railway/deploy.mjs +37 -0
- package/dist/railway/deploy.mjs.map +1 -0
- package/dist/railway/domain.cjs +236 -0
- package/dist/railway/domain.cjs.map +1 -0
- package/dist/railway/domain.d.cts +52 -0
- package/dist/railway/domain.d.cts.map +1 -0
- package/dist/railway/domain.d.mts +52 -0
- package/dist/railway/domain.d.mts.map +1 -0
- package/dist/railway/domain.mjs +234 -0
- package/dist/railway/domain.mjs.map +1 -0
- package/dist/railway/index.cjs +16 -0
- package/dist/railway/index.d.cts +8 -0
- package/dist/railway/index.d.mts +8 -0
- package/dist/railway/index.mjs +9 -0
- package/dist/railway/project.cjs +231 -0
- package/dist/railway/project.cjs.map +1 -0
- package/dist/railway/project.d.cts +52 -0
- package/dist/railway/project.d.cts.map +1 -0
- package/dist/railway/project.d.mts +52 -0
- package/dist/railway/project.d.mts.map +1 -0
- package/dist/railway/project.mjs +229 -0
- package/dist/railway/project.mjs.map +1 -0
- package/dist/railway/service.cjs +268 -0
- package/dist/railway/service.cjs.map +1 -0
- package/dist/railway/service.d.cts +88 -0
- package/dist/railway/service.d.cts.map +1 -0
- package/dist/railway/service.d.mts +88 -0
- package/dist/railway/service.d.mts.map +1 -0
- package/dist/railway/service.mjs +266 -0
- package/dist/railway/service.mjs.map +1 -0
- package/dist/railway/variable.cjs +149 -0
- package/dist/railway/variable.cjs.map +1 -0
- package/dist/railway/variable.d.cts +52 -0
- package/dist/railway/variable.d.cts.map +1 -0
- package/dist/railway/variable.d.mts +52 -0
- package/dist/railway/variable.d.mts.map +1 -0
- package/dist/railway/variable.mjs +147 -0
- package/dist/railway/variable.mjs.map +1 -0
- package/dist/railway/volume.cjs +179 -0
- package/dist/railway/volume.cjs.map +1 -0
- package/dist/railway/volume.d.cts +52 -0
- package/dist/railway/volume.d.cts.map +1 -0
- package/dist/railway/volume.d.mts +52 -0
- package/dist/railway/volume.d.mts.map +1 -0
- package/dist/railway/volume.mjs +177 -0
- package/dist/railway/volume.mjs.map +1 -0
- package/dist/vercel/deploy.cjs +65 -0
- package/dist/vercel/deploy.cjs.map +1 -0
- package/dist/vercel/deploy.d.cts +54 -0
- package/dist/vercel/deploy.d.cts.map +1 -0
- package/dist/vercel/deploy.d.mts +54 -0
- package/dist/vercel/deploy.d.mts.map +1 -0
- package/dist/vercel/deploy.mjs +61 -0
- package/dist/vercel/deploy.mjs.map +1 -0
- package/dist/vercel/index.cjs +6 -0
- package/dist/vercel/index.d.cts +3 -0
- package/dist/vercel/index.d.mts +3 -0
- package/dist/vercel/index.mjs +4 -0
- package/dist/vercel/variable.cjs +280 -0
- package/dist/vercel/variable.cjs.map +1 -0
- package/dist/vercel/variable.d.cts +55 -0
- package/dist/vercel/variable.d.cts.map +1 -0
- package/dist/vercel/variable.d.mts +55 -0
- package/dist/vercel/variable.d.mts.map +1 -0
- package/dist/vercel/variable.mjs +278 -0
- package/dist/vercel/variable.mjs.map +1 -0
- package/package.json +62 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"endpoint.mjs","names":[],"sources":["../../src/neon/endpoint.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\nimport { NeonClient } from \"./client.js\";\n\n/** Resolved inputs for the Neon endpoint dynamic provider. */\nexport interface NeonEndpointInputs {\n\t/** Neon API key. */\n\tapiKey: string;\n\n\t/** Neon project ID. */\n\tprojectId: string;\n\n\t/** Branch ID to attach the endpoint to. */\n\tbranchId: string;\n\n\t/** Minimum compute units (e.g. `0.25`). */\n\tminCu: number;\n\n\t/** Maximum compute units (e.g. `2`). */\n\tmaxCu: number;\n\n\t/** Seconds of inactivity before suspending. `0` means use global default. */\n\tsuspendTimeout: number;\n}\n\n/** Persisted state for the Neon endpoint. */\ninterface NeonEndpointOutputs extends NeonEndpointInputs {\n\t/** Endpoint hostname (e.g. `\"ep-delicate-union-ah0ekn7n.us-east-1.aws.neon.tech\"`). */\n\thost: string;\n}\n\n/** Neon API response for an endpoint. */\ninterface EndpointResponse {\n\tendpoint: {\n\t\tid: string;\n\t\thost: string;\n\t\tbranch_id: string;\n\t\tautoscaling_limit_min_cu: number;\n\t\tautoscaling_limit_max_cu: number;\n\t\tsuspend_timeout_seconds: number;\n\t};\n}\n\n/** Neon API response for listing endpoints. */\ninterface EndpointListResponse {\n\tendpoints: Array<{\n\t\tid: string;\n\t\thost: string;\n\t\tbranch_id: string;\n\t\ttype: string;\n\t}>;\n}\n\n/**\n * Finds an existing read-write endpoint for a branch.\n *\n * @param client Authenticated Neon API client\n * @param projectId Neon project ID\n * @param branchId Branch ID to search within\n * @returns The endpoint ID and host if found, `undefined` otherwise\n */\nasync function findEndpointByBranch(\n\tclient: NeonClient,\n\tprojectId: string,\n\tbranchId: string,\n): Promise<{ id: string; host: string } | undefined> {\n\tconst result = await client.get<EndpointListResponse>(\n\t\t`/projects/${projectId}/branches/${branchId}/endpoints`,\n\t);\n\n\tconst match = result.endpoints.find((e) => e.type === \"read_write\");\n\n\treturn match ? { id: match.id, host: match.host } : undefined;\n}\n\n/**\n * Dynamic provider implementing CRUD for Neon compute endpoints.\n *\n * Uses adopt-or-create on `create()`: finds an existing read-write endpoint\n * on the target branch before creating a new one.\n */\nclass NeonEndpointProvider implements pulumi.dynamic.ResourceProvider {\n\t/**\n\t * Creates or adopts a Neon endpoint on the target branch.\n\t *\n\t * @param inputs Resolved endpoint configuration\n\t * @returns The Neon endpoint ID as the resource ID\n\t */\n\tasync create(\n\t\tinputs: NeonEndpointInputs,\n\t): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst client = new NeonClient(inputs.apiKey);\n\n\t\tconst existing = await findEndpointByBranch(\n\t\t\tclient,\n\t\t\tinputs.projectId,\n\t\t\tinputs.branchId,\n\t\t);\n\n\t\tif (existing) {\n\t\t\tpulumi.log.info(`Adopting existing Neon endpoint (${existing.id})`);\n\n\t\t\tawait client.patch(\n\t\t\t\t`/projects/${inputs.projectId}/endpoints/${existing.id}`,\n\t\t\t\t{\n\t\t\t\t\tendpoint: {\n\t\t\t\t\t\tautoscaling_limit_min_cu: inputs.minCu,\n\t\t\t\t\t\tautoscaling_limit_max_cu: inputs.maxCu,\n\t\t\t\t\t\tsuspend_timeout_seconds: inputs.suspendTimeout,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t);\n\n\t\t\treturn {\n\t\t\t\tid: existing.id,\n\t\t\t\touts: { ...inputs, host: existing.host },\n\t\t\t};\n\t\t}\n\n\t\tconst result = await client.post<EndpointResponse>(\n\t\t\t`/projects/${inputs.projectId}/branches/${inputs.branchId}/endpoints`,\n\t\t\t{\n\t\t\t\tendpoint: {\n\t\t\t\t\ttype: \"read_write\",\n\t\t\t\t\tautoscaling_limit_min_cu: inputs.minCu,\n\t\t\t\t\tautoscaling_limit_max_cu: inputs.maxCu,\n\t\t\t\t\tsuspend_timeout_seconds: inputs.suspendTimeout,\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\n\t\treturn {\n\t\t\tid: result.endpoint.id,\n\t\t\touts: { ...inputs, host: result.endpoint.host },\n\t\t};\n\t}\n\n\t/**\n\t * Updates endpoint compute settings in place.\n\t *\n\t * @param id Current Neon endpoint ID\n\t * @param _olds Previous persisted state\n\t * @param news New desired configuration\n\t * @returns Updated outputs with current host\n\t */\n\tasync update(\n\t\tid: string,\n\t\t_olds: NeonEndpointOutputs,\n\t\tnews: NeonEndpointInputs,\n\t): Promise<pulumi.dynamic.UpdateResult> {\n\t\tconst client = new NeonClient(news.apiKey);\n\n\t\tconst result = await client.patch<EndpointResponse>(\n\t\t\t`/projects/${news.projectId}/endpoints/${id}`,\n\t\t\t{\n\t\t\t\tendpoint: {\n\t\t\t\t\tautoscaling_limit_min_cu: news.minCu,\n\t\t\t\t\tautoscaling_limit_max_cu: news.maxCu,\n\t\t\t\t\tsuspend_timeout_seconds: news.suspendTimeout,\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\n\t\treturn { outs: { ...news, host: result.endpoint.host } };\n\t}\n\n\t/**\n\t * Reads current state for `pulumi refresh`.\n\t *\n\t * @param id Current Neon endpoint ID\n\t * @param props Last known persisted state\n\t * @returns Refreshed resource ID and properties\n\t * @throws {Error} If the endpoint no longer exists\n\t */\n\tasync read(\n\t\tid: string,\n\t\tprops: NeonEndpointOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\tconst client = new NeonClient(props.apiKey);\n\n\t\tconst result = await client.get<EndpointResponse>(\n\t\t\t`/projects/${props.projectId}/endpoints/${id}`,\n\t\t);\n\n\t\treturn {\n\t\t\tid: result.endpoint.id,\n\t\t\tprops: {\n\t\t\t\t...props,\n\t\t\t\thost: result.endpoint.host,\n\t\t\t\tminCu: result.endpoint.autoscaling_limit_min_cu,\n\t\t\t\tmaxCu: result.endpoint.autoscaling_limit_max_cu,\n\t\t\t\tsuspendTimeout: result.endpoint.suspend_timeout_seconds,\n\t\t\t},\n\t\t};\n\t}\n\n\t/**\n\t * Deletes the Neon endpoint. Silently succeeds if already deleted.\n\t */\n\tasync delete(id: string, props: NeonEndpointOutputs): Promise<void> {\n\t\tconst client = new NeonClient(props.apiKey);\n\n\t\ttry {\n\t\t\tawait client.delete(`/projects/${props.projectId}/endpoints/${id}`);\n\t\t} catch {\n\t\t\tpulumi.log.warn(\n\t\t\t\t`Failed to delete Neon endpoint (may already be deleted)`,\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Compares old and new inputs. Changing `projectId` or `branchId` triggers replacement.\n\t */\n\tasync diff(\n\t\t_id: string,\n\t\tolds: NeonEndpointOutputs,\n\t\tnews: NeonEndpointInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst replaces: string[] = [];\n\n\t\tif (olds.projectId !== news.projectId) {\n\t\t\treplaces.push(\"projectId\");\n\t\t}\n\n\t\tif (olds.branchId !== news.branchId) {\n\t\t\treplaces.push(\"branchId\");\n\t\t}\n\n\t\tconst hasChanges =\n\t\t\treplaces.length > 0 ||\n\t\t\tolds.minCu !== news.minCu ||\n\t\t\tolds.maxCu !== news.maxCu ||\n\t\t\tolds.suspendTimeout !== news.suspendTimeout;\n\n\t\treturn { changes: hasChanges, replaces, deleteBeforeReplace: true };\n\t}\n}\n\n/**\n * Manages a Neon compute endpoint with adopt-or-create semantics.\n * Exposes `host` as an output for connection string composition.\n *\n * @example\n * ```typescript\n * const endpoint = new NeonEndpoint(\"neon-endpoint-production\", {\n * apiKey: config.requireSecret(\"neonApiKey\"),\n * projectId: \"quiet-forest-69719462\",\n * branchId: branch.id,\n * minCu: 0.25,\n * maxCu: 2,\n * suspendTimeout: 0,\n * });\n *\n * const host = endpoint.host;\n * ```\n */\nexport class NeonEndpoint extends pulumi.dynamic.Resource {\n\t/** Endpoint hostname for connection strings. */\n\tpublic declare readonly host: pulumi.Output<string>;\n\n\t/**\n\t * @param name Pulumi resource name\n\t * @param args Endpoint configuration inputs\n\t * @param opts Standard Pulumi resource options\n\t */\n\tconstructor(\n\t\tname: string,\n\t\targs: {\n\t\t\t/** Neon API key. */\n\t\t\tapiKey: pulumi.Input<string>;\n\n\t\t\t/** Neon project ID. */\n\t\t\tprojectId: pulumi.Input<string>;\n\n\t\t\t/** Branch ID to attach the endpoint to. */\n\t\t\tbranchId: pulumi.Input<string>;\n\n\t\t\t/** Minimum compute units. */\n\t\t\tminCu: pulumi.Input<number>;\n\n\t\t\t/** Maximum compute units. */\n\t\t\tmaxCu: pulumi.Input<number>;\n\n\t\t\t/** Seconds of inactivity before suspending. */\n\t\t\tsuspendTimeout: pulumi.Input<number>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\tsuper(new NeonEndpointProvider(), name, { ...args, host: undefined }, opts);\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;AA4DA,eAAe,qBACd,QACA,WACA,UACoD;CAKpD,MAAM,SAAQ,MAJO,OAAO,IAC3B,aAAa,UAAU,YAAY,SAAS,WAC7C,GAEqB,UAAU,MAAM,MAAM,EAAE,SAAS,YAAY;CAElE,OAAO,QAAQ;EAAE,IAAI,MAAM;EAAI,MAAM,MAAM;CAAK,IAAI;AACrD;;;;;;;AAQA,IAAM,uBAAN,MAAsE;;;;;;;CAOrE,MAAM,OACL,QACuC;EACvC,MAAM,SAAS,IAAI,WAAW,OAAO,MAAM;EAE3C,MAAM,WAAW,MAAM,qBACtB,QACA,OAAO,WACP,OAAO,QACR;EAEA,IAAI,UAAU;GACb,OAAO,IAAI,KAAK,oCAAoC,SAAS,GAAG,EAAE;GAElE,MAAM,OAAO,MACZ,aAAa,OAAO,UAAU,aAAa,SAAS,MACpD,EACC,UAAU;IACT,0BAA0B,OAAO;IACjC,0BAA0B,OAAO;IACjC,yBAAyB,OAAO;GACjC,EACD,CACD;GAEA,OAAO;IACN,IAAI,SAAS;IACb,MAAM;KAAE,GAAG;KAAQ,MAAM,SAAS;IAAK;GACxC;EACD;EAEA,MAAM,SAAS,MAAM,OAAO,KAC3B,aAAa,OAAO,UAAU,YAAY,OAAO,SAAS,aAC1D,EACC,UAAU;GACT,MAAM;GACN,0BAA0B,OAAO;GACjC,0BAA0B,OAAO;GACjC,yBAAyB,OAAO;EACjC,EACD,CACD;EAEA,OAAO;GACN,IAAI,OAAO,SAAS;GACpB,MAAM;IAAE,GAAG;IAAQ,MAAM,OAAO,SAAS;GAAK;EAC/C;CACD;;;;;;;;;CAUA,MAAM,OACL,IACA,OACA,MACuC;EAGvC,MAAM,SAAS,MAAM,IAFF,WAAW,KAAK,MAET,EAAE,MAC3B,aAAa,KAAK,UAAU,aAAa,MACzC,EACC,UAAU;GACT,0BAA0B,KAAK;GAC/B,0BAA0B,KAAK;GAC/B,yBAAyB,KAAK;EAC/B,EACD,CACD;EAEA,OAAO,EAAE,MAAM;GAAE,GAAG;GAAM,MAAM,OAAO,SAAS;EAAK,EAAE;CACxD;;;;;;;;;CAUA,MAAM,KACL,IACA,OACqC;EAGrC,MAAM,SAAS,MAAM,IAFF,WAAW,MAAM,MAEV,EAAE,IAC3B,aAAa,MAAM,UAAU,aAAa,IAC3C;EAEA,OAAO;GACN,IAAI,OAAO,SAAS;GACpB,OAAO;IACN,GAAG;IACH,MAAM,OAAO,SAAS;IACtB,OAAO,OAAO,SAAS;IACvB,OAAO,OAAO,SAAS;IACvB,gBAAgB,OAAO,SAAS;GACjC;EACD;CACD;;;;CAKA,MAAM,OAAO,IAAY,OAA2C;EACnE,MAAM,SAAS,IAAI,WAAW,MAAM,MAAM;EAE1C,IAAI;GACH,MAAM,OAAO,OAAO,aAAa,MAAM,UAAU,aAAa,IAAI;EACnE,QAAQ;GACP,OAAO,IAAI,KACV,yDACD;EACD;CACD;;;;CAKA,MAAM,KACL,KACA,MACA,MACqC;EACrC,MAAM,WAAqB,CAAC;EAE5B,IAAI,KAAK,cAAc,KAAK,WAC3B,SAAS,KAAK,WAAW;EAG1B,IAAI,KAAK,aAAa,KAAK,UAC1B,SAAS,KAAK,UAAU;EASzB,OAAO;GAAE,SALR,SAAS,SAAS,KAClB,KAAK,UAAU,KAAK,SACpB,KAAK,UAAU,KAAK,SACpB,KAAK,mBAAmB,KAAK;GAEA;GAAU,qBAAqB;EAAK;CACnE;AACD;;;;;;;;;;;;;;;;;;;AAoBA,IAAa,eAAb,cAAkC,OAAO,QAAQ,SAAS;;;;;;CASzD,YACC,MACA,MAmBA,MACC;EACD,MAAM,IAAI,qBAAqB,GAAG,MAAM;GAAE,GAAG;GAAM,MAAM;EAAU,GAAG,IAAI;CAC3E;AACD"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
const require_neon_client = require('./client.cjs');
|
|
3
|
+
const require_neon_branch = require('./branch.cjs');
|
|
4
|
+
const require_neon_database = require('./database.cjs');
|
|
5
|
+
const require_neon_endpoint = require('./endpoint.cjs');
|
|
6
|
+
const require_neon_project = require('./project.cjs');
|
|
7
|
+
const require_neon_role = require('./role.cjs');
|
|
8
|
+
|
|
9
|
+
exports.NeonBranch = require_neon_branch.NeonBranch;
|
|
10
|
+
exports.NeonClient = require_neon_client.NeonClient;
|
|
11
|
+
exports.NeonDatabase = require_neon_database.NeonDatabase;
|
|
12
|
+
exports.NeonEndpoint = require_neon_endpoint.NeonEndpoint;
|
|
13
|
+
exports.NeonProject = require_neon_project.NeonProject;
|
|
14
|
+
exports.NeonRole = require_neon_role.NeonRole;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { NeonBranch, NeonBranchInputs } from "./branch.cjs";
|
|
2
|
+
import { NeonClient } from "./client.cjs";
|
|
3
|
+
import { NeonDatabase, NeonDatabaseInputs } from "./database.cjs";
|
|
4
|
+
import { NeonEndpoint, NeonEndpointInputs } from "./endpoint.cjs";
|
|
5
|
+
import { NeonProject, NeonProjectInputs } from "./project.cjs";
|
|
6
|
+
import { NeonRole, NeonRoleInputs } from "./role.cjs";
|
|
7
|
+
export { NeonBranch, type NeonBranchInputs, NeonClient, NeonDatabase, type NeonDatabaseInputs, NeonEndpoint, type NeonEndpointInputs, NeonProject, type NeonProjectInputs, NeonRole, type NeonRoleInputs };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { NeonBranch, NeonBranchInputs } from "./branch.mjs";
|
|
2
|
+
import { NeonClient } from "./client.mjs";
|
|
3
|
+
import { NeonDatabase, NeonDatabaseInputs } from "./database.mjs";
|
|
4
|
+
import { NeonEndpoint, NeonEndpointInputs } from "./endpoint.mjs";
|
|
5
|
+
import { NeonProject, NeonProjectInputs } from "./project.mjs";
|
|
6
|
+
import { NeonRole, NeonRoleInputs } from "./role.mjs";
|
|
7
|
+
export { NeonBranch, type NeonBranchInputs, NeonClient, NeonDatabase, type NeonDatabaseInputs, NeonEndpoint, type NeonEndpointInputs, NeonProject, type NeonProjectInputs, NeonRole, type NeonRoleInputs };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { NeonClient } from "./client.mjs";
|
|
2
|
+
import { NeonBranch } from "./branch.mjs";
|
|
3
|
+
import { NeonDatabase } from "./database.mjs";
|
|
4
|
+
import { NeonEndpoint } from "./endpoint.mjs";
|
|
5
|
+
import { NeonProject } from "./project.mjs";
|
|
6
|
+
import { NeonRole } from "./role.mjs";
|
|
7
|
+
|
|
8
|
+
export { NeonBranch, NeonClient, NeonDatabase, NeonEndpoint, NeonProject, NeonRole };
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
const require_chunk = require('../chunk-BVYJZCqc.cjs');
|
|
3
|
+
const require_neon_client = require('./client.cjs');
|
|
4
|
+
let _pulumi_pulumi = require("@pulumi/pulumi");
|
|
5
|
+
_pulumi_pulumi = require_chunk.__toESM(_pulumi_pulumi, 1);
|
|
6
|
+
|
|
7
|
+
//#region src/neon/project.ts
|
|
8
|
+
/**
|
|
9
|
+
* Dynamic provider implementing adopt-or-create for Neon projects.
|
|
10
|
+
*
|
|
11
|
+
* On `create()`, queries `GET /projects` and performs an exact name match.
|
|
12
|
+
* If found, adopts the existing project. If not, creates a new one via
|
|
13
|
+
* `POST /projects`. Deletion is a no-op to protect production databases.
|
|
14
|
+
*/
|
|
15
|
+
var NeonProjectProvider = class {
|
|
16
|
+
/**
|
|
17
|
+
* Creates or adopts a Neon project by name.
|
|
18
|
+
*
|
|
19
|
+
* @param inputs Resolved project configuration
|
|
20
|
+
* @returns The Neon project ID as the resource ID
|
|
21
|
+
*/
|
|
22
|
+
async create(inputs) {
|
|
23
|
+
const client = new require_neon_client.NeonClient(inputs.apiKey);
|
|
24
|
+
const query = inputs.orgId ? `/projects?org_id=${inputs.orgId}&search=${encodeURIComponent(inputs.name)}` : "/projects";
|
|
25
|
+
const existing = (await client.get(query)).projects.find((p) => p.name === inputs.name);
|
|
26
|
+
let projectId;
|
|
27
|
+
if (existing) {
|
|
28
|
+
_pulumi_pulumi.log.info(`Adopting existing Neon project "${inputs.name}" (${existing.id})`);
|
|
29
|
+
projectId = existing.id;
|
|
30
|
+
} else {
|
|
31
|
+
_pulumi_pulumi.log.info(`Neon project "${inputs.name}" not found — creating...`);
|
|
32
|
+
projectId = (await client.post("/projects", { project: { name: inputs.name } })).project.id;
|
|
33
|
+
}
|
|
34
|
+
const outs = {
|
|
35
|
+
...inputs,
|
|
36
|
+
projectId
|
|
37
|
+
};
|
|
38
|
+
return {
|
|
39
|
+
id: projectId,
|
|
40
|
+
outs
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Reads current state for `pulumi refresh`.
|
|
45
|
+
*
|
|
46
|
+
* @param id Current Neon project ID
|
|
47
|
+
* @param props Last known persisted state
|
|
48
|
+
* @returns Refreshed resource ID and properties
|
|
49
|
+
* @throws {Error} If the project no longer exists
|
|
50
|
+
*/
|
|
51
|
+
async read(id, props) {
|
|
52
|
+
const result = await new require_neon_client.NeonClient(props.apiKey).get(`/projects/${id}`);
|
|
53
|
+
return {
|
|
54
|
+
id: result.project.id,
|
|
55
|
+
props: {
|
|
56
|
+
...props,
|
|
57
|
+
name: result.project.name,
|
|
58
|
+
projectId: result.project.id
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Skips deletion to protect production databases.
|
|
64
|
+
*/
|
|
65
|
+
async delete() {
|
|
66
|
+
_pulumi_pulumi.log.warn("Neon project deletion skipped — projects are not deleted by Pulumi");
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Compares old and new inputs. `name` or `orgId` changes trigger replacement.
|
|
70
|
+
*/
|
|
71
|
+
async diff(_id, olds, news) {
|
|
72
|
+
const replaces = [];
|
|
73
|
+
if (olds.name !== news.name) replaces.push("name");
|
|
74
|
+
if (olds.orgId !== news.orgId) replaces.push("orgId");
|
|
75
|
+
return {
|
|
76
|
+
changes: replaces.length > 0,
|
|
77
|
+
replaces,
|
|
78
|
+
deleteBeforeReplace: true
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Manages a Neon project with adopt-or-create semantics.
|
|
84
|
+
*
|
|
85
|
+
* Discovers or creates the project by exact name match. Deletion is a no-op
|
|
86
|
+
* to prevent accidental removal of production databases.
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```typescript
|
|
90
|
+
* const project = new NeonProject("neon-project", {
|
|
91
|
+
* apiKey: config.requireSecret("neonApiKey"),
|
|
92
|
+
* name: "my-app",
|
|
93
|
+
* orgId: "org-abc123",
|
|
94
|
+
* });
|
|
95
|
+
*
|
|
96
|
+
* // Use the resolved project ID downstream
|
|
97
|
+
* const branch = new NeonBranch("neon-branch-production", {
|
|
98
|
+
* apiKey: config.requireSecret("neonApiKey"),
|
|
99
|
+
* projectId: project.projectId,
|
|
100
|
+
* name: "production",
|
|
101
|
+
* });
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
var NeonProject = class extends _pulumi_pulumi.dynamic.Resource {
|
|
105
|
+
/**
|
|
106
|
+
* @param name Pulumi resource name
|
|
107
|
+
* @param args Project configuration inputs
|
|
108
|
+
* @param opts Standard Pulumi resource options
|
|
109
|
+
*/
|
|
110
|
+
constructor(name, args, opts) {
|
|
111
|
+
super(new NeonProjectProvider(), name, {
|
|
112
|
+
...args,
|
|
113
|
+
projectId: void 0
|
|
114
|
+
}, opts);
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
//#endregion
|
|
119
|
+
exports.NeonProject = NeonProject;
|
|
120
|
+
//# sourceMappingURL=project.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project.cjs","names":["NeonClient","pulumi"],"sources":["../../src/neon/project.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\nimport { NeonClient } from \"./client.js\";\n\n/** Resolved inputs for the Neon project dynamic provider. */\nexport interface NeonProjectInputs {\n\t/** Neon API key. */\n\tapiKey: string;\n\n\t/** Exact project display name to adopt or create. */\n\tname: string;\n\n\t/** Optional Neon organization ID to scope the project search. */\n\torgId?: string;\n}\n\n/** Persisted state for the Neon project. */\ninterface NeonProjectOutputs extends NeonProjectInputs {\n\t/** Neon-assigned project ID (e.g. `\"quiet-forest-69719462\"`). */\n\tprojectId: string;\n}\n\n/** Neon API response for listing projects. */\ninterface ProjectListResponse {\n\tprojects: Array<{ id: string; name: string }>;\n}\n\n/** Neon API response for project creation. */\ninterface ProjectCreateResponse {\n\tproject: { id: string; name: string };\n}\n\n/** Neon API response for reading a single project. */\ninterface ProjectReadResponse {\n\tproject: { id: string; name: string };\n}\n\n/**\n * Dynamic provider implementing adopt-or-create for Neon projects.\n *\n * On `create()`, queries `GET /projects` and performs an exact name match.\n * If found, adopts the existing project. If not, creates a new one via\n * `POST /projects`. Deletion is a no-op to protect production databases.\n */\nclass NeonProjectProvider implements pulumi.dynamic.ResourceProvider {\n\t/**\n\t * Creates or adopts a Neon project by name.\n\t *\n\t * @param inputs Resolved project configuration\n\t * @returns The Neon project ID as the resource ID\n\t */\n\tasync create(\n\t\tinputs: NeonProjectInputs,\n\t): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst client = new NeonClient(inputs.apiKey);\n\n\t\tconst query = inputs.orgId\n\t\t\t? `/projects?org_id=${inputs.orgId}&search=${encodeURIComponent(inputs.name)}`\n\t\t\t: \"/projects\";\n\n\t\tconst result = await client.get<ProjectListResponse>(query);\n\n\t\tconst existing = result.projects.find((p) => p.name === inputs.name);\n\n\t\tlet projectId: string;\n\n\t\tif (existing) {\n\t\t\tpulumi.log.info(\n\t\t\t\t`Adopting existing Neon project \"${inputs.name}\" (${existing.id})`,\n\t\t\t);\n\n\t\t\tprojectId = existing.id;\n\t\t} else {\n\t\t\tpulumi.log.info(`Neon project \"${inputs.name}\" not found — creating...`);\n\n\t\t\tconst created = await client.post<ProjectCreateResponse>(\"/projects\", {\n\t\t\t\tproject: { name: inputs.name },\n\t\t\t});\n\n\t\t\tprojectId = created.project.id;\n\t\t}\n\n\t\tconst outs: NeonProjectOutputs = { ...inputs, projectId };\n\n\t\treturn { id: projectId, outs };\n\t}\n\n\t/**\n\t * Reads current state for `pulumi refresh`.\n\t *\n\t * @param id Current Neon project ID\n\t * @param props Last known persisted state\n\t * @returns Refreshed resource ID and properties\n\t * @throws {Error} If the project no longer exists\n\t */\n\tasync read(\n\t\tid: string,\n\t\tprops: NeonProjectOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\tconst client = new NeonClient(props.apiKey);\n\n\t\tconst result = await client.get<ProjectReadResponse>(`/projects/${id}`);\n\n\t\treturn {\n\t\t\tid: result.project.id,\n\t\t\tprops: {\n\t\t\t\t...props,\n\t\t\t\tname: result.project.name,\n\t\t\t\tprojectId: result.project.id,\n\t\t\t},\n\t\t};\n\t}\n\n\t/**\n\t * Skips deletion to protect production databases.\n\t */\n\tasync delete(): Promise<void> {\n\t\tpulumi.log.warn(\n\t\t\t\"Neon project deletion skipped — projects are not deleted by Pulumi\",\n\t\t);\n\t}\n\n\t/**\n\t * Compares old and new inputs. `name` or `orgId` changes trigger replacement.\n\t */\n\tasync diff(\n\t\t_id: string,\n\t\tolds: NeonProjectOutputs,\n\t\tnews: NeonProjectInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst replaces: string[] = [];\n\n\t\tif (olds.name !== news.name) {\n\t\t\treplaces.push(\"name\");\n\t\t}\n\n\t\tif (olds.orgId !== news.orgId) {\n\t\t\treplaces.push(\"orgId\");\n\t\t}\n\n\t\treturn {\n\t\t\tchanges: replaces.length > 0,\n\t\t\treplaces,\n\t\t\tdeleteBeforeReplace: true,\n\t\t};\n\t}\n}\n\n/**\n * Manages a Neon project with adopt-or-create semantics.\n *\n * Discovers or creates the project by exact name match. Deletion is a no-op\n * to prevent accidental removal of production databases.\n *\n * @example\n * ```typescript\n * const project = new NeonProject(\"neon-project\", {\n * apiKey: config.requireSecret(\"neonApiKey\"),\n * name: \"my-app\",\n * orgId: \"org-abc123\",\n * });\n *\n * // Use the resolved project ID downstream\n * const branch = new NeonBranch(\"neon-branch-production\", {\n * apiKey: config.requireSecret(\"neonApiKey\"),\n * projectId: project.projectId,\n * name: \"production\",\n * });\n * ```\n */\nexport class NeonProject extends pulumi.dynamic.Resource {\n\t/** Neon-assigned project ID. */\n\tpublic declare readonly projectId: pulumi.Output<string>;\n\n\t/**\n\t * @param name Pulumi resource name\n\t * @param args Project configuration inputs\n\t * @param opts Standard Pulumi resource options\n\t */\n\tconstructor(\n\t\tname: string,\n\t\targs: {\n\t\t\t/** Neon API key. */\n\t\t\tapiKey: pulumi.Input<string>;\n\n\t\t\t/** Exact project display name to adopt or create. */\n\t\t\tname: pulumi.Input<string>;\n\n\t\t\t/** Optional Neon organization ID to scope the project search. */\n\t\t\torgId?: pulumi.Input<string>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\tsuper(\n\t\t\tnew NeonProjectProvider(),\n\t\t\tname,\n\t\t\t{ ...args, projectId: undefined },\n\t\t\topts,\n\t\t);\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;AA2CA,IAAM,sBAAN,MAAqE;;;;;;;CAOpE,MAAM,OACL,QACuC;EACvC,MAAM,SAAS,IAAIA,+BAAW,OAAO,MAAM;EAE3C,MAAM,QAAQ,OAAO,QAClB,oBAAoB,OAAO,MAAM,UAAU,mBAAmB,OAAO,IAAI,MACzE;EAIH,MAAM,YAAW,MAFI,OAAO,IAAyB,KAAK,GAElC,SAAS,MAAM,MAAM,EAAE,SAAS,OAAO,IAAI;EAEnE,IAAI;EAEJ,IAAI,UAAU;GACb,eAAO,IAAI,KACV,mCAAmC,OAAO,KAAK,KAAK,SAAS,GAAG,EACjE;GAEA,YAAY,SAAS;EACtB,OAAO;GACN,eAAO,IAAI,KAAK,iBAAiB,OAAO,KAAK,0BAA0B;GAMvE,aAAY,MAJU,OAAO,KAA4B,aAAa,EACrE,SAAS,EAAE,MAAM,OAAO,KAAK,EAC9B,CAAC,GAEmB,QAAQ;EAC7B;EAEA,MAAM,OAA2B;GAAE,GAAG;GAAQ;EAAU;EAExD,OAAO;GAAE,IAAI;GAAW;EAAK;CAC9B;;;;;;;;;CAUA,MAAM,KACL,IACA,OACqC;EAGrC,MAAM,SAAS,MAAM,IAFFA,+BAAW,MAAM,MAEV,EAAE,IAAyB,aAAa,IAAI;EAEtE,OAAO;GACN,IAAI,OAAO,QAAQ;GACnB,OAAO;IACN,GAAG;IACH,MAAM,OAAO,QAAQ;IACrB,WAAW,OAAO,QAAQ;GAC3B;EACD;CACD;;;;CAKA,MAAM,SAAwB;EAC7B,eAAO,IAAI,KACV,oEACD;CACD;;;;CAKA,MAAM,KACL,KACA,MACA,MACqC;EACrC,MAAM,WAAqB,CAAC;EAE5B,IAAI,KAAK,SAAS,KAAK,MACtB,SAAS,KAAK,MAAM;EAGrB,IAAI,KAAK,UAAU,KAAK,OACvB,SAAS,KAAK,OAAO;EAGtB,OAAO;GACN,SAAS,SAAS,SAAS;GAC3B;GACA,qBAAqB;EACtB;CACD;AACD;;;;;;;;;;;;;;;;;;;;;;;AAwBA,IAAa,cAAb,cAAiCC,eAAO,QAAQ,SAAS;;;;;;CASxD,YACC,MACA,MAUA,MACC;EACD,MACC,IAAI,oBAAoB,GACxB,MACA;GAAE,GAAG;GAAM,WAAW;EAAU,GAChC,IACD;CACD;AACD"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { t as __name } from "../chunk-OPjESj5l.cjs";
|
|
2
|
+
import * as pulumi from "@pulumi/pulumi";
|
|
3
|
+
|
|
4
|
+
//#region src/neon/project.d.ts
|
|
5
|
+
/** Resolved inputs for the Neon project dynamic provider. */
|
|
6
|
+
interface NeonProjectInputs {
|
|
7
|
+
/** Neon API key. */
|
|
8
|
+
apiKey: string;
|
|
9
|
+
/** Exact project display name to adopt or create. */
|
|
10
|
+
name: string;
|
|
11
|
+
/** Optional Neon organization ID to scope the project search. */
|
|
12
|
+
orgId?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Manages a Neon project with adopt-or-create semantics.
|
|
16
|
+
*
|
|
17
|
+
* Discovers or creates the project by exact name match. Deletion is a no-op
|
|
18
|
+
* to prevent accidental removal of production databases.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* const project = new NeonProject("neon-project", {
|
|
23
|
+
* apiKey: config.requireSecret("neonApiKey"),
|
|
24
|
+
* name: "my-app",
|
|
25
|
+
* orgId: "org-abc123",
|
|
26
|
+
* });
|
|
27
|
+
*
|
|
28
|
+
* // Use the resolved project ID downstream
|
|
29
|
+
* const branch = new NeonBranch("neon-branch-production", {
|
|
30
|
+
* apiKey: config.requireSecret("neonApiKey"),
|
|
31
|
+
* projectId: project.projectId,
|
|
32
|
+
* name: "production",
|
|
33
|
+
* });
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
declare class NeonProject extends pulumi.dynamic.Resource {
|
|
37
|
+
/** Neon-assigned project ID. */
|
|
38
|
+
readonly projectId: pulumi.Output<string>;
|
|
39
|
+
/**
|
|
40
|
+
* @param name Pulumi resource name
|
|
41
|
+
* @param args Project configuration inputs
|
|
42
|
+
* @param opts Standard Pulumi resource options
|
|
43
|
+
*/
|
|
44
|
+
constructor(name: string, args: {
|
|
45
|
+
/** Neon API key. */apiKey: pulumi.Input<string>; /** Exact project display name to adopt or create. */
|
|
46
|
+
name: pulumi.Input<string>; /** Optional Neon organization ID to scope the project search. */
|
|
47
|
+
orgId?: pulumi.Input<string>;
|
|
48
|
+
}, opts?: pulumi.CustomResourceOptions);
|
|
49
|
+
}
|
|
50
|
+
//#endregion
|
|
51
|
+
export { NeonProject, NeonProjectInputs };
|
|
52
|
+
//# sourceMappingURL=project.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project.d.cts","names":[],"sources":["../../src/neon/project.ts"],"mappings":";;;;;UAIiB,iBAAA;;EAEhB,MAAA;EAFiC;EAKjC,IAAA;EALiC;EAQjC,KAAA;AAAA;;;AAAK;AA6JN;;;;;;;;;;;;;;;;;;;cAAa,WAAA,SAAoB,MAAA,CAAO,OAAA,CAAQ,QAAA;EAarC;EAAA,SAXc,SAAA,EAAW,MAAA,CAAO,MAAA;EAcxC;;;;;cAND,IAAA,UACA,IAAA;IAAA,oBAEC,MAAA,EAAQ,MAAA,CAAO,KAAA,UAQF;IALb,IAAA,EAAM,MAAA,CAAO,KAAA,UAKqB;IAFlC,KAAA,GAAQ,MAAA,CAAO,KAAA;EAAA,GAEhB,IAAA,GAAO,MAAA,CAAO,qBAAA;AAAA"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { t as __name } from "../chunk-OPjESj5l.mjs";
|
|
2
|
+
import * as pulumi from "@pulumi/pulumi";
|
|
3
|
+
|
|
4
|
+
//#region src/neon/project.d.ts
|
|
5
|
+
/** Resolved inputs for the Neon project dynamic provider. */
|
|
6
|
+
interface NeonProjectInputs {
|
|
7
|
+
/** Neon API key. */
|
|
8
|
+
apiKey: string;
|
|
9
|
+
/** Exact project display name to adopt or create. */
|
|
10
|
+
name: string;
|
|
11
|
+
/** Optional Neon organization ID to scope the project search. */
|
|
12
|
+
orgId?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Manages a Neon project with adopt-or-create semantics.
|
|
16
|
+
*
|
|
17
|
+
* Discovers or creates the project by exact name match. Deletion is a no-op
|
|
18
|
+
* to prevent accidental removal of production databases.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* const project = new NeonProject("neon-project", {
|
|
23
|
+
* apiKey: config.requireSecret("neonApiKey"),
|
|
24
|
+
* name: "my-app",
|
|
25
|
+
* orgId: "org-abc123",
|
|
26
|
+
* });
|
|
27
|
+
*
|
|
28
|
+
* // Use the resolved project ID downstream
|
|
29
|
+
* const branch = new NeonBranch("neon-branch-production", {
|
|
30
|
+
* apiKey: config.requireSecret("neonApiKey"),
|
|
31
|
+
* projectId: project.projectId,
|
|
32
|
+
* name: "production",
|
|
33
|
+
* });
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
declare class NeonProject extends pulumi.dynamic.Resource {
|
|
37
|
+
/** Neon-assigned project ID. */
|
|
38
|
+
readonly projectId: pulumi.Output<string>;
|
|
39
|
+
/**
|
|
40
|
+
* @param name Pulumi resource name
|
|
41
|
+
* @param args Project configuration inputs
|
|
42
|
+
* @param opts Standard Pulumi resource options
|
|
43
|
+
*/
|
|
44
|
+
constructor(name: string, args: {
|
|
45
|
+
/** Neon API key. */apiKey: pulumi.Input<string>; /** Exact project display name to adopt or create. */
|
|
46
|
+
name: pulumi.Input<string>; /** Optional Neon organization ID to scope the project search. */
|
|
47
|
+
orgId?: pulumi.Input<string>;
|
|
48
|
+
}, opts?: pulumi.CustomResourceOptions);
|
|
49
|
+
}
|
|
50
|
+
//#endregion
|
|
51
|
+
export { NeonProject, NeonProjectInputs };
|
|
52
|
+
//# sourceMappingURL=project.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project.d.mts","names":[],"sources":["../../src/neon/project.ts"],"mappings":";;;;;UAIiB,iBAAA;;EAEhB,MAAA;EAFiC;EAKjC,IAAA;EALiC;EAQjC,KAAA;AAAA;;;AAAK;AA6JN;;;;;;;;;;;;;;;;;;;cAAa,WAAA,SAAoB,MAAA,CAAO,OAAA,CAAQ,QAAA;EAarC;EAAA,SAXc,SAAA,EAAW,MAAA,CAAO,MAAA;EAcxC;;;;;cAND,IAAA,UACA,IAAA;IAAA,oBAEC,MAAA,EAAQ,MAAA,CAAO,KAAA,UAQF;IALb,IAAA,EAAM,MAAA,CAAO,KAAA,UAKqB;IAFlC,KAAA,GAAQ,MAAA,CAAO,KAAA;EAAA,GAEhB,IAAA,GAAO,MAAA,CAAO,qBAAA;AAAA"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { t as __name } from "../chunk-OPjESj5l.mjs";
|
|
2
|
+
import { NeonClient } from "./client.mjs";
|
|
3
|
+
import * as pulumi from "@pulumi/pulumi";
|
|
4
|
+
|
|
5
|
+
//#region src/neon/project.ts
|
|
6
|
+
/**
|
|
7
|
+
* Dynamic provider implementing adopt-or-create for Neon projects.
|
|
8
|
+
*
|
|
9
|
+
* On `create()`, queries `GET /projects` and performs an exact name match.
|
|
10
|
+
* If found, adopts the existing project. If not, creates a new one via
|
|
11
|
+
* `POST /projects`. Deletion is a no-op to protect production databases.
|
|
12
|
+
*/
|
|
13
|
+
var NeonProjectProvider = class {
|
|
14
|
+
/**
|
|
15
|
+
* Creates or adopts a Neon project by name.
|
|
16
|
+
*
|
|
17
|
+
* @param inputs Resolved project configuration
|
|
18
|
+
* @returns The Neon project ID as the resource ID
|
|
19
|
+
*/
|
|
20
|
+
async create(inputs) {
|
|
21
|
+
const client = new NeonClient(inputs.apiKey);
|
|
22
|
+
const query = inputs.orgId ? `/projects?org_id=${inputs.orgId}&search=${encodeURIComponent(inputs.name)}` : "/projects";
|
|
23
|
+
const existing = (await client.get(query)).projects.find((p) => p.name === inputs.name);
|
|
24
|
+
let projectId;
|
|
25
|
+
if (existing) {
|
|
26
|
+
pulumi.log.info(`Adopting existing Neon project "${inputs.name}" (${existing.id})`);
|
|
27
|
+
projectId = existing.id;
|
|
28
|
+
} else {
|
|
29
|
+
pulumi.log.info(`Neon project "${inputs.name}" not found — creating...`);
|
|
30
|
+
projectId = (await client.post("/projects", { project: { name: inputs.name } })).project.id;
|
|
31
|
+
}
|
|
32
|
+
const outs = {
|
|
33
|
+
...inputs,
|
|
34
|
+
projectId
|
|
35
|
+
};
|
|
36
|
+
return {
|
|
37
|
+
id: projectId,
|
|
38
|
+
outs
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Reads current state for `pulumi refresh`.
|
|
43
|
+
*
|
|
44
|
+
* @param id Current Neon project ID
|
|
45
|
+
* @param props Last known persisted state
|
|
46
|
+
* @returns Refreshed resource ID and properties
|
|
47
|
+
* @throws {Error} If the project no longer exists
|
|
48
|
+
*/
|
|
49
|
+
async read(id, props) {
|
|
50
|
+
const result = await new NeonClient(props.apiKey).get(`/projects/${id}`);
|
|
51
|
+
return {
|
|
52
|
+
id: result.project.id,
|
|
53
|
+
props: {
|
|
54
|
+
...props,
|
|
55
|
+
name: result.project.name,
|
|
56
|
+
projectId: result.project.id
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Skips deletion to protect production databases.
|
|
62
|
+
*/
|
|
63
|
+
async delete() {
|
|
64
|
+
pulumi.log.warn("Neon project deletion skipped — projects are not deleted by Pulumi");
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Compares old and new inputs. `name` or `orgId` changes trigger replacement.
|
|
68
|
+
*/
|
|
69
|
+
async diff(_id, olds, news) {
|
|
70
|
+
const replaces = [];
|
|
71
|
+
if (olds.name !== news.name) replaces.push("name");
|
|
72
|
+
if (olds.orgId !== news.orgId) replaces.push("orgId");
|
|
73
|
+
return {
|
|
74
|
+
changes: replaces.length > 0,
|
|
75
|
+
replaces,
|
|
76
|
+
deleteBeforeReplace: true
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Manages a Neon project with adopt-or-create semantics.
|
|
82
|
+
*
|
|
83
|
+
* Discovers or creates the project by exact name match. Deletion is a no-op
|
|
84
|
+
* to prevent accidental removal of production databases.
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```typescript
|
|
88
|
+
* const project = new NeonProject("neon-project", {
|
|
89
|
+
* apiKey: config.requireSecret("neonApiKey"),
|
|
90
|
+
* name: "my-app",
|
|
91
|
+
* orgId: "org-abc123",
|
|
92
|
+
* });
|
|
93
|
+
*
|
|
94
|
+
* // Use the resolved project ID downstream
|
|
95
|
+
* const branch = new NeonBranch("neon-branch-production", {
|
|
96
|
+
* apiKey: config.requireSecret("neonApiKey"),
|
|
97
|
+
* projectId: project.projectId,
|
|
98
|
+
* name: "production",
|
|
99
|
+
* });
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
var NeonProject = class extends pulumi.dynamic.Resource {
|
|
103
|
+
/**
|
|
104
|
+
* @param name Pulumi resource name
|
|
105
|
+
* @param args Project configuration inputs
|
|
106
|
+
* @param opts Standard Pulumi resource options
|
|
107
|
+
*/
|
|
108
|
+
constructor(name, args, opts) {
|
|
109
|
+
super(new NeonProjectProvider(), name, {
|
|
110
|
+
...args,
|
|
111
|
+
projectId: void 0
|
|
112
|
+
}, opts);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
//#endregion
|
|
117
|
+
export { NeonProject };
|
|
118
|
+
//# sourceMappingURL=project.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project.mjs","names":[],"sources":["../../src/neon/project.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\nimport { NeonClient } from \"./client.js\";\n\n/** Resolved inputs for the Neon project dynamic provider. */\nexport interface NeonProjectInputs {\n\t/** Neon API key. */\n\tapiKey: string;\n\n\t/** Exact project display name to adopt or create. */\n\tname: string;\n\n\t/** Optional Neon organization ID to scope the project search. */\n\torgId?: string;\n}\n\n/** Persisted state for the Neon project. */\ninterface NeonProjectOutputs extends NeonProjectInputs {\n\t/** Neon-assigned project ID (e.g. `\"quiet-forest-69719462\"`). */\n\tprojectId: string;\n}\n\n/** Neon API response for listing projects. */\ninterface ProjectListResponse {\n\tprojects: Array<{ id: string; name: string }>;\n}\n\n/** Neon API response for project creation. */\ninterface ProjectCreateResponse {\n\tproject: { id: string; name: string };\n}\n\n/** Neon API response for reading a single project. */\ninterface ProjectReadResponse {\n\tproject: { id: string; name: string };\n}\n\n/**\n * Dynamic provider implementing adopt-or-create for Neon projects.\n *\n * On `create()`, queries `GET /projects` and performs an exact name match.\n * If found, adopts the existing project. If not, creates a new one via\n * `POST /projects`. Deletion is a no-op to protect production databases.\n */\nclass NeonProjectProvider implements pulumi.dynamic.ResourceProvider {\n\t/**\n\t * Creates or adopts a Neon project by name.\n\t *\n\t * @param inputs Resolved project configuration\n\t * @returns The Neon project ID as the resource ID\n\t */\n\tasync create(\n\t\tinputs: NeonProjectInputs,\n\t): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst client = new NeonClient(inputs.apiKey);\n\n\t\tconst query = inputs.orgId\n\t\t\t? `/projects?org_id=${inputs.orgId}&search=${encodeURIComponent(inputs.name)}`\n\t\t\t: \"/projects\";\n\n\t\tconst result = await client.get<ProjectListResponse>(query);\n\n\t\tconst existing = result.projects.find((p) => p.name === inputs.name);\n\n\t\tlet projectId: string;\n\n\t\tif (existing) {\n\t\t\tpulumi.log.info(\n\t\t\t\t`Adopting existing Neon project \"${inputs.name}\" (${existing.id})`,\n\t\t\t);\n\n\t\t\tprojectId = existing.id;\n\t\t} else {\n\t\t\tpulumi.log.info(`Neon project \"${inputs.name}\" not found — creating...`);\n\n\t\t\tconst created = await client.post<ProjectCreateResponse>(\"/projects\", {\n\t\t\t\tproject: { name: inputs.name },\n\t\t\t});\n\n\t\t\tprojectId = created.project.id;\n\t\t}\n\n\t\tconst outs: NeonProjectOutputs = { ...inputs, projectId };\n\n\t\treturn { id: projectId, outs };\n\t}\n\n\t/**\n\t * Reads current state for `pulumi refresh`.\n\t *\n\t * @param id Current Neon project ID\n\t * @param props Last known persisted state\n\t * @returns Refreshed resource ID and properties\n\t * @throws {Error} If the project no longer exists\n\t */\n\tasync read(\n\t\tid: string,\n\t\tprops: NeonProjectOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\tconst client = new NeonClient(props.apiKey);\n\n\t\tconst result = await client.get<ProjectReadResponse>(`/projects/${id}`);\n\n\t\treturn {\n\t\t\tid: result.project.id,\n\t\t\tprops: {\n\t\t\t\t...props,\n\t\t\t\tname: result.project.name,\n\t\t\t\tprojectId: result.project.id,\n\t\t\t},\n\t\t};\n\t}\n\n\t/**\n\t * Skips deletion to protect production databases.\n\t */\n\tasync delete(): Promise<void> {\n\t\tpulumi.log.warn(\n\t\t\t\"Neon project deletion skipped — projects are not deleted by Pulumi\",\n\t\t);\n\t}\n\n\t/**\n\t * Compares old and new inputs. `name` or `orgId` changes trigger replacement.\n\t */\n\tasync diff(\n\t\t_id: string,\n\t\tolds: NeonProjectOutputs,\n\t\tnews: NeonProjectInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst replaces: string[] = [];\n\n\t\tif (olds.name !== news.name) {\n\t\t\treplaces.push(\"name\");\n\t\t}\n\n\t\tif (olds.orgId !== news.orgId) {\n\t\t\treplaces.push(\"orgId\");\n\t\t}\n\n\t\treturn {\n\t\t\tchanges: replaces.length > 0,\n\t\t\treplaces,\n\t\t\tdeleteBeforeReplace: true,\n\t\t};\n\t}\n}\n\n/**\n * Manages a Neon project with adopt-or-create semantics.\n *\n * Discovers or creates the project by exact name match. Deletion is a no-op\n * to prevent accidental removal of production databases.\n *\n * @example\n * ```typescript\n * const project = new NeonProject(\"neon-project\", {\n * apiKey: config.requireSecret(\"neonApiKey\"),\n * name: \"my-app\",\n * orgId: \"org-abc123\",\n * });\n *\n * // Use the resolved project ID downstream\n * const branch = new NeonBranch(\"neon-branch-production\", {\n * apiKey: config.requireSecret(\"neonApiKey\"),\n * projectId: project.projectId,\n * name: \"production\",\n * });\n * ```\n */\nexport class NeonProject extends pulumi.dynamic.Resource {\n\t/** Neon-assigned project ID. */\n\tpublic declare readonly projectId: pulumi.Output<string>;\n\n\t/**\n\t * @param name Pulumi resource name\n\t * @param args Project configuration inputs\n\t * @param opts Standard Pulumi resource options\n\t */\n\tconstructor(\n\t\tname: string,\n\t\targs: {\n\t\t\t/** Neon API key. */\n\t\t\tapiKey: pulumi.Input<string>;\n\n\t\t\t/** Exact project display name to adopt or create. */\n\t\t\tname: pulumi.Input<string>;\n\n\t\t\t/** Optional Neon organization ID to scope the project search. */\n\t\t\torgId?: pulumi.Input<string>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\tsuper(\n\t\t\tnew NeonProjectProvider(),\n\t\t\tname,\n\t\t\t{ ...args, projectId: undefined },\n\t\t\topts,\n\t\t);\n\t}\n}\n"],"mappings":";;;;;;;;;;;;AA2CA,IAAM,sBAAN,MAAqE;;;;;;;CAOpE,MAAM,OACL,QACuC;EACvC,MAAM,SAAS,IAAI,WAAW,OAAO,MAAM;EAE3C,MAAM,QAAQ,OAAO,QAClB,oBAAoB,OAAO,MAAM,UAAU,mBAAmB,OAAO,IAAI,MACzE;EAIH,MAAM,YAAW,MAFI,OAAO,IAAyB,KAAK,GAElC,SAAS,MAAM,MAAM,EAAE,SAAS,OAAO,IAAI;EAEnE,IAAI;EAEJ,IAAI,UAAU;GACb,OAAO,IAAI,KACV,mCAAmC,OAAO,KAAK,KAAK,SAAS,GAAG,EACjE;GAEA,YAAY,SAAS;EACtB,OAAO;GACN,OAAO,IAAI,KAAK,iBAAiB,OAAO,KAAK,0BAA0B;GAMvE,aAAY,MAJU,OAAO,KAA4B,aAAa,EACrE,SAAS,EAAE,MAAM,OAAO,KAAK,EAC9B,CAAC,GAEmB,QAAQ;EAC7B;EAEA,MAAM,OAA2B;GAAE,GAAG;GAAQ;EAAU;EAExD,OAAO;GAAE,IAAI;GAAW;EAAK;CAC9B;;;;;;;;;CAUA,MAAM,KACL,IACA,OACqC;EAGrC,MAAM,SAAS,MAAM,IAFF,WAAW,MAAM,MAEV,EAAE,IAAyB,aAAa,IAAI;EAEtE,OAAO;GACN,IAAI,OAAO,QAAQ;GACnB,OAAO;IACN,GAAG;IACH,MAAM,OAAO,QAAQ;IACrB,WAAW,OAAO,QAAQ;GAC3B;EACD;CACD;;;;CAKA,MAAM,SAAwB;EAC7B,OAAO,IAAI,KACV,oEACD;CACD;;;;CAKA,MAAM,KACL,KACA,MACA,MACqC;EACrC,MAAM,WAAqB,CAAC;EAE5B,IAAI,KAAK,SAAS,KAAK,MACtB,SAAS,KAAK,MAAM;EAGrB,IAAI,KAAK,UAAU,KAAK,OACvB,SAAS,KAAK,OAAO;EAGtB,OAAO;GACN,SAAS,SAAS,SAAS;GAC3B;GACA,qBAAqB;EACtB;CACD;AACD;;;;;;;;;;;;;;;;;;;;;;;AAwBA,IAAa,cAAb,cAAiC,OAAO,QAAQ,SAAS;;;;;;CASxD,YACC,MACA,MAUA,MACC;EACD,MACC,IAAI,oBAAoB,GACxB,MACA;GAAE,GAAG;GAAM,WAAW;EAAU,GAChC,IACD;CACD;AACD"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
const require_chunk = require('../chunk-BVYJZCqc.cjs');
|
|
3
|
+
const require_neon_client = require('./client.cjs');
|
|
4
|
+
let _pulumi_pulumi = require("@pulumi/pulumi");
|
|
5
|
+
_pulumi_pulumi = require_chunk.__toESM(_pulumi_pulumi, 1);
|
|
6
|
+
|
|
7
|
+
//#region src/neon/role.ts
|
|
8
|
+
/**
|
|
9
|
+
* Checks if a role exists by name on a branch.
|
|
10
|
+
*
|
|
11
|
+
* @param client Authenticated Neon API client
|
|
12
|
+
* @param projectId Neon project ID
|
|
13
|
+
* @param branchId Branch ID to search within
|
|
14
|
+
* @param name Exact role name to match
|
|
15
|
+
* @returns `true` if found, `false` otherwise
|
|
16
|
+
*/
|
|
17
|
+
async function roleExists(client, projectId, branchId, name) {
|
|
18
|
+
return (await client.get(`/projects/${projectId}/branches/${branchId}/roles`)).roles.some((r) => r.name === name);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Reveals the password for an existing role via the dedicated API endpoint.
|
|
22
|
+
*
|
|
23
|
+
* @param client Authenticated Neon API client
|
|
24
|
+
* @param projectId Neon project ID
|
|
25
|
+
* @param branchId Branch ID
|
|
26
|
+
* @param name Role name
|
|
27
|
+
* @returns The role password
|
|
28
|
+
*/
|
|
29
|
+
async function revealPassword(client, projectId, branchId, name) {
|
|
30
|
+
return (await client.get(`/projects/${projectId}/branches/${branchId}/roles/${name}/reveal_password`)).password;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Dynamic provider implementing CRUD for Neon database roles.
|
|
34
|
+
*
|
|
35
|
+
* Uses adopt-or-create on `create()`: finds an existing role by name
|
|
36
|
+
* before creating a new one.
|
|
37
|
+
*/
|
|
38
|
+
var NeonRoleProvider = class {
|
|
39
|
+
/**
|
|
40
|
+
* Creates or adopts a Neon role by name.
|
|
41
|
+
*
|
|
42
|
+
* @param inputs Resolved role configuration
|
|
43
|
+
* @returns Composite ID `{branchId}/{roleName}` as the resource ID
|
|
44
|
+
*/
|
|
45
|
+
async create(inputs) {
|
|
46
|
+
const client = new require_neon_client.NeonClient(inputs.apiKey);
|
|
47
|
+
if (!await roleExists(client, inputs.projectId, inputs.branchId, inputs.name)) await client.post(`/projects/${inputs.projectId}/branches/${inputs.branchId}/roles`, { role: { name: inputs.name } });
|
|
48
|
+
else _pulumi_pulumi.log.info(`Adopting existing Neon role "${inputs.name}"`);
|
|
49
|
+
const password = await revealPassword(client, inputs.projectId, inputs.branchId, inputs.name);
|
|
50
|
+
return {
|
|
51
|
+
id: `${inputs.branchId}/${inputs.name}`,
|
|
52
|
+
outs: {
|
|
53
|
+
...inputs,
|
|
54
|
+
password
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Reads current state for `pulumi refresh`.
|
|
60
|
+
*
|
|
61
|
+
* @param id Current composite role ID
|
|
62
|
+
* @param props Last known persisted state
|
|
63
|
+
* @returns Refreshed resource ID and properties with current password
|
|
64
|
+
* @throws {Error} If the role no longer exists
|
|
65
|
+
*/
|
|
66
|
+
async read(id, props) {
|
|
67
|
+
const password = await revealPassword(new require_neon_client.NeonClient(props.apiKey), props.projectId, props.branchId, props.name);
|
|
68
|
+
return {
|
|
69
|
+
id,
|
|
70
|
+
props: {
|
|
71
|
+
...props,
|
|
72
|
+
password
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Deletes the Neon role. Silently succeeds if already deleted.
|
|
78
|
+
*/
|
|
79
|
+
async delete(_id, props) {
|
|
80
|
+
const client = new require_neon_client.NeonClient(props.apiKey);
|
|
81
|
+
try {
|
|
82
|
+
await client.delete(`/projects/${props.projectId}/branches/${props.branchId}/roles/${props.name}`);
|
|
83
|
+
} catch {
|
|
84
|
+
_pulumi_pulumi.log.warn(`Failed to delete Neon role "${props.name}" (may already be deleted)`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Compares old and new inputs. Changing any field triggers replacement
|
|
89
|
+
* since roles cannot be renamed or moved between branches.
|
|
90
|
+
*/
|
|
91
|
+
async diff(_id, olds, news) {
|
|
92
|
+
const replaces = [];
|
|
93
|
+
if (olds.projectId !== news.projectId) replaces.push("projectId");
|
|
94
|
+
if (olds.branchId !== news.branchId) replaces.push("branchId");
|
|
95
|
+
if (olds.name !== news.name) replaces.push("name");
|
|
96
|
+
return {
|
|
97
|
+
changes: replaces.length > 0,
|
|
98
|
+
replaces,
|
|
99
|
+
deleteBeforeReplace: true
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* Manages a Neon database role with adopt-or-create semantics.
|
|
105
|
+
* Exposes `password` as a secret output for connection string composition.
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* ```typescript
|
|
109
|
+
* const role = new NeonRole("neon-role-owner", {
|
|
110
|
+
* apiKey: config.requireSecret("neonApiKey"),
|
|
111
|
+
* projectId: "quiet-forest-69719462",
|
|
112
|
+
* branchId: branch.id,
|
|
113
|
+
* name: "neondb_owner",
|
|
114
|
+
* });
|
|
115
|
+
*
|
|
116
|
+
* const password = role.password;
|
|
117
|
+
* ```
|
|
118
|
+
*/
|
|
119
|
+
var NeonRole = class extends _pulumi_pulumi.dynamic.Resource {
|
|
120
|
+
/**
|
|
121
|
+
* @param name Pulumi resource name
|
|
122
|
+
* @param args Role configuration inputs
|
|
123
|
+
* @param opts Standard Pulumi resource options
|
|
124
|
+
*/
|
|
125
|
+
constructor(name, args, opts) {
|
|
126
|
+
super(new NeonRoleProvider(), name, {
|
|
127
|
+
...args,
|
|
128
|
+
password: void 0
|
|
129
|
+
}, opts);
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
//#endregion
|
|
134
|
+
exports.NeonRole = NeonRole;
|
|
135
|
+
//# sourceMappingURL=role.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"role.cjs","names":["NeonClient","pulumi"],"sources":["../../src/neon/role.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\nimport { NeonClient } from \"./client.js\";\n\n/** Resolved inputs for the Neon role dynamic provider. */\nexport interface NeonRoleInputs {\n\t/** Neon API key. */\n\tapiKey: string;\n\n\t/** Neon project ID. */\n\tprojectId: string;\n\n\t/** Branch ID the role belongs to. */\n\tbranchId: string;\n\n\t/** Role name (e.g. `\"neondb_owner\"`). */\n\tname: string;\n}\n\n/** Persisted state for the Neon role. */\ninterface NeonRoleOutputs extends NeonRoleInputs {\n\t/** Role password (auto-generated by Neon). */\n\tpassword: string;\n}\n\n/** Neon API response for the reveal_password endpoint. */\ninterface PasswordResponse {\n\tpassword: string;\n}\n\n/** Neon API response for listing roles. */\ninterface RoleListResponse {\n\troles: Array<{\n\t\tname: string;\n\t}>;\n}\n\n/**\n * Checks if a role exists by name on a branch.\n *\n * @param client Authenticated Neon API client\n * @param projectId Neon project ID\n * @param branchId Branch ID to search within\n * @param name Exact role name to match\n * @returns `true` if found, `false` otherwise\n */\nasync function roleExists(\n\tclient: NeonClient,\n\tprojectId: string,\n\tbranchId: string,\n\tname: string,\n): Promise<boolean> {\n\tconst result = await client.get<RoleListResponse>(\n\t\t`/projects/${projectId}/branches/${branchId}/roles`,\n\t);\n\n\treturn result.roles.some((r) => r.name === name);\n}\n\n/**\n * Reveals the password for an existing role via the dedicated API endpoint.\n *\n * @param client Authenticated Neon API client\n * @param projectId Neon project ID\n * @param branchId Branch ID\n * @param name Role name\n * @returns The role password\n */\nasync function revealPassword(\n\tclient: NeonClient,\n\tprojectId: string,\n\tbranchId: string,\n\tname: string,\n): Promise<string> {\n\tconst result = await client.get<PasswordResponse>(\n\t\t`/projects/${projectId}/branches/${branchId}/roles/${name}/reveal_password`,\n\t);\n\n\treturn result.password;\n}\n\n/**\n * Dynamic provider implementing CRUD for Neon database roles.\n *\n * Uses adopt-or-create on `create()`: finds an existing role by name\n * before creating a new one.\n */\nclass NeonRoleProvider implements pulumi.dynamic.ResourceProvider {\n\t/**\n\t * Creates or adopts a Neon role by name.\n\t *\n\t * @param inputs Resolved role configuration\n\t * @returns Composite ID `{branchId}/{roleName}` as the resource ID\n\t */\n\tasync create(inputs: NeonRoleInputs): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst client = new NeonClient(inputs.apiKey);\n\n\t\tconst exists = await roleExists(\n\t\t\tclient,\n\t\t\tinputs.projectId,\n\t\t\tinputs.branchId,\n\t\t\tinputs.name,\n\t\t);\n\n\t\tif (!exists) {\n\t\t\tawait client.post(\n\t\t\t\t`/projects/${inputs.projectId}/branches/${inputs.branchId}/roles`,\n\t\t\t\t{ role: { name: inputs.name } },\n\t\t\t);\n\t\t} else {\n\t\t\tpulumi.log.info(`Adopting existing Neon role \"${inputs.name}\"`);\n\t\t}\n\n\t\tconst password = await revealPassword(\n\t\t\tclient,\n\t\t\tinputs.projectId,\n\t\t\tinputs.branchId,\n\t\t\tinputs.name,\n\t\t);\n\n\t\treturn {\n\t\t\tid: `${inputs.branchId}/${inputs.name}`,\n\t\t\touts: { ...inputs, password },\n\t\t};\n\t}\n\n\t/**\n\t * Reads current state for `pulumi refresh`.\n\t *\n\t * @param id Current composite role ID\n\t * @param props Last known persisted state\n\t * @returns Refreshed resource ID and properties with current password\n\t * @throws {Error} If the role no longer exists\n\t */\n\tasync read(\n\t\tid: string,\n\t\tprops: NeonRoleOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\tconst client = new NeonClient(props.apiKey);\n\n\t\tconst password = await revealPassword(\n\t\t\tclient,\n\t\t\tprops.projectId,\n\t\t\tprops.branchId,\n\t\t\tprops.name,\n\t\t);\n\n\t\treturn {\n\t\t\tid,\n\t\t\tprops: { ...props, password },\n\t\t};\n\t}\n\n\t/**\n\t * Deletes the Neon role. Silently succeeds if already deleted.\n\t */\n\tasync delete(_id: string, props: NeonRoleOutputs): Promise<void> {\n\t\tconst client = new NeonClient(props.apiKey);\n\n\t\ttry {\n\t\t\tawait client.delete(\n\t\t\t\t`/projects/${props.projectId}/branches/${props.branchId}/roles/${props.name}`,\n\t\t\t);\n\t\t} catch {\n\t\t\tpulumi.log.warn(\n\t\t\t\t`Failed to delete Neon role \"${props.name}\" (may already be deleted)`,\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Compares old and new inputs. Changing any field triggers replacement\n\t * since roles cannot be renamed or moved between branches.\n\t */\n\tasync diff(\n\t\t_id: string,\n\t\tolds: NeonRoleOutputs,\n\t\tnews: NeonRoleInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst replaces: string[] = [];\n\n\t\tif (olds.projectId !== news.projectId) {\n\t\t\treplaces.push(\"projectId\");\n\t\t}\n\n\t\tif (olds.branchId !== news.branchId) {\n\t\t\treplaces.push(\"branchId\");\n\t\t}\n\n\t\tif (olds.name !== news.name) {\n\t\t\treplaces.push(\"name\");\n\t\t}\n\n\t\treturn {\n\t\t\tchanges: replaces.length > 0,\n\t\t\treplaces,\n\t\t\tdeleteBeforeReplace: true,\n\t\t};\n\t}\n}\n\n/**\n * Manages a Neon database role with adopt-or-create semantics.\n * Exposes `password` as a secret output for connection string composition.\n *\n * @example\n * ```typescript\n * const role = new NeonRole(\"neon-role-owner\", {\n * apiKey: config.requireSecret(\"neonApiKey\"),\n * projectId: \"quiet-forest-69719462\",\n * branchId: branch.id,\n * name: \"neondb_owner\",\n * });\n *\n * const password = role.password;\n * ```\n */\nexport class NeonRole extends pulumi.dynamic.Resource {\n\t/** Role password (auto-generated by Neon). */\n\tpublic declare readonly password: pulumi.Output<string>;\n\n\t/**\n\t * @param name Pulumi resource name\n\t * @param args Role configuration inputs\n\t * @param opts Standard Pulumi resource options\n\t */\n\tconstructor(\n\t\tname: string,\n\t\targs: {\n\t\t\t/** Neon API key. */\n\t\t\tapiKey: pulumi.Input<string>;\n\n\t\t\t/** Neon project ID. */\n\t\t\tprojectId: pulumi.Input<string>;\n\n\t\t\t/** Branch ID the role belongs to. */\n\t\t\tbranchId: pulumi.Input<string>;\n\n\t\t\t/** Role name (e.g. `\"neondb_owner\"`). */\n\t\t\tname: pulumi.Input<string>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\tsuper(new NeonRoleProvider(), name, { ...args, password: undefined }, opts);\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;AA6CA,eAAe,WACd,QACA,WACA,UACA,MACmB;CAKnB,QAAO,MAJc,OAAO,IAC3B,aAAa,UAAU,YAAY,SAAS,OAC7C,GAEc,MAAM,MAAM,MAAM,EAAE,SAAS,IAAI;AAChD;;;;;;;;;;AAWA,eAAe,eACd,QACA,WACA,UACA,MACkB;CAKlB,QAAO,MAJc,OAAO,IAC3B,aAAa,UAAU,YAAY,SAAS,SAAS,KAAK,iBAC3D,GAEc;AACf;;;;;;;AAQA,IAAM,mBAAN,MAAkE;;;;;;;CAOjE,MAAM,OAAO,QAA8D;EAC1E,MAAM,SAAS,IAAIA,+BAAW,OAAO,MAAM;EAS3C,IAAI,CAAC,MAPgB,WACpB,QACA,OAAO,WACP,OAAO,UACP,OAAO,IACR,GAGC,MAAM,OAAO,KACZ,aAAa,OAAO,UAAU,YAAY,OAAO,SAAS,SAC1D,EAAE,MAAM,EAAE,MAAM,OAAO,KAAK,EAAE,CAC/B;OAEA,eAAO,IAAI,KAAK,gCAAgC,OAAO,KAAK,EAAE;EAG/D,MAAM,WAAW,MAAM,eACtB,QACA,OAAO,WACP,OAAO,UACP,OAAO,IACR;EAEA,OAAO;GACN,IAAI,GAAG,OAAO,SAAS,GAAG,OAAO;GACjC,MAAM;IAAE,GAAG;IAAQ;GAAS;EAC7B;CACD;;;;;;;;;CAUA,MAAM,KACL,IACA,OACqC;EAGrC,MAAM,WAAW,MAAM,eACtB,IAHkBA,+BAAW,MAAM,MAG9B,GACL,MAAM,WACN,MAAM,UACN,MAAM,IACP;EAEA,OAAO;GACN;GACA,OAAO;IAAE,GAAG;IAAO;GAAS;EAC7B;CACD;;;;CAKA,MAAM,OAAO,KAAa,OAAuC;EAChE,MAAM,SAAS,IAAIA,+BAAW,MAAM,MAAM;EAE1C,IAAI;GACH,MAAM,OAAO,OACZ,aAAa,MAAM,UAAU,YAAY,MAAM,SAAS,SAAS,MAAM,MACxE;EACD,QAAQ;GACP,eAAO,IAAI,KACV,+BAA+B,MAAM,KAAK,2BAC3C;EACD;CACD;;;;;CAMA,MAAM,KACL,KACA,MACA,MACqC;EACrC,MAAM,WAAqB,CAAC;EAE5B,IAAI,KAAK,cAAc,KAAK,WAC3B,SAAS,KAAK,WAAW;EAG1B,IAAI,KAAK,aAAa,KAAK,UAC1B,SAAS,KAAK,UAAU;EAGzB,IAAI,KAAK,SAAS,KAAK,MACtB,SAAS,KAAK,MAAM;EAGrB,OAAO;GACN,SAAS,SAAS,SAAS;GAC3B;GACA,qBAAqB;EACtB;CACD;AACD;;;;;;;;;;;;;;;;;AAkBA,IAAa,WAAb,cAA8BC,eAAO,QAAQ,SAAS;;;;;;CASrD,YACC,MACA,MAaA,MACC;EACD,MAAM,IAAI,iBAAiB,GAAG,MAAM;GAAE,GAAG;GAAM,UAAU;EAAU,GAAG,IAAI;CAC3E;AACD"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { t as __name } from "../chunk-OPjESj5l.cjs";
|
|
2
|
+
import * as pulumi from "@pulumi/pulumi";
|
|
3
|
+
|
|
4
|
+
//#region src/neon/role.d.ts
|
|
5
|
+
/** Resolved inputs for the Neon role dynamic provider. */
|
|
6
|
+
interface NeonRoleInputs {
|
|
7
|
+
/** Neon API key. */
|
|
8
|
+
apiKey: string;
|
|
9
|
+
/** Neon project ID. */
|
|
10
|
+
projectId: string;
|
|
11
|
+
/** Branch ID the role belongs to. */
|
|
12
|
+
branchId: string;
|
|
13
|
+
/** Role name (e.g. `"neondb_owner"`). */
|
|
14
|
+
name: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Manages a Neon database role with adopt-or-create semantics.
|
|
18
|
+
* Exposes `password` as a secret output for connection string composition.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* const role = new NeonRole("neon-role-owner", {
|
|
23
|
+
* apiKey: config.requireSecret("neonApiKey"),
|
|
24
|
+
* projectId: "quiet-forest-69719462",
|
|
25
|
+
* branchId: branch.id,
|
|
26
|
+
* name: "neondb_owner",
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* const password = role.password;
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
declare class NeonRole extends pulumi.dynamic.Resource {
|
|
33
|
+
/** Role password (auto-generated by Neon). */
|
|
34
|
+
readonly password: pulumi.Output<string>;
|
|
35
|
+
/**
|
|
36
|
+
* @param name Pulumi resource name
|
|
37
|
+
* @param args Role configuration inputs
|
|
38
|
+
* @param opts Standard Pulumi resource options
|
|
39
|
+
*/
|
|
40
|
+
constructor(name: string, args: {
|
|
41
|
+
/** Neon API key. */apiKey: pulumi.Input<string>; /** Neon project ID. */
|
|
42
|
+
projectId: pulumi.Input<string>; /** Branch ID the role belongs to. */
|
|
43
|
+
branchId: pulumi.Input<string>; /** Role name (e.g. `"neondb_owner"`). */
|
|
44
|
+
name: pulumi.Input<string>;
|
|
45
|
+
}, opts?: pulumi.CustomResourceOptions);
|
|
46
|
+
}
|
|
47
|
+
//#endregion
|
|
48
|
+
export { NeonRole, NeonRoleInputs };
|
|
49
|
+
//# sourceMappingURL=role.d.cts.map
|