@infracraft/pulumi 1.30.0 → 1.30.1

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.
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require_chunk = require('../chunk-BVYJZCqc.cjs');
3
- const require_dynamic_resolve_credential = require('../dynamic/resolve-credential.cjs');
4
3
  const require_errors_api_not_found_error = require('../errors/api-not-found-error.cjs');
4
+ const require_dynamic_resolve_credential = require('../dynamic/resolve-credential.cjs');
5
5
  const require_fly_client = require('./client.cjs');
6
6
  let _pulumi_pulumi = require("@pulumi/pulumi");
7
7
  _pulumi_pulumi = require_chunk.__toESM(_pulumi_pulumi, 1);
@@ -1,6 +1,6 @@
1
1
  import { t as __name } from "../chunk-OPjESj5l.mjs";
2
- import { resolveCredential } from "../dynamic/resolve-credential.mjs";
3
2
  import { ApiNotFoundError } from "../errors/api-not-found-error.mjs";
3
+ import { resolveCredential } from "../dynamic/resolve-credential.mjs";
4
4
  import { FlyClient } from "./client.mjs";
5
5
  import * as pulumi from "@pulumi/pulumi";
6
6
 
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require_chunk = require('../chunk-BVYJZCqc.cjs');
3
- const require_dynamic_resolve_credential = require('../dynamic/resolve-credential.cjs');
4
3
  const require_errors_api_not_found_error = require('../errors/api-not-found-error.cjs');
4
+ const require_dynamic_resolve_credential = require('../dynamic/resolve-credential.cjs');
5
5
  const require_fly_client = require('./client.cjs');
6
6
  let _pulumi_pulumi = require("@pulumi/pulumi");
7
7
  _pulumi_pulumi = require_chunk.__toESM(_pulumi_pulumi, 1);
@@ -1,6 +1,6 @@
1
1
  import { t as __name } from "../chunk-OPjESj5l.mjs";
2
- import { resolveCredential } from "../dynamic/resolve-credential.mjs";
3
2
  import { ApiNotFoundError } from "../errors/api-not-found-error.mjs";
3
+ import { resolveCredential } from "../dynamic/resolve-credential.mjs";
4
4
  import { FlyClient } from "./client.mjs";
5
5
  import * as pulumi from "@pulumi/pulumi";
6
6
 
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require_chunk = require('../chunk-BVYJZCqc.cjs');
3
- const require_dynamic_resolve_credential = require('../dynamic/resolve-credential.cjs');
4
3
  const require_errors_api_not_found_error = require('../errors/api-not-found-error.cjs');
4
+ const require_dynamic_resolve_credential = require('../dynamic/resolve-credential.cjs');
5
5
  const require_fly_client = require('./client.cjs');
6
6
  let _pulumi_pulumi = require("@pulumi/pulumi");
7
7
  _pulumi_pulumi = require_chunk.__toESM(_pulumi_pulumi, 1);
@@ -1,6 +1,6 @@
1
1
  import { t as __name } from "../chunk-OPjESj5l.mjs";
2
- import { resolveCredential } from "../dynamic/resolve-credential.mjs";
3
2
  import { ApiNotFoundError } from "../errors/api-not-found-error.mjs";
3
+ import { resolveCredential } from "../dynamic/resolve-credential.mjs";
4
4
  import { FlyClient } from "./client.mjs";
5
5
  import * as pulumi from "@pulumi/pulumi";
6
6
 
@@ -1,8 +1,8 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require_chunk = require('../chunk-BVYJZCqc.cjs');
3
+ const require_errors_api_not_found_error = require('../errors/api-not-found-error.cjs');
3
4
  const require_dynamic_is_resolved_string = require('../dynamic/is-resolved-string.cjs');
4
5
  const require_dynamic_resolve_credential = require('../dynamic/resolve-credential.cjs');
5
- const require_errors_api_not_found_error = require('../errors/api-not-found-error.cjs');
6
6
  const require_neon_client = require('./client.cjs');
7
7
  let _pulumi_pulumi = require("@pulumi/pulumi");
8
8
  _pulumi_pulumi = require_chunk.__toESM(_pulumi_pulumi, 1);
@@ -1,7 +1,7 @@
1
1
  import { t as __name } from "../chunk-OPjESj5l.mjs";
2
+ import { ApiNotFoundError } from "../errors/api-not-found-error.mjs";
2
3
  import { isResolvedString } from "../dynamic/is-resolved-string.mjs";
3
4
  import { resolveCredential } from "../dynamic/resolve-credential.mjs";
4
- import { ApiNotFoundError } from "../errors/api-not-found-error.mjs";
5
5
  import { NeonClient } from "./client.mjs";
6
6
  import * as pulumi from "@pulumi/pulumi";
7
7
 
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require_chunk = require('../chunk-BVYJZCqc.cjs');
3
- const require_dynamic_resolve_credential = require('../dynamic/resolve-credential.cjs');
4
3
  const require_errors_api_not_found_error = require('../errors/api-not-found-error.cjs');
4
+ const require_dynamic_resolve_credential = require('../dynamic/resolve-credential.cjs');
5
5
  const require_neon_client = require('./client.cjs');
6
6
  let _pulumi_pulumi = require("@pulumi/pulumi");
7
7
  _pulumi_pulumi = require_chunk.__toESM(_pulumi_pulumi, 1);
@@ -1,6 +1,6 @@
1
1
  import { t as __name } from "../chunk-OPjESj5l.mjs";
2
- import { resolveCredential } from "../dynamic/resolve-credential.mjs";
3
2
  import { ApiNotFoundError } from "../errors/api-not-found-error.mjs";
3
+ import { resolveCredential } from "../dynamic/resolve-credential.mjs";
4
4
  import { NeonClient } from "./client.mjs";
5
5
  import * as pulumi from "@pulumi/pulumi";
6
6
 
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require_chunk = require('../chunk-BVYJZCqc.cjs');
3
- const require_dynamic_resolve_credential = require('../dynamic/resolve-credential.cjs');
4
3
  const require_errors_api_not_found_error = require('../errors/api-not-found-error.cjs');
4
+ const require_dynamic_resolve_credential = require('../dynamic/resolve-credential.cjs');
5
5
  const require_neon_client = require('./client.cjs');
6
6
  let _pulumi_pulumi = require("@pulumi/pulumi");
7
7
  _pulumi_pulumi = require_chunk.__toESM(_pulumi_pulumi, 1);
@@ -1,6 +1,6 @@
1
1
  import { t as __name } from "../chunk-OPjESj5l.mjs";
2
- import { resolveCredential } from "../dynamic/resolve-credential.mjs";
3
2
  import { ApiNotFoundError } from "../errors/api-not-found-error.mjs";
3
+ import { resolveCredential } from "../dynamic/resolve-credential.mjs";
4
4
  import { NeonClient } from "./client.mjs";
5
5
  import * as pulumi from "@pulumi/pulumi";
6
6
 
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require_chunk = require('../chunk-BVYJZCqc.cjs');
3
- const require_dynamic_resolve_credential = require('../dynamic/resolve-credential.cjs');
4
3
  const require_errors_api_not_found_error = require('../errors/api-not-found-error.cjs');
4
+ const require_dynamic_resolve_credential = require('../dynamic/resolve-credential.cjs');
5
5
  const require_neon_client = require('./client.cjs');
6
6
  let _pulumi_pulumi = require("@pulumi/pulumi");
7
7
  _pulumi_pulumi = require_chunk.__toESM(_pulumi_pulumi, 1);
@@ -1,6 +1,6 @@
1
1
  import { t as __name } from "../chunk-OPjESj5l.mjs";
2
- import { resolveCredential } from "../dynamic/resolve-credential.mjs";
3
2
  import { ApiNotFoundError } from "../errors/api-not-found-error.mjs";
3
+ import { resolveCredential } from "../dynamic/resolve-credential.mjs";
4
4
  import { NeonClient } from "./client.mjs";
5
5
  import * as pulumi from "@pulumi/pulumi";
6
6
 
@@ -1,8 +1,8 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require_chunk = require('../chunk-BVYJZCqc.cjs');
3
+ const require_errors_api_not_found_error = require('../errors/api-not-found-error.cjs');
3
4
  const require_dynamic_is_resolved_string = require('../dynamic/is-resolved-string.cjs');
4
5
  const require_dynamic_resolve_credential = require('../dynamic/resolve-credential.cjs');
5
- const require_errors_api_not_found_error = require('../errors/api-not-found-error.cjs');
6
6
  const require_neon_client = require('./client.cjs');
7
7
  let _pulumi_pulumi = require("@pulumi/pulumi");
8
8
  _pulumi_pulumi = require_chunk.__toESM(_pulumi_pulumi, 1);
@@ -1,7 +1,7 @@
1
1
  import { t as __name } from "../chunk-OPjESj5l.mjs";
2
+ import { ApiNotFoundError } from "../errors/api-not-found-error.mjs";
2
3
  import { isResolvedString } from "../dynamic/is-resolved-string.mjs";
3
4
  import { resolveCredential } from "../dynamic/resolve-credential.mjs";
4
- import { ApiNotFoundError } from "../errors/api-not-found-error.mjs";
5
5
  import { NeonClient } from "./client.mjs";
6
6
  import * as pulumi from "@pulumi/pulumi";
7
7
 
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require_chunk = require('../chunk-BVYJZCqc.cjs');
3
- const require_dynamic_resolve_credential = require('../dynamic/resolve-credential.cjs');
4
3
  const require_errors_api_not_found_error = require('../errors/api-not-found-error.cjs');
4
+ const require_dynamic_resolve_credential = require('../dynamic/resolve-credential.cjs');
5
5
  const require_vercel_client = require('./client.cjs');
6
6
  let _pulumi_pulumi = require("@pulumi/pulumi");
7
7
  _pulumi_pulumi = require_chunk.__toESM(_pulumi_pulumi, 1);
@@ -1,6 +1,6 @@
1
1
  import { t as __name } from "../chunk-OPjESj5l.mjs";
2
- import { resolveCredential } from "../dynamic/resolve-credential.mjs";
3
2
  import { ApiNotFoundError } from "../errors/api-not-found-error.mjs";
3
+ import { resolveCredential } from "../dynamic/resolve-credential.mjs";
4
4
  import { VercelClient } from "./client.mjs";
5
5
  import * as pulumi from "@pulumi/pulumi";
6
6
 
@@ -1,8 +1,8 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require_chunk = require('../chunk-BVYJZCqc.cjs');
3
+ const require_errors_api_not_found_error = require('../errors/api-not-found-error.cjs');
3
4
  const require_dynamic_is_resolved_string = require('../dynamic/is-resolved-string.cjs');
4
5
  const require_dynamic_resolve_credential = require('../dynamic/resolve-credential.cjs');
5
- const require_errors_api_not_found_error = require('../errors/api-not-found-error.cjs');
6
6
  const require_vercel_client = require('./client.cjs');
7
7
  let _pulumi_pulumi = require("@pulumi/pulumi");
8
8
  _pulumi_pulumi = require_chunk.__toESM(_pulumi_pulumi, 1);
@@ -1,7 +1,7 @@
1
1
  import { t as __name } from "../chunk-OPjESj5l.mjs";
2
+ import { ApiNotFoundError } from "../errors/api-not-found-error.mjs";
2
3
  import { isResolvedString } from "../dynamic/is-resolved-string.mjs";
3
4
  import { resolveCredential, resolveCredentialOutput } from "../dynamic/resolve-credential.mjs";
4
- import { ApiNotFoundError } from "../errors/api-not-found-error.mjs";
5
5
  import { VercelClient } from "./client.mjs";
6
6
  import * as pulumi from "@pulumi/pulumi";
7
7
 
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require_chunk = require('../chunk-BVYJZCqc.cjs');
3
- const require_dynamic_resolve_credential = require('../dynamic/resolve-credential.cjs');
4
3
  const require_errors_api_not_found_error = require('../errors/api-not-found-error.cjs');
4
+ const require_dynamic_resolve_credential = require('../dynamic/resolve-credential.cjs');
5
5
  const require_vercel_client = require('./client.cjs');
6
6
  let _pulumi_pulumi = require("@pulumi/pulumi");
7
7
  _pulumi_pulumi = require_chunk.__toESM(_pulumi_pulumi, 1);
@@ -164,11 +164,7 @@ var VercelVariableResourceProvider = class {
164
164
  /** Internal dynamic resource — not part of the public API. */
165
165
  var VercelVariableResource = class extends _pulumi_pulumi.dynamic.Resource {
166
166
  constructor(name, args, opts) {
167
- super(new VercelVariableResourceProvider(), name, {
168
- ...args,
169
- envIds: void 0,
170
- contentHash: void 0
171
- }, {
167
+ super(new VercelVariableResourceProvider(), name, args, {
172
168
  ...opts,
173
169
  additionalSecretOutputs: ["token"]
174
170
  });
@@ -193,15 +189,14 @@ var VercelVariable = class extends _pulumi_pulumi.ComponentResource {
193
189
  super("infracraft:vercel:Variable", name, {}, pulumiOpts);
194
190
  const projectId = project ? project.id : args.projectId;
195
191
  if (!projectId) throw new Error("VercelVariable: either `args.projectId` or `opts.project` must be provided");
196
- const resource = new VercelVariableResource(`${name}-resource`, {
192
+ new VercelVariableResource(`${name}-resource`, {
197
193
  token: provider.token,
198
194
  tokenEnvVar: provider.tokenEnvVar,
199
195
  teamId: provider.teamId,
200
196
  projectId,
201
197
  variables: args.variables
202
198
  }, { parent: this });
203
- this.contentHash = resource.contentHash;
204
- this.registerOutputs({ contentHash: this.contentHash });
199
+ this.registerOutputs({});
205
200
  }
206
201
  };
207
202
 
@@ -1 +1 @@
1
- {"version":3,"file":"variable.cjs","names":["ApiNotFoundError","VercelClient","resolveCredential","pulumi"],"sources":["../../src/vercel/variable.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\nimport { resolveCredential } from \"../dynamic/resolve-credential\";\nimport { ApiNotFoundError } from \"../errors/api-not-found-error\";\nimport { VercelClient } from \"./client\";\nimport type { VercelProject } from \"./project\";\nimport type { VercelProvider } from \"./provider\";\n\n/** Resolved inputs for the Vercel variable dynamic provider. */\ninterface VercelVariableInputs {\n\t/** Vercel API bearer token. Absent when `tokenEnvVar` is used instead. */\n\ttoken?: string;\n\n\t/** Env var name resolved to the token when `token` is absent (see `VercelProviderArgs.tokenEnvVar`). */\n\ttokenEnvVar?: string;\n\n\t/** Vercel team/org ID. */\n\tteamId: string;\n\n\t/** Vercel project ID. */\n\tprojectId: string;\n\n\t/** Key-value map of environment variable names to their values. */\n\tvariables: Record<string, string>;\n}\n\n/** Persisted state for Vercel variables. */\ninterface VercelVariableOutputs extends VercelVariableInputs {\n\t/** Map of env var keys to their Vercel-assigned IDs (for updates and deletes). */\n\tenvIds: Record<string, string>;\n\n\t/** SHA-256 hash of all key-value pairs. Changes when any value is modified (including drift fixes). */\n\tcontentHash: string;\n}\n\n/**\n * Computes a deterministic content hash of a variables map.\n */\nasync function hashVariables(\n\tvariables: Record<string, string>,\n): Promise<string> {\n\tconst crypto = await import(\"node:crypto\");\n\tconst hash = crypto.createHash(\"sha256\");\n\n\tconst sorted = Object.entries(variables).sort(([a], [b]) =>\n\t\ta.localeCompare(b),\n\t);\n\n\tfor (const [key, value] of sorted) {\n\t\thash.update(key);\n\t\thash.update(value);\n\t}\n\n\treturn hash.digest(\"hex\");\n}\n\n/** Vercel API response for a single env var. */\ninterface VercelEnvVar {\n\tid: string;\n\tkey: string;\n\tvalue: string;\n\ttype: string;\n\ttarget: string[];\n}\n\n/**\n * Fetches all environment variables for a Vercel project with decrypted values.\n * A variable that vanishes between the list and its decrypt read is skipped.\n */\nasync function fetchEnvVars(\n\tclient: VercelClient,\n\tprojectId: string,\n): Promise<VercelEnvVar[]> {\n\tconst list = await client.get<{ envs: VercelEnvVar[] }>(\n\t\t`/v9/projects/${projectId}/env`,\n\t);\n\n\tconst decrypted: VercelEnvVar[] = [];\n\n\tfor (const env of list.envs) {\n\t\tconst decryptedVar = await client.tryGet<VercelEnvVar>(\n\t\t\t`/v9/projects/${projectId}/env/${env.id}?decrypt=true`,\n\t\t);\n\n\t\tif (decryptedVar) {\n\t\t\tdecrypted.push(decryptedVar);\n\t\t}\n\t}\n\n\treturn decrypted;\n}\n\n/**\n * Creates a single Vercel env var targeting all environments.\n * An already-existing key (ENV_CONFLICT) is updated in place instead.\n */\nasync function createEnvVar(\n\tclient: VercelClient,\n\tprojectId: string,\n\tkey: string,\n\tvalue: string,\n): Promise<VercelEnvVar> {\n\ttry {\n\t\treturn await client.post<VercelEnvVar>(`/v10/projects/${projectId}/env`, {\n\t\t\tkey,\n\t\t\tvalue,\n\t\t\ttype: \"encrypted\",\n\t\t\ttarget: [\"production\", \"preview\", \"development\"],\n\t\t});\n\t} catch (error) {\n\t\t// ENV_CONFLICT is Vercel's error code for \"key already exists\"; the client\n\t\t// surfaces the API error body in the message, so it is matched there.\n\t\tif (error instanceof Error && error.message.includes(\"ENV_CONFLICT\")) {\n\t\t\tpulumi.log.info(\n\t\t\t\t`Vercel env var \"${key}\" already exists, will update instead`,\n\t\t\t);\n\n\t\t\tconst existing = await fetchEnvVars(client, projectId);\n\t\t\tconst match = existing.find((e) => e.key === key);\n\n\t\t\tif (match) {\n\t\t\t\tawait updateEnvVar(client, projectId, match.id, value);\n\n\t\t\t\treturn { ...match, value };\n\t\t\t}\n\t\t}\n\n\t\tthrow error;\n\t}\n}\n\n/**\n * Updates a single Vercel env var value.\n */\nasync function updateEnvVar(\n\tclient: VercelClient,\n\tprojectId: string,\n\tenvId: string,\n\tvalue: string,\n): Promise<void> {\n\tawait client.patch(`/v9/projects/${projectId}/env/${envId}`, { value });\n}\n\n/**\n * Deletes a single Vercel env var. Tolerates 404 (already gone) —\n * deletion is idempotent.\n */\nasync function deleteEnvVar(\n\tclient: VercelClient,\n\tprojectId: string,\n\tenvId: string,\n): Promise<void> {\n\ttry {\n\t\tawait client.delete(`/v9/projects/${projectId}/env/${envId}`);\n\t} catch (error) {\n\t\tif (!(error instanceof ApiNotFoundError)) {\n\t\t\tthrow error;\n\t\t}\n\t}\n}\n\n/**\n * Dynamic provider implementing CRUD for Vercel project environment variables.\n *\n * @internal Exported only for unit testing; not part of the public API surface.\n */\nexport class VercelVariableResourceProvider\n\timplements pulumi.dynamic.ResourceProvider\n{\n\tasync create(\n\t\tinputs: VercelVariableInputs,\n\t): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst client = new VercelClient(\n\t\t\tresolveCredential(inputs.token, inputs.tokenEnvVar),\n\t\t\tinputs.teamId,\n\t\t);\n\n\t\tconst envIds: Record<string, string> = {};\n\n\t\tfor (const [key, value] of Object.entries(inputs.variables)) {\n\t\t\tconst result = await createEnvVar(client, inputs.projectId, key, value);\n\n\t\t\tenvIds[key] = result.id;\n\t\t}\n\n\t\treturn {\n\t\t\tid: `${inputs.projectId}:variables`,\n\t\t\touts: {\n\t\t\t\t...inputs,\n\t\t\t\tenvIds,\n\t\t\t\tcontentHash: await hashVariables(inputs.variables),\n\t\t\t},\n\t\t};\n\t}\n\n\tasync update(\n\t\t_id: string,\n\t\tolds: VercelVariableOutputs,\n\t\tnews: VercelVariableInputs,\n\t): Promise<pulumi.dynamic.UpdateResult> {\n\t\tconst client = new VercelClient(\n\t\t\tresolveCredential(news.token, news.tokenEnvVar),\n\t\t\tnews.teamId,\n\t\t);\n\n\t\tconst envIds = { ...olds.envIds };\n\n\t\tconst removedKeys = Object.keys(olds.variables).filter(\n\t\t\t(key) => !(key in news.variables),\n\t\t);\n\n\t\tfor (const key of removedKeys) {\n\t\t\tconst envId = envIds[key];\n\n\t\t\tif (envId) {\n\t\t\t\ttry {\n\t\t\t\t\tawait deleteEnvVar(client, news.projectId, envId);\n\t\t\t\t} catch {\n\t\t\t\t\tpulumi.log.warn(\n\t\t\t\t\t\t`Failed to delete Vercel env var \"${key}\" (may already be deleted)`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tdelete envIds[key];\n\t\t\t}\n\t\t}\n\n\t\tfor (const [key, value] of Object.entries(news.variables)) {\n\t\t\tconst existingId = envIds[key];\n\n\t\t\tif (existingId && olds.variables[key] !== value) {\n\t\t\t\tawait updateEnvVar(client, news.projectId, existingId, value);\n\t\t\t} else if (!existingId) {\n\t\t\t\tconst result = await createEnvVar(client, news.projectId, key, value);\n\n\t\t\t\tenvIds[key] = result.id;\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\touts: {\n\t\t\t\t...news,\n\t\t\t\tenvIds,\n\t\t\t\tcontentHash: await hashVariables(news.variables),\n\t\t\t},\n\t\t};\n\t}\n\n\tasync read(\n\t\tid: string,\n\t\tprops: VercelVariableOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\tconst client = new VercelClient(\n\t\t\tresolveCredential(props.token, props.tokenEnvVar),\n\t\t\tprops.teamId,\n\t\t);\n\n\t\tconst actual = await fetchEnvVars(client, props.projectId);\n\n\t\tconst actualVariables: Record<string, string> = {};\n\t\tconst actualEnvIds: Record<string, string> = {};\n\n\t\tfor (const [key] of Object.entries(props.variables)) {\n\t\t\tconst match = actual.find((e) => e.key === key);\n\n\t\t\tif (match) {\n\t\t\t\tactualVariables[key] = match.value;\n\t\t\t\tactualEnvIds[key] = match.id;\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tid,\n\t\t\tprops: {\n\t\t\t\t...props,\n\t\t\t\tvariables: actualVariables,\n\t\t\t\tenvIds: actualEnvIds,\n\t\t\t\tcontentHash: await hashVariables(actualVariables),\n\t\t\t},\n\t\t};\n\t}\n\n\tasync delete(_id: string, props: VercelVariableOutputs): Promise<void> {\n\t\tconst client = new VercelClient(\n\t\t\tresolveCredential(props.token, props.tokenEnvVar),\n\t\t\tprops.teamId,\n\t\t);\n\n\t\tfor (const [key, envId] of Object.entries(props.envIds)) {\n\t\t\ttry {\n\t\t\t\tawait deleteEnvVar(client, props.projectId, envId);\n\t\t\t} catch {\n\t\t\t\tpulumi.log.warn(\n\t\t\t\t\t`Failed to delete Vercel env var \"${key}\" (may already be deleted)`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tasync diff(\n\t\t_id: string,\n\t\tolds: VercelVariableOutputs,\n\t\tnews: VercelVariableInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst oldKeys = Object.keys(olds.variables).sort().join(\",\");\n\t\tconst newKeys = Object.keys(news.variables).sort().join(\",\");\n\n\t\tconst valuesChanged = Object.entries(news.variables).some(\n\t\t\t([key, value]) => olds.variables[key] !== value,\n\t\t);\n\n\t\treturn {\n\t\t\tchanges: oldKeys !== newKeys || valuesChanged,\n\t\t};\n\t}\n}\n\n/** Internal dynamic resource — not part of the public API. */\nclass VercelVariableResource extends pulumi.dynamic.Resource {\n\tpublic declare readonly contentHash: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: {\n\t\t\ttoken?: pulumi.Input<string>;\n\t\t\ttokenEnvVar?: pulumi.Input<string>;\n\t\t\tteamId: pulumi.Input<string>;\n\t\t\tprojectId: pulumi.Input<string>;\n\t\t\tvariables: pulumi.Input<Record<string, pulumi.Input<string>>>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\tsuper(\n\t\t\tnew VercelVariableResourceProvider(),\n\t\t\tname,\n\t\t\t{ ...args, envIds: undefined, contentHash: undefined },\n\t\t\t// The API token flows into dynamic-provider state with the outputs — mark it secret there.\n\t\t\t{ ...opts, additionalSecretOutputs: [\"token\"] },\n\t\t);\n\t}\n}\n\n/** Options type for VercelVariable — replaces Pulumi's native `provider` field. */\ntype VercelVariableOptions = Omit<\n\tpulumi.ComponentResourceOptions,\n\t\"provider\"\n> & {\n\t/** Vercel authentication context. */\n\tprovider: VercelProvider;\n\n\t/**\n\t * VercelProject resource to source the project ID from.\n\t * When provided, `args.projectId` is optional and ignored if both are given.\n\t */\n\tproject?: VercelProject;\n};\n\n/** Args for VercelVariable. */\nexport interface VercelVariableArgs {\n\t/**\n\t * Vercel project ID.\n\t * Required when `opts.project` is not provided.\n\t */\n\tprojectId?: pulumi.Input<string>;\n\n\t/** Key-value map of environment variable names to their values. */\n\tvariables: pulumi.Input<Record<string, pulumi.Input<string>>>;\n}\n\n/**\n * Manages Vercel project environment variables as a batch with drift detection.\n *\n * @example\n * ```typescript\n * new VercelVariable(\"nexus-vars\", {\n * projectId: vercelProject.id,\n * variables: {\n * NEXT_PUBLIC_API_URL: meshUrl,\n * },\n * }, { provider });\n * ```\n */\nexport class VercelVariable extends pulumi.ComponentResource {\n\t/** SHA-256 hash of all key-value pairs. Use as a deploy trigger for drift-aware redeployment. */\n\tpublic readonly contentHash: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: VercelVariableArgs,\n\t\topts: VercelVariableOptions,\n\t) {\n\t\tconst { provider, project, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:vercel:Variable\", name, {}, pulumiOpts);\n\n\t\tconst projectId = project\n\t\t\t? project.id\n\t\t\t: (args.projectId as pulumi.Input<string>);\n\n\t\tif (!projectId) {\n\t\t\tthrow new Error(\n\t\t\t\t\"VercelVariable: either `args.projectId` or `opts.project` must be provided\",\n\t\t\t);\n\t\t}\n\n\t\tconst resource = new VercelVariableResource(\n\t\t\t`${name}-resource`,\n\t\t\t{\n\t\t\t\ttoken: provider.token,\n\t\t\t\ttokenEnvVar: provider.tokenEnvVar,\n\t\t\t\tteamId: provider.teamId,\n\t\t\t\tprojectId,\n\t\t\t\tvariables: args.variables,\n\t\t\t},\n\t\t\t{ parent: this },\n\t\t);\n\n\t\tthis.contentHash = resource.contentHash;\n\n\t\tthis.registerOutputs({ contentHash: this.contentHash });\n\t}\n}\n"],"mappings":";;;;;;;;;;;;AAqCA,eAAe,cACd,WACkB;CAElB,MAAM,QAAO,MADQ,OAAO,gBACR,WAAW,QAAQ;CAEvC,MAAM,SAAS,OAAO,QAAQ,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,OACpD,EAAE,cAAc,CAAC,CAClB;CAEA,KAAK,MAAM,CAAC,KAAK,UAAU,QAAQ;EAClC,KAAK,OAAO,GAAG;EACf,KAAK,OAAO,KAAK;CAClB;CAEA,OAAO,KAAK,OAAO,KAAK;AACzB;;;;;AAeA,eAAe,aACd,QACA,WAC0B;CAC1B,MAAM,OAAO,MAAM,OAAO,IACzB,gBAAgB,UAAU,KAC3B;CAEA,MAAM,YAA4B,CAAC;CAEnC,KAAK,MAAM,OAAO,KAAK,MAAM;EAC5B,MAAM,eAAe,MAAM,OAAO,OACjC,gBAAgB,UAAU,OAAO,IAAI,GAAG,cACzC;EAEA,IAAI,cACH,UAAU,KAAK,YAAY;CAE7B;CAEA,OAAO;AACR;;;;;AAMA,eAAe,aACd,QACA,WACA,KACA,OACwB;CACxB,IAAI;EACH,OAAO,MAAM,OAAO,KAAmB,iBAAiB,UAAU,OAAO;GACxE;GACA;GACA,MAAM;GACN,QAAQ;IAAC;IAAc;IAAW;GAAa;EAChD,CAAC;CACF,SAAS,OAAO;EAGf,IAAI,iBAAiB,SAAS,MAAM,QAAQ,SAAS,cAAc,GAAG;GACrE,eAAO,IAAI,KACV,mBAAmB,IAAI,sCACxB;GAGA,MAAM,SAAQ,MADS,aAAa,QAAQ,SAAS,GAC9B,MAAM,MAAM,EAAE,QAAQ,GAAG;GAEhD,IAAI,OAAO;IACV,MAAM,aAAa,QAAQ,WAAW,MAAM,IAAI,KAAK;IAErD,OAAO;KAAE,GAAG;KAAO;IAAM;GAC1B;EACD;EAEA,MAAM;CACP;AACD;;;;AAKA,eAAe,aACd,QACA,WACA,OACA,OACgB;CAChB,MAAM,OAAO,MAAM,gBAAgB,UAAU,OAAO,SAAS,EAAE,MAAM,CAAC;AACvE;;;;;AAMA,eAAe,aACd,QACA,WACA,OACgB;CAChB,IAAI;EACH,MAAM,OAAO,OAAO,gBAAgB,UAAU,OAAO,OAAO;CAC7D,SAAS,OAAO;EACf,IAAI,EAAE,iBAAiBA,sDACtB,MAAM;CAER;AACD;;;;;;AAOA,IAAa,iCAAb,MAEA;CACC,MAAM,OACL,QACuC;EACvC,MAAM,SAAS,IAAIC,mCAClBC,qDAAkB,OAAO,OAAO,OAAO,WAAW,GAClD,OAAO,MACR;EAEA,MAAM,SAAiC,CAAC;EAExC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,SAAS,GAGzD,OAAO,QAAO,MAFO,aAAa,QAAQ,OAAO,WAAW,KAAK,KAAK,GAEjD;EAGtB,OAAO;GACN,IAAI,GAAG,OAAO,UAAU;GACxB,MAAM;IACL,GAAG;IACH;IACA,aAAa,MAAM,cAAc,OAAO,SAAS;GAClD;EACD;CACD;CAEA,MAAM,OACL,KACA,MACA,MACuC;EACvC,MAAM,SAAS,IAAID,mCAClBC,qDAAkB,KAAK,OAAO,KAAK,WAAW,GAC9C,KAAK,MACN;EAEA,MAAM,SAAS,EAAE,GAAG,KAAK,OAAO;EAEhC,MAAM,cAAc,OAAO,KAAK,KAAK,SAAS,EAAE,QAC9C,QAAQ,EAAE,OAAO,KAAK,UACxB;EAEA,KAAK,MAAM,OAAO,aAAa;GAC9B,MAAM,QAAQ,OAAO;GAErB,IAAI,OAAO;IACV,IAAI;KACH,MAAM,aAAa,QAAQ,KAAK,WAAW,KAAK;IACjD,QAAQ;KACP,eAAO,IAAI,KACV,oCAAoC,IAAI,2BACzC;IACD;IAEA,OAAO,OAAO;GACf;EACD;EAEA,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,SAAS,GAAG;GAC1D,MAAM,aAAa,OAAO;GAE1B,IAAI,cAAc,KAAK,UAAU,SAAS,OACzC,MAAM,aAAa,QAAQ,KAAK,WAAW,YAAY,KAAK;QACtD,IAAI,CAAC,YAGX,OAAO,QAAO,MAFO,aAAa,QAAQ,KAAK,WAAW,KAAK,KAAK,GAE/C;EAEvB;EAEA,OAAO,EACN,MAAM;GACL,GAAG;GACH;GACA,aAAa,MAAM,cAAc,KAAK,SAAS;EAChD,EACD;CACD;CAEA,MAAM,KACL,IACA,OACqC;EAMrC,MAAM,SAAS,MAAM,aAAa,IALfD,mCAClBC,qDAAkB,MAAM,OAAO,MAAM,WAAW,GAChD,MAAM,MAGgC,GAAG,MAAM,SAAS;EAEzD,MAAM,kBAA0C,CAAC;EACjD,MAAM,eAAuC,CAAC;EAE9C,KAAK,MAAM,CAAC,QAAQ,OAAO,QAAQ,MAAM,SAAS,GAAG;GACpD,MAAM,QAAQ,OAAO,MAAM,MAAM,EAAE,QAAQ,GAAG;GAE9C,IAAI,OAAO;IACV,gBAAgB,OAAO,MAAM;IAC7B,aAAa,OAAO,MAAM;GAC3B;EACD;EAEA,OAAO;GACN;GACA,OAAO;IACN,GAAG;IACH,WAAW;IACX,QAAQ;IACR,aAAa,MAAM,cAAc,eAAe;GACjD;EACD;CACD;CAEA,MAAM,OAAO,KAAa,OAA6C;EACtE,MAAM,SAAS,IAAID,mCAClBC,qDAAkB,MAAM,OAAO,MAAM,WAAW,GAChD,MAAM,MACP;EAEA,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,MAAM,GACrD,IAAI;GACH,MAAM,aAAa,QAAQ,MAAM,WAAW,KAAK;EAClD,QAAQ;GACP,eAAO,IAAI,KACV,oCAAoC,IAAI,2BACzC;EACD;CAEF;CAEA,MAAM,KACL,KACA,MACA,MACqC;EACrC,MAAM,UAAU,OAAO,KAAK,KAAK,SAAS,EAAE,KAAK,EAAE,KAAK,GAAG;EAC3D,MAAM,UAAU,OAAO,KAAK,KAAK,SAAS,EAAE,KAAK,EAAE,KAAK,GAAG;EAE3D,MAAM,gBAAgB,OAAO,QAAQ,KAAK,SAAS,EAAE,MACnD,CAAC,KAAK,WAAW,KAAK,UAAU,SAAS,KAC3C;EAEA,OAAO,EACN,SAAS,YAAY,WAAW,cACjC;CACD;AACD;;AAGA,IAAM,yBAAN,cAAqCC,eAAO,QAAQ,SAAS;CAG5D,YACC,MACA,MAOA,MACC;EACD,MACC,IAAI,+BAA+B,GACnC,MACA;GAAE,GAAG;GAAM,QAAQ;GAAW,aAAa;EAAU,GAErD;GAAE,GAAG;GAAM,yBAAyB,CAAC,OAAO;EAAE,CAC/C;CACD;AACD;;;;;;;;;;;;;;AA0CA,IAAa,iBAAb,cAAoCA,eAAO,kBAAkB;CAI5D,YACC,MACA,MACA,MACC;EACD,MAAM,EAAE,UAAU,SAAS,GAAG,eAAe;EAE7C,MAAM,8BAA8B,MAAM,CAAC,GAAG,UAAU;EAExD,MAAM,YAAY,UACf,QAAQ,KACP,KAAK;EAET,IAAI,CAAC,WACJ,MAAM,IAAI,MACT,4EACD;EAGD,MAAM,WAAW,IAAI,uBACpB,GAAG,KAAK,YACR;GACC,OAAO,SAAS;GAChB,aAAa,SAAS;GACtB,QAAQ,SAAS;GACjB;GACA,WAAW,KAAK;EACjB,GACA,EAAE,QAAQ,KAAK,CAChB;EAEA,KAAK,cAAc,SAAS;EAE5B,KAAK,gBAAgB,EAAE,aAAa,KAAK,YAAY,CAAC;CACvD;AACD"}
1
+ {"version":3,"file":"variable.cjs","names":["ApiNotFoundError","VercelClient","resolveCredential","pulumi"],"sources":["../../src/vercel/variable.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\nimport { resolveCredential } from \"../dynamic/resolve-credential\";\nimport { ApiNotFoundError } from \"../errors/api-not-found-error\";\nimport { VercelClient } from \"./client\";\nimport type { VercelProject } from \"./project\";\nimport type { VercelProvider } from \"./provider\";\n\n/** Resolved inputs for the Vercel variable dynamic provider. */\ninterface VercelVariableInputs {\n\t/** Vercel API bearer token. Absent when `tokenEnvVar` is used instead. */\n\ttoken?: string;\n\n\t/** Env var name resolved to the token when `token` is absent (see `VercelProviderArgs.tokenEnvVar`). */\n\ttokenEnvVar?: string;\n\n\t/** Vercel team/org ID. */\n\tteamId: string;\n\n\t/** Vercel project ID. */\n\tprojectId: string;\n\n\t/** Key-value map of environment variable names to their values. */\n\tvariables: Record<string, string>;\n}\n\n/** Persisted state for Vercel variables. */\ninterface VercelVariableOutputs extends VercelVariableInputs {\n\t/** Map of env var keys to their Vercel-assigned IDs (for updates and deletes). */\n\tenvIds: Record<string, string>;\n\n\t/** SHA-256 hash of all key-value pairs. Changes when any value is modified (including drift fixes). */\n\tcontentHash: string;\n}\n\n/**\n * Computes a deterministic content hash of a variables map.\n */\nasync function hashVariables(\n\tvariables: Record<string, string>,\n): Promise<string> {\n\tconst crypto = await import(\"node:crypto\");\n\tconst hash = crypto.createHash(\"sha256\");\n\n\tconst sorted = Object.entries(variables).sort(([a], [b]) =>\n\t\ta.localeCompare(b),\n\t);\n\n\tfor (const [key, value] of sorted) {\n\t\thash.update(key);\n\t\thash.update(value);\n\t}\n\n\treturn hash.digest(\"hex\");\n}\n\n/** Vercel API response for a single env var. */\ninterface VercelEnvVar {\n\tid: string;\n\tkey: string;\n\tvalue: string;\n\ttype: string;\n\ttarget: string[];\n}\n\n/**\n * Fetches all environment variables for a Vercel project with decrypted values.\n * A variable that vanishes between the list and its decrypt read is skipped.\n */\nasync function fetchEnvVars(\n\tclient: VercelClient,\n\tprojectId: string,\n): Promise<VercelEnvVar[]> {\n\tconst list = await client.get<{ envs: VercelEnvVar[] }>(\n\t\t`/v9/projects/${projectId}/env`,\n\t);\n\n\tconst decrypted: VercelEnvVar[] = [];\n\n\tfor (const env of list.envs) {\n\t\tconst decryptedVar = await client.tryGet<VercelEnvVar>(\n\t\t\t`/v9/projects/${projectId}/env/${env.id}?decrypt=true`,\n\t\t);\n\n\t\tif (decryptedVar) {\n\t\t\tdecrypted.push(decryptedVar);\n\t\t}\n\t}\n\n\treturn decrypted;\n}\n\n/**\n * Creates a single Vercel env var targeting all environments.\n * An already-existing key (ENV_CONFLICT) is updated in place instead.\n */\nasync function createEnvVar(\n\tclient: VercelClient,\n\tprojectId: string,\n\tkey: string,\n\tvalue: string,\n): Promise<VercelEnvVar> {\n\ttry {\n\t\treturn await client.post<VercelEnvVar>(`/v10/projects/${projectId}/env`, {\n\t\t\tkey,\n\t\t\tvalue,\n\t\t\ttype: \"encrypted\",\n\t\t\ttarget: [\"production\", \"preview\", \"development\"],\n\t\t});\n\t} catch (error) {\n\t\t// ENV_CONFLICT is Vercel's error code for \"key already exists\"; the client\n\t\t// surfaces the API error body in the message, so it is matched there.\n\t\tif (error instanceof Error && error.message.includes(\"ENV_CONFLICT\")) {\n\t\t\tpulumi.log.info(\n\t\t\t\t`Vercel env var \"${key}\" already exists, will update instead`,\n\t\t\t);\n\n\t\t\tconst existing = await fetchEnvVars(client, projectId);\n\t\t\tconst match = existing.find((e) => e.key === key);\n\n\t\t\tif (match) {\n\t\t\t\tawait updateEnvVar(client, projectId, match.id, value);\n\n\t\t\t\treturn { ...match, value };\n\t\t\t}\n\t\t}\n\n\t\tthrow error;\n\t}\n}\n\n/**\n * Updates a single Vercel env var value.\n */\nasync function updateEnvVar(\n\tclient: VercelClient,\n\tprojectId: string,\n\tenvId: string,\n\tvalue: string,\n): Promise<void> {\n\tawait client.patch(`/v9/projects/${projectId}/env/${envId}`, { value });\n}\n\n/**\n * Deletes a single Vercel env var. Tolerates 404 (already gone) —\n * deletion is idempotent.\n */\nasync function deleteEnvVar(\n\tclient: VercelClient,\n\tprojectId: string,\n\tenvId: string,\n): Promise<void> {\n\ttry {\n\t\tawait client.delete(`/v9/projects/${projectId}/env/${envId}`);\n\t} catch (error) {\n\t\tif (!(error instanceof ApiNotFoundError)) {\n\t\t\tthrow error;\n\t\t}\n\t}\n}\n\n/**\n * Dynamic provider implementing CRUD for Vercel project environment variables.\n *\n * @internal Exported only for unit testing; not part of the public API surface.\n */\nexport class VercelVariableResourceProvider\n\timplements pulumi.dynamic.ResourceProvider\n{\n\tasync create(\n\t\tinputs: VercelVariableInputs,\n\t): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst client = new VercelClient(\n\t\t\tresolveCredential(inputs.token, inputs.tokenEnvVar),\n\t\t\tinputs.teamId,\n\t\t);\n\n\t\tconst envIds: Record<string, string> = {};\n\n\t\tfor (const [key, value] of Object.entries(inputs.variables)) {\n\t\t\tconst result = await createEnvVar(client, inputs.projectId, key, value);\n\n\t\t\tenvIds[key] = result.id;\n\t\t}\n\n\t\treturn {\n\t\t\tid: `${inputs.projectId}:variables`,\n\t\t\touts: {\n\t\t\t\t...inputs,\n\t\t\t\tenvIds,\n\t\t\t\tcontentHash: await hashVariables(inputs.variables),\n\t\t\t},\n\t\t};\n\t}\n\n\tasync update(\n\t\t_id: string,\n\t\tolds: VercelVariableOutputs,\n\t\tnews: VercelVariableInputs,\n\t): Promise<pulumi.dynamic.UpdateResult> {\n\t\tconst client = new VercelClient(\n\t\t\tresolveCredential(news.token, news.tokenEnvVar),\n\t\t\tnews.teamId,\n\t\t);\n\n\t\tconst envIds = { ...olds.envIds };\n\n\t\tconst removedKeys = Object.keys(olds.variables).filter(\n\t\t\t(key) => !(key in news.variables),\n\t\t);\n\n\t\tfor (const key of removedKeys) {\n\t\t\tconst envId = envIds[key];\n\n\t\t\tif (envId) {\n\t\t\t\ttry {\n\t\t\t\t\tawait deleteEnvVar(client, news.projectId, envId);\n\t\t\t\t} catch {\n\t\t\t\t\tpulumi.log.warn(\n\t\t\t\t\t\t`Failed to delete Vercel env var \"${key}\" (may already be deleted)`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tdelete envIds[key];\n\t\t\t}\n\t\t}\n\n\t\tfor (const [key, value] of Object.entries(news.variables)) {\n\t\t\tconst existingId = envIds[key];\n\n\t\t\tif (existingId && olds.variables[key] !== value) {\n\t\t\t\tawait updateEnvVar(client, news.projectId, existingId, value);\n\t\t\t} else if (!existingId) {\n\t\t\t\tconst result = await createEnvVar(client, news.projectId, key, value);\n\n\t\t\t\tenvIds[key] = result.id;\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\touts: {\n\t\t\t\t...news,\n\t\t\t\tenvIds,\n\t\t\t\tcontentHash: await hashVariables(news.variables),\n\t\t\t},\n\t\t};\n\t}\n\n\tasync read(\n\t\tid: string,\n\t\tprops: VercelVariableOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\tconst client = new VercelClient(\n\t\t\tresolveCredential(props.token, props.tokenEnvVar),\n\t\t\tprops.teamId,\n\t\t);\n\n\t\tconst actual = await fetchEnvVars(client, props.projectId);\n\n\t\tconst actualVariables: Record<string, string> = {};\n\t\tconst actualEnvIds: Record<string, string> = {};\n\n\t\tfor (const [key] of Object.entries(props.variables)) {\n\t\t\tconst match = actual.find((e) => e.key === key);\n\n\t\t\tif (match) {\n\t\t\t\tactualVariables[key] = match.value;\n\t\t\t\tactualEnvIds[key] = match.id;\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tid,\n\t\t\tprops: {\n\t\t\t\t...props,\n\t\t\t\tvariables: actualVariables,\n\t\t\t\tenvIds: actualEnvIds,\n\t\t\t\tcontentHash: await hashVariables(actualVariables),\n\t\t\t},\n\t\t};\n\t}\n\n\tasync delete(_id: string, props: VercelVariableOutputs): Promise<void> {\n\t\tconst client = new VercelClient(\n\t\t\tresolveCredential(props.token, props.tokenEnvVar),\n\t\t\tprops.teamId,\n\t\t);\n\n\t\tfor (const [key, envId] of Object.entries(props.envIds)) {\n\t\t\ttry {\n\t\t\t\tawait deleteEnvVar(client, props.projectId, envId);\n\t\t\t} catch {\n\t\t\t\tpulumi.log.warn(\n\t\t\t\t\t`Failed to delete Vercel env var \"${key}\" (may already be deleted)`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tasync diff(\n\t\t_id: string,\n\t\tolds: VercelVariableOutputs,\n\t\tnews: VercelVariableInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst oldKeys = Object.keys(olds.variables).sort().join(\",\");\n\t\tconst newKeys = Object.keys(news.variables).sort().join(\",\");\n\n\t\tconst valuesChanged = Object.entries(news.variables).some(\n\t\t\t([key, value]) => olds.variables[key] !== value,\n\t\t);\n\n\t\treturn {\n\t\t\tchanges: oldKeys !== newKeys || valuesChanged,\n\t\t};\n\t}\n}\n\n/** Internal dynamic resource — not part of the public API. */\nclass VercelVariableResource extends pulumi.dynamic.Resource {\n\tconstructor(\n\t\tname: string,\n\t\targs: {\n\t\t\ttoken?: pulumi.Input<string>;\n\t\t\ttokenEnvVar?: pulumi.Input<string>;\n\t\t\tteamId: pulumi.Input<string>;\n\t\t\tprojectId: pulumi.Input<string>;\n\t\t\tvariables: pulumi.Input<Record<string, pulumi.Input<string>>>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\t// envIds/contentHash are state-only bookkeeping (read back by update());\n\t\t// they are deliberately NOT declared via `<name>: undefined` input\n\t\t// placeholders — undefined placeholders next to an Output-valued map input\n\t\t// deterministically failed engine serialization with \"Unexpected struct\n\t\t// type\" on this resource's create AND update (three from-zero runs,\n\t\t// 2026-07-06), the same class the project-token secret-placeholder\n\t\t// incident documented. Nothing consumes them as Outputs.\n\t\tsuper(\n\t\t\tnew VercelVariableResourceProvider(),\n\t\t\tname,\n\t\t\targs,\n\t\t\t// The API token flows into dynamic-provider state with the outputs — mark it secret there.\n\t\t\t{ ...opts, additionalSecretOutputs: [\"token\"] },\n\t\t);\n\t}\n}\n\n/** Options type for VercelVariable — replaces Pulumi's native `provider` field. */\ntype VercelVariableOptions = Omit<\n\tpulumi.ComponentResourceOptions,\n\t\"provider\"\n> & {\n\t/** Vercel authentication context. */\n\tprovider: VercelProvider;\n\n\t/**\n\t * VercelProject resource to source the project ID from.\n\t * When provided, `args.projectId` is optional and ignored if both are given.\n\t */\n\tproject?: VercelProject;\n};\n\n/** Args for VercelVariable. */\nexport interface VercelVariableArgs {\n\t/**\n\t * Vercel project ID.\n\t * Required when `opts.project` is not provided.\n\t */\n\tprojectId?: pulumi.Input<string>;\n\n\t/** Key-value map of environment variable names to their values. */\n\tvariables: pulumi.Input<Record<string, pulumi.Input<string>>>;\n}\n\n/**\n * Manages Vercel project environment variables as a batch with drift detection.\n *\n * @example\n * ```typescript\n * new VercelVariable(\"nexus-vars\", {\n * projectId: vercelProject.id,\n * variables: {\n * NEXT_PUBLIC_API_URL: meshUrl,\n * },\n * }, { provider });\n * ```\n */\nexport class VercelVariable extends pulumi.ComponentResource {\n\tconstructor(\n\t\tname: string,\n\t\targs: VercelVariableArgs,\n\t\topts: VercelVariableOptions,\n\t) {\n\t\tconst { provider, project, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:vercel:Variable\", name, {}, pulumiOpts);\n\n\t\tconst projectId = project\n\t\t\t? project.id\n\t\t\t: (args.projectId as pulumi.Input<string>);\n\n\t\tif (!projectId) {\n\t\t\tthrow new Error(\n\t\t\t\t\"VercelVariable: either `args.projectId` or `opts.project` must be provided\",\n\t\t\t);\n\t\t}\n\n\t\tnew VercelVariableResource(\n\t\t\t`${name}-resource`,\n\t\t\t{\n\t\t\t\ttoken: provider.token,\n\t\t\t\ttokenEnvVar: provider.tokenEnvVar,\n\t\t\t\tteamId: provider.teamId,\n\t\t\t\tprojectId,\n\t\t\t\tvariables: args.variables,\n\t\t\t},\n\t\t\t{ parent: this },\n\t\t);\n\n\t\tthis.registerOutputs({});\n\t}\n}\n"],"mappings":"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"}
@@ -69,8 +69,6 @@ interface VercelVariableArgs {
69
69
  * ```
70
70
  */
71
71
  declare class VercelVariable extends pulumi.ComponentResource {
72
- /** SHA-256 hash of all key-value pairs. Use as a deploy trigger for drift-aware redeployment. */
73
- readonly contentHash: pulumi.Output<string>;
74
72
  constructor(name: string, args: VercelVariableArgs, opts: VercelVariableOptions);
75
73
  }
76
74
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"variable.d.cts","names":[],"sources":["../../src/vercel/variable.ts"],"mappings":";;;;;;;UAQU,oBAAA;;EAET,KAAA;EAF6B;EAK7B,WAAA;EASiB;EANjB,MAAA;EAHA;EAMA,SAAA;EAAA;EAGA,SAAA,EAAW,MAAM;AAAA;;UAIR,qBAAA,SAA8B,oBAAoB;EAAlD;EAET,MAAA,EAAQ,MAAA;;EAGR,WAAA;AAAA;;;;;AAAW;cAsIC,8BAAA,YACD,MAAA,CAAO,OAAA,CAAQ,gBAAA;EAEpB,MAAA,CACL,MAAA,EAAQ,oBAAA,GACN,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EAwBpB,MAAA,CACL,GAAA,UACA,IAAA,EAAM,qBAAA,EACN,IAAA,EAAM,oBAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EAiDpB,IAAA,CACL,EAAA,UACA,KAAA,EAAO,qBAAA,GACL,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;EA+BpB,MAAA,CAAO,GAAA,UAAa,KAAA,EAAO,qBAAA,GAAwB,OAAA;EAiBnD,IAAA,CACL,GAAA,UACA,IAAA,EAAM,qBAAA,EACN,IAAA,EAAM,oBAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;AAAA;;KAwCtB,qBAAA,GAAwB,IAAA,CAC5B,MAAA,CAAO,wBAAA;EAjJJ,qCAqJH,QAAA,EAAU,cAAA;EAjGC;;;;EAuGX,OAAA,GAAU,aAAA;AAAA;;UAIM,kBAAA;EA/LL;;;;EAoMX,SAAA,GAAY,MAAA,CAAO,KAAA;EApMO;EAuM1B,SAAA,EAAW,MAAA,CAAO,KAAA,CAAM,MAAA,SAAe,MAAA,CAAO,KAAA;AAAA;;;;;;;;;;;;;;cAgBlC,cAAA,SAAuB,MAAA,CAAO,iBAAA;EAvLhB;EAAA,SAyLV,WAAA,EAAa,MAAA,CAAO,MAAA;cAGnC,IAAA,UACA,IAAA,EAAM,kBAAA,EACN,IAAA,EAAM,qBAAA;AAAA"}
1
+ {"version":3,"file":"variable.d.cts","names":[],"sources":["../../src/vercel/variable.ts"],"mappings":";;;;;;;UAQU,oBAAA;;EAET,KAAA;EAF6B;EAK7B,WAAA;EASiB;EANjB,MAAA;EAHA;EAMA,SAAA;EAAA;EAGA,SAAA,EAAW,MAAM;AAAA;;UAIR,qBAAA,SAA8B,oBAAoB;EAAlD;EAET,MAAA,EAAQ,MAAA;;EAGR,WAAA;AAAA;;;;;AAAW;cAsIC,8BAAA,YACD,MAAA,CAAO,OAAA,CAAQ,gBAAA;EAEpB,MAAA,CACL,MAAA,EAAQ,oBAAA,GACN,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EAwBpB,MAAA,CACL,GAAA,UACA,IAAA,EAAM,qBAAA,EACN,IAAA,EAAM,oBAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EAiDpB,IAAA,CACL,EAAA,UACA,KAAA,EAAO,qBAAA,GACL,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;EA+BpB,MAAA,CAAO,GAAA,UAAa,KAAA,EAAO,qBAAA,GAAwB,OAAA;EAiBnD,IAAA,CACL,GAAA,UACA,IAAA,EAAM,qBAAA,EACN,IAAA,EAAM,oBAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;AAAA;;KA6CtB,qBAAA,GAAwB,IAAA,CAC5B,MAAA,CAAO,wBAAA;EAtJJ,qCA0JH,QAAA,EAAU,cAAA;EAtGC;;;;EA4GX,OAAA,GAAU,aAAA;AAAA;;UAIM,kBAAA;EApML;;;;EAyMX,SAAA,GAAY,MAAA,CAAO,KAAA;EAzMO;EA4M1B,SAAA,EAAW,MAAA,CAAO,KAAA,CAAM,MAAA,SAAe,MAAA,CAAO,KAAA;AAAA;;;;;;;;;;;;;;cAgBlC,cAAA,SAAuB,MAAA,CAAO,iBAAA;cAEzC,IAAA,UACA,IAAA,EAAM,kBAAA,EACN,IAAA,EAAM,qBAAA;AAAA"}
@@ -69,8 +69,6 @@ interface VercelVariableArgs {
69
69
  * ```
70
70
  */
71
71
  declare class VercelVariable extends pulumi.ComponentResource {
72
- /** SHA-256 hash of all key-value pairs. Use as a deploy trigger for drift-aware redeployment. */
73
- readonly contentHash: pulumi.Output<string>;
74
72
  constructor(name: string, args: VercelVariableArgs, opts: VercelVariableOptions);
75
73
  }
76
74
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"variable.d.mts","names":[],"sources":["../../src/vercel/variable.ts"],"mappings":";;;;;;;UAQU,oBAAA;;EAET,KAAA;EAF6B;EAK7B,WAAA;EASiB;EANjB,MAAA;EAHA;EAMA,SAAA;EAAA;EAGA,SAAA,EAAW,MAAM;AAAA;;UAIR,qBAAA,SAA8B,oBAAoB;EAAlD;EAET,MAAA,EAAQ,MAAA;;EAGR,WAAA;AAAA;;;;;AAAW;cAsIC,8BAAA,YACD,MAAA,CAAO,OAAA,CAAQ,gBAAA;EAEpB,MAAA,CACL,MAAA,EAAQ,oBAAA,GACN,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EAwBpB,MAAA,CACL,GAAA,UACA,IAAA,EAAM,qBAAA,EACN,IAAA,EAAM,oBAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EAiDpB,IAAA,CACL,EAAA,UACA,KAAA,EAAO,qBAAA,GACL,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;EA+BpB,MAAA,CAAO,GAAA,UAAa,KAAA,EAAO,qBAAA,GAAwB,OAAA;EAiBnD,IAAA,CACL,GAAA,UACA,IAAA,EAAM,qBAAA,EACN,IAAA,EAAM,oBAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;AAAA;;KAwCtB,qBAAA,GAAwB,IAAA,CAC5B,MAAA,CAAO,wBAAA;EAjJJ,qCAqJH,QAAA,EAAU,cAAA;EAjGC;;;;EAuGX,OAAA,GAAU,aAAA;AAAA;;UAIM,kBAAA;EA/LL;;;;EAoMX,SAAA,GAAY,MAAA,CAAO,KAAA;EApMO;EAuM1B,SAAA,EAAW,MAAA,CAAO,KAAA,CAAM,MAAA,SAAe,MAAA,CAAO,KAAA;AAAA;;;;;;;;;;;;;;cAgBlC,cAAA,SAAuB,MAAA,CAAO,iBAAA;EAvLhB;EAAA,SAyLV,WAAA,EAAa,MAAA,CAAO,MAAA;cAGnC,IAAA,UACA,IAAA,EAAM,kBAAA,EACN,IAAA,EAAM,qBAAA;AAAA"}
1
+ {"version":3,"file":"variable.d.mts","names":[],"sources":["../../src/vercel/variable.ts"],"mappings":";;;;;;;UAQU,oBAAA;;EAET,KAAA;EAF6B;EAK7B,WAAA;EASiB;EANjB,MAAA;EAHA;EAMA,SAAA;EAAA;EAGA,SAAA,EAAW,MAAM;AAAA;;UAIR,qBAAA,SAA8B,oBAAoB;EAAlD;EAET,MAAA,EAAQ,MAAA;;EAGR,WAAA;AAAA;;;;;AAAW;cAsIC,8BAAA,YACD,MAAA,CAAO,OAAA,CAAQ,gBAAA;EAEpB,MAAA,CACL,MAAA,EAAQ,oBAAA,GACN,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EAwBpB,MAAA,CACL,GAAA,UACA,IAAA,EAAM,qBAAA,EACN,IAAA,EAAM,oBAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EAiDpB,IAAA,CACL,EAAA,UACA,KAAA,EAAO,qBAAA,GACL,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;EA+BpB,MAAA,CAAO,GAAA,UAAa,KAAA,EAAO,qBAAA,GAAwB,OAAA;EAiBnD,IAAA,CACL,GAAA,UACA,IAAA,EAAM,qBAAA,EACN,IAAA,EAAM,oBAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;AAAA;;KA6CtB,qBAAA,GAAwB,IAAA,CAC5B,MAAA,CAAO,wBAAA;EAtJJ,qCA0JH,QAAA,EAAU,cAAA;EAtGC;;;;EA4GX,OAAA,GAAU,aAAA;AAAA;;UAIM,kBAAA;EApML;;;;EAyMX,SAAA,GAAY,MAAA,CAAO,KAAA;EAzMO;EA4M1B,SAAA,EAAW,MAAA,CAAO,KAAA,CAAM,MAAA,SAAe,MAAA,CAAO,KAAA;AAAA;;;;;;;;;;;;;;cAgBlC,cAAA,SAAuB,MAAA,CAAO,iBAAA;cAEzC,IAAA,UACA,IAAA,EAAM,kBAAA,EACN,IAAA,EAAM,qBAAA;AAAA"}
@@ -1,6 +1,6 @@
1
1
  import { t as __name } from "../chunk-OPjESj5l.mjs";
2
- import { resolveCredential } from "../dynamic/resolve-credential.mjs";
3
2
  import { ApiNotFoundError } from "../errors/api-not-found-error.mjs";
3
+ import { resolveCredential } from "../dynamic/resolve-credential.mjs";
4
4
  import { VercelClient } from "./client.mjs";
5
5
  import * as pulumi from "@pulumi/pulumi";
6
6
 
@@ -162,11 +162,7 @@ var VercelVariableResourceProvider = class {
162
162
  /** Internal dynamic resource — not part of the public API. */
163
163
  var VercelVariableResource = class extends pulumi.dynamic.Resource {
164
164
  constructor(name, args, opts) {
165
- super(new VercelVariableResourceProvider(), name, {
166
- ...args,
167
- envIds: void 0,
168
- contentHash: void 0
169
- }, {
165
+ super(new VercelVariableResourceProvider(), name, args, {
170
166
  ...opts,
171
167
  additionalSecretOutputs: ["token"]
172
168
  });
@@ -191,15 +187,14 @@ var VercelVariable = class extends pulumi.ComponentResource {
191
187
  super("infracraft:vercel:Variable", name, {}, pulumiOpts);
192
188
  const projectId = project ? project.id : args.projectId;
193
189
  if (!projectId) throw new Error("VercelVariable: either `args.projectId` or `opts.project` must be provided");
194
- const resource = new VercelVariableResource(`${name}-resource`, {
190
+ new VercelVariableResource(`${name}-resource`, {
195
191
  token: provider.token,
196
192
  tokenEnvVar: provider.tokenEnvVar,
197
193
  teamId: provider.teamId,
198
194
  projectId,
199
195
  variables: args.variables
200
196
  }, { parent: this });
201
- this.contentHash = resource.contentHash;
202
- this.registerOutputs({ contentHash: this.contentHash });
197
+ this.registerOutputs({});
203
198
  }
204
199
  };
205
200
 
@@ -1 +1 @@
1
- {"version":3,"file":"variable.mjs","names":[],"sources":["../../src/vercel/variable.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\nimport { resolveCredential } from \"../dynamic/resolve-credential\";\nimport { ApiNotFoundError } from \"../errors/api-not-found-error\";\nimport { VercelClient } from \"./client\";\nimport type { VercelProject } from \"./project\";\nimport type { VercelProvider } from \"./provider\";\n\n/** Resolved inputs for the Vercel variable dynamic provider. */\ninterface VercelVariableInputs {\n\t/** Vercel API bearer token. Absent when `tokenEnvVar` is used instead. */\n\ttoken?: string;\n\n\t/** Env var name resolved to the token when `token` is absent (see `VercelProviderArgs.tokenEnvVar`). */\n\ttokenEnvVar?: string;\n\n\t/** Vercel team/org ID. */\n\tteamId: string;\n\n\t/** Vercel project ID. */\n\tprojectId: string;\n\n\t/** Key-value map of environment variable names to their values. */\n\tvariables: Record<string, string>;\n}\n\n/** Persisted state for Vercel variables. */\ninterface VercelVariableOutputs extends VercelVariableInputs {\n\t/** Map of env var keys to their Vercel-assigned IDs (for updates and deletes). */\n\tenvIds: Record<string, string>;\n\n\t/** SHA-256 hash of all key-value pairs. Changes when any value is modified (including drift fixes). */\n\tcontentHash: string;\n}\n\n/**\n * Computes a deterministic content hash of a variables map.\n */\nasync function hashVariables(\n\tvariables: Record<string, string>,\n): Promise<string> {\n\tconst crypto = await import(\"node:crypto\");\n\tconst hash = crypto.createHash(\"sha256\");\n\n\tconst sorted = Object.entries(variables).sort(([a], [b]) =>\n\t\ta.localeCompare(b),\n\t);\n\n\tfor (const [key, value] of sorted) {\n\t\thash.update(key);\n\t\thash.update(value);\n\t}\n\n\treturn hash.digest(\"hex\");\n}\n\n/** Vercel API response for a single env var. */\ninterface VercelEnvVar {\n\tid: string;\n\tkey: string;\n\tvalue: string;\n\ttype: string;\n\ttarget: string[];\n}\n\n/**\n * Fetches all environment variables for a Vercel project with decrypted values.\n * A variable that vanishes between the list and its decrypt read is skipped.\n */\nasync function fetchEnvVars(\n\tclient: VercelClient,\n\tprojectId: string,\n): Promise<VercelEnvVar[]> {\n\tconst list = await client.get<{ envs: VercelEnvVar[] }>(\n\t\t`/v9/projects/${projectId}/env`,\n\t);\n\n\tconst decrypted: VercelEnvVar[] = [];\n\n\tfor (const env of list.envs) {\n\t\tconst decryptedVar = await client.tryGet<VercelEnvVar>(\n\t\t\t`/v9/projects/${projectId}/env/${env.id}?decrypt=true`,\n\t\t);\n\n\t\tif (decryptedVar) {\n\t\t\tdecrypted.push(decryptedVar);\n\t\t}\n\t}\n\n\treturn decrypted;\n}\n\n/**\n * Creates a single Vercel env var targeting all environments.\n * An already-existing key (ENV_CONFLICT) is updated in place instead.\n */\nasync function createEnvVar(\n\tclient: VercelClient,\n\tprojectId: string,\n\tkey: string,\n\tvalue: string,\n): Promise<VercelEnvVar> {\n\ttry {\n\t\treturn await client.post<VercelEnvVar>(`/v10/projects/${projectId}/env`, {\n\t\t\tkey,\n\t\t\tvalue,\n\t\t\ttype: \"encrypted\",\n\t\t\ttarget: [\"production\", \"preview\", \"development\"],\n\t\t});\n\t} catch (error) {\n\t\t// ENV_CONFLICT is Vercel's error code for \"key already exists\"; the client\n\t\t// surfaces the API error body in the message, so it is matched there.\n\t\tif (error instanceof Error && error.message.includes(\"ENV_CONFLICT\")) {\n\t\t\tpulumi.log.info(\n\t\t\t\t`Vercel env var \"${key}\" already exists, will update instead`,\n\t\t\t);\n\n\t\t\tconst existing = await fetchEnvVars(client, projectId);\n\t\t\tconst match = existing.find((e) => e.key === key);\n\n\t\t\tif (match) {\n\t\t\t\tawait updateEnvVar(client, projectId, match.id, value);\n\n\t\t\t\treturn { ...match, value };\n\t\t\t}\n\t\t}\n\n\t\tthrow error;\n\t}\n}\n\n/**\n * Updates a single Vercel env var value.\n */\nasync function updateEnvVar(\n\tclient: VercelClient,\n\tprojectId: string,\n\tenvId: string,\n\tvalue: string,\n): Promise<void> {\n\tawait client.patch(`/v9/projects/${projectId}/env/${envId}`, { value });\n}\n\n/**\n * Deletes a single Vercel env var. Tolerates 404 (already gone) —\n * deletion is idempotent.\n */\nasync function deleteEnvVar(\n\tclient: VercelClient,\n\tprojectId: string,\n\tenvId: string,\n): Promise<void> {\n\ttry {\n\t\tawait client.delete(`/v9/projects/${projectId}/env/${envId}`);\n\t} catch (error) {\n\t\tif (!(error instanceof ApiNotFoundError)) {\n\t\t\tthrow error;\n\t\t}\n\t}\n}\n\n/**\n * Dynamic provider implementing CRUD for Vercel project environment variables.\n *\n * @internal Exported only for unit testing; not part of the public API surface.\n */\nexport class VercelVariableResourceProvider\n\timplements pulumi.dynamic.ResourceProvider\n{\n\tasync create(\n\t\tinputs: VercelVariableInputs,\n\t): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst client = new VercelClient(\n\t\t\tresolveCredential(inputs.token, inputs.tokenEnvVar),\n\t\t\tinputs.teamId,\n\t\t);\n\n\t\tconst envIds: Record<string, string> = {};\n\n\t\tfor (const [key, value] of Object.entries(inputs.variables)) {\n\t\t\tconst result = await createEnvVar(client, inputs.projectId, key, value);\n\n\t\t\tenvIds[key] = result.id;\n\t\t}\n\n\t\treturn {\n\t\t\tid: `${inputs.projectId}:variables`,\n\t\t\touts: {\n\t\t\t\t...inputs,\n\t\t\t\tenvIds,\n\t\t\t\tcontentHash: await hashVariables(inputs.variables),\n\t\t\t},\n\t\t};\n\t}\n\n\tasync update(\n\t\t_id: string,\n\t\tolds: VercelVariableOutputs,\n\t\tnews: VercelVariableInputs,\n\t): Promise<pulumi.dynamic.UpdateResult> {\n\t\tconst client = new VercelClient(\n\t\t\tresolveCredential(news.token, news.tokenEnvVar),\n\t\t\tnews.teamId,\n\t\t);\n\n\t\tconst envIds = { ...olds.envIds };\n\n\t\tconst removedKeys = Object.keys(olds.variables).filter(\n\t\t\t(key) => !(key in news.variables),\n\t\t);\n\n\t\tfor (const key of removedKeys) {\n\t\t\tconst envId = envIds[key];\n\n\t\t\tif (envId) {\n\t\t\t\ttry {\n\t\t\t\t\tawait deleteEnvVar(client, news.projectId, envId);\n\t\t\t\t} catch {\n\t\t\t\t\tpulumi.log.warn(\n\t\t\t\t\t\t`Failed to delete Vercel env var \"${key}\" (may already be deleted)`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tdelete envIds[key];\n\t\t\t}\n\t\t}\n\n\t\tfor (const [key, value] of Object.entries(news.variables)) {\n\t\t\tconst existingId = envIds[key];\n\n\t\t\tif (existingId && olds.variables[key] !== value) {\n\t\t\t\tawait updateEnvVar(client, news.projectId, existingId, value);\n\t\t\t} else if (!existingId) {\n\t\t\t\tconst result = await createEnvVar(client, news.projectId, key, value);\n\n\t\t\t\tenvIds[key] = result.id;\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\touts: {\n\t\t\t\t...news,\n\t\t\t\tenvIds,\n\t\t\t\tcontentHash: await hashVariables(news.variables),\n\t\t\t},\n\t\t};\n\t}\n\n\tasync read(\n\t\tid: string,\n\t\tprops: VercelVariableOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\tconst client = new VercelClient(\n\t\t\tresolveCredential(props.token, props.tokenEnvVar),\n\t\t\tprops.teamId,\n\t\t);\n\n\t\tconst actual = await fetchEnvVars(client, props.projectId);\n\n\t\tconst actualVariables: Record<string, string> = {};\n\t\tconst actualEnvIds: Record<string, string> = {};\n\n\t\tfor (const [key] of Object.entries(props.variables)) {\n\t\t\tconst match = actual.find((e) => e.key === key);\n\n\t\t\tif (match) {\n\t\t\t\tactualVariables[key] = match.value;\n\t\t\t\tactualEnvIds[key] = match.id;\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tid,\n\t\t\tprops: {\n\t\t\t\t...props,\n\t\t\t\tvariables: actualVariables,\n\t\t\t\tenvIds: actualEnvIds,\n\t\t\t\tcontentHash: await hashVariables(actualVariables),\n\t\t\t},\n\t\t};\n\t}\n\n\tasync delete(_id: string, props: VercelVariableOutputs): Promise<void> {\n\t\tconst client = new VercelClient(\n\t\t\tresolveCredential(props.token, props.tokenEnvVar),\n\t\t\tprops.teamId,\n\t\t);\n\n\t\tfor (const [key, envId] of Object.entries(props.envIds)) {\n\t\t\ttry {\n\t\t\t\tawait deleteEnvVar(client, props.projectId, envId);\n\t\t\t} catch {\n\t\t\t\tpulumi.log.warn(\n\t\t\t\t\t`Failed to delete Vercel env var \"${key}\" (may already be deleted)`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tasync diff(\n\t\t_id: string,\n\t\tolds: VercelVariableOutputs,\n\t\tnews: VercelVariableInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst oldKeys = Object.keys(olds.variables).sort().join(\",\");\n\t\tconst newKeys = Object.keys(news.variables).sort().join(\",\");\n\n\t\tconst valuesChanged = Object.entries(news.variables).some(\n\t\t\t([key, value]) => olds.variables[key] !== value,\n\t\t);\n\n\t\treturn {\n\t\t\tchanges: oldKeys !== newKeys || valuesChanged,\n\t\t};\n\t}\n}\n\n/** Internal dynamic resource — not part of the public API. */\nclass VercelVariableResource extends pulumi.dynamic.Resource {\n\tpublic declare readonly contentHash: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: {\n\t\t\ttoken?: pulumi.Input<string>;\n\t\t\ttokenEnvVar?: pulumi.Input<string>;\n\t\t\tteamId: pulumi.Input<string>;\n\t\t\tprojectId: pulumi.Input<string>;\n\t\t\tvariables: pulumi.Input<Record<string, pulumi.Input<string>>>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\tsuper(\n\t\t\tnew VercelVariableResourceProvider(),\n\t\t\tname,\n\t\t\t{ ...args, envIds: undefined, contentHash: undefined },\n\t\t\t// The API token flows into dynamic-provider state with the outputs — mark it secret there.\n\t\t\t{ ...opts, additionalSecretOutputs: [\"token\"] },\n\t\t);\n\t}\n}\n\n/** Options type for VercelVariable — replaces Pulumi's native `provider` field. */\ntype VercelVariableOptions = Omit<\n\tpulumi.ComponentResourceOptions,\n\t\"provider\"\n> & {\n\t/** Vercel authentication context. */\n\tprovider: VercelProvider;\n\n\t/**\n\t * VercelProject resource to source the project ID from.\n\t * When provided, `args.projectId` is optional and ignored if both are given.\n\t */\n\tproject?: VercelProject;\n};\n\n/** Args for VercelVariable. */\nexport interface VercelVariableArgs {\n\t/**\n\t * Vercel project ID.\n\t * Required when `opts.project` is not provided.\n\t */\n\tprojectId?: pulumi.Input<string>;\n\n\t/** Key-value map of environment variable names to their values. */\n\tvariables: pulumi.Input<Record<string, pulumi.Input<string>>>;\n}\n\n/**\n * Manages Vercel project environment variables as a batch with drift detection.\n *\n * @example\n * ```typescript\n * new VercelVariable(\"nexus-vars\", {\n * projectId: vercelProject.id,\n * variables: {\n * NEXT_PUBLIC_API_URL: meshUrl,\n * },\n * }, { provider });\n * ```\n */\nexport class VercelVariable extends pulumi.ComponentResource {\n\t/** SHA-256 hash of all key-value pairs. Use as a deploy trigger for drift-aware redeployment. */\n\tpublic readonly contentHash: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: VercelVariableArgs,\n\t\topts: VercelVariableOptions,\n\t) {\n\t\tconst { provider, project, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:vercel:Variable\", name, {}, pulumiOpts);\n\n\t\tconst projectId = project\n\t\t\t? project.id\n\t\t\t: (args.projectId as pulumi.Input<string>);\n\n\t\tif (!projectId) {\n\t\t\tthrow new Error(\n\t\t\t\t\"VercelVariable: either `args.projectId` or `opts.project` must be provided\",\n\t\t\t);\n\t\t}\n\n\t\tconst resource = new VercelVariableResource(\n\t\t\t`${name}-resource`,\n\t\t\t{\n\t\t\t\ttoken: provider.token,\n\t\t\t\ttokenEnvVar: provider.tokenEnvVar,\n\t\t\t\tteamId: provider.teamId,\n\t\t\t\tprojectId,\n\t\t\t\tvariables: args.variables,\n\t\t\t},\n\t\t\t{ parent: this },\n\t\t);\n\n\t\tthis.contentHash = resource.contentHash;\n\n\t\tthis.registerOutputs({ contentHash: this.contentHash });\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"variable.mjs","names":[],"sources":["../../src/vercel/variable.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\nimport { resolveCredential } from \"../dynamic/resolve-credential\";\nimport { ApiNotFoundError } from \"../errors/api-not-found-error\";\nimport { VercelClient } from \"./client\";\nimport type { VercelProject } from \"./project\";\nimport type { VercelProvider } from \"./provider\";\n\n/** Resolved inputs for the Vercel variable dynamic provider. */\ninterface VercelVariableInputs {\n\t/** Vercel API bearer token. Absent when `tokenEnvVar` is used instead. */\n\ttoken?: string;\n\n\t/** Env var name resolved to the token when `token` is absent (see `VercelProviderArgs.tokenEnvVar`). */\n\ttokenEnvVar?: string;\n\n\t/** Vercel team/org ID. */\n\tteamId: string;\n\n\t/** Vercel project ID. */\n\tprojectId: string;\n\n\t/** Key-value map of environment variable names to their values. */\n\tvariables: Record<string, string>;\n}\n\n/** Persisted state for Vercel variables. */\ninterface VercelVariableOutputs extends VercelVariableInputs {\n\t/** Map of env var keys to their Vercel-assigned IDs (for updates and deletes). */\n\tenvIds: Record<string, string>;\n\n\t/** SHA-256 hash of all key-value pairs. Changes when any value is modified (including drift fixes). */\n\tcontentHash: string;\n}\n\n/**\n * Computes a deterministic content hash of a variables map.\n */\nasync function hashVariables(\n\tvariables: Record<string, string>,\n): Promise<string> {\n\tconst crypto = await import(\"node:crypto\");\n\tconst hash = crypto.createHash(\"sha256\");\n\n\tconst sorted = Object.entries(variables).sort(([a], [b]) =>\n\t\ta.localeCompare(b),\n\t);\n\n\tfor (const [key, value] of sorted) {\n\t\thash.update(key);\n\t\thash.update(value);\n\t}\n\n\treturn hash.digest(\"hex\");\n}\n\n/** Vercel API response for a single env var. */\ninterface VercelEnvVar {\n\tid: string;\n\tkey: string;\n\tvalue: string;\n\ttype: string;\n\ttarget: string[];\n}\n\n/**\n * Fetches all environment variables for a Vercel project with decrypted values.\n * A variable that vanishes between the list and its decrypt read is skipped.\n */\nasync function fetchEnvVars(\n\tclient: VercelClient,\n\tprojectId: string,\n): Promise<VercelEnvVar[]> {\n\tconst list = await client.get<{ envs: VercelEnvVar[] }>(\n\t\t`/v9/projects/${projectId}/env`,\n\t);\n\n\tconst decrypted: VercelEnvVar[] = [];\n\n\tfor (const env of list.envs) {\n\t\tconst decryptedVar = await client.tryGet<VercelEnvVar>(\n\t\t\t`/v9/projects/${projectId}/env/${env.id}?decrypt=true`,\n\t\t);\n\n\t\tif (decryptedVar) {\n\t\t\tdecrypted.push(decryptedVar);\n\t\t}\n\t}\n\n\treturn decrypted;\n}\n\n/**\n * Creates a single Vercel env var targeting all environments.\n * An already-existing key (ENV_CONFLICT) is updated in place instead.\n */\nasync function createEnvVar(\n\tclient: VercelClient,\n\tprojectId: string,\n\tkey: string,\n\tvalue: string,\n): Promise<VercelEnvVar> {\n\ttry {\n\t\treturn await client.post<VercelEnvVar>(`/v10/projects/${projectId}/env`, {\n\t\t\tkey,\n\t\t\tvalue,\n\t\t\ttype: \"encrypted\",\n\t\t\ttarget: [\"production\", \"preview\", \"development\"],\n\t\t});\n\t} catch (error) {\n\t\t// ENV_CONFLICT is Vercel's error code for \"key already exists\"; the client\n\t\t// surfaces the API error body in the message, so it is matched there.\n\t\tif (error instanceof Error && error.message.includes(\"ENV_CONFLICT\")) {\n\t\t\tpulumi.log.info(\n\t\t\t\t`Vercel env var \"${key}\" already exists, will update instead`,\n\t\t\t);\n\n\t\t\tconst existing = await fetchEnvVars(client, projectId);\n\t\t\tconst match = existing.find((e) => e.key === key);\n\n\t\t\tif (match) {\n\t\t\t\tawait updateEnvVar(client, projectId, match.id, value);\n\n\t\t\t\treturn { ...match, value };\n\t\t\t}\n\t\t}\n\n\t\tthrow error;\n\t}\n}\n\n/**\n * Updates a single Vercel env var value.\n */\nasync function updateEnvVar(\n\tclient: VercelClient,\n\tprojectId: string,\n\tenvId: string,\n\tvalue: string,\n): Promise<void> {\n\tawait client.patch(`/v9/projects/${projectId}/env/${envId}`, { value });\n}\n\n/**\n * Deletes a single Vercel env var. Tolerates 404 (already gone) —\n * deletion is idempotent.\n */\nasync function deleteEnvVar(\n\tclient: VercelClient,\n\tprojectId: string,\n\tenvId: string,\n): Promise<void> {\n\ttry {\n\t\tawait client.delete(`/v9/projects/${projectId}/env/${envId}`);\n\t} catch (error) {\n\t\tif (!(error instanceof ApiNotFoundError)) {\n\t\t\tthrow error;\n\t\t}\n\t}\n}\n\n/**\n * Dynamic provider implementing CRUD for Vercel project environment variables.\n *\n * @internal Exported only for unit testing; not part of the public API surface.\n */\nexport class VercelVariableResourceProvider\n\timplements pulumi.dynamic.ResourceProvider\n{\n\tasync create(\n\t\tinputs: VercelVariableInputs,\n\t): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst client = new VercelClient(\n\t\t\tresolveCredential(inputs.token, inputs.tokenEnvVar),\n\t\t\tinputs.teamId,\n\t\t);\n\n\t\tconst envIds: Record<string, string> = {};\n\n\t\tfor (const [key, value] of Object.entries(inputs.variables)) {\n\t\t\tconst result = await createEnvVar(client, inputs.projectId, key, value);\n\n\t\t\tenvIds[key] = result.id;\n\t\t}\n\n\t\treturn {\n\t\t\tid: `${inputs.projectId}:variables`,\n\t\t\touts: {\n\t\t\t\t...inputs,\n\t\t\t\tenvIds,\n\t\t\t\tcontentHash: await hashVariables(inputs.variables),\n\t\t\t},\n\t\t};\n\t}\n\n\tasync update(\n\t\t_id: string,\n\t\tolds: VercelVariableOutputs,\n\t\tnews: VercelVariableInputs,\n\t): Promise<pulumi.dynamic.UpdateResult> {\n\t\tconst client = new VercelClient(\n\t\t\tresolveCredential(news.token, news.tokenEnvVar),\n\t\t\tnews.teamId,\n\t\t);\n\n\t\tconst envIds = { ...olds.envIds };\n\n\t\tconst removedKeys = Object.keys(olds.variables).filter(\n\t\t\t(key) => !(key in news.variables),\n\t\t);\n\n\t\tfor (const key of removedKeys) {\n\t\t\tconst envId = envIds[key];\n\n\t\t\tif (envId) {\n\t\t\t\ttry {\n\t\t\t\t\tawait deleteEnvVar(client, news.projectId, envId);\n\t\t\t\t} catch {\n\t\t\t\t\tpulumi.log.warn(\n\t\t\t\t\t\t`Failed to delete Vercel env var \"${key}\" (may already be deleted)`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tdelete envIds[key];\n\t\t\t}\n\t\t}\n\n\t\tfor (const [key, value] of Object.entries(news.variables)) {\n\t\t\tconst existingId = envIds[key];\n\n\t\t\tif (existingId && olds.variables[key] !== value) {\n\t\t\t\tawait updateEnvVar(client, news.projectId, existingId, value);\n\t\t\t} else if (!existingId) {\n\t\t\t\tconst result = await createEnvVar(client, news.projectId, key, value);\n\n\t\t\t\tenvIds[key] = result.id;\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\touts: {\n\t\t\t\t...news,\n\t\t\t\tenvIds,\n\t\t\t\tcontentHash: await hashVariables(news.variables),\n\t\t\t},\n\t\t};\n\t}\n\n\tasync read(\n\t\tid: string,\n\t\tprops: VercelVariableOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\tconst client = new VercelClient(\n\t\t\tresolveCredential(props.token, props.tokenEnvVar),\n\t\t\tprops.teamId,\n\t\t);\n\n\t\tconst actual = await fetchEnvVars(client, props.projectId);\n\n\t\tconst actualVariables: Record<string, string> = {};\n\t\tconst actualEnvIds: Record<string, string> = {};\n\n\t\tfor (const [key] of Object.entries(props.variables)) {\n\t\t\tconst match = actual.find((e) => e.key === key);\n\n\t\t\tif (match) {\n\t\t\t\tactualVariables[key] = match.value;\n\t\t\t\tactualEnvIds[key] = match.id;\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tid,\n\t\t\tprops: {\n\t\t\t\t...props,\n\t\t\t\tvariables: actualVariables,\n\t\t\t\tenvIds: actualEnvIds,\n\t\t\t\tcontentHash: await hashVariables(actualVariables),\n\t\t\t},\n\t\t};\n\t}\n\n\tasync delete(_id: string, props: VercelVariableOutputs): Promise<void> {\n\t\tconst client = new VercelClient(\n\t\t\tresolveCredential(props.token, props.tokenEnvVar),\n\t\t\tprops.teamId,\n\t\t);\n\n\t\tfor (const [key, envId] of Object.entries(props.envIds)) {\n\t\t\ttry {\n\t\t\t\tawait deleteEnvVar(client, props.projectId, envId);\n\t\t\t} catch {\n\t\t\t\tpulumi.log.warn(\n\t\t\t\t\t`Failed to delete Vercel env var \"${key}\" (may already be deleted)`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tasync diff(\n\t\t_id: string,\n\t\tolds: VercelVariableOutputs,\n\t\tnews: VercelVariableInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst oldKeys = Object.keys(olds.variables).sort().join(\",\");\n\t\tconst newKeys = Object.keys(news.variables).sort().join(\",\");\n\n\t\tconst valuesChanged = Object.entries(news.variables).some(\n\t\t\t([key, value]) => olds.variables[key] !== value,\n\t\t);\n\n\t\treturn {\n\t\t\tchanges: oldKeys !== newKeys || valuesChanged,\n\t\t};\n\t}\n}\n\n/** Internal dynamic resource — not part of the public API. */\nclass VercelVariableResource extends pulumi.dynamic.Resource {\n\tconstructor(\n\t\tname: string,\n\t\targs: {\n\t\t\ttoken?: pulumi.Input<string>;\n\t\t\ttokenEnvVar?: pulumi.Input<string>;\n\t\t\tteamId: pulumi.Input<string>;\n\t\t\tprojectId: pulumi.Input<string>;\n\t\t\tvariables: pulumi.Input<Record<string, pulumi.Input<string>>>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\t// envIds/contentHash are state-only bookkeeping (read back by update());\n\t\t// they are deliberately NOT declared via `<name>: undefined` input\n\t\t// placeholders — undefined placeholders next to an Output-valued map input\n\t\t// deterministically failed engine serialization with \"Unexpected struct\n\t\t// type\" on this resource's create AND update (three from-zero runs,\n\t\t// 2026-07-06), the same class the project-token secret-placeholder\n\t\t// incident documented. Nothing consumes them as Outputs.\n\t\tsuper(\n\t\t\tnew VercelVariableResourceProvider(),\n\t\t\tname,\n\t\t\targs,\n\t\t\t// The API token flows into dynamic-provider state with the outputs — mark it secret there.\n\t\t\t{ ...opts, additionalSecretOutputs: [\"token\"] },\n\t\t);\n\t}\n}\n\n/** Options type for VercelVariable — replaces Pulumi's native `provider` field. */\ntype VercelVariableOptions = Omit<\n\tpulumi.ComponentResourceOptions,\n\t\"provider\"\n> & {\n\t/** Vercel authentication context. */\n\tprovider: VercelProvider;\n\n\t/**\n\t * VercelProject resource to source the project ID from.\n\t * When provided, `args.projectId` is optional and ignored if both are given.\n\t */\n\tproject?: VercelProject;\n};\n\n/** Args for VercelVariable. */\nexport interface VercelVariableArgs {\n\t/**\n\t * Vercel project ID.\n\t * Required when `opts.project` is not provided.\n\t */\n\tprojectId?: pulumi.Input<string>;\n\n\t/** Key-value map of environment variable names to their values. */\n\tvariables: pulumi.Input<Record<string, pulumi.Input<string>>>;\n}\n\n/**\n * Manages Vercel project environment variables as a batch with drift detection.\n *\n * @example\n * ```typescript\n * new VercelVariable(\"nexus-vars\", {\n * projectId: vercelProject.id,\n * variables: {\n * NEXT_PUBLIC_API_URL: meshUrl,\n * },\n * }, { provider });\n * ```\n */\nexport class VercelVariable extends pulumi.ComponentResource {\n\tconstructor(\n\t\tname: string,\n\t\targs: VercelVariableArgs,\n\t\topts: VercelVariableOptions,\n\t) {\n\t\tconst { provider, project, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:vercel:Variable\", name, {}, pulumiOpts);\n\n\t\tconst projectId = project\n\t\t\t? project.id\n\t\t\t: (args.projectId as pulumi.Input<string>);\n\n\t\tif (!projectId) {\n\t\t\tthrow new Error(\n\t\t\t\t\"VercelVariable: either `args.projectId` or `opts.project` must be provided\",\n\t\t\t);\n\t\t}\n\n\t\tnew VercelVariableResource(\n\t\t\t`${name}-resource`,\n\t\t\t{\n\t\t\t\ttoken: provider.token,\n\t\t\t\ttokenEnvVar: provider.tokenEnvVar,\n\t\t\t\tteamId: provider.teamId,\n\t\t\t\tprojectId,\n\t\t\t\tvariables: args.variables,\n\t\t\t},\n\t\t\t{ parent: this },\n\t\t);\n\n\t\tthis.registerOutputs({});\n\t}\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@infracraft/pulumi",
3
- "version": "1.30.0",
3
+ "version": "1.30.1",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Native Pulumi providers for Railway, Neon, Vercel, and Fly.io with adopt-or-create semantics and deploy orchestration. No Terraform bridge.",