@infracraft/pulumi 1.6.3 → 1.6.5
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/neon/endpoint.cjs +2 -1
- package/dist/neon/endpoint.cjs.map +1 -1
- package/dist/neon/endpoint.d.cts.map +1 -1
- package/dist/neon/endpoint.d.mts.map +1 -1
- package/dist/neon/endpoint.mjs +2 -1
- package/dist/neon/endpoint.mjs.map +1 -1
- package/dist/vercel/resource-connection.cjs +37 -20
- package/dist/vercel/resource-connection.cjs.map +1 -1
- package/dist/vercel/resource-connection.d.cts +32 -29
- package/dist/vercel/resource-connection.d.cts.map +1 -1
- package/dist/vercel/resource-connection.d.mts +32 -29
- package/dist/vercel/resource-connection.d.mts.map +1 -1
- package/dist/vercel/resource-connection.mjs +37 -20
- package/dist/vercel/resource-connection.mjs.map +1 -1
- package/package.json +1 -1
package/dist/neon/endpoint.cjs
CHANGED
|
@@ -40,7 +40,8 @@ var NeonEndpointResourceProvider = class {
|
|
|
40
40
|
}
|
|
41
41
|
};
|
|
42
42
|
}
|
|
43
|
-
const result = await client.post(`/projects/${inputs.projectId}/
|
|
43
|
+
const result = await client.post(`/projects/${inputs.projectId}/endpoints`, { endpoint: {
|
|
44
|
+
branch_id: inputs.branchId,
|
|
44
45
|
type: "read_write",
|
|
45
46
|
autoscaling_limit_min_cu: inputs.minCu,
|
|
46
47
|
autoscaling_limit_max_cu: inputs.maxCu,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"endpoint.cjs","names":["NeonClient","pulumi"],"sources":["../../src/neon/endpoint.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\nimport type { NeonBranch } from \"./branch\";\nimport { NeonClient } from \"./client\";\nimport type { NeonProject } from \"./project\";\nimport type { NeonProvider } from \"./provider\";\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 */\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 NeonEndpointResourceProvider implements pulumi.dynamic.ResourceProvider {\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\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\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\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\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/** Internal dynamic resource — not part of the public API. */\nclass NeonEndpointResource extends pulumi.dynamic.Resource {\n\tpublic declare readonly host: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: {\n\t\t\tapiKey: pulumi.Input<string>;\n\t\t\tprojectId: pulumi.Input<string>;\n\t\t\tbranchId: pulumi.Input<string>;\n\t\t\tminCu: pulumi.Input<number>;\n\t\t\tmaxCu: pulumi.Input<number>;\n\t\t\tsuspendTimeout: pulumi.Input<number>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\tsuper(\n\t\t\tnew NeonEndpointResourceProvider(),\n\t\t\tname,\n\t\t\t{ ...args, host: undefined },\n\t\t\topts,\n\t\t);\n\t}\n}\n\n/** Options type for NeonEndpoint — replaces Pulumi's native `provider` field. */\ntype NeonEndpointOptions = Omit<pulumi.ComponentResourceOptions, \"provider\"> & {\n\t/** Neon authentication context. */\n\tprovider: NeonProvider;\n\n\t/** Neon project context. */\n\tproject: NeonProject;\n\n\t/** Neon branch context. */\n\tbranch: NeonBranch;\n};\n\n/** Args for NeonEndpoint. */\nexport interface NeonEndpointArgs {\n\t/** Minimum compute units. */\n\tminCu: pulumi.Input<number>;\n\n\t/** Maximum compute units. */\n\tmaxCu: pulumi.Input<number>;\n\n\t/** Seconds of inactivity before suspending. */\n\tsuspendTimeout: pulumi.Input<number>;\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(\"production\", {\n * minCu: 0.25,\n * maxCu: 1,\n * suspendTimeout: 0,\n * }, { provider, project, branch });\n * ```\n */\nexport class NeonEndpoint extends pulumi.ComponentResource {\n\t/** Endpoint hostname for connection strings. */\n\tpublic readonly host: pulumi.Output<string>;\n\n\tconstructor(name: string, args: NeonEndpointArgs, opts: NeonEndpointOptions) {\n\t\tconst { provider, project, branch, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:neon:Endpoint\", name, {}, pulumiOpts);\n\n\t\tconst resource = new NeonEndpointResource(\n\t\t\t`${name}-resource`,\n\t\t\t{\n\t\t\t\tapiKey: provider.apiKey,\n\t\t\t\tprojectId: project.id,\n\t\t\t\tbranchId: branch.id,\n\t\t\t\t...args,\n\t\t\t},\n\t\t\t{ parent: this },\n\t\t);\n\n\t\tthis.host = resource.host;\n\n\t\tthis.registerOutputs({ host: this.host });\n\t}\n}\n"],"mappings":";;;;;;;;;;AA0DA,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,+BAAN,MAA8E;CAC7E,MAAM,OACL,QACuC;EACvC,MAAM,SAAS,IAAIA,+BAAW,OAAO,MAAM;EAE3C,MAAM,WAAW,MAAM,qBACtB,QACA,OAAO,WACP,OAAO,QACR;EAEA,IAAI,UAAU;GACb,eAAO,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;CAEA,MAAM,OACL,IACA,OACA,MACuC;EAGvC,MAAM,SAAS,MAAM,IAFFA,+BAAW,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;CAEA,MAAM,KACL,IACA,OACqC;EAGrC,MAAM,SAAS,MAAM,IAFFA,+BAAW,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;CAEA,MAAM,OAAO,IAAY,OAA2C;EACnE,MAAM,SAAS,IAAIA,+BAAW,MAAM,MAAM;EAE1C,IAAI;GACH,MAAM,OAAO,OAAO,aAAa,MAAM,UAAU,aAAa,IAAI;EACnE,QAAQ;GACP,eAAO,IAAI,KACV,yDACD;EACD;CACD;CAEA,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;;AAGA,IAAM,uBAAN,cAAmCC,eAAO,QAAQ,SAAS;CAG1D,YACC,MACA,MAQA,MACC;EACD,MACC,IAAI,6BAA6B,GACjC,MACA;GAAE,GAAG;GAAM,MAAM;EAAU,GAC3B,IACD;CACD;AACD;;;;;;;;;;;;;;AAuCA,IAAa,eAAb,cAAkCA,eAAO,kBAAkB;CAI1D,YAAY,MAAc,MAAwB,MAA2B;EAC5E,MAAM,EAAE,UAAU,SAAS,QAAQ,GAAG,eAAe;EAErD,MAAM,4BAA4B,MAAM,CAAC,GAAG,UAAU;EAEtD,MAAM,WAAW,IAAI,qBACpB,GAAG,KAAK,YACR;GACC,QAAQ,SAAS;GACjB,WAAW,QAAQ;GACnB,UAAU,OAAO;GACjB,GAAG;EACJ,GACA,EAAE,QAAQ,KAAK,CAChB;EAEA,KAAK,OAAO,SAAS;EAErB,KAAK,gBAAgB,EAAE,MAAM,KAAK,KAAK,CAAC;CACzC;AACD"}
|
|
1
|
+
{"version":3,"file":"endpoint.cjs","names":["NeonClient","pulumi"],"sources":["../../src/neon/endpoint.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\nimport type { NeonBranch } from \"./branch\";\nimport { NeonClient } from \"./client\";\nimport type { NeonProject } from \"./project\";\nimport type { NeonProvider } from \"./provider\";\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 */\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 NeonEndpointResourceProvider implements pulumi.dynamic.ResourceProvider {\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\t// Create the compute endpoint via the project-level endpoints collection with\n\t\t// branch_id in the body. The branch-scoped path (/branches/{id}/endpoints) is\n\t\t// GET-only and returns 405 on POST.\n\t\tconst result = await client.post<EndpointResponse>(\n\t\t\t`/projects/${inputs.projectId}/endpoints`,\n\t\t\t{\n\t\t\t\tendpoint: {\n\t\t\t\t\tbranch_id: inputs.branchId,\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\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\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\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\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/** Internal dynamic resource — not part of the public API. */\nclass NeonEndpointResource extends pulumi.dynamic.Resource {\n\tpublic declare readonly host: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: {\n\t\t\tapiKey: pulumi.Input<string>;\n\t\t\tprojectId: pulumi.Input<string>;\n\t\t\tbranchId: pulumi.Input<string>;\n\t\t\tminCu: pulumi.Input<number>;\n\t\t\tmaxCu: pulumi.Input<number>;\n\t\t\tsuspendTimeout: pulumi.Input<number>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\tsuper(\n\t\t\tnew NeonEndpointResourceProvider(),\n\t\t\tname,\n\t\t\t{ ...args, host: undefined },\n\t\t\topts,\n\t\t);\n\t}\n}\n\n/** Options type for NeonEndpoint — replaces Pulumi's native `provider` field. */\ntype NeonEndpointOptions = Omit<pulumi.ComponentResourceOptions, \"provider\"> & {\n\t/** Neon authentication context. */\n\tprovider: NeonProvider;\n\n\t/** Neon project context. */\n\tproject: NeonProject;\n\n\t/** Neon branch context. */\n\tbranch: NeonBranch;\n};\n\n/** Args for NeonEndpoint. */\nexport interface NeonEndpointArgs {\n\t/** Minimum compute units. */\n\tminCu: pulumi.Input<number>;\n\n\t/** Maximum compute units. */\n\tmaxCu: pulumi.Input<number>;\n\n\t/** Seconds of inactivity before suspending. */\n\tsuspendTimeout: pulumi.Input<number>;\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(\"production\", {\n * minCu: 0.25,\n * maxCu: 1,\n * suspendTimeout: 0,\n * }, { provider, project, branch });\n * ```\n */\nexport class NeonEndpoint extends pulumi.ComponentResource {\n\t/** Endpoint hostname for connection strings. */\n\tpublic readonly host: pulumi.Output<string>;\n\n\tconstructor(name: string, args: NeonEndpointArgs, opts: NeonEndpointOptions) {\n\t\tconst { provider, project, branch, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:neon:Endpoint\", name, {}, pulumiOpts);\n\n\t\tconst resource = new NeonEndpointResource(\n\t\t\t`${name}-resource`,\n\t\t\t{\n\t\t\t\tapiKey: provider.apiKey,\n\t\t\t\tprojectId: project.id,\n\t\t\t\tbranchId: branch.id,\n\t\t\t\t...args,\n\t\t\t},\n\t\t\t{ parent: this },\n\t\t);\n\n\t\tthis.host = resource.host;\n\n\t\tthis.registerOutputs({ host: this.host });\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"endpoint.d.cts","names":[],"sources":["../../src/neon/endpoint.ts"],"mappings":";;;;;;;;UAOiB,kBAAA;;EAEhB,MAAA;EAFkC;EAKlC,SAAA;EALkC;EAQlC,QAAA;EAHA;EAMA,KAAA;EAAA;EAGA,KAAA;EAGA;EAAA,cAAA;AAAA;AACA;AAAA,
|
|
1
|
+
{"version":3,"file":"endpoint.d.cts","names":[],"sources":["../../src/neon/endpoint.ts"],"mappings":";;;;;;;;UAOiB,kBAAA;;EAEhB,MAAA;EAFkC;EAKlC,SAAA;EALkC;EAQlC,QAAA;EAHA;EAMA,KAAA;EAAA;EAGA,KAAA;EAGA;EAAA,cAAA;AAAA;AACA;AAAA,KAqNI,mBAAA,GAAsB,IAAA,CAAK,MAAA,CAAO,wBAAA;qCAEtC,QAAA,EAAU,YAAA,EAFgB;EAK1B,OAAA,EAAS,WAAA,EAAA;EAGT,MAAA,EAAQ,UAAA;AAAA;;UAIQ,gBAAA;EAZe;EAc/B,KAAA,EAAO,MAAA,CAAO,KAAA;EAZd;EAeA,KAAA,EAAO,MAAA,CAAO,KAAA;EAZd;EAeA,cAAA,EAAgB,MAAA,CAAO,KAAA;AAAA;;;AAZL;AAInB;;;;;;;;;;cAwBa,YAAA,SAAqB,MAAA,CAAO,iBAAA;EAnBxC;EAAA,SAqBgB,IAAA,EAAM,MAAA,CAAO,MAAA;cAEjB,IAAA,UAAc,IAAA,EAAM,gBAAA,EAAkB,IAAA,EAAM,mBAAA;AAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"endpoint.d.mts","names":[],"sources":["../../src/neon/endpoint.ts"],"mappings":";;;;;;;;UAOiB,kBAAA;;EAEhB,MAAA;EAFkC;EAKlC,SAAA;EALkC;EAQlC,QAAA;EAHA;EAMA,KAAA;EAAA;EAGA,KAAA;EAGA;EAAA,cAAA;AAAA;AACA;AAAA,
|
|
1
|
+
{"version":3,"file":"endpoint.d.mts","names":[],"sources":["../../src/neon/endpoint.ts"],"mappings":";;;;;;;;UAOiB,kBAAA;;EAEhB,MAAA;EAFkC;EAKlC,SAAA;EALkC;EAQlC,QAAA;EAHA;EAMA,KAAA;EAAA;EAGA,KAAA;EAGA;EAAA,cAAA;AAAA;AACA;AAAA,KAqNI,mBAAA,GAAsB,IAAA,CAAK,MAAA,CAAO,wBAAA;qCAEtC,QAAA,EAAU,YAAA,EAFgB;EAK1B,OAAA,EAAS,WAAA,EAAA;EAGT,MAAA,EAAQ,UAAA;AAAA;;UAIQ,gBAAA;EAZe;EAc/B,KAAA,EAAO,MAAA,CAAO,KAAA;EAZd;EAeA,KAAA,EAAO,MAAA,CAAO,KAAA;EAZd;EAeA,cAAA,EAAgB,MAAA,CAAO,KAAA;AAAA;;;AAZL;AAInB;;;;;;;;;;cAwBa,YAAA,SAAqB,MAAA,CAAO,iBAAA;EAnBxC;EAAA,SAqBgB,IAAA,EAAM,MAAA,CAAO,MAAA;cAEjB,IAAA,UAAc,IAAA,EAAM,gBAAA,EAAkB,IAAA,EAAM,mBAAA;AAAA"}
|
package/dist/neon/endpoint.mjs
CHANGED
|
@@ -38,7 +38,8 @@ var NeonEndpointResourceProvider = class {
|
|
|
38
38
|
}
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
|
-
const result = await client.post(`/projects/${inputs.projectId}/
|
|
41
|
+
const result = await client.post(`/projects/${inputs.projectId}/endpoints`, { endpoint: {
|
|
42
|
+
branch_id: inputs.branchId,
|
|
42
43
|
type: "read_write",
|
|
43
44
|
autoscaling_limit_min_cu: inputs.minCu,
|
|
44
45
|
autoscaling_limit_max_cu: inputs.maxCu,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"endpoint.mjs","names":[],"sources":["../../src/neon/endpoint.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\nimport type { NeonBranch } from \"./branch\";\nimport { NeonClient } from \"./client\";\nimport type { NeonProject } from \"./project\";\nimport type { NeonProvider } from \"./provider\";\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 */\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 NeonEndpointResourceProvider implements pulumi.dynamic.ResourceProvider {\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\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\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\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\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/** Internal dynamic resource — not part of the public API. */\nclass NeonEndpointResource extends pulumi.dynamic.Resource {\n\tpublic declare readonly host: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: {\n\t\t\tapiKey: pulumi.Input<string>;\n\t\t\tprojectId: pulumi.Input<string>;\n\t\t\tbranchId: pulumi.Input<string>;\n\t\t\tminCu: pulumi.Input<number>;\n\t\t\tmaxCu: pulumi.Input<number>;\n\t\t\tsuspendTimeout: pulumi.Input<number>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\tsuper(\n\t\t\tnew NeonEndpointResourceProvider(),\n\t\t\tname,\n\t\t\t{ ...args, host: undefined },\n\t\t\topts,\n\t\t);\n\t}\n}\n\n/** Options type for NeonEndpoint — replaces Pulumi's native `provider` field. */\ntype NeonEndpointOptions = Omit<pulumi.ComponentResourceOptions, \"provider\"> & {\n\t/** Neon authentication context. */\n\tprovider: NeonProvider;\n\n\t/** Neon project context. */\n\tproject: NeonProject;\n\n\t/** Neon branch context. */\n\tbranch: NeonBranch;\n};\n\n/** Args for NeonEndpoint. */\nexport interface NeonEndpointArgs {\n\t/** Minimum compute units. */\n\tminCu: pulumi.Input<number>;\n\n\t/** Maximum compute units. */\n\tmaxCu: pulumi.Input<number>;\n\n\t/** Seconds of inactivity before suspending. */\n\tsuspendTimeout: pulumi.Input<number>;\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(\"production\", {\n * minCu: 0.25,\n * maxCu: 1,\n * suspendTimeout: 0,\n * }, { provider, project, branch });\n * ```\n */\nexport class NeonEndpoint extends pulumi.ComponentResource {\n\t/** Endpoint hostname for connection strings. */\n\tpublic readonly host: pulumi.Output<string>;\n\n\tconstructor(name: string, args: NeonEndpointArgs, opts: NeonEndpointOptions) {\n\t\tconst { provider, project, branch, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:neon:Endpoint\", name, {}, pulumiOpts);\n\n\t\tconst resource = new NeonEndpointResource(\n\t\t\t`${name}-resource`,\n\t\t\t{\n\t\t\t\tapiKey: provider.apiKey,\n\t\t\t\tprojectId: project.id,\n\t\t\t\tbranchId: branch.id,\n\t\t\t\t...args,\n\t\t\t},\n\t\t\t{ parent: this },\n\t\t);\n\n\t\tthis.host = resource.host;\n\n\t\tthis.registerOutputs({ host: this.host });\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"endpoint.mjs","names":[],"sources":["../../src/neon/endpoint.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\nimport type { NeonBranch } from \"./branch\";\nimport { NeonClient } from \"./client\";\nimport type { NeonProject } from \"./project\";\nimport type { NeonProvider } from \"./provider\";\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 */\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 NeonEndpointResourceProvider implements pulumi.dynamic.ResourceProvider {\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\t// Create the compute endpoint via the project-level endpoints collection with\n\t\t// branch_id in the body. The branch-scoped path (/branches/{id}/endpoints) is\n\t\t// GET-only and returns 405 on POST.\n\t\tconst result = await client.post<EndpointResponse>(\n\t\t\t`/projects/${inputs.projectId}/endpoints`,\n\t\t\t{\n\t\t\t\tendpoint: {\n\t\t\t\t\tbranch_id: inputs.branchId,\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\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\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\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\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/** Internal dynamic resource — not part of the public API. */\nclass NeonEndpointResource extends pulumi.dynamic.Resource {\n\tpublic declare readonly host: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: {\n\t\t\tapiKey: pulumi.Input<string>;\n\t\t\tprojectId: pulumi.Input<string>;\n\t\t\tbranchId: pulumi.Input<string>;\n\t\t\tminCu: pulumi.Input<number>;\n\t\t\tmaxCu: pulumi.Input<number>;\n\t\t\tsuspendTimeout: pulumi.Input<number>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\tsuper(\n\t\t\tnew NeonEndpointResourceProvider(),\n\t\t\tname,\n\t\t\t{ ...args, host: undefined },\n\t\t\topts,\n\t\t);\n\t}\n}\n\n/** Options type for NeonEndpoint — replaces Pulumi's native `provider` field. */\ntype NeonEndpointOptions = Omit<pulumi.ComponentResourceOptions, \"provider\"> & {\n\t/** Neon authentication context. */\n\tprovider: NeonProvider;\n\n\t/** Neon project context. */\n\tproject: NeonProject;\n\n\t/** Neon branch context. */\n\tbranch: NeonBranch;\n};\n\n/** Args for NeonEndpoint. */\nexport interface NeonEndpointArgs {\n\t/** Minimum compute units. */\n\tminCu: pulumi.Input<number>;\n\n\t/** Maximum compute units. */\n\tmaxCu: pulumi.Input<number>;\n\n\t/** Seconds of inactivity before suspending. */\n\tsuspendTimeout: pulumi.Input<number>;\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(\"production\", {\n * minCu: 0.25,\n * maxCu: 1,\n * suspendTimeout: 0,\n * }, { provider, project, branch });\n * ```\n */\nexport class NeonEndpoint extends pulumi.ComponentResource {\n\t/** Endpoint hostname for connection strings. */\n\tpublic readonly host: pulumi.Output<string>;\n\n\tconstructor(name: string, args: NeonEndpointArgs, opts: NeonEndpointOptions) {\n\t\tconst { provider, project, branch, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:neon:Endpoint\", name, {}, pulumiOpts);\n\n\t\tconst resource = new NeonEndpointResource(\n\t\t\t`${name}-resource`,\n\t\t\t{\n\t\t\t\tapiKey: provider.apiKey,\n\t\t\t\tprojectId: project.id,\n\t\t\t\tbranchId: branch.id,\n\t\t\t\t...args,\n\t\t\t},\n\t\t\t{ parent: this },\n\t\t);\n\n\t\tthis.host = resource.host;\n\n\t\tthis.registerOutputs({ host: this.host });\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -6,14 +6,31 @@ _pulumi_pulumi = require_chunk.__toESM(_pulumi_pulumi, 1);
|
|
|
6
6
|
//#region src/vercel/resource-connection.ts
|
|
7
7
|
const VERCEL_API_URL = "https://api.vercel.com";
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
10
|
-
|
|
9
|
+
* Finds an existing connection from a store to a specific project.
|
|
10
|
+
*/
|
|
11
|
+
async function findConnection(token, teamId, storeId, projectId) {
|
|
12
|
+
const response = await fetch(`${VERCEL_API_URL}/v1/storage/stores/${storeId}/connections?teamId=${teamId}`, { headers: { Authorization: `Bearer ${token}` } });
|
|
13
|
+
if (!response.ok) return;
|
|
14
|
+
return (await response.json()).connections.find((c) => c.projectId === projectId);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Dynamic provider that connects a Vercel marketplace store to a project,
|
|
18
|
+
* injecting the store's env vars into the specified deployment environments.
|
|
11
19
|
*
|
|
12
20
|
* @internal Exported only for unit testing; not part of the public API surface.
|
|
13
21
|
*/
|
|
14
22
|
var VercelResourceConnectionProvider = class {
|
|
15
23
|
async create(inputs) {
|
|
16
|
-
|
|
24
|
+
if (inputs.makeEnvVarsSensitive && inputs.targets.includes("development")) throw new Error("VercelResourceConnection: Vercel rejects sensitive env vars on the 'development' target. Either drop 'development' from targets or set makeEnvVarsSensitive to false.");
|
|
25
|
+
const existing = await findConnection(inputs.token, inputs.teamId, inputs.storeId, inputs.projectId);
|
|
26
|
+
if (existing) {
|
|
27
|
+
_pulumi_pulumi.log.info(`Adopting existing Vercel store connection (${existing.id})`);
|
|
28
|
+
return {
|
|
29
|
+
id: `${inputs.storeId}:${inputs.projectId}`,
|
|
30
|
+
outs: { ...inputs }
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
const response = await fetch(`${VERCEL_API_URL}/v1/storage/stores/${inputs.storeId}/connections?teamId=${inputs.teamId}`, {
|
|
17
34
|
method: "POST",
|
|
18
35
|
headers: {
|
|
19
36
|
Authorization: `Bearer ${inputs.token}`,
|
|
@@ -22,14 +39,13 @@ var VercelResourceConnectionProvider = class {
|
|
|
22
39
|
body: JSON.stringify({
|
|
23
40
|
projectId: inputs.projectId,
|
|
24
41
|
envVarEnvironments: inputs.targets,
|
|
25
|
-
makeEnvVarsSensitive:
|
|
42
|
+
makeEnvVarsSensitive: inputs.makeEnvVarsSensitive
|
|
26
43
|
})
|
|
27
44
|
});
|
|
28
45
|
if (!response.ok) throw new Error(`Vercel resource connection failed (${response.status}): ${await response.text()}`);
|
|
29
|
-
const outs = { ...inputs };
|
|
30
46
|
return {
|
|
31
|
-
id: `${inputs.
|
|
32
|
-
outs
|
|
47
|
+
id: `${inputs.storeId}:${inputs.projectId}`,
|
|
48
|
+
outs: { ...inputs }
|
|
33
49
|
};
|
|
34
50
|
}
|
|
35
51
|
async read(id, props) {
|
|
@@ -44,10 +60,10 @@ var VercelResourceConnectionProvider = class {
|
|
|
44
60
|
async diff(_id, olds, news) {
|
|
45
61
|
const replaces = [];
|
|
46
62
|
if (olds.teamId !== news.teamId) replaces.push("teamId");
|
|
47
|
-
if (olds.
|
|
48
|
-
if (olds.resourceId !== news.resourceId) replaces.push("resourceId");
|
|
63
|
+
if (olds.storeId !== news.storeId) replaces.push("storeId");
|
|
49
64
|
if (olds.projectId !== news.projectId) replaces.push("projectId");
|
|
50
65
|
if (JSON.stringify(olds.targets) !== JSON.stringify(news.targets)) replaces.push("targets");
|
|
66
|
+
if (olds.makeEnvVarsSensitive !== news.makeEnvVarsSensitive) replaces.push("makeEnvVarsSensitive");
|
|
51
67
|
return {
|
|
52
68
|
changes: replaces.length > 0,
|
|
53
69
|
replaces,
|
|
@@ -62,17 +78,16 @@ var VercelResourceConnectionResource = class extends _pulumi_pulumi.dynamic.Reso
|
|
|
62
78
|
}
|
|
63
79
|
};
|
|
64
80
|
/**
|
|
65
|
-
* Connects a Vercel marketplace
|
|
66
|
-
*
|
|
81
|
+
* Connects a Vercel marketplace store to a project, injecting its env vars
|
|
82
|
+
* into the specified deployment environments.
|
|
67
83
|
*
|
|
68
|
-
* Calls `POST /v1/
|
|
69
|
-
*
|
|
70
|
-
*
|
|
84
|
+
* Calls `POST /v1/storage/stores/{storeId}/connections`. Uses adopt-or-create:
|
|
85
|
+
* an existing connection from the store to the project is adopted rather than
|
|
86
|
+
* re-created. Deletion is a no-op — there is no public per-project disconnect
|
|
87
|
+
* endpoint; disconnect manually from the Vercel dashboard if needed.
|
|
71
88
|
*
|
|
72
89
|
* @example
|
|
73
90
|
* ```typescript
|
|
74
|
-
* const upstash = new VercelIntegration("upstash", { slug: "upstash" }, { provider });
|
|
75
|
-
*
|
|
76
91
|
* const kvStore = new VercelMarketplaceResource("humanes-kv", {
|
|
77
92
|
* integrationConfigurationId: upstash.configurationId,
|
|
78
93
|
* name: "rby-humanes-kv",
|
|
@@ -81,10 +96,9 @@ var VercelResourceConnectionResource = class extends _pulumi_pulumi.dynamic.Reso
|
|
|
81
96
|
* }, { provider });
|
|
82
97
|
*
|
|
83
98
|
* new VercelResourceConnection("humanes-kv-conn", {
|
|
84
|
-
*
|
|
85
|
-
* resourceId: kvStore.externalResourceId,
|
|
99
|
+
* storeId: kvStore.id,
|
|
86
100
|
* projectId: humanesProject.id,
|
|
87
|
-
* targets: ["production", "preview"
|
|
101
|
+
* targets: ["production", "preview"],
|
|
88
102
|
* }, { provider });
|
|
89
103
|
* ```
|
|
90
104
|
*/
|
|
@@ -95,7 +109,10 @@ var VercelResourceConnection = class extends _pulumi_pulumi.ComponentResource {
|
|
|
95
109
|
new VercelResourceConnectionResource(`${name}-resource`, {
|
|
96
110
|
token: provider.token,
|
|
97
111
|
teamId: provider.teamId,
|
|
98
|
-
|
|
112
|
+
storeId: args.storeId,
|
|
113
|
+
projectId: args.projectId,
|
|
114
|
+
targets: args.targets,
|
|
115
|
+
makeEnvVarsSensitive: args.makeEnvVarsSensitive ?? true
|
|
99
116
|
}, { parent: this });
|
|
100
117
|
this.registerOutputs({});
|
|
101
118
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resource-connection.cjs","names":["pulumi"],"sources":["../../src/vercel/resource-connection.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\nimport type { VercelProvider } from \"./provider\";\n\nconst VERCEL_API_URL = \"https://api.vercel.com\";\n\n/** Resolved inputs for the Vercel resource connection dynamic provider. */\ninterface VercelResourceConnectionInputs {\n\t/** Vercel API bearer token. */\n\ttoken: string;\n\n\t/** Vercel team/org ID. */\n\tteamId: string;\n\n\t/** Integration configuration ID (e.g. `\"icfg_…\"`). */\n\tintegrationConfigurationId: string;\n\n\t/** The external resource ID of the provisioned marketplace store. */\n\tresourceId: string;\n\n\t/** The Vercel project ID to connect the resource to. */\n\tprojectId: string;\n\n\t/** Deployment environments to inject env vars into. */\n\ttargets: string[];\n}\n\n/** Persisted state for the Vercel resource connection. */\ntype VercelResourceConnectionOutputs = VercelResourceConnectionInputs;\n\n/**\n * Dynamic provider that connects a Vercel marketplace resource to a project,\n * injecting env vars into the specified deployment environments.\n *\n * @internal Exported only for unit testing; not part of the public API surface.\n */\nexport class VercelResourceConnectionProvider\n\timplements pulumi.dynamic.ResourceProvider\n{\n\tasync create(\n\t\tinputs: VercelResourceConnectionInputs,\n\t): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst response = await fetch(\n\t\t\t`${VERCEL_API_URL}/v1/integrations/installations/${inputs.integrationConfigurationId}/resources/${inputs.resourceId}/connections?teamId=${inputs.teamId}`,\n\t\t\t{\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\tAuthorization: `Bearer ${inputs.token}`,\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tprojectId: inputs.projectId,\n\t\t\t\t\tenvVarEnvironments: inputs.targets,\n\t\t\t\t\tmakeEnvVarsSensitive: true,\n\t\t\t\t}),\n\t\t\t},\n\t\t);\n\n\t\tif (!response.ok) {\n\t\t\tthrow new Error(\n\t\t\t\t`Vercel resource connection failed (${response.status}): ${await response.text()}`,\n\t\t\t);\n\t\t}\n\n\t\tconst outs: VercelResourceConnectionOutputs = { ...inputs };\n\n\t\treturn { id: `${inputs.resourceId}:${inputs.projectId}`, outs };\n\t}\n\n\tasync read(\n\t\tid: string,\n\t\tprops: VercelResourceConnectionOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\t// There is no public read endpoint for resource connections; drift is not refreshed here.\n\t\treturn { id, props };\n\t}\n\n\tasync delete(): Promise<void> {\n\t\tpulumi.log.warn(\n\t\t\t\"VercelResourceConnection deletion skipped — no public per-project disconnect endpoint; disconnect from the Vercel dashboard if intended\",\n\t\t);\n\t}\n\n\tasync diff(\n\t\t_id: string,\n\t\tolds: VercelResourceConnectionOutputs,\n\t\tnews: VercelResourceConnectionInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst replaces: string[] = [];\n\n\t\tif (olds.teamId !== news.teamId) {\n\t\t\treplaces.push(\"teamId\");\n\t\t}\n\n\t\tif (olds.integrationConfigurationId !== news.integrationConfigurationId) {\n\t\t\treplaces.push(\"integrationConfigurationId\");\n\t\t}\n\n\t\tif (olds.resourceId !== news.resourceId) {\n\t\t\treplaces.push(\"resourceId\");\n\t\t}\n\n\t\tif (olds.projectId !== news.projectId) {\n\t\t\treplaces.push(\"projectId\");\n\t\t}\n\n\t\t// targets comparison is order-sensitive by design: the array is sent verbatim to the\n\t\t// API as envVarEnvironments, so reordering is a meaningful change.\n\t\tif (JSON.stringify(olds.targets) !== JSON.stringify(news.targets)) {\n\t\t\treplaces.push(\"targets\");\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/** Internal dynamic resource — not part of the public API. */\nclass VercelResourceConnectionResource extends pulumi.dynamic.Resource {\n\tconstructor(\n\t\tname: string,\n\t\targs: {\n\t\t\ttoken: pulumi.Input<string>;\n\t\t\tteamId: pulumi.Input<string>;\n\t\t\tintegrationConfigurationId: pulumi.Input<string>;\n\t\t\tresourceId: pulumi.Input<string>;\n\t\t\tprojectId: pulumi.Input<string>;\n\t\t\ttargets: pulumi.Input<string[]>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\tsuper(new VercelResourceConnectionProvider(), name, { ...args }, opts);\n\t}\n}\n\n/** Options type for VercelResourceConnection — replaces Pulumi's native `provider` field. */\ntype VercelResourceConnectionOptions = Omit<\n\tpulumi.ComponentResourceOptions,\n\t\"provider\"\n> & {\n\t/** Vercel authentication context. */\n\tprovider: VercelProvider;\n};\n\n/**\n * Args for {@link VercelResourceConnection}.\n */\nexport interface VercelResourceConnectionArgs {\n\t/**\n\t * Integration configuration ID (e.g. `\"icfg_…\"`).\n\t * Obtain this from {@link VercelIntegration.configurationId}.\n\t */\n\tintegrationConfigurationId: pulumi.Input<string>;\n\n\t/**\n\t * The external resource ID of the provisioned marketplace store.\n\t * Obtain this from {@link VercelMarketplaceResource.externalResourceId}.\n\t */\n\tresourceId: pulumi.Input<string>;\n\n\t/** The Vercel project ID to connect the resource to. */\n\tprojectId: pulumi.Input<string>;\n\n\t/**\n\t * Deployment environments into which the integration env vars will be injected.\n\t * Typical values: `[\"production\", \"preview\", \"development\"]`.\n\t */\n\ttargets: pulumi.Input<string[]>;\n}\n\n/**\n * Connects a Vercel marketplace resource to a project, injecting its env vars\n * as sensitive variables into the specified deployment environments.\n *\n * Calls `POST /v1/integrations/installations/{icfg}/resources/{resourceId}/connections`.\n * Deletion is a no-op — there is no public per-project disconnect endpoint;\n * disconnect manually from the Vercel dashboard if needed.\n *\n * @example\n * ```typescript\n * const upstash = new VercelIntegration(\"upstash\", { slug: \"upstash\" }, { provider });\n *\n * const kvStore = new VercelMarketplaceResource(\"humanes-kv\", {\n * integrationConfigurationId: upstash.configurationId,\n * name: \"rby-humanes-kv\",\n * type: \"upstash-kv\",\n * externalId: \"rby-humanes-kv\",\n * }, { provider });\n *\n * new VercelResourceConnection(\"humanes-kv-conn\", {\n * integrationConfigurationId: upstash.configurationId,\n * resourceId: kvStore.externalResourceId,\n * projectId: humanesProject.id,\n * targets: [\"production\", \"preview\", \"development\"],\n * }, { provider });\n * ```\n */\nexport class VercelResourceConnection extends pulumi.ComponentResource {\n\tconstructor(\n\t\tname: string,\n\t\targs: VercelResourceConnectionArgs,\n\t\topts: VercelResourceConnectionOptions,\n\t) {\n\t\tconst { provider, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:vercel:ResourceConnection\", name, {}, pulumiOpts);\n\n\t\tnew VercelResourceConnectionResource(\n\t\t\t`${name}-resource`,\n\t\t\t{\n\t\t\t\ttoken: provider.token,\n\t\t\t\tteamId: provider.teamId,\n\t\t\t\t...args,\n\t\t\t},\n\t\t\t{ parent: this },\n\t\t);\n\n\t\tthis.registerOutputs({});\n\t}\n}\n"],"mappings":";;;;;;AAGA,MAAM,iBAAiB;;;;;;;AAgCvB,IAAa,mCAAb,MAEA;CACC,MAAM,OACL,QACuC;EACvC,MAAM,WAAW,MAAM,MACtB,GAAG,eAAe,iCAAiC,OAAO,2BAA2B,aAAa,OAAO,WAAW,sBAAsB,OAAO,UACjJ;GACC,QAAQ;GACR,SAAS;IACR,eAAe,UAAU,OAAO;IAChC,gBAAgB;GACjB;GACA,MAAM,KAAK,UAAU;IACpB,WAAW,OAAO;IAClB,oBAAoB,OAAO;IAC3B,sBAAsB;GACvB,CAAC;EACF,CACD;EAEA,IAAI,CAAC,SAAS,IACb,MAAM,IAAI,MACT,sCAAsC,SAAS,OAAO,KAAK,MAAM,SAAS,KAAK,GAChF;EAGD,MAAM,OAAwC,EAAE,GAAG,OAAO;EAE1D,OAAO;GAAE,IAAI,GAAG,OAAO,WAAW,GAAG,OAAO;GAAa;EAAK;CAC/D;CAEA,MAAM,KACL,IACA,OACqC;EAErC,OAAO;GAAE;GAAI;EAAM;CACpB;CAEA,MAAM,SAAwB;EAC7B,eAAO,IAAI,KACV,yIACD;CACD;CAEA,MAAM,KACL,KACA,MACA,MACqC;EACrC,MAAM,WAAqB,CAAC;EAE5B,IAAI,KAAK,WAAW,KAAK,QACxB,SAAS,KAAK,QAAQ;EAGvB,IAAI,KAAK,+BAA+B,KAAK,4BAC5C,SAAS,KAAK,4BAA4B;EAG3C,IAAI,KAAK,eAAe,KAAK,YAC5B,SAAS,KAAK,YAAY;EAG3B,IAAI,KAAK,cAAc,KAAK,WAC3B,SAAS,KAAK,WAAW;EAK1B,IAAI,KAAK,UAAU,KAAK,OAAO,MAAM,KAAK,UAAU,KAAK,OAAO,GAC/D,SAAS,KAAK,SAAS;EAGxB,OAAO;GACN,SAAS,SAAS,SAAS;GAC3B;GACA,qBAAqB;EACtB;CACD;AACD;;AAGA,IAAM,mCAAN,cAA+CA,eAAO,QAAQ,SAAS;CACtE,YACC,MACA,MAQA,MACC;EACD,MAAM,IAAI,iCAAiC,GAAG,MAAM,EAAE,GAAG,KAAK,GAAG,IAAI;CACtE;AACD;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgEA,IAAa,2BAAb,cAA8CA,eAAO,kBAAkB;CACtE,YACC,MACA,MACA,MACC;EACD,MAAM,EAAE,UAAU,GAAG,eAAe;EAEpC,MAAM,wCAAwC,MAAM,CAAC,GAAG,UAAU;EAElE,IAAI,iCACH,GAAG,KAAK,YACR;GACC,OAAO,SAAS;GAChB,QAAQ,SAAS;GACjB,GAAG;EACJ,GACA,EAAE,QAAQ,KAAK,CAChB;EAEA,KAAK,gBAAgB,CAAC,CAAC;CACxB;AACD"}
|
|
1
|
+
{"version":3,"file":"resource-connection.cjs","names":["pulumi"],"sources":["../../src/vercel/resource-connection.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\nimport type { VercelProvider } from \"./provider\";\n\nconst VERCEL_API_URL = \"https://api.vercel.com\";\n\n/** Resolved inputs for the Vercel resource connection dynamic provider. */\ninterface VercelResourceConnectionInputs {\n\t/** Vercel API bearer token. */\n\ttoken: string;\n\n\t/** Vercel team/org ID. */\n\tteamId: string;\n\n\t/** The Vercel store ID of the provisioned marketplace resource (e.g. `\"store_…\"`). */\n\tstoreId: string;\n\n\t/** The Vercel project ID to connect the store to. */\n\tprojectId: string;\n\n\t/** Deployment environments to inject env vars into (e.g. `[\"production\", \"preview\"]`). */\n\ttargets: string[];\n\n\t/**\n\t * Whether the injected env vars are marked sensitive.\n\t * Vercel rejects sensitive env vars on the `development` target, so `targets`\n\t * must not include `development` when this is `true`.\n\t */\n\tmakeEnvVarsSensitive: boolean;\n}\n\n/** Persisted state for the Vercel resource connection. */\ntype VercelResourceConnectionOutputs = VercelResourceConnectionInputs;\n\n/** A single store-to-project connection as returned by the Vercel API. */\ninterface StoreConnection {\n\tid: string;\n\tprojectId: string;\n}\n\n/** Vercel API response for listing a store's connections. */\ninterface StoreConnectionsResponse {\n\tconnections: StoreConnection[];\n}\n\n/**\n * Finds an existing connection from a store to a specific project.\n */\nasync function findConnection(\n\ttoken: string,\n\tteamId: string,\n\tstoreId: string,\n\tprojectId: string,\n): Promise<StoreConnection | undefined> {\n\tconst response = await fetch(\n\t\t`${VERCEL_API_URL}/v1/storage/stores/${storeId}/connections?teamId=${teamId}`,\n\t\t{ headers: { Authorization: `Bearer ${token}` } },\n\t);\n\n\tif (!response.ok) {\n\t\treturn undefined;\n\t}\n\n\tconst data = (await response.json()) as StoreConnectionsResponse;\n\n\treturn data.connections.find((c) => c.projectId === projectId);\n}\n\n/**\n * Dynamic provider that connects a Vercel marketplace store to a project,\n * injecting the store's env vars into the specified deployment environments.\n *\n * @internal Exported only for unit testing; not part of the public API surface.\n */\nexport class VercelResourceConnectionProvider\n\timplements pulumi.dynamic.ResourceProvider\n{\n\tasync create(\n\t\tinputs: VercelResourceConnectionInputs,\n\t): Promise<pulumi.dynamic.CreateResult> {\n\t\tif (inputs.makeEnvVarsSensitive && inputs.targets.includes(\"development\")) {\n\t\t\tthrow new Error(\n\t\t\t\t\"VercelResourceConnection: Vercel rejects sensitive env vars on the 'development' target. \" +\n\t\t\t\t\t\"Either drop 'development' from targets or set makeEnvVarsSensitive to false.\",\n\t\t\t);\n\t\t}\n\n\t\t// Adopt-or-create: a store can only be connected to a given project once,\n\t\t// so re-creating an out-of-band connection (or a prior partial apply) adopts it.\n\t\tconst existing = await findConnection(\n\t\t\tinputs.token,\n\t\t\tinputs.teamId,\n\t\t\tinputs.storeId,\n\t\t\tinputs.projectId,\n\t\t);\n\n\t\tif (existing) {\n\t\t\tpulumi.log.info(\n\t\t\t\t`Adopting existing Vercel store connection (${existing.id})`,\n\t\t\t);\n\n\t\t\treturn {\n\t\t\t\tid: `${inputs.storeId}:${inputs.projectId}`,\n\t\t\t\touts: { ...inputs },\n\t\t\t};\n\t\t}\n\n\t\tconst response = await fetch(\n\t\t\t`${VERCEL_API_URL}/v1/storage/stores/${inputs.storeId}/connections?teamId=${inputs.teamId}`,\n\t\t\t{\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\tAuthorization: `Bearer ${inputs.token}`,\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tprojectId: inputs.projectId,\n\t\t\t\t\tenvVarEnvironments: inputs.targets,\n\t\t\t\t\tmakeEnvVarsSensitive: inputs.makeEnvVarsSensitive,\n\t\t\t\t}),\n\t\t\t},\n\t\t);\n\n\t\tif (!response.ok) {\n\t\t\tthrow new Error(\n\t\t\t\t`Vercel resource connection failed (${response.status}): ${await response.text()}`,\n\t\t\t);\n\t\t}\n\n\t\treturn {\n\t\t\tid: `${inputs.storeId}:${inputs.projectId}`,\n\t\t\touts: { ...inputs },\n\t\t};\n\t}\n\n\tasync read(\n\t\tid: string,\n\t\tprops: VercelResourceConnectionOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\t// The connection list endpoint exposes presence but not the targeted environments,\n\t\t// so env-var drift is not refreshed here — only the connection's existence.\n\t\treturn { id, props };\n\t}\n\n\tasync delete(): Promise<void> {\n\t\tpulumi.log.warn(\n\t\t\t\"VercelResourceConnection deletion skipped — no public per-project disconnect endpoint; disconnect from the Vercel dashboard if intended\",\n\t\t);\n\t}\n\n\tasync diff(\n\t\t_id: string,\n\t\tolds: VercelResourceConnectionOutputs,\n\t\tnews: VercelResourceConnectionInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst replaces: string[] = [];\n\n\t\tif (olds.teamId !== news.teamId) {\n\t\t\treplaces.push(\"teamId\");\n\t\t}\n\n\t\tif (olds.storeId !== news.storeId) {\n\t\t\treplaces.push(\"storeId\");\n\t\t}\n\n\t\tif (olds.projectId !== news.projectId) {\n\t\t\treplaces.push(\"projectId\");\n\t\t}\n\n\t\t// targets comparison is order-sensitive by design: the array is sent verbatim to the\n\t\t// API as envVarEnvironments, so reordering is a meaningful change.\n\t\tif (JSON.stringify(olds.targets) !== JSON.stringify(news.targets)) {\n\t\t\treplaces.push(\"targets\");\n\t\t}\n\n\t\tif (olds.makeEnvVarsSensitive !== news.makeEnvVarsSensitive) {\n\t\t\treplaces.push(\"makeEnvVarsSensitive\");\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/** Internal dynamic resource — not part of the public API. */\nclass VercelResourceConnectionResource extends pulumi.dynamic.Resource {\n\tconstructor(\n\t\tname: string,\n\t\targs: {\n\t\t\ttoken: pulumi.Input<string>;\n\t\t\tteamId: pulumi.Input<string>;\n\t\t\tstoreId: pulumi.Input<string>;\n\t\t\tprojectId: pulumi.Input<string>;\n\t\t\ttargets: pulumi.Input<string[]>;\n\t\t\tmakeEnvVarsSensitive: pulumi.Input<boolean>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\tsuper(new VercelResourceConnectionProvider(), name, { ...args }, opts);\n\t}\n}\n\n/** Options type for VercelResourceConnection — replaces Pulumi's native `provider` field. */\ntype VercelResourceConnectionOptions = Omit<\n\tpulumi.ComponentResourceOptions,\n\t\"provider\"\n> & {\n\t/** Vercel authentication context. */\n\tprovider: VercelProvider;\n};\n\n/**\n * Args for {@link VercelResourceConnection}.\n */\nexport interface VercelResourceConnectionArgs {\n\t/**\n\t * The Vercel store ID of the provisioned marketplace resource (e.g. `\"store_…\"`).\n\t * Obtain this from {@link VercelMarketplaceResource.id}.\n\t */\n\tstoreId: pulumi.Input<string>;\n\n\t/** The Vercel project ID to connect the store to. */\n\tprojectId: pulumi.Input<string>;\n\n\t/**\n\t * Deployment environments into which the store's env vars will be injected.\n\t * Typical values: `[\"production\", \"preview\"]`. Note that `development`\n\t * cannot be combined with `makeEnvVarsSensitive: true` (Vercel rejects it).\n\t */\n\ttargets: pulumi.Input<string[]>;\n\n\t/**\n\t * Whether the injected env vars are marked sensitive (hidden after creation).\n\t * Defaults to `true`. When `true`, `targets` must not include `development`.\n\t */\n\tmakeEnvVarsSensitive?: pulumi.Input<boolean>;\n}\n\n/**\n * Connects a Vercel marketplace store to a project, injecting its env vars\n * into the specified deployment environments.\n *\n * Calls `POST /v1/storage/stores/{storeId}/connections`. Uses adopt-or-create:\n * an existing connection from the store to the project is adopted rather than\n * re-created. Deletion is a no-op — there is no public per-project disconnect\n * endpoint; disconnect manually from the Vercel dashboard if needed.\n *\n * @example\n * ```typescript\n * const kvStore = new VercelMarketplaceResource(\"humanes-kv\", {\n * integrationConfigurationId: upstash.configurationId,\n * name: \"rby-humanes-kv\",\n * type: \"upstash-kv\",\n * externalId: \"rby-humanes-kv\",\n * }, { provider });\n *\n * new VercelResourceConnection(\"humanes-kv-conn\", {\n * storeId: kvStore.id,\n * projectId: humanesProject.id,\n * targets: [\"production\", \"preview\"],\n * }, { provider });\n * ```\n */\nexport class VercelResourceConnection extends pulumi.ComponentResource {\n\tconstructor(\n\t\tname: string,\n\t\targs: VercelResourceConnectionArgs,\n\t\topts: VercelResourceConnectionOptions,\n\t) {\n\t\tconst { provider, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:vercel:ResourceConnection\", name, {}, pulumiOpts);\n\n\t\tnew VercelResourceConnectionResource(\n\t\t\t`${name}-resource`,\n\t\t\t{\n\t\t\t\ttoken: provider.token,\n\t\t\t\tteamId: provider.teamId,\n\t\t\t\tstoreId: args.storeId,\n\t\t\t\tprojectId: args.projectId,\n\t\t\t\ttargets: args.targets,\n\t\t\t\tmakeEnvVarsSensitive: args.makeEnvVarsSensitive ?? true,\n\t\t\t},\n\t\t\t{ parent: this },\n\t\t);\n\n\t\tthis.registerOutputs({});\n\t}\n}\n"],"mappings":";;;;;;AAGA,MAAM,iBAAiB;;;;AA4CvB,eAAe,eACd,OACA,QACA,SACA,WACuC;CACvC,MAAM,WAAW,MAAM,MACtB,GAAG,eAAe,qBAAqB,QAAQ,sBAAsB,UACrE,EAAE,SAAS,EAAE,eAAe,UAAU,QAAQ,EAAE,CACjD;CAEA,IAAI,CAAC,SAAS,IACb;CAKD,QAAO,MAFa,SAAS,KAAK,GAEtB,YAAY,MAAM,MAAM,EAAE,cAAc,SAAS;AAC9D;;;;;;;AAQA,IAAa,mCAAb,MAEA;CACC,MAAM,OACL,QACuC;EACvC,IAAI,OAAO,wBAAwB,OAAO,QAAQ,SAAS,aAAa,GACvE,MAAM,IAAI,MACT,uKAED;EAKD,MAAM,WAAW,MAAM,eACtB,OAAO,OACP,OAAO,QACP,OAAO,SACP,OAAO,SACR;EAEA,IAAI,UAAU;GACb,eAAO,IAAI,KACV,8CAA8C,SAAS,GAAG,EAC3D;GAEA,OAAO;IACN,IAAI,GAAG,OAAO,QAAQ,GAAG,OAAO;IAChC,MAAM,EAAE,GAAG,OAAO;GACnB;EACD;EAEA,MAAM,WAAW,MAAM,MACtB,GAAG,eAAe,qBAAqB,OAAO,QAAQ,sBAAsB,OAAO,UACnF;GACC,QAAQ;GACR,SAAS;IACR,eAAe,UAAU,OAAO;IAChC,gBAAgB;GACjB;GACA,MAAM,KAAK,UAAU;IACpB,WAAW,OAAO;IAClB,oBAAoB,OAAO;IAC3B,sBAAsB,OAAO;GAC9B,CAAC;EACF,CACD;EAEA,IAAI,CAAC,SAAS,IACb,MAAM,IAAI,MACT,sCAAsC,SAAS,OAAO,KAAK,MAAM,SAAS,KAAK,GAChF;EAGD,OAAO;GACN,IAAI,GAAG,OAAO,QAAQ,GAAG,OAAO;GAChC,MAAM,EAAE,GAAG,OAAO;EACnB;CACD;CAEA,MAAM,KACL,IACA,OACqC;EAGrC,OAAO;GAAE;GAAI;EAAM;CACpB;CAEA,MAAM,SAAwB;EAC7B,eAAO,IAAI,KACV,yIACD;CACD;CAEA,MAAM,KACL,KACA,MACA,MACqC;EACrC,MAAM,WAAqB,CAAC;EAE5B,IAAI,KAAK,WAAW,KAAK,QACxB,SAAS,KAAK,QAAQ;EAGvB,IAAI,KAAK,YAAY,KAAK,SACzB,SAAS,KAAK,SAAS;EAGxB,IAAI,KAAK,cAAc,KAAK,WAC3B,SAAS,KAAK,WAAW;EAK1B,IAAI,KAAK,UAAU,KAAK,OAAO,MAAM,KAAK,UAAU,KAAK,OAAO,GAC/D,SAAS,KAAK,SAAS;EAGxB,IAAI,KAAK,yBAAyB,KAAK,sBACtC,SAAS,KAAK,sBAAsB;EAGrC,OAAO;GACN,SAAS,SAAS,SAAS;GAC3B;GACA,qBAAqB;EACtB;CACD;AACD;;AAGA,IAAM,mCAAN,cAA+CA,eAAO,QAAQ,SAAS;CACtE,YACC,MACA,MAQA,MACC;EACD,MAAM,IAAI,iCAAiC,GAAG,MAAM,EAAE,GAAG,KAAK,GAAG,IAAI;CACtE;AACD;;;;;;;;;;;;;;;;;;;;;;;;;;AA+DA,IAAa,2BAAb,cAA8CA,eAAO,kBAAkB;CACtE,YACC,MACA,MACA,MACC;EACD,MAAM,EAAE,UAAU,GAAG,eAAe;EAEpC,MAAM,wCAAwC,MAAM,CAAC,GAAG,UAAU;EAElE,IAAI,iCACH,GAAG,KAAK,YACR;GACC,OAAO,SAAS;GAChB,QAAQ,SAAS;GACjB,SAAS,KAAK;GACd,WAAW,KAAK;GAChB,SAAS,KAAK;GACd,sBAAsB,KAAK,wBAAwB;EACpD,GACA,EAAE,QAAQ,KAAK,CAChB;EAEA,KAAK,gBAAgB,CAAC,CAAC;CACxB;AACD"}
|
|
@@ -9,20 +9,24 @@ interface VercelResourceConnectionInputs {
|
|
|
9
9
|
token: string;
|
|
10
10
|
/** Vercel team/org ID. */
|
|
11
11
|
teamId: string;
|
|
12
|
-
/**
|
|
13
|
-
|
|
14
|
-
/** The
|
|
15
|
-
resourceId: string;
|
|
16
|
-
/** The Vercel project ID to connect the resource to. */
|
|
12
|
+
/** The Vercel store ID of the provisioned marketplace resource (e.g. `"store_…"`). */
|
|
13
|
+
storeId: string;
|
|
14
|
+
/** The Vercel project ID to connect the store to. */
|
|
17
15
|
projectId: string;
|
|
18
|
-
/** Deployment environments to inject env vars into. */
|
|
16
|
+
/** Deployment environments to inject env vars into (e.g. `["production", "preview"]`). */
|
|
19
17
|
targets: string[];
|
|
18
|
+
/**
|
|
19
|
+
* Whether the injected env vars are marked sensitive.
|
|
20
|
+
* Vercel rejects sensitive env vars on the `development` target, so `targets`
|
|
21
|
+
* must not include `development` when this is `true`.
|
|
22
|
+
*/
|
|
23
|
+
makeEnvVarsSensitive: boolean;
|
|
20
24
|
}
|
|
21
25
|
/** Persisted state for the Vercel resource connection. */
|
|
22
26
|
type VercelResourceConnectionOutputs = VercelResourceConnectionInputs;
|
|
23
27
|
/**
|
|
24
|
-
* Dynamic provider that connects a Vercel marketplace
|
|
25
|
-
* injecting env vars into the specified deployment environments.
|
|
28
|
+
* Dynamic provider that connects a Vercel marketplace store to a project,
|
|
29
|
+
* injecting the store's env vars into the specified deployment environments.
|
|
26
30
|
*
|
|
27
31
|
* @internal Exported only for unit testing; not part of the public API surface.
|
|
28
32
|
*/
|
|
@@ -41,35 +45,35 @@ type VercelResourceConnectionOptions = Omit<pulumi.ComponentResourceOptions, "pr
|
|
|
41
45
|
*/
|
|
42
46
|
interface VercelResourceConnectionArgs {
|
|
43
47
|
/**
|
|
44
|
-
*
|
|
45
|
-
* Obtain this from {@link
|
|
46
|
-
*/
|
|
47
|
-
integrationConfigurationId: pulumi.Input<string>;
|
|
48
|
-
/**
|
|
49
|
-
* The external resource ID of the provisioned marketplace store.
|
|
50
|
-
* Obtain this from {@link VercelMarketplaceResource.externalResourceId}.
|
|
48
|
+
* The Vercel store ID of the provisioned marketplace resource (e.g. `"store_…"`).
|
|
49
|
+
* Obtain this from {@link VercelMarketplaceResource.id}.
|
|
51
50
|
*/
|
|
52
|
-
|
|
53
|
-
/** The Vercel project ID to connect the
|
|
51
|
+
storeId: pulumi.Input<string>;
|
|
52
|
+
/** The Vercel project ID to connect the store to. */
|
|
54
53
|
projectId: pulumi.Input<string>;
|
|
55
54
|
/**
|
|
56
|
-
* Deployment environments into which the
|
|
57
|
-
* Typical values: `["production", "preview"
|
|
55
|
+
* Deployment environments into which the store's env vars will be injected.
|
|
56
|
+
* Typical values: `["production", "preview"]`. Note that `development`
|
|
57
|
+
* cannot be combined with `makeEnvVarsSensitive: true` (Vercel rejects it).
|
|
58
58
|
*/
|
|
59
59
|
targets: pulumi.Input<string[]>;
|
|
60
|
+
/**
|
|
61
|
+
* Whether the injected env vars are marked sensitive (hidden after creation).
|
|
62
|
+
* Defaults to `true`. When `true`, `targets` must not include `development`.
|
|
63
|
+
*/
|
|
64
|
+
makeEnvVarsSensitive?: pulumi.Input<boolean>;
|
|
60
65
|
}
|
|
61
66
|
/**
|
|
62
|
-
* Connects a Vercel marketplace
|
|
63
|
-
*
|
|
67
|
+
* Connects a Vercel marketplace store to a project, injecting its env vars
|
|
68
|
+
* into the specified deployment environments.
|
|
64
69
|
*
|
|
65
|
-
* Calls `POST /v1/
|
|
66
|
-
*
|
|
67
|
-
*
|
|
70
|
+
* Calls `POST /v1/storage/stores/{storeId}/connections`. Uses adopt-or-create:
|
|
71
|
+
* an existing connection from the store to the project is adopted rather than
|
|
72
|
+
* re-created. Deletion is a no-op — there is no public per-project disconnect
|
|
73
|
+
* endpoint; disconnect manually from the Vercel dashboard if needed.
|
|
68
74
|
*
|
|
69
75
|
* @example
|
|
70
76
|
* ```typescript
|
|
71
|
-
* const upstash = new VercelIntegration("upstash", { slug: "upstash" }, { provider });
|
|
72
|
-
*
|
|
73
77
|
* const kvStore = new VercelMarketplaceResource("humanes-kv", {
|
|
74
78
|
* integrationConfigurationId: upstash.configurationId,
|
|
75
79
|
* name: "rby-humanes-kv",
|
|
@@ -78,10 +82,9 @@ interface VercelResourceConnectionArgs {
|
|
|
78
82
|
* }, { provider });
|
|
79
83
|
*
|
|
80
84
|
* new VercelResourceConnection("humanes-kv-conn", {
|
|
81
|
-
*
|
|
82
|
-
* resourceId: kvStore.externalResourceId,
|
|
85
|
+
* storeId: kvStore.id,
|
|
83
86
|
* projectId: humanesProject.id,
|
|
84
|
-
* targets: ["production", "preview"
|
|
87
|
+
* targets: ["production", "preview"],
|
|
85
88
|
* }, { provider });
|
|
86
89
|
* ```
|
|
87
90
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resource-connection.d.cts","names":[],"sources":["../../src/vercel/resource-connection.ts"],"mappings":";;;;;;UAMU,8BAAA;;EAET,KAAA;EAFuC;EAKvC,MAAA;EALuC;EAQvC,
|
|
1
|
+
{"version":3,"file":"resource-connection.d.cts","names":[],"sources":["../../src/vercel/resource-connection.ts"],"mappings":";;;;;;UAMU,8BAAA;;EAET,KAAA;EAFuC;EAKvC,MAAA;EALuC;EAQvC,OAAA;EAHA;EAMA,SAAA;EAAA;EAGA,OAAA;EAOA;;AAAoB;AAAA;;EAApB,oBAAA;AAAA;AAIoE;AAAA,KAAhE,+BAAA,GAAkC,8BAA8B;;;;;;;cA0CxD,gCAAA,YACD,MAAA,CAAO,OAAA,CAAQ,gBAAA;EAEpB,MAAA,CACL,MAAA,EAAQ,8BAAA,GACN,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EAwDpB,IAAA,CACL,EAAA,UACA,KAAA,EAAO,+BAAA,GACL,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;EAMpB,MAAA,CAAA,GAAU,OAAA;EAMV,IAAA,CACL,GAAA,UACA,IAAA,EAAM,+BAAA,EACN,IAAA,EAAM,8BAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;AAAA;;KAoDtB,+BAAA,GAAkC,IAAA,CACtC,MAAA,CAAO,wBAAA;EApImC,qCAwI1C,QAAA,EAAU,cAAA;AAAA;;;;UAMM,4BAAA;EA3If;;;;EAgJD,OAAA,EAAS,MAAA,CAAO,KAAA;EAvFV;EA0FN,SAAA,EAAW,MAAA,CAAO,KAAA;EAxFV;;;;;EA+FR,OAAA,EAAS,MAAA,CAAO,KAAA;EAxFV;;;;EA8FN,oBAAA,GAAuB,MAAA,CAAO,KAAA;AAAA;;;;;;;;AApFM;AA+BpC;;;;;;;;;;;;;;AA0BwB;AAMzB;;cAiDa,wBAAA,SAAiC,MAAA,CAAO,iBAAA;cAEnD,IAAA,UACA,IAAA,EAAM,4BAAA,EACN,IAAA,EAAM,+BAAA;AAAA"}
|
|
@@ -9,20 +9,24 @@ interface VercelResourceConnectionInputs {
|
|
|
9
9
|
token: string;
|
|
10
10
|
/** Vercel team/org ID. */
|
|
11
11
|
teamId: string;
|
|
12
|
-
/**
|
|
13
|
-
|
|
14
|
-
/** The
|
|
15
|
-
resourceId: string;
|
|
16
|
-
/** The Vercel project ID to connect the resource to. */
|
|
12
|
+
/** The Vercel store ID of the provisioned marketplace resource (e.g. `"store_…"`). */
|
|
13
|
+
storeId: string;
|
|
14
|
+
/** The Vercel project ID to connect the store to. */
|
|
17
15
|
projectId: string;
|
|
18
|
-
/** Deployment environments to inject env vars into. */
|
|
16
|
+
/** Deployment environments to inject env vars into (e.g. `["production", "preview"]`). */
|
|
19
17
|
targets: string[];
|
|
18
|
+
/**
|
|
19
|
+
* Whether the injected env vars are marked sensitive.
|
|
20
|
+
* Vercel rejects sensitive env vars on the `development` target, so `targets`
|
|
21
|
+
* must not include `development` when this is `true`.
|
|
22
|
+
*/
|
|
23
|
+
makeEnvVarsSensitive: boolean;
|
|
20
24
|
}
|
|
21
25
|
/** Persisted state for the Vercel resource connection. */
|
|
22
26
|
type VercelResourceConnectionOutputs = VercelResourceConnectionInputs;
|
|
23
27
|
/**
|
|
24
|
-
* Dynamic provider that connects a Vercel marketplace
|
|
25
|
-
* injecting env vars into the specified deployment environments.
|
|
28
|
+
* Dynamic provider that connects a Vercel marketplace store to a project,
|
|
29
|
+
* injecting the store's env vars into the specified deployment environments.
|
|
26
30
|
*
|
|
27
31
|
* @internal Exported only for unit testing; not part of the public API surface.
|
|
28
32
|
*/
|
|
@@ -41,35 +45,35 @@ type VercelResourceConnectionOptions = Omit<pulumi.ComponentResourceOptions, "pr
|
|
|
41
45
|
*/
|
|
42
46
|
interface VercelResourceConnectionArgs {
|
|
43
47
|
/**
|
|
44
|
-
*
|
|
45
|
-
* Obtain this from {@link
|
|
46
|
-
*/
|
|
47
|
-
integrationConfigurationId: pulumi.Input<string>;
|
|
48
|
-
/**
|
|
49
|
-
* The external resource ID of the provisioned marketplace store.
|
|
50
|
-
* Obtain this from {@link VercelMarketplaceResource.externalResourceId}.
|
|
48
|
+
* The Vercel store ID of the provisioned marketplace resource (e.g. `"store_…"`).
|
|
49
|
+
* Obtain this from {@link VercelMarketplaceResource.id}.
|
|
51
50
|
*/
|
|
52
|
-
|
|
53
|
-
/** The Vercel project ID to connect the
|
|
51
|
+
storeId: pulumi.Input<string>;
|
|
52
|
+
/** The Vercel project ID to connect the store to. */
|
|
54
53
|
projectId: pulumi.Input<string>;
|
|
55
54
|
/**
|
|
56
|
-
* Deployment environments into which the
|
|
57
|
-
* Typical values: `["production", "preview"
|
|
55
|
+
* Deployment environments into which the store's env vars will be injected.
|
|
56
|
+
* Typical values: `["production", "preview"]`. Note that `development`
|
|
57
|
+
* cannot be combined with `makeEnvVarsSensitive: true` (Vercel rejects it).
|
|
58
58
|
*/
|
|
59
59
|
targets: pulumi.Input<string[]>;
|
|
60
|
+
/**
|
|
61
|
+
* Whether the injected env vars are marked sensitive (hidden after creation).
|
|
62
|
+
* Defaults to `true`. When `true`, `targets` must not include `development`.
|
|
63
|
+
*/
|
|
64
|
+
makeEnvVarsSensitive?: pulumi.Input<boolean>;
|
|
60
65
|
}
|
|
61
66
|
/**
|
|
62
|
-
* Connects a Vercel marketplace
|
|
63
|
-
*
|
|
67
|
+
* Connects a Vercel marketplace store to a project, injecting its env vars
|
|
68
|
+
* into the specified deployment environments.
|
|
64
69
|
*
|
|
65
|
-
* Calls `POST /v1/
|
|
66
|
-
*
|
|
67
|
-
*
|
|
70
|
+
* Calls `POST /v1/storage/stores/{storeId}/connections`. Uses adopt-or-create:
|
|
71
|
+
* an existing connection from the store to the project is adopted rather than
|
|
72
|
+
* re-created. Deletion is a no-op — there is no public per-project disconnect
|
|
73
|
+
* endpoint; disconnect manually from the Vercel dashboard if needed.
|
|
68
74
|
*
|
|
69
75
|
* @example
|
|
70
76
|
* ```typescript
|
|
71
|
-
* const upstash = new VercelIntegration("upstash", { slug: "upstash" }, { provider });
|
|
72
|
-
*
|
|
73
77
|
* const kvStore = new VercelMarketplaceResource("humanes-kv", {
|
|
74
78
|
* integrationConfigurationId: upstash.configurationId,
|
|
75
79
|
* name: "rby-humanes-kv",
|
|
@@ -78,10 +82,9 @@ interface VercelResourceConnectionArgs {
|
|
|
78
82
|
* }, { provider });
|
|
79
83
|
*
|
|
80
84
|
* new VercelResourceConnection("humanes-kv-conn", {
|
|
81
|
-
*
|
|
82
|
-
* resourceId: kvStore.externalResourceId,
|
|
85
|
+
* storeId: kvStore.id,
|
|
83
86
|
* projectId: humanesProject.id,
|
|
84
|
-
* targets: ["production", "preview"
|
|
87
|
+
* targets: ["production", "preview"],
|
|
85
88
|
* }, { provider });
|
|
86
89
|
* ```
|
|
87
90
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resource-connection.d.mts","names":[],"sources":["../../src/vercel/resource-connection.ts"],"mappings":";;;;;;UAMU,8BAAA;;EAET,KAAA;EAFuC;EAKvC,MAAA;EALuC;EAQvC,
|
|
1
|
+
{"version":3,"file":"resource-connection.d.mts","names":[],"sources":["../../src/vercel/resource-connection.ts"],"mappings":";;;;;;UAMU,8BAAA;;EAET,KAAA;EAFuC;EAKvC,MAAA;EALuC;EAQvC,OAAA;EAHA;EAMA,SAAA;EAAA;EAGA,OAAA;EAOA;;AAAoB;AAAA;;EAApB,oBAAA;AAAA;AAIoE;AAAA,KAAhE,+BAAA,GAAkC,8BAA8B;;;;;;;cA0CxD,gCAAA,YACD,MAAA,CAAO,OAAA,CAAQ,gBAAA;EAEpB,MAAA,CACL,MAAA,EAAQ,8BAAA,GACN,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EAwDpB,IAAA,CACL,EAAA,UACA,KAAA,EAAO,+BAAA,GACL,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;EAMpB,MAAA,CAAA,GAAU,OAAA;EAMV,IAAA,CACL,GAAA,UACA,IAAA,EAAM,+BAAA,EACN,IAAA,EAAM,8BAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;AAAA;;KAoDtB,+BAAA,GAAkC,IAAA,CACtC,MAAA,CAAO,wBAAA;EApImC,qCAwI1C,QAAA,EAAU,cAAA;AAAA;;;;UAMM,4BAAA;EA3If;;;;EAgJD,OAAA,EAAS,MAAA,CAAO,KAAA;EAvFV;EA0FN,SAAA,EAAW,MAAA,CAAO,KAAA;EAxFV;;;;;EA+FR,OAAA,EAAS,MAAA,CAAO,KAAA;EAxFV;;;;EA8FN,oBAAA,GAAuB,MAAA,CAAO,KAAA;AAAA;;;;;;;;AApFM;AA+BpC;;;;;;;;;;;;;;AA0BwB;AAMzB;;cAiDa,wBAAA,SAAiC,MAAA,CAAO,iBAAA;cAEnD,IAAA,UACA,IAAA,EAAM,4BAAA,EACN,IAAA,EAAM,+BAAA;AAAA"}
|
|
@@ -4,14 +4,31 @@ import * as pulumi from "@pulumi/pulumi";
|
|
|
4
4
|
//#region src/vercel/resource-connection.ts
|
|
5
5
|
const VERCEL_API_URL = "https://api.vercel.com";
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
|
|
7
|
+
* Finds an existing connection from a store to a specific project.
|
|
8
|
+
*/
|
|
9
|
+
async function findConnection(token, teamId, storeId, projectId) {
|
|
10
|
+
const response = await fetch(`${VERCEL_API_URL}/v1/storage/stores/${storeId}/connections?teamId=${teamId}`, { headers: { Authorization: `Bearer ${token}` } });
|
|
11
|
+
if (!response.ok) return;
|
|
12
|
+
return (await response.json()).connections.find((c) => c.projectId === projectId);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Dynamic provider that connects a Vercel marketplace store to a project,
|
|
16
|
+
* injecting the store's env vars into the specified deployment environments.
|
|
9
17
|
*
|
|
10
18
|
* @internal Exported only for unit testing; not part of the public API surface.
|
|
11
19
|
*/
|
|
12
20
|
var VercelResourceConnectionProvider = class {
|
|
13
21
|
async create(inputs) {
|
|
14
|
-
|
|
22
|
+
if (inputs.makeEnvVarsSensitive && inputs.targets.includes("development")) throw new Error("VercelResourceConnection: Vercel rejects sensitive env vars on the 'development' target. Either drop 'development' from targets or set makeEnvVarsSensitive to false.");
|
|
23
|
+
const existing = await findConnection(inputs.token, inputs.teamId, inputs.storeId, inputs.projectId);
|
|
24
|
+
if (existing) {
|
|
25
|
+
pulumi.log.info(`Adopting existing Vercel store connection (${existing.id})`);
|
|
26
|
+
return {
|
|
27
|
+
id: `${inputs.storeId}:${inputs.projectId}`,
|
|
28
|
+
outs: { ...inputs }
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
const response = await fetch(`${VERCEL_API_URL}/v1/storage/stores/${inputs.storeId}/connections?teamId=${inputs.teamId}`, {
|
|
15
32
|
method: "POST",
|
|
16
33
|
headers: {
|
|
17
34
|
Authorization: `Bearer ${inputs.token}`,
|
|
@@ -20,14 +37,13 @@ var VercelResourceConnectionProvider = class {
|
|
|
20
37
|
body: JSON.stringify({
|
|
21
38
|
projectId: inputs.projectId,
|
|
22
39
|
envVarEnvironments: inputs.targets,
|
|
23
|
-
makeEnvVarsSensitive:
|
|
40
|
+
makeEnvVarsSensitive: inputs.makeEnvVarsSensitive
|
|
24
41
|
})
|
|
25
42
|
});
|
|
26
43
|
if (!response.ok) throw new Error(`Vercel resource connection failed (${response.status}): ${await response.text()}`);
|
|
27
|
-
const outs = { ...inputs };
|
|
28
44
|
return {
|
|
29
|
-
id: `${inputs.
|
|
30
|
-
outs
|
|
45
|
+
id: `${inputs.storeId}:${inputs.projectId}`,
|
|
46
|
+
outs: { ...inputs }
|
|
31
47
|
};
|
|
32
48
|
}
|
|
33
49
|
async read(id, props) {
|
|
@@ -42,10 +58,10 @@ var VercelResourceConnectionProvider = class {
|
|
|
42
58
|
async diff(_id, olds, news) {
|
|
43
59
|
const replaces = [];
|
|
44
60
|
if (olds.teamId !== news.teamId) replaces.push("teamId");
|
|
45
|
-
if (olds.
|
|
46
|
-
if (olds.resourceId !== news.resourceId) replaces.push("resourceId");
|
|
61
|
+
if (olds.storeId !== news.storeId) replaces.push("storeId");
|
|
47
62
|
if (olds.projectId !== news.projectId) replaces.push("projectId");
|
|
48
63
|
if (JSON.stringify(olds.targets) !== JSON.stringify(news.targets)) replaces.push("targets");
|
|
64
|
+
if (olds.makeEnvVarsSensitive !== news.makeEnvVarsSensitive) replaces.push("makeEnvVarsSensitive");
|
|
49
65
|
return {
|
|
50
66
|
changes: replaces.length > 0,
|
|
51
67
|
replaces,
|
|
@@ -60,17 +76,16 @@ var VercelResourceConnectionResource = class extends pulumi.dynamic.Resource {
|
|
|
60
76
|
}
|
|
61
77
|
};
|
|
62
78
|
/**
|
|
63
|
-
* Connects a Vercel marketplace
|
|
64
|
-
*
|
|
79
|
+
* Connects a Vercel marketplace store to a project, injecting its env vars
|
|
80
|
+
* into the specified deployment environments.
|
|
65
81
|
*
|
|
66
|
-
* Calls `POST /v1/
|
|
67
|
-
*
|
|
68
|
-
*
|
|
82
|
+
* Calls `POST /v1/storage/stores/{storeId}/connections`. Uses adopt-or-create:
|
|
83
|
+
* an existing connection from the store to the project is adopted rather than
|
|
84
|
+
* re-created. Deletion is a no-op — there is no public per-project disconnect
|
|
85
|
+
* endpoint; disconnect manually from the Vercel dashboard if needed.
|
|
69
86
|
*
|
|
70
87
|
* @example
|
|
71
88
|
* ```typescript
|
|
72
|
-
* const upstash = new VercelIntegration("upstash", { slug: "upstash" }, { provider });
|
|
73
|
-
*
|
|
74
89
|
* const kvStore = new VercelMarketplaceResource("humanes-kv", {
|
|
75
90
|
* integrationConfigurationId: upstash.configurationId,
|
|
76
91
|
* name: "rby-humanes-kv",
|
|
@@ -79,10 +94,9 @@ var VercelResourceConnectionResource = class extends pulumi.dynamic.Resource {
|
|
|
79
94
|
* }, { provider });
|
|
80
95
|
*
|
|
81
96
|
* new VercelResourceConnection("humanes-kv-conn", {
|
|
82
|
-
*
|
|
83
|
-
* resourceId: kvStore.externalResourceId,
|
|
97
|
+
* storeId: kvStore.id,
|
|
84
98
|
* projectId: humanesProject.id,
|
|
85
|
-
* targets: ["production", "preview"
|
|
99
|
+
* targets: ["production", "preview"],
|
|
86
100
|
* }, { provider });
|
|
87
101
|
* ```
|
|
88
102
|
*/
|
|
@@ -93,7 +107,10 @@ var VercelResourceConnection = class extends pulumi.ComponentResource {
|
|
|
93
107
|
new VercelResourceConnectionResource(`${name}-resource`, {
|
|
94
108
|
token: provider.token,
|
|
95
109
|
teamId: provider.teamId,
|
|
96
|
-
|
|
110
|
+
storeId: args.storeId,
|
|
111
|
+
projectId: args.projectId,
|
|
112
|
+
targets: args.targets,
|
|
113
|
+
makeEnvVarsSensitive: args.makeEnvVarsSensitive ?? true
|
|
97
114
|
}, { parent: this });
|
|
98
115
|
this.registerOutputs({});
|
|
99
116
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resource-connection.mjs","names":[],"sources":["../../src/vercel/resource-connection.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\nimport type { VercelProvider } from \"./provider\";\n\nconst VERCEL_API_URL = \"https://api.vercel.com\";\n\n/** Resolved inputs for the Vercel resource connection dynamic provider. */\ninterface VercelResourceConnectionInputs {\n\t/** Vercel API bearer token. */\n\ttoken: string;\n\n\t/** Vercel team/org ID. */\n\tteamId: string;\n\n\t/** Integration configuration ID (e.g. `\"icfg_…\"`). */\n\tintegrationConfigurationId: string;\n\n\t/** The external resource ID of the provisioned marketplace store. */\n\tresourceId: string;\n\n\t/** The Vercel project ID to connect the resource to. */\n\tprojectId: string;\n\n\t/** Deployment environments to inject env vars into. */\n\ttargets: string[];\n}\n\n/** Persisted state for the Vercel resource connection. */\ntype VercelResourceConnectionOutputs = VercelResourceConnectionInputs;\n\n/**\n * Dynamic provider that connects a Vercel marketplace resource to a project,\n * injecting env vars into the specified deployment environments.\n *\n * @internal Exported only for unit testing; not part of the public API surface.\n */\nexport class VercelResourceConnectionProvider\n\timplements pulumi.dynamic.ResourceProvider\n{\n\tasync create(\n\t\tinputs: VercelResourceConnectionInputs,\n\t): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst response = await fetch(\n\t\t\t`${VERCEL_API_URL}/v1/integrations/installations/${inputs.integrationConfigurationId}/resources/${inputs.resourceId}/connections?teamId=${inputs.teamId}`,\n\t\t\t{\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\tAuthorization: `Bearer ${inputs.token}`,\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tprojectId: inputs.projectId,\n\t\t\t\t\tenvVarEnvironments: inputs.targets,\n\t\t\t\t\tmakeEnvVarsSensitive: true,\n\t\t\t\t}),\n\t\t\t},\n\t\t);\n\n\t\tif (!response.ok) {\n\t\t\tthrow new Error(\n\t\t\t\t`Vercel resource connection failed (${response.status}): ${await response.text()}`,\n\t\t\t);\n\t\t}\n\n\t\tconst outs: VercelResourceConnectionOutputs = { ...inputs };\n\n\t\treturn { id: `${inputs.resourceId}:${inputs.projectId}`, outs };\n\t}\n\n\tasync read(\n\t\tid: string,\n\t\tprops: VercelResourceConnectionOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\t// There is no public read endpoint for resource connections; drift is not refreshed here.\n\t\treturn { id, props };\n\t}\n\n\tasync delete(): Promise<void> {\n\t\tpulumi.log.warn(\n\t\t\t\"VercelResourceConnection deletion skipped — no public per-project disconnect endpoint; disconnect from the Vercel dashboard if intended\",\n\t\t);\n\t}\n\n\tasync diff(\n\t\t_id: string,\n\t\tolds: VercelResourceConnectionOutputs,\n\t\tnews: VercelResourceConnectionInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst replaces: string[] = [];\n\n\t\tif (olds.teamId !== news.teamId) {\n\t\t\treplaces.push(\"teamId\");\n\t\t}\n\n\t\tif (olds.integrationConfigurationId !== news.integrationConfigurationId) {\n\t\t\treplaces.push(\"integrationConfigurationId\");\n\t\t}\n\n\t\tif (olds.resourceId !== news.resourceId) {\n\t\t\treplaces.push(\"resourceId\");\n\t\t}\n\n\t\tif (olds.projectId !== news.projectId) {\n\t\t\treplaces.push(\"projectId\");\n\t\t}\n\n\t\t// targets comparison is order-sensitive by design: the array is sent verbatim to the\n\t\t// API as envVarEnvironments, so reordering is a meaningful change.\n\t\tif (JSON.stringify(olds.targets) !== JSON.stringify(news.targets)) {\n\t\t\treplaces.push(\"targets\");\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/** Internal dynamic resource — not part of the public API. */\nclass VercelResourceConnectionResource extends pulumi.dynamic.Resource {\n\tconstructor(\n\t\tname: string,\n\t\targs: {\n\t\t\ttoken: pulumi.Input<string>;\n\t\t\tteamId: pulumi.Input<string>;\n\t\t\tintegrationConfigurationId: pulumi.Input<string>;\n\t\t\tresourceId: pulumi.Input<string>;\n\t\t\tprojectId: pulumi.Input<string>;\n\t\t\ttargets: pulumi.Input<string[]>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\tsuper(new VercelResourceConnectionProvider(), name, { ...args }, opts);\n\t}\n}\n\n/** Options type for VercelResourceConnection — replaces Pulumi's native `provider` field. */\ntype VercelResourceConnectionOptions = Omit<\n\tpulumi.ComponentResourceOptions,\n\t\"provider\"\n> & {\n\t/** Vercel authentication context. */\n\tprovider: VercelProvider;\n};\n\n/**\n * Args for {@link VercelResourceConnection}.\n */\nexport interface VercelResourceConnectionArgs {\n\t/**\n\t * Integration configuration ID (e.g. `\"icfg_…\"`).\n\t * Obtain this from {@link VercelIntegration.configurationId}.\n\t */\n\tintegrationConfigurationId: pulumi.Input<string>;\n\n\t/**\n\t * The external resource ID of the provisioned marketplace store.\n\t * Obtain this from {@link VercelMarketplaceResource.externalResourceId}.\n\t */\n\tresourceId: pulumi.Input<string>;\n\n\t/** The Vercel project ID to connect the resource to. */\n\tprojectId: pulumi.Input<string>;\n\n\t/**\n\t * Deployment environments into which the integration env vars will be injected.\n\t * Typical values: `[\"production\", \"preview\", \"development\"]`.\n\t */\n\ttargets: pulumi.Input<string[]>;\n}\n\n/**\n * Connects a Vercel marketplace resource to a project, injecting its env vars\n * as sensitive variables into the specified deployment environments.\n *\n * Calls `POST /v1/integrations/installations/{icfg}/resources/{resourceId}/connections`.\n * Deletion is a no-op — there is no public per-project disconnect endpoint;\n * disconnect manually from the Vercel dashboard if needed.\n *\n * @example\n * ```typescript\n * const upstash = new VercelIntegration(\"upstash\", { slug: \"upstash\" }, { provider });\n *\n * const kvStore = new VercelMarketplaceResource(\"humanes-kv\", {\n * integrationConfigurationId: upstash.configurationId,\n * name: \"rby-humanes-kv\",\n * type: \"upstash-kv\",\n * externalId: \"rby-humanes-kv\",\n * }, { provider });\n *\n * new VercelResourceConnection(\"humanes-kv-conn\", {\n * integrationConfigurationId: upstash.configurationId,\n * resourceId: kvStore.externalResourceId,\n * projectId: humanesProject.id,\n * targets: [\"production\", \"preview\", \"development\"],\n * }, { provider });\n * ```\n */\nexport class VercelResourceConnection extends pulumi.ComponentResource {\n\tconstructor(\n\t\tname: string,\n\t\targs: VercelResourceConnectionArgs,\n\t\topts: VercelResourceConnectionOptions,\n\t) {\n\t\tconst { provider, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:vercel:ResourceConnection\", name, {}, pulumiOpts);\n\n\t\tnew VercelResourceConnectionResource(\n\t\t\t`${name}-resource`,\n\t\t\t{\n\t\t\t\ttoken: provider.token,\n\t\t\t\tteamId: provider.teamId,\n\t\t\t\t...args,\n\t\t\t},\n\t\t\t{ parent: this },\n\t\t);\n\n\t\tthis.registerOutputs({});\n\t}\n}\n"],"mappings":";;;;AAGA,MAAM,iBAAiB;;;;;;;AAgCvB,IAAa,mCAAb,MAEA;CACC,MAAM,OACL,QACuC;EACvC,MAAM,WAAW,MAAM,MACtB,GAAG,eAAe,iCAAiC,OAAO,2BAA2B,aAAa,OAAO,WAAW,sBAAsB,OAAO,UACjJ;GACC,QAAQ;GACR,SAAS;IACR,eAAe,UAAU,OAAO;IAChC,gBAAgB;GACjB;GACA,MAAM,KAAK,UAAU;IACpB,WAAW,OAAO;IAClB,oBAAoB,OAAO;IAC3B,sBAAsB;GACvB,CAAC;EACF,CACD;EAEA,IAAI,CAAC,SAAS,IACb,MAAM,IAAI,MACT,sCAAsC,SAAS,OAAO,KAAK,MAAM,SAAS,KAAK,GAChF;EAGD,MAAM,OAAwC,EAAE,GAAG,OAAO;EAE1D,OAAO;GAAE,IAAI,GAAG,OAAO,WAAW,GAAG,OAAO;GAAa;EAAK;CAC/D;CAEA,MAAM,KACL,IACA,OACqC;EAErC,OAAO;GAAE;GAAI;EAAM;CACpB;CAEA,MAAM,SAAwB;EAC7B,OAAO,IAAI,KACV,yIACD;CACD;CAEA,MAAM,KACL,KACA,MACA,MACqC;EACrC,MAAM,WAAqB,CAAC;EAE5B,IAAI,KAAK,WAAW,KAAK,QACxB,SAAS,KAAK,QAAQ;EAGvB,IAAI,KAAK,+BAA+B,KAAK,4BAC5C,SAAS,KAAK,4BAA4B;EAG3C,IAAI,KAAK,eAAe,KAAK,YAC5B,SAAS,KAAK,YAAY;EAG3B,IAAI,KAAK,cAAc,KAAK,WAC3B,SAAS,KAAK,WAAW;EAK1B,IAAI,KAAK,UAAU,KAAK,OAAO,MAAM,KAAK,UAAU,KAAK,OAAO,GAC/D,SAAS,KAAK,SAAS;EAGxB,OAAO;GACN,SAAS,SAAS,SAAS;GAC3B;GACA,qBAAqB;EACtB;CACD;AACD;;AAGA,IAAM,mCAAN,cAA+C,OAAO,QAAQ,SAAS;CACtE,YACC,MACA,MAQA,MACC;EACD,MAAM,IAAI,iCAAiC,GAAG,MAAM,EAAE,GAAG,KAAK,GAAG,IAAI;CACtE;AACD;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgEA,IAAa,2BAAb,cAA8C,OAAO,kBAAkB;CACtE,YACC,MACA,MACA,MACC;EACD,MAAM,EAAE,UAAU,GAAG,eAAe;EAEpC,MAAM,wCAAwC,MAAM,CAAC,GAAG,UAAU;EAElE,IAAI,iCACH,GAAG,KAAK,YACR;GACC,OAAO,SAAS;GAChB,QAAQ,SAAS;GACjB,GAAG;EACJ,GACA,EAAE,QAAQ,KAAK,CAChB;EAEA,KAAK,gBAAgB,CAAC,CAAC;CACxB;AACD"}
|
|
1
|
+
{"version":3,"file":"resource-connection.mjs","names":[],"sources":["../../src/vercel/resource-connection.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\nimport type { VercelProvider } from \"./provider\";\n\nconst VERCEL_API_URL = \"https://api.vercel.com\";\n\n/** Resolved inputs for the Vercel resource connection dynamic provider. */\ninterface VercelResourceConnectionInputs {\n\t/** Vercel API bearer token. */\n\ttoken: string;\n\n\t/** Vercel team/org ID. */\n\tteamId: string;\n\n\t/** The Vercel store ID of the provisioned marketplace resource (e.g. `\"store_…\"`). */\n\tstoreId: string;\n\n\t/** The Vercel project ID to connect the store to. */\n\tprojectId: string;\n\n\t/** Deployment environments to inject env vars into (e.g. `[\"production\", \"preview\"]`). */\n\ttargets: string[];\n\n\t/**\n\t * Whether the injected env vars are marked sensitive.\n\t * Vercel rejects sensitive env vars on the `development` target, so `targets`\n\t * must not include `development` when this is `true`.\n\t */\n\tmakeEnvVarsSensitive: boolean;\n}\n\n/** Persisted state for the Vercel resource connection. */\ntype VercelResourceConnectionOutputs = VercelResourceConnectionInputs;\n\n/** A single store-to-project connection as returned by the Vercel API. */\ninterface StoreConnection {\n\tid: string;\n\tprojectId: string;\n}\n\n/** Vercel API response for listing a store's connections. */\ninterface StoreConnectionsResponse {\n\tconnections: StoreConnection[];\n}\n\n/**\n * Finds an existing connection from a store to a specific project.\n */\nasync function findConnection(\n\ttoken: string,\n\tteamId: string,\n\tstoreId: string,\n\tprojectId: string,\n): Promise<StoreConnection | undefined> {\n\tconst response = await fetch(\n\t\t`${VERCEL_API_URL}/v1/storage/stores/${storeId}/connections?teamId=${teamId}`,\n\t\t{ headers: { Authorization: `Bearer ${token}` } },\n\t);\n\n\tif (!response.ok) {\n\t\treturn undefined;\n\t}\n\n\tconst data = (await response.json()) as StoreConnectionsResponse;\n\n\treturn data.connections.find((c) => c.projectId === projectId);\n}\n\n/**\n * Dynamic provider that connects a Vercel marketplace store to a project,\n * injecting the store's env vars into the specified deployment environments.\n *\n * @internal Exported only for unit testing; not part of the public API surface.\n */\nexport class VercelResourceConnectionProvider\n\timplements pulumi.dynamic.ResourceProvider\n{\n\tasync create(\n\t\tinputs: VercelResourceConnectionInputs,\n\t): Promise<pulumi.dynamic.CreateResult> {\n\t\tif (inputs.makeEnvVarsSensitive && inputs.targets.includes(\"development\")) {\n\t\t\tthrow new Error(\n\t\t\t\t\"VercelResourceConnection: Vercel rejects sensitive env vars on the 'development' target. \" +\n\t\t\t\t\t\"Either drop 'development' from targets or set makeEnvVarsSensitive to false.\",\n\t\t\t);\n\t\t}\n\n\t\t// Adopt-or-create: a store can only be connected to a given project once,\n\t\t// so re-creating an out-of-band connection (or a prior partial apply) adopts it.\n\t\tconst existing = await findConnection(\n\t\t\tinputs.token,\n\t\t\tinputs.teamId,\n\t\t\tinputs.storeId,\n\t\t\tinputs.projectId,\n\t\t);\n\n\t\tif (existing) {\n\t\t\tpulumi.log.info(\n\t\t\t\t`Adopting existing Vercel store connection (${existing.id})`,\n\t\t\t);\n\n\t\t\treturn {\n\t\t\t\tid: `${inputs.storeId}:${inputs.projectId}`,\n\t\t\t\touts: { ...inputs },\n\t\t\t};\n\t\t}\n\n\t\tconst response = await fetch(\n\t\t\t`${VERCEL_API_URL}/v1/storage/stores/${inputs.storeId}/connections?teamId=${inputs.teamId}`,\n\t\t\t{\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\tAuthorization: `Bearer ${inputs.token}`,\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tprojectId: inputs.projectId,\n\t\t\t\t\tenvVarEnvironments: inputs.targets,\n\t\t\t\t\tmakeEnvVarsSensitive: inputs.makeEnvVarsSensitive,\n\t\t\t\t}),\n\t\t\t},\n\t\t);\n\n\t\tif (!response.ok) {\n\t\t\tthrow new Error(\n\t\t\t\t`Vercel resource connection failed (${response.status}): ${await response.text()}`,\n\t\t\t);\n\t\t}\n\n\t\treturn {\n\t\t\tid: `${inputs.storeId}:${inputs.projectId}`,\n\t\t\touts: { ...inputs },\n\t\t};\n\t}\n\n\tasync read(\n\t\tid: string,\n\t\tprops: VercelResourceConnectionOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\t// The connection list endpoint exposes presence but not the targeted environments,\n\t\t// so env-var drift is not refreshed here — only the connection's existence.\n\t\treturn { id, props };\n\t}\n\n\tasync delete(): Promise<void> {\n\t\tpulumi.log.warn(\n\t\t\t\"VercelResourceConnection deletion skipped — no public per-project disconnect endpoint; disconnect from the Vercel dashboard if intended\",\n\t\t);\n\t}\n\n\tasync diff(\n\t\t_id: string,\n\t\tolds: VercelResourceConnectionOutputs,\n\t\tnews: VercelResourceConnectionInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst replaces: string[] = [];\n\n\t\tif (olds.teamId !== news.teamId) {\n\t\t\treplaces.push(\"teamId\");\n\t\t}\n\n\t\tif (olds.storeId !== news.storeId) {\n\t\t\treplaces.push(\"storeId\");\n\t\t}\n\n\t\tif (olds.projectId !== news.projectId) {\n\t\t\treplaces.push(\"projectId\");\n\t\t}\n\n\t\t// targets comparison is order-sensitive by design: the array is sent verbatim to the\n\t\t// API as envVarEnvironments, so reordering is a meaningful change.\n\t\tif (JSON.stringify(olds.targets) !== JSON.stringify(news.targets)) {\n\t\t\treplaces.push(\"targets\");\n\t\t}\n\n\t\tif (olds.makeEnvVarsSensitive !== news.makeEnvVarsSensitive) {\n\t\t\treplaces.push(\"makeEnvVarsSensitive\");\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/** Internal dynamic resource — not part of the public API. */\nclass VercelResourceConnectionResource extends pulumi.dynamic.Resource {\n\tconstructor(\n\t\tname: string,\n\t\targs: {\n\t\t\ttoken: pulumi.Input<string>;\n\t\t\tteamId: pulumi.Input<string>;\n\t\t\tstoreId: pulumi.Input<string>;\n\t\t\tprojectId: pulumi.Input<string>;\n\t\t\ttargets: pulumi.Input<string[]>;\n\t\t\tmakeEnvVarsSensitive: pulumi.Input<boolean>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\tsuper(new VercelResourceConnectionProvider(), name, { ...args }, opts);\n\t}\n}\n\n/** Options type for VercelResourceConnection — replaces Pulumi's native `provider` field. */\ntype VercelResourceConnectionOptions = Omit<\n\tpulumi.ComponentResourceOptions,\n\t\"provider\"\n> & {\n\t/** Vercel authentication context. */\n\tprovider: VercelProvider;\n};\n\n/**\n * Args for {@link VercelResourceConnection}.\n */\nexport interface VercelResourceConnectionArgs {\n\t/**\n\t * The Vercel store ID of the provisioned marketplace resource (e.g. `\"store_…\"`).\n\t * Obtain this from {@link VercelMarketplaceResource.id}.\n\t */\n\tstoreId: pulumi.Input<string>;\n\n\t/** The Vercel project ID to connect the store to. */\n\tprojectId: pulumi.Input<string>;\n\n\t/**\n\t * Deployment environments into which the store's env vars will be injected.\n\t * Typical values: `[\"production\", \"preview\"]`. Note that `development`\n\t * cannot be combined with `makeEnvVarsSensitive: true` (Vercel rejects it).\n\t */\n\ttargets: pulumi.Input<string[]>;\n\n\t/**\n\t * Whether the injected env vars are marked sensitive (hidden after creation).\n\t * Defaults to `true`. When `true`, `targets` must not include `development`.\n\t */\n\tmakeEnvVarsSensitive?: pulumi.Input<boolean>;\n}\n\n/**\n * Connects a Vercel marketplace store to a project, injecting its env vars\n * into the specified deployment environments.\n *\n * Calls `POST /v1/storage/stores/{storeId}/connections`. Uses adopt-or-create:\n * an existing connection from the store to the project is adopted rather than\n * re-created. Deletion is a no-op — there is no public per-project disconnect\n * endpoint; disconnect manually from the Vercel dashboard if needed.\n *\n * @example\n * ```typescript\n * const kvStore = new VercelMarketplaceResource(\"humanes-kv\", {\n * integrationConfigurationId: upstash.configurationId,\n * name: \"rby-humanes-kv\",\n * type: \"upstash-kv\",\n * externalId: \"rby-humanes-kv\",\n * }, { provider });\n *\n * new VercelResourceConnection(\"humanes-kv-conn\", {\n * storeId: kvStore.id,\n * projectId: humanesProject.id,\n * targets: [\"production\", \"preview\"],\n * }, { provider });\n * ```\n */\nexport class VercelResourceConnection extends pulumi.ComponentResource {\n\tconstructor(\n\t\tname: string,\n\t\targs: VercelResourceConnectionArgs,\n\t\topts: VercelResourceConnectionOptions,\n\t) {\n\t\tconst { provider, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:vercel:ResourceConnection\", name, {}, pulumiOpts);\n\n\t\tnew VercelResourceConnectionResource(\n\t\t\t`${name}-resource`,\n\t\t\t{\n\t\t\t\ttoken: provider.token,\n\t\t\t\tteamId: provider.teamId,\n\t\t\t\tstoreId: args.storeId,\n\t\t\t\tprojectId: args.projectId,\n\t\t\t\ttargets: args.targets,\n\t\t\t\tmakeEnvVarsSensitive: args.makeEnvVarsSensitive ?? true,\n\t\t\t},\n\t\t\t{ parent: this },\n\t\t);\n\n\t\tthis.registerOutputs({});\n\t}\n}\n"],"mappings":";;;;AAGA,MAAM,iBAAiB;;;;AA4CvB,eAAe,eACd,OACA,QACA,SACA,WACuC;CACvC,MAAM,WAAW,MAAM,MACtB,GAAG,eAAe,qBAAqB,QAAQ,sBAAsB,UACrE,EAAE,SAAS,EAAE,eAAe,UAAU,QAAQ,EAAE,CACjD;CAEA,IAAI,CAAC,SAAS,IACb;CAKD,QAAO,MAFa,SAAS,KAAK,GAEtB,YAAY,MAAM,MAAM,EAAE,cAAc,SAAS;AAC9D;;;;;;;AAQA,IAAa,mCAAb,MAEA;CACC,MAAM,OACL,QACuC;EACvC,IAAI,OAAO,wBAAwB,OAAO,QAAQ,SAAS,aAAa,GACvE,MAAM,IAAI,MACT,uKAED;EAKD,MAAM,WAAW,MAAM,eACtB,OAAO,OACP,OAAO,QACP,OAAO,SACP,OAAO,SACR;EAEA,IAAI,UAAU;GACb,OAAO,IAAI,KACV,8CAA8C,SAAS,GAAG,EAC3D;GAEA,OAAO;IACN,IAAI,GAAG,OAAO,QAAQ,GAAG,OAAO;IAChC,MAAM,EAAE,GAAG,OAAO;GACnB;EACD;EAEA,MAAM,WAAW,MAAM,MACtB,GAAG,eAAe,qBAAqB,OAAO,QAAQ,sBAAsB,OAAO,UACnF;GACC,QAAQ;GACR,SAAS;IACR,eAAe,UAAU,OAAO;IAChC,gBAAgB;GACjB;GACA,MAAM,KAAK,UAAU;IACpB,WAAW,OAAO;IAClB,oBAAoB,OAAO;IAC3B,sBAAsB,OAAO;GAC9B,CAAC;EACF,CACD;EAEA,IAAI,CAAC,SAAS,IACb,MAAM,IAAI,MACT,sCAAsC,SAAS,OAAO,KAAK,MAAM,SAAS,KAAK,GAChF;EAGD,OAAO;GACN,IAAI,GAAG,OAAO,QAAQ,GAAG,OAAO;GAChC,MAAM,EAAE,GAAG,OAAO;EACnB;CACD;CAEA,MAAM,KACL,IACA,OACqC;EAGrC,OAAO;GAAE;GAAI;EAAM;CACpB;CAEA,MAAM,SAAwB;EAC7B,OAAO,IAAI,KACV,yIACD;CACD;CAEA,MAAM,KACL,KACA,MACA,MACqC;EACrC,MAAM,WAAqB,CAAC;EAE5B,IAAI,KAAK,WAAW,KAAK,QACxB,SAAS,KAAK,QAAQ;EAGvB,IAAI,KAAK,YAAY,KAAK,SACzB,SAAS,KAAK,SAAS;EAGxB,IAAI,KAAK,cAAc,KAAK,WAC3B,SAAS,KAAK,WAAW;EAK1B,IAAI,KAAK,UAAU,KAAK,OAAO,MAAM,KAAK,UAAU,KAAK,OAAO,GAC/D,SAAS,KAAK,SAAS;EAGxB,IAAI,KAAK,yBAAyB,KAAK,sBACtC,SAAS,KAAK,sBAAsB;EAGrC,OAAO;GACN,SAAS,SAAS,SAAS;GAC3B;GACA,qBAAqB;EACtB;CACD;AACD;;AAGA,IAAM,mCAAN,cAA+C,OAAO,QAAQ,SAAS;CACtE,YACC,MACA,MAQA,MACC;EACD,MAAM,IAAI,iCAAiC,GAAG,MAAM,EAAE,GAAG,KAAK,GAAG,IAAI;CACtE;AACD;;;;;;;;;;;;;;;;;;;;;;;;;;AA+DA,IAAa,2BAAb,cAA8C,OAAO,kBAAkB;CACtE,YACC,MACA,MACA,MACC;EACD,MAAM,EAAE,UAAU,GAAG,eAAe;EAEpC,MAAM,wCAAwC,MAAM,CAAC,GAAG,UAAU;EAElE,IAAI,iCACH,GAAG,KAAK,YACR;GACC,OAAO,SAAS;GAChB,QAAQ,SAAS;GACjB,SAAS,KAAK;GACd,WAAW,KAAK;GAChB,SAAS,KAAK;GACd,sBAAsB,KAAK,wBAAwB;EACpD,GACA,EAAE,QAAQ,KAAK,CAChB;EAEA,KAAK,gBAAgB,CAAC,CAAC;CACxB;AACD"}
|