@infracraft/pulumi 1.30.3 β†’ 1.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +19 -9
  2. package/dist/vercel/bin/apply-env.cjs +37 -0
  3. package/dist/vercel/bin/apply-env.cjs.map +1 -0
  4. package/dist/vercel/bin/apply-env.d.cts +1 -0
  5. package/dist/vercel/bin/apply-env.d.mts +1 -0
  6. package/dist/vercel/bin/apply-env.mjs +38 -0
  7. package/dist/vercel/bin/apply-env.mjs.map +1 -0
  8. package/dist/vercel/deploy.cjs +37 -9
  9. package/dist/vercel/deploy.cjs.map +1 -1
  10. package/dist/vercel/deploy.d.cts +24 -2
  11. package/dist/vercel/deploy.d.cts.map +1 -1
  12. package/dist/vercel/deploy.d.mts +24 -2
  13. package/dist/vercel/deploy.d.mts.map +1 -1
  14. package/dist/vercel/deploy.mjs +36 -9
  15. package/dist/vercel/deploy.mjs.map +1 -1
  16. package/dist/vercel/env-applier.cjs +48 -0
  17. package/dist/vercel/env-applier.cjs.map +1 -0
  18. package/dist/vercel/env-applier.d.cts +31 -0
  19. package/dist/vercel/env-applier.d.cts.map +1 -0
  20. package/dist/vercel/env-applier.d.mts +31 -0
  21. package/dist/vercel/env-applier.d.mts.map +1 -0
  22. package/dist/vercel/env-applier.mjs +47 -0
  23. package/dist/vercel/env-applier.mjs.map +1 -0
  24. package/dist/vercel/env-var-api.cjs +83 -0
  25. package/dist/vercel/env-var-api.cjs.map +1 -0
  26. package/dist/vercel/env-var-api.d.cts +34 -0
  27. package/dist/vercel/env-var-api.d.cts.map +1 -0
  28. package/dist/vercel/env-var-api.d.mts +34 -0
  29. package/dist/vercel/env-var-api.d.mts.map +1 -0
  30. package/dist/vercel/env-var-api.mjs +79 -0
  31. package/dist/vercel/env-var-api.mjs.map +1 -0
  32. package/dist/vercel/variable.cjs +18 -68
  33. package/dist/vercel/variable.cjs.map +1 -1
  34. package/dist/vercel/variable.d.cts +11 -0
  35. package/dist/vercel/variable.d.cts.map +1 -1
  36. package/dist/vercel/variable.d.mts +11 -0
  37. package/dist/vercel/variable.d.mts.map +1 -1
  38. package/dist/vercel/variable.mjs +14 -64
  39. package/dist/vercel/variable.mjs.map +1 -1
  40. package/package.json +1 -1
package/README.md CHANGED
@@ -33,7 +33,7 @@ Native Pulumi providers with adopt-or-create semantics and deploy orchestration.
33
33
  |---|---|---|---|
34
34
  | πŸš‚ | **Railway** | `@infracraft/pulumi/railway` | The only Pulumi provider for Railway. Projects, environments, services, variables, volumes, domains, deploy tokens, deploys. |
35
35
  | 🐘 | **Neon** | `@infracraft/pulumi/neon` | Adopt-or-create layer for Neon Postgres. Projects, branches, endpoints, roles, databases. |
36
- | β–² | **Vercel** | `@infracraft/pulumi/vercel` | Projects with adopt-or-create, deploy orchestration, custom domains, marketplace resources, and sensitive env var drift detection. |
36
+ | β–² | **Vercel** | `@infracraft/pulumi/vercel` | Projects with adopt-or-create, deploy orchestration with deploy-integrated env vars, custom domains, and marketplace resources. |
37
37
  | 🎯 | **Fly.io** | `@infracraft/pulumi/fly` | App, Secret, Volume, Certificate, IP, and Deploy resources via the Machines REST API and Fly GraphQL API. |
38
38
  | πŸ€– | **Agents** | `@infracraft/pulumi/agents` | Emit operating hints for AI coding agents working on the stack. |
39
39
  | #️⃣ | **Hash** | `@infracraft/pulumi/hash` | Deterministic directory/env-var/app hashing for deploy triggers. |
@@ -232,7 +232,6 @@ Leave the version unset until the first rotation is needed. Identity changes (na
232
232
  import {
233
233
  VercelProvider,
234
234
  VercelProject,
235
- VercelVariable,
236
235
  VercelDeploy,
237
236
  VercelDomain,
238
237
  VercelIntegration,
@@ -255,12 +254,12 @@ const project = new VercelProject("web", {
255
254
  // project.url is a full https:// URL β€” custom domain or <name>.vercel.app
256
255
  export const url = project.url
257
256
 
258
- const vars = new VercelVariable("web-vars", {
259
- variables: { NEXT_PUBLIC_API_URL: apiUrl },
260
- }, { provider, project })
261
-
257
+ // Env vars are applied by the deploy command itself, right before
258
+ // `vercel deploy` β€” see "Deploy-integrated env vars" below. A change to any
259
+ // variable redeploys automatically (a non-secret digest joins the triggers).
262
260
  new VercelDeploy("web-deploy", {
263
- triggers: [hash("apps/web"), vars.contentHash],
261
+ variables: { NEXT_PUBLIC_API_URL: apiUrl },
262
+ triggers: [hash("apps/web")],
264
263
  }, { provider, project })
265
264
 
266
265
  // Custom domain: point the domain's DNS CNAME at `cnameTarget`
@@ -295,8 +294,8 @@ new VercelResourceConnection("kv-conn", {
295
294
  |---|---|---|
296
295
  | `VercelProvider` | β€” | `token` or `tokenEnvVar` (see [Provider credentials](#provider-credentials)) + `teamId` |
297
296
  | `VercelProject` | `.id`, `.url` | `.url` is a full `https://` URL; prefers the custom production domain over `<name>.vercel.app`. Deletes the project on destroy β€” `protect: true` production projects |
298
- | `VercelVariable` | `.contentHash` | Use as a deploy trigger to redeploy on env var changes. Takes `opts.project` or `args.projectId` |
299
- | `VercelDeploy` | `.deploymentUrl` | Runs `vercel deploy --prod --yes` |
297
+ | `VercelVariable` | `.contentHash` | ⚠️ Deprecated β€” prefer `VercelDeploy.variables`. As a dynamic resource it hits a Pulumi engine-level marshal bug on clean-slate creates ("Unexpected struct type", alternating pass/fail across identical from-zero runs, reproduced with plain-literal inputs) β€” see [Deploy-integrated env vars](#deploy-integrated-env-vars). Stays exported for existing stacks |
298
+ | `VercelDeploy` | `.deploymentUrl` | Runs `vercel deploy --prod --yes`; optional `variables` map is upserted (production + preview + development) by the deploy command itself right before the deploy, and hashed into the triggers so variable changes redeploy |
300
299
  | `VercelDomain` | `.name`, `.verified`, `.cnameTarget` | Attaches a custom domain to a project (adopt-or-create); `.cnameTarget` is Vercel's own DNS recommendation for that specific domain |
301
300
  | `VercelIntegration` | `.configurationId` (`icfg_…`) | Resolves an installed marketplace integration by slug (install it once via the dashboard first) |
302
301
  | `VercelMarketplaceResource` | `.id`, `.externalResourceId`, `.status` | Provisions a marketplace store; `type` is the integration product ID or slug |
@@ -305,6 +304,17 @@ new VercelResourceConnection("kv-conn", {
305
304
 
306
305
  **Helpers:** `VERCEL_FRAMEWORKS` (const array), `VercelFramework` (derived union type)
307
306
 
307
+ ### Deploy-integrated env vars
308
+
309
+ `VercelDeploy.variables` applies env vars inside the deploy command flow instead of as a Pulumi resource, because the dynamic-resource path (`VercelVariable`) hits a Pulumi engine-internal stateful bug on clean-slate first creates: "Unexpected struct type", strictly alternating pass/fail across identical from-zero runs, reproduced with plain-literal inputs β€” zero Outputs or secrets β€” on matched CLI/SDK versions, with four structural theories falsified by bisection. No input shape avoids an engine-internal bug, so the fix is architectural: keep env vars off the dynamic-provider marshal path entirely, the same way `RailwayDeploy`'s monitor bin owns Railway's imperative deploy steps.
310
+
311
+ When `variables` is set, the deploy command runs a standalone applier bin (`node dist/vercel/bin/apply-env.mjs`) before `vercel deploy --prod --yes`:
312
+
313
+ - Each entry is upserted for production + preview + development β€” created, or updated in place when the key already exists (ENV_CONFLICT) β€” via the same REST logic `VercelVariable` uses.
314
+ - The key→value payload travels as a secret JSON value in the command environment (`IC_VC_ENV_JSON`), masked in state and never present in the script text pulumi-command echoes on failure. Values must be known at preview (config-derived) — the same class as the token the command env already carries.
315
+ - A non-secret digest of the variables joins the command triggers automatically, so any variable change redeploys.
316
+ - The applier logs one line per applied key (names only, never values) and exits non-zero on the first failed key β€” the deploy never runs against a half-applied environment.
317
+
308
318
  ## Fly.io
309
319
 
310
320
  ```typescript
@@ -0,0 +1,37 @@
1
+ const require_chunk = require('../../chunk-BVYJZCqc.cjs');
2
+ const require_vercel_env_applier = require('../env-applier.cjs');
3
+
4
+ //#region src/vercel/bin/apply-env.ts
5
+ /**
6
+ * Runnable entry for the Vercel env applier β€” invoked by `VercelDeploy` as
7
+ * `node <dist>/vercel/bin/apply-env.mjs` right before `vercel deploy` when
8
+ * `variables` are configured.
9
+ *
10
+ * Reads the deploy context from the command environment (`VERCEL_TOKEN`,
11
+ * `VERCEL_ORG_ID`, `VERCEL_PROJECT_ID` β€” the same names the deploy CLI already
12
+ * receives — plus `IC_VC_ENV_JSON`, the JSON key→value payload), applies the
13
+ * variables via the Vercel REST API, and exits non-zero on the first failure.
14
+ * All upsert logic lives in the unit-tested `env-applier` module; this file is
15
+ * only IO + process glue.
16
+ */
17
+ /** Reads a required env var, failing loudly β€” naming it β€” when unset. */
18
+ function requireEnv(name) {
19
+ const value = process.env[name];
20
+ if (!value) throw new Error(`${name} is not set in the deploy command environment`);
21
+ return value;
22
+ }
23
+ async function main() {
24
+ await require_vercel_env_applier.applyVercelEnv({
25
+ token: requireEnv("VERCEL_TOKEN"),
26
+ teamId: requireEnv("VERCEL_ORG_ID"),
27
+ projectId: requireEnv("VERCEL_PROJECT_ID"),
28
+ variables: JSON.parse(requireEnv("IC_VC_ENV_JSON"))
29
+ }, { log: (line) => process.stderr.write(`[infracraft] ${line}\n`) });
30
+ }
31
+ main().catch((error) => {
32
+ process.stderr.write(`[infracraft] vercel env applier failed: ${error instanceof Error ? error.message : String(error)}\n`);
33
+ process.exit(1);
34
+ });
35
+
36
+ //#endregion
37
+ //# sourceMappingURL=apply-env.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply-env.cjs","names":["applyVercelEnv"],"sources":["../../../src/vercel/bin/apply-env.ts"],"sourcesContent":["/**\n * Runnable entry for the Vercel env applier — invoked by `VercelDeploy` as\n * `node <dist>/vercel/bin/apply-env.mjs` right before `vercel deploy` when\n * `variables` are configured.\n *\n * Reads the deploy context from the command environment (`VERCEL_TOKEN`,\n * `VERCEL_ORG_ID`, `VERCEL_PROJECT_ID` — the same names the deploy CLI already\n * receives — plus `IC_VC_ENV_JSON`, the JSON key→value payload), applies the\n * variables via the Vercel REST API, and exits non-zero on the first failure.\n * All upsert logic lives in the unit-tested `env-applier` module; this file is\n * only IO + process glue.\n */\nimport { applyVercelEnv } from \"../env-applier\";\n\n/** Reads a required env var, failing loudly — naming it — when unset. */\nfunction requireEnv(name: string): string {\n\tconst value = process.env[name];\n\n\tif (!value) {\n\t\tthrow new Error(`${name} is not set in the deploy command environment`);\n\t}\n\n\treturn value;\n}\n\nasync function main(): Promise<void> {\n\tawait applyVercelEnv(\n\t\t{\n\t\t\ttoken: requireEnv(\"VERCEL_TOKEN\"),\n\t\t\tteamId: requireEnv(\"VERCEL_ORG_ID\"),\n\t\t\tprojectId: requireEnv(\"VERCEL_PROJECT_ID\"),\n\t\t\tvariables: JSON.parse(requireEnv(\"IC_VC_ENV_JSON\")) as Record<\n\t\t\t\tstring,\n\t\t\t\tstring\n\t\t\t>,\n\t\t},\n\t\t{ log: (line) => process.stderr.write(`[infracraft] ${line}\\n`) },\n\t);\n}\n\nmain().catch((error) => {\n\tprocess.stderr.write(\n\t\t`[infracraft] vercel env applier failed: ${error instanceof Error ? error.message : String(error)}\\n`,\n\t);\n\n\tprocess.exit(1);\n});\n"],"mappings":";;;;;;;;;;;;;;;;;AAeA,SAAS,WAAW,MAAsB;CACzC,MAAM,QAAQ,QAAQ,IAAI;CAE1B,IAAI,CAAC,OACJ,MAAM,IAAI,MAAM,GAAG,KAAK,8CAA8C;CAGvE,OAAO;AACR;AAEA,eAAe,OAAsB;CACpC,MAAMA,0CACL;EACC,OAAO,WAAW,cAAc;EAChC,QAAQ,WAAW,eAAe;EAClC,WAAW,WAAW,mBAAmB;EACzC,WAAW,KAAK,MAAM,WAAW,gBAAgB,CAAC;CAInD,GACA,EAAE,MAAM,SAAS,QAAQ,OAAO,MAAM,gBAAgB,KAAK,GAAG,EAAE,CACjE;AACD;AAEA,KAAK,EAAE,OAAO,UAAU;CACvB,QAAQ,OAAO,MACd,2CAA2C,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAE,GACnG;CAEA,QAAQ,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1,38 @@
1
+ import { t as __name } from "../../chunk-OPjESj5l.mjs";
2
+ import { applyVercelEnv } from "../env-applier.mjs";
3
+
4
+ //#region src/vercel/bin/apply-env.ts
5
+ /**
6
+ * Runnable entry for the Vercel env applier β€” invoked by `VercelDeploy` as
7
+ * `node <dist>/vercel/bin/apply-env.mjs` right before `vercel deploy` when
8
+ * `variables` are configured.
9
+ *
10
+ * Reads the deploy context from the command environment (`VERCEL_TOKEN`,
11
+ * `VERCEL_ORG_ID`, `VERCEL_PROJECT_ID` β€” the same names the deploy CLI already
12
+ * receives — plus `IC_VC_ENV_JSON`, the JSON key→value payload), applies the
13
+ * variables via the Vercel REST API, and exits non-zero on the first failure.
14
+ * All upsert logic lives in the unit-tested `env-applier` module; this file is
15
+ * only IO + process glue.
16
+ */
17
+ /** Reads a required env var, failing loudly β€” naming it β€” when unset. */
18
+ function requireEnv(name) {
19
+ const value = process.env[name];
20
+ if (!value) throw new Error(`${name} is not set in the deploy command environment`);
21
+ return value;
22
+ }
23
+ async function main() {
24
+ await applyVercelEnv({
25
+ token: requireEnv("VERCEL_TOKEN"),
26
+ teamId: requireEnv("VERCEL_ORG_ID"),
27
+ projectId: requireEnv("VERCEL_PROJECT_ID"),
28
+ variables: JSON.parse(requireEnv("IC_VC_ENV_JSON"))
29
+ }, { log: (line) => process.stderr.write(`[infracraft] ${line}\n`) });
30
+ }
31
+ main().catch((error) => {
32
+ process.stderr.write(`[infracraft] vercel env applier failed: ${error instanceof Error ? error.message : String(error)}\n`);
33
+ process.exit(1);
34
+ });
35
+
36
+ //#endregion
37
+ export { };
38
+ //# sourceMappingURL=apply-env.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply-env.mjs","names":[],"sources":["../../../src/vercel/bin/apply-env.ts"],"sourcesContent":["/**\n * Runnable entry for the Vercel env applier — invoked by `VercelDeploy` as\n * `node <dist>/vercel/bin/apply-env.mjs` right before `vercel deploy` when\n * `variables` are configured.\n *\n * Reads the deploy context from the command environment (`VERCEL_TOKEN`,\n * `VERCEL_ORG_ID`, `VERCEL_PROJECT_ID` — the same names the deploy CLI already\n * receives — plus `IC_VC_ENV_JSON`, the JSON key→value payload), applies the\n * variables via the Vercel REST API, and exits non-zero on the first failure.\n * All upsert logic lives in the unit-tested `env-applier` module; this file is\n * only IO + process glue.\n */\nimport { applyVercelEnv } from \"../env-applier\";\n\n/** Reads a required env var, failing loudly — naming it — when unset. */\nfunction requireEnv(name: string): string {\n\tconst value = process.env[name];\n\n\tif (!value) {\n\t\tthrow new Error(`${name} is not set in the deploy command environment`);\n\t}\n\n\treturn value;\n}\n\nasync function main(): Promise<void> {\n\tawait applyVercelEnv(\n\t\t{\n\t\t\ttoken: requireEnv(\"VERCEL_TOKEN\"),\n\t\t\tteamId: requireEnv(\"VERCEL_ORG_ID\"),\n\t\t\tprojectId: requireEnv(\"VERCEL_PROJECT_ID\"),\n\t\t\tvariables: JSON.parse(requireEnv(\"IC_VC_ENV_JSON\")) as Record<\n\t\t\t\tstring,\n\t\t\t\tstring\n\t\t\t>,\n\t\t},\n\t\t{ log: (line) => process.stderr.write(`[infracraft] ${line}\\n`) },\n\t);\n}\n\nmain().catch((error) => {\n\tprocess.stderr.write(\n\t\t`[infracraft] vercel env applier failed: ${error instanceof Error ? error.message : String(error)}\\n`,\n\t);\n\n\tprocess.exit(1);\n});\n"],"mappings":";;;;;;;;;;;;;;;;;AAeA,SAAS,WAAW,MAAsB;CACzC,MAAM,QAAQ,QAAQ,IAAI;CAE1B,IAAI,CAAC,OACJ,MAAM,IAAI,MAAM,GAAG,KAAK,8CAA8C;CAGvE,OAAO;AACR;AAEA,eAAe,OAAsB;CACpC,MAAM,eACL;EACC,OAAO,WAAW,cAAc;EAChC,QAAQ,WAAW,eAAe;EAClC,WAAW,WAAW,mBAAmB;EACzC,WAAW,KAAK,MAAM,WAAW,gBAAgB,CAAC;CAInD,GACA,EAAE,MAAM,SAAS,QAAQ,OAAO,MAAM,gBAAgB,KAAK,GAAG,EAAE,CACjE;AACD;AAEA,KAAK,EAAE,OAAO,UAAU;CACvB,QAAQ,OAAO,MACd,2CAA2C,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAE,GACnG;CAEA,QAAQ,KAAK,CAAC;AACf,CAAC"}
@@ -4,21 +4,39 @@ const require_commands_deploy = require('../commands/deploy.cjs');
4
4
  const require_dynamic_resolve_credential = require('../dynamic/resolve-credential.cjs');
5
5
  let _pulumi_pulumi = require("@pulumi/pulumi");
6
6
  _pulumi_pulumi = require_chunk.__toESM(_pulumi_pulumi, 1);
7
+ let node_crypto = require("node:crypto");
8
+ node_crypto = require_chunk.__toESM(node_crypto, 1);
9
+ let node_url = require("node:url");
7
10
 
8
11
  //#region src/vercel/deploy.ts
9
12
  /**
13
+ * Absolute path to the runnable env applier, resolved next to this module in
14
+ * `dist`. Runs as a plain Node step of the deploy command (mirroring Railway's
15
+ * deploy monitor bin) so env vars never touch the dynamic-provider marshal
16
+ * path. Its logic lives in the unit-tested `env-applier` module.
17
+ */
18
+ const APPLY_ENV_BIN = (0, node_url.fileURLToPath)(new URL("./bin/apply-env.mjs", require("url").pathToFileURL(__filename).href));
19
+ /**
10
20
  * Deploys a Vercel project via `vercel deploy --prod --yes`. Isolation and
11
21
  * git-metadata handling are entirely the seam's job β€” list a `DeploySandbox`
12
22
  * (and optionally a `GitGuard`) in `opts.dependsOn` to control them.
13
23
  *
24
+ * Pass `variables` to apply the project's env vars as part of the deploy
25
+ * command (see `VercelDeployArgs.variables` for why this replaces
26
+ * `VercelVariable`).
27
+ *
14
28
  * Recommended preflight: `assertHostBinaries(["vercel"])` (from
15
29
  * `@infracraft/pulumi/sandbox`) at program start, so a missing CLI fails fast
16
30
  * with an install hint instead of mid-deploy.
17
31
  *
18
32
  * @example
19
33
  * ```typescript
20
- * new VercelDeploy("nexus", { projectId: project.id, triggers: [sourceHash, envHash], excludePaths: ["apps/mesh"] },
21
- * { provider, dependsOn: [sandbox, gitGuard] });
34
+ * new VercelDeploy("nexus", {
35
+ * projectId: project.id,
36
+ * variables: { NEXT_PUBLIC_API_URL: apiUrl },
37
+ * triggers: [sourceHash],
38
+ * excludePaths: ["apps/mesh"],
39
+ * }, { provider, dependsOn: [sandbox, gitGuard] });
22
40
  * ```
23
41
  */
24
42
  var VercelDeploy = class extends _pulumi_pulumi.ComponentResource {
@@ -27,16 +45,26 @@ var VercelDeploy = class extends _pulumi_pulumi.ComponentResource {
27
45
  super("infracraft:vercel:Deploy", name, {}, pulumiOpts);
28
46
  const projectId = project ? project.id : args.projectId;
29
47
  if (!projectId) throw new Error("VercelDeploy: either `args.projectId` or `opts.project` must be provided");
48
+ const environment = {
49
+ VERCEL_TOKEN: require_dynamic_resolve_credential.resolveCredentialOutput(provider.token, provider.tokenEnvVar),
50
+ VERCEL_ORG_ID: provider.teamId,
51
+ VERCEL_PROJECT_ID: projectId
52
+ };
53
+ let cli = "vercel deploy --prod --yes";
54
+ let triggers = args.triggers;
55
+ if (args.variables) {
56
+ const variablesJson = _pulumi_pulumi.output(args.variables).apply((variables) => JSON.stringify(Object.fromEntries(Object.entries(variables).sort(([a], [b]) => a.localeCompare(b)))));
57
+ environment.IC_VC_ENV_JSON = _pulumi_pulumi.secret(variablesJson);
58
+ const variablesDigest = _pulumi_pulumi.unsecret(variablesJson.apply((json) => node_crypto.createHash("sha256").update(json).digest("hex")));
59
+ triggers = _pulumi_pulumi.output(args.triggers).apply((existing) => [...existing, variablesDigest]);
60
+ cli = `node "${APPLY_ENV_BIN}" && ${cli}`;
61
+ }
30
62
  const { deploymentUrl } = require_commands_deploy.createDeployCommand({
31
63
  name,
32
- cli: "vercel deploy --prod --yes",
33
- triggers: args.triggers,
64
+ cli,
65
+ triggers,
34
66
  excludePaths: args.excludePaths,
35
- environment: {
36
- VERCEL_TOKEN: require_dynamic_resolve_credential.resolveCredentialOutput(provider.token, provider.tokenEnvVar),
37
- VERCEL_ORG_ID: provider.teamId,
38
- VERCEL_PROJECT_ID: projectId
39
- }
67
+ environment
40
68
  }, {
41
69
  parent: this,
42
70
  ...pulumiOpts
@@ -1 +1 @@
1
- {"version":3,"file":"deploy.cjs","names":["pulumi","createDeployCommand","resolveCredentialOutput"],"sources":["../../src/vercel/deploy.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\n\nimport { createDeployCommand } from \"../commands/deploy\";\nimport { resolveCredentialOutput } from \"../dynamic/resolve-credential\";\nimport type { VercelProject } from \"./project\";\nimport type { VercelProvider } from \"./provider\";\n\ntype VercelDeployOptions = Omit<pulumi.ComponentResourceOptions, \"provider\"> & {\n\t/** Vercel authentication context. */\n\tprovider: VercelProvider;\n\t/** VercelProject to source the project ID from (optional if `args.projectId` given). */\n\tproject?: VercelProject;\n};\n\nexport interface VercelDeployArgs {\n\t/** Vercel project ID. Required when `opts.project` is not provided. */\n\tprojectId?: pulumi.Input<string>;\n\t/** Redeploy triggers (e.g. source hash, env content hash). */\n\ttriggers: pulumi.Input<pulumi.Input<string>[]>;\n\t/** Paths excluded from the upload when running with `DeploySandbox` + `GitGuard`. */\n\texcludePaths?: string[];\n}\n\n/**\n * Deploys a Vercel project via `vercel deploy --prod --yes`. Isolation and\n * git-metadata handling are entirely the seam's job β€” list a `DeploySandbox`\n * (and optionally a `GitGuard`) in `opts.dependsOn` to control them.\n *\n * Recommended preflight: `assertHostBinaries([\"vercel\"])` (from\n * `@infracraft/pulumi/sandbox`) at program start, so a missing CLI fails fast\n * with an install hint instead of mid-deploy.\n *\n * @example\n * ```typescript\n * new VercelDeploy(\"nexus\", { projectId: project.id, triggers: [sourceHash, envHash], excludePaths: [\"apps/mesh\"] },\n * { provider, dependsOn: [sandbox, gitGuard] });\n * ```\n */\nexport class VercelDeploy extends pulumi.ComponentResource {\n\tpublic readonly deploymentUrl: pulumi.Output<string>;\n\n\tconstructor(name: string, args: VercelDeployArgs, opts: VercelDeployOptions) {\n\t\tconst { provider, project, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:vercel:Deploy\", 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\"VercelDeploy: either `args.projectId` or `opts.project` must be provided\",\n\t\t\t);\n\t\t}\n\n\t\tconst { deploymentUrl } = createDeployCommand(\n\t\t\t{\n\t\t\t\tname,\n\t\t\t\tcli: \"vercel deploy --prod --yes\",\n\t\t\t\ttriggers: args.triggers,\n\t\t\t\texcludePaths: args.excludePaths,\n\t\t\t\tenvironment: {\n\t\t\t\t\t// Resolved at program runtime (secret) so the CLI still gets the\n\t\t\t\t\t// actual value when the provider is configured via tokenEnvVar β€”\n\t\t\t\t\t// without the credential ever being a dynamic-resource input.\n\t\t\t\t\tVERCEL_TOKEN: resolveCredentialOutput(\n\t\t\t\t\t\tprovider.token,\n\t\t\t\t\t\tprovider.tokenEnvVar,\n\t\t\t\t\t),\n\t\t\t\t\tVERCEL_ORG_ID: provider.teamId,\n\t\t\t\t\tVERCEL_PROJECT_ID: projectId,\n\t\t\t\t},\n\t\t\t},\n\t\t\t{ parent: this, ...pulumiOpts },\n\t\t);\n\n\t\tthis.deploymentUrl = deploymentUrl;\n\n\t\tthis.registerOutputs({ deploymentUrl: this.deploymentUrl });\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAsCA,IAAa,eAAb,cAAkCA,eAAO,kBAAkB;CAG1D,YAAY,MAAc,MAAwB,MAA2B;EAC5E,MAAM,EAAE,UAAU,SAAS,GAAG,eAAe;EAE7C,MAAM,4BAA4B,MAAM,CAAC,GAAG,UAAU;EAEtD,MAAM,YAAY,UACf,QAAQ,KACP,KAAK;EAET,IAAI,CAAC,WACJ,MAAM,IAAI,MACT,0EACD;EAGD,MAAM,EAAE,kBAAkBC,4CACzB;GACC;GACA,KAAK;GACL,UAAU,KAAK;GACf,cAAc,KAAK;GACnB,aAAa;IAIZ,cAAcC,2DACb,SAAS,OACT,SAAS,WACV;IACA,eAAe,SAAS;IACxB,mBAAmB;GACpB;EACD,GACA;GAAE,QAAQ;GAAM,GAAG;EAAW,CAC/B;EAEA,KAAK,gBAAgB;EAErB,KAAK,gBAAgB,EAAE,eAAe,KAAK,cAAc,CAAC;CAC3D;AACD"}
1
+ {"version":3,"file":"deploy.cjs","names":["pulumi","resolveCredentialOutput","crypto","createDeployCommand"],"sources":["../../src/vercel/deploy.ts"],"sourcesContent":["import * as crypto from \"node:crypto\";\nimport { fileURLToPath } from \"node:url\";\n\nimport * as pulumi from \"@pulumi/pulumi\";\n\nimport { createDeployCommand } from \"../commands/deploy\";\nimport { resolveCredentialOutput } from \"../dynamic/resolve-credential\";\nimport type { VercelProject } from \"./project\";\nimport type { VercelProvider } from \"./provider\";\n\ntype VercelDeployOptions = Omit<pulumi.ComponentResourceOptions, \"provider\"> & {\n\t/** Vercel authentication context. */\n\tprovider: VercelProvider;\n\t/** VercelProject to source the project ID from (optional if `args.projectId` given). */\n\tproject?: VercelProject;\n};\n\nexport interface VercelDeployArgs {\n\t/** Vercel project ID. Required when `opts.project` is not provided. */\n\tprojectId?: pulumi.Input<string>;\n\t/** Redeploy triggers (e.g. source hash, env content hash). */\n\ttriggers: pulumi.Input<pulumi.Input<string>[]>;\n\t/** Paths excluded from the upload when running with `DeploySandbox` + `GitGuard`. */\n\texcludePaths?: string[];\n\t/**\n\t * Env vars upserted for the project (production + preview + development)\n\t * by the deploy command itself, right before `vercel deploy` runs β€” the\n\t * preferred replacement for `VercelVariable`. The dynamic-resource path\n\t * hits a Pulumi engine-internal stateful bug on clean-slate first creates\n\t * (\"Unexpected struct type\", strictly alternating pass/fail across\n\t * identical from-zero runs, reproduced with plain-literal inputs and zero\n\t * secrets), so env vars are applied inside the command flow, off the\n\t * dynamic-provider marshal path entirely. A change to any variable\n\t * redeploys automatically (a non-secret digest of the map joins the\n\t * command triggers). Values must be known at preview (config-derived) β€”\n\t * the same class as the token the command env already carries.\n\t */\n\tvariables?: pulumi.Input<Record<string, pulumi.Input<string>>>;\n}\n\n/**\n * Absolute path to the runnable env applier, resolved next to this module in\n * `dist`. Runs as a plain Node step of the deploy command (mirroring Railway's\n * deploy monitor bin) so env vars never touch the dynamic-provider marshal\n * path. Its logic lives in the unit-tested `env-applier` module.\n */\nconst APPLY_ENV_BIN = fileURLToPath(\n\tnew URL(\"./bin/apply-env.mjs\", import.meta.url),\n);\n\n/**\n * Deploys a Vercel project via `vercel deploy --prod --yes`. Isolation and\n * git-metadata handling are entirely the seam's job β€” list a `DeploySandbox`\n * (and optionally a `GitGuard`) in `opts.dependsOn` to control them.\n *\n * Pass `variables` to apply the project's env vars as part of the deploy\n * command (see `VercelDeployArgs.variables` for why this replaces\n * `VercelVariable`).\n *\n * Recommended preflight: `assertHostBinaries([\"vercel\"])` (from\n * `@infracraft/pulumi/sandbox`) at program start, so a missing CLI fails fast\n * with an install hint instead of mid-deploy.\n *\n * @example\n * ```typescript\n * new VercelDeploy(\"nexus\", {\n * projectId: project.id,\n * variables: { NEXT_PUBLIC_API_URL: apiUrl },\n * triggers: [sourceHash],\n * excludePaths: [\"apps/mesh\"],\n * }, { provider, dependsOn: [sandbox, gitGuard] });\n * ```\n */\nexport class VercelDeploy extends pulumi.ComponentResource {\n\tpublic readonly deploymentUrl: pulumi.Output<string>;\n\n\tconstructor(name: string, args: VercelDeployArgs, opts: VercelDeployOptions) {\n\t\tconst { provider, project, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:vercel:Deploy\", 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\"VercelDeploy: either `args.projectId` or `opts.project` must be provided\",\n\t\t\t);\n\t\t}\n\n\t\tconst environment: Record<string, pulumi.Input<string>> = {\n\t\t\t// Resolved at program runtime (secret) so the CLI still gets the\n\t\t\t// actual value when the provider is configured via tokenEnvVar β€”\n\t\t\t// without the credential ever being a dynamic-resource input.\n\t\t\tVERCEL_TOKEN: resolveCredentialOutput(\n\t\t\t\tprovider.token,\n\t\t\t\tprovider.tokenEnvVar,\n\t\t\t),\n\t\t\tVERCEL_ORG_ID: provider.teamId,\n\t\t\tVERCEL_PROJECT_ID: projectId,\n\t\t};\n\n\t\tlet cli = \"vercel deploy --prod --yes\";\n\t\tlet triggers = args.triggers;\n\n\t\tif (args.variables) {\n\t\t\t// Sorted-key JSON so the payload β€” and the trigger digest below β€” is\n\t\t\t// insensitive to declaration order. Marked secret so the values are\n\t\t\t// masked in state and diagnostics; they reach the applier bin through\n\t\t\t// the command environment, never the script text.\n\t\t\tconst variablesJson = pulumi\n\t\t\t\t.output(args.variables)\n\t\t\t\t.apply((variables) =>\n\t\t\t\t\tJSON.stringify(\n\t\t\t\t\t\tObject.fromEntries(\n\t\t\t\t\t\t\tObject.entries(variables).sort(([a], [b]) => a.localeCompare(b)),\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t);\n\n\t\t\tenvironment.IC_VC_ENV_JSON = pulumi.secret(variablesJson);\n\n\t\t\t// Non-secret digest of the payload (same rationale as `hash(env)`):\n\t\t\t// any variable change redeploys, without carrying a secret trigger.\n\t\t\tconst variablesDigest = pulumi.unsecret(\n\t\t\t\tvariablesJson.apply((json) =>\n\t\t\t\t\tcrypto.createHash(\"sha256\").update(json).digest(\"hex\"),\n\t\t\t\t),\n\t\t\t);\n\n\t\t\ttriggers = pulumi\n\t\t\t\t.output(args.triggers)\n\t\t\t\t.apply((existing) => [...existing, variablesDigest]);\n\n\t\t\tcli = `node \"${APPLY_ENV_BIN}\" && ${cli}`;\n\t\t}\n\n\t\tconst { deploymentUrl } = createDeployCommand(\n\t\t\t{\n\t\t\t\tname,\n\t\t\t\tcli,\n\t\t\t\ttriggers,\n\t\t\t\texcludePaths: args.excludePaths,\n\t\t\t\tenvironment,\n\t\t\t},\n\t\t\t{ parent: this, ...pulumiOpts },\n\t\t);\n\n\t\tthis.deploymentUrl = deploymentUrl;\n\n\t\tthis.registerOutputs({ deploymentUrl: this.deploymentUrl });\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AA8CA,MAAM,4CACL,IAAI,IAAI,oEAAsC,CAC/C;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,IAAa,eAAb,cAAkCA,eAAO,kBAAkB;CAG1D,YAAY,MAAc,MAAwB,MAA2B;EAC5E,MAAM,EAAE,UAAU,SAAS,GAAG,eAAe;EAE7C,MAAM,4BAA4B,MAAM,CAAC,GAAG,UAAU;EAEtD,MAAM,YAAY,UACf,QAAQ,KACP,KAAK;EAET,IAAI,CAAC,WACJ,MAAM,IAAI,MACT,0EACD;EAGD,MAAM,cAAoD;GAIzD,cAAcC,2DACb,SAAS,OACT,SAAS,WACV;GACA,eAAe,SAAS;GACxB,mBAAmB;EACpB;EAEA,IAAI,MAAM;EACV,IAAI,WAAW,KAAK;EAEpB,IAAI,KAAK,WAAW;GAKnB,MAAM,gBAAgBD,eACpB,OAAO,KAAK,SAAS,EACrB,OAAO,cACP,KAAK,UACJ,OAAO,YACN,OAAO,QAAQ,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC,CAChE,CACD,CACD;GAED,YAAY,iBAAiBA,eAAO,OAAO,aAAa;GAIxD,MAAM,kBAAkBA,eAAO,SAC9B,cAAc,OAAO,SACpBE,YAAO,WAAW,QAAQ,EAAE,OAAO,IAAI,EAAE,OAAO,KAAK,CACtD,CACD;GAEA,WAAWF,eACT,OAAO,KAAK,QAAQ,EACpB,OAAO,aAAa,CAAC,GAAG,UAAU,eAAe,CAAC;GAEpD,MAAM,SAAS,cAAc,OAAO;EACrC;EAEA,MAAM,EAAE,kBAAkBG,4CACzB;GACC;GACA;GACA;GACA,cAAc,KAAK;GACnB;EACD,GACA;GAAE,QAAQ;GAAM,GAAG;EAAW,CAC/B;EAEA,KAAK,gBAAgB;EAErB,KAAK,gBAAgB,EAAE,eAAe,KAAK,cAAc,CAAC;CAC3D;AACD"}
@@ -15,20 +15,42 @@ interface VercelDeployArgs {
15
15
  triggers: pulumi.Input<pulumi.Input<string>[]>;
16
16
  /** Paths excluded from the upload when running with `DeploySandbox` + `GitGuard`. */
17
17
  excludePaths?: string[];
18
+ /**
19
+ * Env vars upserted for the project (production + preview + development)
20
+ * by the deploy command itself, right before `vercel deploy` runs β€” the
21
+ * preferred replacement for `VercelVariable`. The dynamic-resource path
22
+ * hits a Pulumi engine-internal stateful bug on clean-slate first creates
23
+ * ("Unexpected struct type", strictly alternating pass/fail across
24
+ * identical from-zero runs, reproduced with plain-literal inputs and zero
25
+ * secrets), so env vars are applied inside the command flow, off the
26
+ * dynamic-provider marshal path entirely. A change to any variable
27
+ * redeploys automatically (a non-secret digest of the map joins the
28
+ * command triggers). Values must be known at preview (config-derived) β€”
29
+ * the same class as the token the command env already carries.
30
+ */
31
+ variables?: pulumi.Input<Record<string, pulumi.Input<string>>>;
18
32
  }
19
33
  /**
20
34
  * Deploys a Vercel project via `vercel deploy --prod --yes`. Isolation and
21
35
  * git-metadata handling are entirely the seam's job β€” list a `DeploySandbox`
22
36
  * (and optionally a `GitGuard`) in `opts.dependsOn` to control them.
23
37
  *
38
+ * Pass `variables` to apply the project's env vars as part of the deploy
39
+ * command (see `VercelDeployArgs.variables` for why this replaces
40
+ * `VercelVariable`).
41
+ *
24
42
  * Recommended preflight: `assertHostBinaries(["vercel"])` (from
25
43
  * `@infracraft/pulumi/sandbox`) at program start, so a missing CLI fails fast
26
44
  * with an install hint instead of mid-deploy.
27
45
  *
28
46
  * @example
29
47
  * ```typescript
30
- * new VercelDeploy("nexus", { projectId: project.id, triggers: [sourceHash, envHash], excludePaths: ["apps/mesh"] },
31
- * { provider, dependsOn: [sandbox, gitGuard] });
48
+ * new VercelDeploy("nexus", {
49
+ * projectId: project.id,
50
+ * variables: { NEXT_PUBLIC_API_URL: apiUrl },
51
+ * triggers: [sourceHash],
52
+ * excludePaths: ["apps/mesh"],
53
+ * }, { provider, dependsOn: [sandbox, gitGuard] });
32
54
  * ```
33
55
  */
34
56
  declare class VercelDeploy extends pulumi.ComponentResource {
@@ -1 +1 @@
1
- {"version":3,"file":"deploy.d.cts","names":[],"sources":["../../src/vercel/deploy.ts"],"mappings":";;;;;;KAOK,mBAAA,GAAsB,IAAA,CAAK,MAAA,CAAO,wBAAA;uCAEtC,QAAA,EAAU,cAAA,EAFN;EAIJ,OAAA,GAAU,aAAA;AAAA;AAAA,UAGM,gBAAA;EAPU;EAS1B,SAAA,GAAY,MAAA,CAAO,KAAA;EALT;EAOV,QAAA,EAAU,MAAA,CAAO,KAAA,CAAM,MAAA,CAAO,KAAA;EAPP;EASvB,YAAA;AAAA;;;;;;;AATuB;AAGxB;;;;;;;;cAwBa,YAAA,SAAqB,MAAA,CAAO,iBAAA;EAAA,SACxB,aAAA,EAAe,MAAA,CAAO,MAAA;cAE1B,IAAA,UAAc,IAAA,EAAM,gBAAA,EAAkB,IAAA,EAAM,mBAAA;AAAA"}
1
+ {"version":3,"file":"deploy.d.cts","names":[],"sources":["../../src/vercel/deploy.ts"],"mappings":";;;;;;KAUK,mBAAA,GAAsB,IAAA,CAAK,MAAA,CAAO,wBAAA;uCAEtC,QAAA,EAAU,cAAA,EAFN;EAIJ,OAAA,GAAU,aAAA;AAAA;AAAA,UAGM,gBAAA;EAPU;EAS1B,SAAA,GAAY,MAAA,CAAO,KAAA;EALT;EAOV,QAAA,EAAU,MAAA,CAAO,KAAA,CAAM,MAAA,CAAO,KAAA;EAPP;EASvB,YAAA;EAb+B;;;;;;;AAIR;AAGxB;;;;;EAoBC,SAAA,GAAY,MAAA,CAAO,KAAA,CAAM,MAAA,SAAe,MAAA,CAAO,KAAA;AAAA;;;;;;;;;;;;;;;;;;;;;AAAK;AAoCrD;;cAAa,YAAA,SAAqB,MAAA,CAAO,iBAAA;EAAA,SACxB,aAAA,EAAe,MAAA,CAAO,MAAA;cAE1B,IAAA,UAAc,IAAA,EAAM,gBAAA,EAAkB,IAAA,EAAM,mBAAA;AAAA"}
@@ -15,20 +15,42 @@ interface VercelDeployArgs {
15
15
  triggers: pulumi.Input<pulumi.Input<string>[]>;
16
16
  /** Paths excluded from the upload when running with `DeploySandbox` + `GitGuard`. */
17
17
  excludePaths?: string[];
18
+ /**
19
+ * Env vars upserted for the project (production + preview + development)
20
+ * by the deploy command itself, right before `vercel deploy` runs β€” the
21
+ * preferred replacement for `VercelVariable`. The dynamic-resource path
22
+ * hits a Pulumi engine-internal stateful bug on clean-slate first creates
23
+ * ("Unexpected struct type", strictly alternating pass/fail across
24
+ * identical from-zero runs, reproduced with plain-literal inputs and zero
25
+ * secrets), so env vars are applied inside the command flow, off the
26
+ * dynamic-provider marshal path entirely. A change to any variable
27
+ * redeploys automatically (a non-secret digest of the map joins the
28
+ * command triggers). Values must be known at preview (config-derived) β€”
29
+ * the same class as the token the command env already carries.
30
+ */
31
+ variables?: pulumi.Input<Record<string, pulumi.Input<string>>>;
18
32
  }
19
33
  /**
20
34
  * Deploys a Vercel project via `vercel deploy --prod --yes`. Isolation and
21
35
  * git-metadata handling are entirely the seam's job β€” list a `DeploySandbox`
22
36
  * (and optionally a `GitGuard`) in `opts.dependsOn` to control them.
23
37
  *
38
+ * Pass `variables` to apply the project's env vars as part of the deploy
39
+ * command (see `VercelDeployArgs.variables` for why this replaces
40
+ * `VercelVariable`).
41
+ *
24
42
  * Recommended preflight: `assertHostBinaries(["vercel"])` (from
25
43
  * `@infracraft/pulumi/sandbox`) at program start, so a missing CLI fails fast
26
44
  * with an install hint instead of mid-deploy.
27
45
  *
28
46
  * @example
29
47
  * ```typescript
30
- * new VercelDeploy("nexus", { projectId: project.id, triggers: [sourceHash, envHash], excludePaths: ["apps/mesh"] },
31
- * { provider, dependsOn: [sandbox, gitGuard] });
48
+ * new VercelDeploy("nexus", {
49
+ * projectId: project.id,
50
+ * variables: { NEXT_PUBLIC_API_URL: apiUrl },
51
+ * triggers: [sourceHash],
52
+ * excludePaths: ["apps/mesh"],
53
+ * }, { provider, dependsOn: [sandbox, gitGuard] });
32
54
  * ```
33
55
  */
34
56
  declare class VercelDeploy extends pulumi.ComponentResource {
@@ -1 +1 @@
1
- {"version":3,"file":"deploy.d.mts","names":[],"sources":["../../src/vercel/deploy.ts"],"mappings":";;;;;;KAOK,mBAAA,GAAsB,IAAA,CAAK,MAAA,CAAO,wBAAA;uCAEtC,QAAA,EAAU,cAAA,EAFN;EAIJ,OAAA,GAAU,aAAA;AAAA;AAAA,UAGM,gBAAA;EAPU;EAS1B,SAAA,GAAY,MAAA,CAAO,KAAA;EALT;EAOV,QAAA,EAAU,MAAA,CAAO,KAAA,CAAM,MAAA,CAAO,KAAA;EAPP;EASvB,YAAA;AAAA;;;;;;;AATuB;AAGxB;;;;;;;;cAwBa,YAAA,SAAqB,MAAA,CAAO,iBAAA;EAAA,SACxB,aAAA,EAAe,MAAA,CAAO,MAAA;cAE1B,IAAA,UAAc,IAAA,EAAM,gBAAA,EAAkB,IAAA,EAAM,mBAAA;AAAA"}
1
+ {"version":3,"file":"deploy.d.mts","names":[],"sources":["../../src/vercel/deploy.ts"],"mappings":";;;;;;KAUK,mBAAA,GAAsB,IAAA,CAAK,MAAA,CAAO,wBAAA;uCAEtC,QAAA,EAAU,cAAA,EAFN;EAIJ,OAAA,GAAU,aAAA;AAAA;AAAA,UAGM,gBAAA;EAPU;EAS1B,SAAA,GAAY,MAAA,CAAO,KAAA;EALT;EAOV,QAAA,EAAU,MAAA,CAAO,KAAA,CAAM,MAAA,CAAO,KAAA;EAPP;EASvB,YAAA;EAb+B;;;;;;;AAIR;AAGxB;;;;;EAoBC,SAAA,GAAY,MAAA,CAAO,KAAA,CAAM,MAAA,SAAe,MAAA,CAAO,KAAA;AAAA;;;;;;;;;;;;;;;;;;;;;AAAK;AAoCrD;;cAAa,YAAA,SAAqB,MAAA,CAAO,iBAAA;EAAA,SACxB,aAAA,EAAe,MAAA,CAAO,MAAA;cAE1B,IAAA,UAAc,IAAA,EAAM,gBAAA,EAAkB,IAAA,EAAM,mBAAA;AAAA"}
@@ -2,21 +2,38 @@ import { t as __name } from "../chunk-OPjESj5l.mjs";
2
2
  import { createDeployCommand } from "../commands/deploy.mjs";
3
3
  import { resolveCredentialOutput } from "../dynamic/resolve-credential.mjs";
4
4
  import * as pulumi from "@pulumi/pulumi";
5
+ import * as crypto from "node:crypto";
6
+ import { fileURLToPath } from "node:url";
5
7
 
6
8
  //#region src/vercel/deploy.ts
7
9
  /**
10
+ * Absolute path to the runnable env applier, resolved next to this module in
11
+ * `dist`. Runs as a plain Node step of the deploy command (mirroring Railway's
12
+ * deploy monitor bin) so env vars never touch the dynamic-provider marshal
13
+ * path. Its logic lives in the unit-tested `env-applier` module.
14
+ */
15
+ const APPLY_ENV_BIN = fileURLToPath(new URL("./bin/apply-env.mjs", import.meta.url));
16
+ /**
8
17
  * Deploys a Vercel project via `vercel deploy --prod --yes`. Isolation and
9
18
  * git-metadata handling are entirely the seam's job β€” list a `DeploySandbox`
10
19
  * (and optionally a `GitGuard`) in `opts.dependsOn` to control them.
11
20
  *
21
+ * Pass `variables` to apply the project's env vars as part of the deploy
22
+ * command (see `VercelDeployArgs.variables` for why this replaces
23
+ * `VercelVariable`).
24
+ *
12
25
  * Recommended preflight: `assertHostBinaries(["vercel"])` (from
13
26
  * `@infracraft/pulumi/sandbox`) at program start, so a missing CLI fails fast
14
27
  * with an install hint instead of mid-deploy.
15
28
  *
16
29
  * @example
17
30
  * ```typescript
18
- * new VercelDeploy("nexus", { projectId: project.id, triggers: [sourceHash, envHash], excludePaths: ["apps/mesh"] },
19
- * { provider, dependsOn: [sandbox, gitGuard] });
31
+ * new VercelDeploy("nexus", {
32
+ * projectId: project.id,
33
+ * variables: { NEXT_PUBLIC_API_URL: apiUrl },
34
+ * triggers: [sourceHash],
35
+ * excludePaths: ["apps/mesh"],
36
+ * }, { provider, dependsOn: [sandbox, gitGuard] });
20
37
  * ```
21
38
  */
22
39
  var VercelDeploy = class extends pulumi.ComponentResource {
@@ -25,16 +42,26 @@ var VercelDeploy = class extends pulumi.ComponentResource {
25
42
  super("infracraft:vercel:Deploy", name, {}, pulumiOpts);
26
43
  const projectId = project ? project.id : args.projectId;
27
44
  if (!projectId) throw new Error("VercelDeploy: either `args.projectId` or `opts.project` must be provided");
45
+ const environment = {
46
+ VERCEL_TOKEN: resolveCredentialOutput(provider.token, provider.tokenEnvVar),
47
+ VERCEL_ORG_ID: provider.teamId,
48
+ VERCEL_PROJECT_ID: projectId
49
+ };
50
+ let cli = "vercel deploy --prod --yes";
51
+ let triggers = args.triggers;
52
+ if (args.variables) {
53
+ const variablesJson = pulumi.output(args.variables).apply((variables) => JSON.stringify(Object.fromEntries(Object.entries(variables).sort(([a], [b]) => a.localeCompare(b)))));
54
+ environment.IC_VC_ENV_JSON = pulumi.secret(variablesJson);
55
+ const variablesDigest = pulumi.unsecret(variablesJson.apply((json) => crypto.createHash("sha256").update(json).digest("hex")));
56
+ triggers = pulumi.output(args.triggers).apply((existing) => [...existing, variablesDigest]);
57
+ cli = `node "${APPLY_ENV_BIN}" && ${cli}`;
58
+ }
28
59
  const { deploymentUrl } = createDeployCommand({
29
60
  name,
30
- cli: "vercel deploy --prod --yes",
31
- triggers: args.triggers,
61
+ cli,
62
+ triggers,
32
63
  excludePaths: args.excludePaths,
33
- environment: {
34
- VERCEL_TOKEN: resolveCredentialOutput(provider.token, provider.tokenEnvVar),
35
- VERCEL_ORG_ID: provider.teamId,
36
- VERCEL_PROJECT_ID: projectId
37
- }
64
+ environment
38
65
  }, {
39
66
  parent: this,
40
67
  ...pulumiOpts
@@ -1 +1 @@
1
- {"version":3,"file":"deploy.mjs","names":[],"sources":["../../src/vercel/deploy.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\n\nimport { createDeployCommand } from \"../commands/deploy\";\nimport { resolveCredentialOutput } from \"../dynamic/resolve-credential\";\nimport type { VercelProject } from \"./project\";\nimport type { VercelProvider } from \"./provider\";\n\ntype VercelDeployOptions = Omit<pulumi.ComponentResourceOptions, \"provider\"> & {\n\t/** Vercel authentication context. */\n\tprovider: VercelProvider;\n\t/** VercelProject to source the project ID from (optional if `args.projectId` given). */\n\tproject?: VercelProject;\n};\n\nexport interface VercelDeployArgs {\n\t/** Vercel project ID. Required when `opts.project` is not provided. */\n\tprojectId?: pulumi.Input<string>;\n\t/** Redeploy triggers (e.g. source hash, env content hash). */\n\ttriggers: pulumi.Input<pulumi.Input<string>[]>;\n\t/** Paths excluded from the upload when running with `DeploySandbox` + `GitGuard`. */\n\texcludePaths?: string[];\n}\n\n/**\n * Deploys a Vercel project via `vercel deploy --prod --yes`. Isolation and\n * git-metadata handling are entirely the seam's job β€” list a `DeploySandbox`\n * (and optionally a `GitGuard`) in `opts.dependsOn` to control them.\n *\n * Recommended preflight: `assertHostBinaries([\"vercel\"])` (from\n * `@infracraft/pulumi/sandbox`) at program start, so a missing CLI fails fast\n * with an install hint instead of mid-deploy.\n *\n * @example\n * ```typescript\n * new VercelDeploy(\"nexus\", { projectId: project.id, triggers: [sourceHash, envHash], excludePaths: [\"apps/mesh\"] },\n * { provider, dependsOn: [sandbox, gitGuard] });\n * ```\n */\nexport class VercelDeploy extends pulumi.ComponentResource {\n\tpublic readonly deploymentUrl: pulumi.Output<string>;\n\n\tconstructor(name: string, args: VercelDeployArgs, opts: VercelDeployOptions) {\n\t\tconst { provider, project, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:vercel:Deploy\", 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\"VercelDeploy: either `args.projectId` or `opts.project` must be provided\",\n\t\t\t);\n\t\t}\n\n\t\tconst { deploymentUrl } = createDeployCommand(\n\t\t\t{\n\t\t\t\tname,\n\t\t\t\tcli: \"vercel deploy --prod --yes\",\n\t\t\t\ttriggers: args.triggers,\n\t\t\t\texcludePaths: args.excludePaths,\n\t\t\t\tenvironment: {\n\t\t\t\t\t// Resolved at program runtime (secret) so the CLI still gets the\n\t\t\t\t\t// actual value when the provider is configured via tokenEnvVar β€”\n\t\t\t\t\t// without the credential ever being a dynamic-resource input.\n\t\t\t\t\tVERCEL_TOKEN: resolveCredentialOutput(\n\t\t\t\t\t\tprovider.token,\n\t\t\t\t\t\tprovider.tokenEnvVar,\n\t\t\t\t\t),\n\t\t\t\t\tVERCEL_ORG_ID: provider.teamId,\n\t\t\t\t\tVERCEL_PROJECT_ID: projectId,\n\t\t\t\t},\n\t\t\t},\n\t\t\t{ parent: this, ...pulumiOpts },\n\t\t);\n\n\t\tthis.deploymentUrl = deploymentUrl;\n\n\t\tthis.registerOutputs({ deploymentUrl: this.deploymentUrl });\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAsCA,IAAa,eAAb,cAAkC,OAAO,kBAAkB;CAG1D,YAAY,MAAc,MAAwB,MAA2B;EAC5E,MAAM,EAAE,UAAU,SAAS,GAAG,eAAe;EAE7C,MAAM,4BAA4B,MAAM,CAAC,GAAG,UAAU;EAEtD,MAAM,YAAY,UACf,QAAQ,KACP,KAAK;EAET,IAAI,CAAC,WACJ,MAAM,IAAI,MACT,0EACD;EAGD,MAAM,EAAE,kBAAkB,oBACzB;GACC;GACA,KAAK;GACL,UAAU,KAAK;GACf,cAAc,KAAK;GACnB,aAAa;IAIZ,cAAc,wBACb,SAAS,OACT,SAAS,WACV;IACA,eAAe,SAAS;IACxB,mBAAmB;GACpB;EACD,GACA;GAAE,QAAQ;GAAM,GAAG;EAAW,CAC/B;EAEA,KAAK,gBAAgB;EAErB,KAAK,gBAAgB,EAAE,eAAe,KAAK,cAAc,CAAC;CAC3D;AACD"}
1
+ {"version":3,"file":"deploy.mjs","names":[],"sources":["../../src/vercel/deploy.ts"],"sourcesContent":["import * as crypto from \"node:crypto\";\nimport { fileURLToPath } from \"node:url\";\n\nimport * as pulumi from \"@pulumi/pulumi\";\n\nimport { createDeployCommand } from \"../commands/deploy\";\nimport { resolveCredentialOutput } from \"../dynamic/resolve-credential\";\nimport type { VercelProject } from \"./project\";\nimport type { VercelProvider } from \"./provider\";\n\ntype VercelDeployOptions = Omit<pulumi.ComponentResourceOptions, \"provider\"> & {\n\t/** Vercel authentication context. */\n\tprovider: VercelProvider;\n\t/** VercelProject to source the project ID from (optional if `args.projectId` given). */\n\tproject?: VercelProject;\n};\n\nexport interface VercelDeployArgs {\n\t/** Vercel project ID. Required when `opts.project` is not provided. */\n\tprojectId?: pulumi.Input<string>;\n\t/** Redeploy triggers (e.g. source hash, env content hash). */\n\ttriggers: pulumi.Input<pulumi.Input<string>[]>;\n\t/** Paths excluded from the upload when running with `DeploySandbox` + `GitGuard`. */\n\texcludePaths?: string[];\n\t/**\n\t * Env vars upserted for the project (production + preview + development)\n\t * by the deploy command itself, right before `vercel deploy` runs β€” the\n\t * preferred replacement for `VercelVariable`. The dynamic-resource path\n\t * hits a Pulumi engine-internal stateful bug on clean-slate first creates\n\t * (\"Unexpected struct type\", strictly alternating pass/fail across\n\t * identical from-zero runs, reproduced with plain-literal inputs and zero\n\t * secrets), so env vars are applied inside the command flow, off the\n\t * dynamic-provider marshal path entirely. A change to any variable\n\t * redeploys automatically (a non-secret digest of the map joins the\n\t * command triggers). Values must be known at preview (config-derived) β€”\n\t * the same class as the token the command env already carries.\n\t */\n\tvariables?: pulumi.Input<Record<string, pulumi.Input<string>>>;\n}\n\n/**\n * Absolute path to the runnable env applier, resolved next to this module in\n * `dist`. Runs as a plain Node step of the deploy command (mirroring Railway's\n * deploy monitor bin) so env vars never touch the dynamic-provider marshal\n * path. Its logic lives in the unit-tested `env-applier` module.\n */\nconst APPLY_ENV_BIN = fileURLToPath(\n\tnew URL(\"./bin/apply-env.mjs\", import.meta.url),\n);\n\n/**\n * Deploys a Vercel project via `vercel deploy --prod --yes`. Isolation and\n * git-metadata handling are entirely the seam's job β€” list a `DeploySandbox`\n * (and optionally a `GitGuard`) in `opts.dependsOn` to control them.\n *\n * Pass `variables` to apply the project's env vars as part of the deploy\n * command (see `VercelDeployArgs.variables` for why this replaces\n * `VercelVariable`).\n *\n * Recommended preflight: `assertHostBinaries([\"vercel\"])` (from\n * `@infracraft/pulumi/sandbox`) at program start, so a missing CLI fails fast\n * with an install hint instead of mid-deploy.\n *\n * @example\n * ```typescript\n * new VercelDeploy(\"nexus\", {\n * projectId: project.id,\n * variables: { NEXT_PUBLIC_API_URL: apiUrl },\n * triggers: [sourceHash],\n * excludePaths: [\"apps/mesh\"],\n * }, { provider, dependsOn: [sandbox, gitGuard] });\n * ```\n */\nexport class VercelDeploy extends pulumi.ComponentResource {\n\tpublic readonly deploymentUrl: pulumi.Output<string>;\n\n\tconstructor(name: string, args: VercelDeployArgs, opts: VercelDeployOptions) {\n\t\tconst { provider, project, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:vercel:Deploy\", 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\"VercelDeploy: either `args.projectId` or `opts.project` must be provided\",\n\t\t\t);\n\t\t}\n\n\t\tconst environment: Record<string, pulumi.Input<string>> = {\n\t\t\t// Resolved at program runtime (secret) so the CLI still gets the\n\t\t\t// actual value when the provider is configured via tokenEnvVar β€”\n\t\t\t// without the credential ever being a dynamic-resource input.\n\t\t\tVERCEL_TOKEN: resolveCredentialOutput(\n\t\t\t\tprovider.token,\n\t\t\t\tprovider.tokenEnvVar,\n\t\t\t),\n\t\t\tVERCEL_ORG_ID: provider.teamId,\n\t\t\tVERCEL_PROJECT_ID: projectId,\n\t\t};\n\n\t\tlet cli = \"vercel deploy --prod --yes\";\n\t\tlet triggers = args.triggers;\n\n\t\tif (args.variables) {\n\t\t\t// Sorted-key JSON so the payload β€” and the trigger digest below β€” is\n\t\t\t// insensitive to declaration order. Marked secret so the values are\n\t\t\t// masked in state and diagnostics; they reach the applier bin through\n\t\t\t// the command environment, never the script text.\n\t\t\tconst variablesJson = pulumi\n\t\t\t\t.output(args.variables)\n\t\t\t\t.apply((variables) =>\n\t\t\t\t\tJSON.stringify(\n\t\t\t\t\t\tObject.fromEntries(\n\t\t\t\t\t\t\tObject.entries(variables).sort(([a], [b]) => a.localeCompare(b)),\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t);\n\n\t\t\tenvironment.IC_VC_ENV_JSON = pulumi.secret(variablesJson);\n\n\t\t\t// Non-secret digest of the payload (same rationale as `hash(env)`):\n\t\t\t// any variable change redeploys, without carrying a secret trigger.\n\t\t\tconst variablesDigest = pulumi.unsecret(\n\t\t\t\tvariablesJson.apply((json) =>\n\t\t\t\t\tcrypto.createHash(\"sha256\").update(json).digest(\"hex\"),\n\t\t\t\t),\n\t\t\t);\n\n\t\t\ttriggers = pulumi\n\t\t\t\t.output(args.triggers)\n\t\t\t\t.apply((existing) => [...existing, variablesDigest]);\n\n\t\t\tcli = `node \"${APPLY_ENV_BIN}\" && ${cli}`;\n\t\t}\n\n\t\tconst { deploymentUrl } = createDeployCommand(\n\t\t\t{\n\t\t\t\tname,\n\t\t\t\tcli,\n\t\t\t\ttriggers,\n\t\t\t\texcludePaths: args.excludePaths,\n\t\t\t\tenvironment,\n\t\t\t},\n\t\t\t{ parent: this, ...pulumiOpts },\n\t\t);\n\n\t\tthis.deploymentUrl = deploymentUrl;\n\n\t\tthis.registerOutputs({ deploymentUrl: this.deploymentUrl });\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;AA8CA,MAAM,gBAAgB,cACrB,IAAI,IAAI,uBAAuB,OAAO,KAAK,GAAG,CAC/C;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,IAAa,eAAb,cAAkC,OAAO,kBAAkB;CAG1D,YAAY,MAAc,MAAwB,MAA2B;EAC5E,MAAM,EAAE,UAAU,SAAS,GAAG,eAAe;EAE7C,MAAM,4BAA4B,MAAM,CAAC,GAAG,UAAU;EAEtD,MAAM,YAAY,UACf,QAAQ,KACP,KAAK;EAET,IAAI,CAAC,WACJ,MAAM,IAAI,MACT,0EACD;EAGD,MAAM,cAAoD;GAIzD,cAAc,wBACb,SAAS,OACT,SAAS,WACV;GACA,eAAe,SAAS;GACxB,mBAAmB;EACpB;EAEA,IAAI,MAAM;EACV,IAAI,WAAW,KAAK;EAEpB,IAAI,KAAK,WAAW;GAKnB,MAAM,gBAAgB,OACpB,OAAO,KAAK,SAAS,EACrB,OAAO,cACP,KAAK,UACJ,OAAO,YACN,OAAO,QAAQ,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC,CAChE,CACD,CACD;GAED,YAAY,iBAAiB,OAAO,OAAO,aAAa;GAIxD,MAAM,kBAAkB,OAAO,SAC9B,cAAc,OAAO,SACpB,OAAO,WAAW,QAAQ,EAAE,OAAO,IAAI,EAAE,OAAO,KAAK,CACtD,CACD;GAEA,WAAW,OACT,OAAO,KAAK,QAAQ,EACpB,OAAO,aAAa,CAAC,GAAG,UAAU,eAAe,CAAC;GAEpD,MAAM,SAAS,cAAc,OAAO;EACrC;EAEA,MAAM,EAAE,kBAAkB,oBACzB;GACC;GACA;GACA;GACA,cAAc,KAAK;GACnB;EACD,GACA;GAAE,QAAQ;GAAM,GAAG;EAAW,CAC/B;EAEA,KAAK,gBAAgB;EAErB,KAAK,gBAAgB,EAAE,eAAe,KAAK,cAAc,CAAC;CAC3D;AACD"}
@@ -0,0 +1,48 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ const require_chunk = require('../chunk-BVYJZCqc.cjs');
3
+ const require_vercel_client = require('./client.cjs');
4
+ const require_vercel_env_var_api = require('./env-var-api.cjs');
5
+
6
+ //#region src/vercel/env-applier.ts
7
+ /**
8
+ * Deploy-integrated Vercel env var applier.
9
+ *
10
+ * Applies a key→value map to a Vercel project (production + preview +
11
+ * development) through the plain REST API, inside the deploy COMMAND flow β€”
12
+ * never as a Pulumi dynamic resource. The dynamic-resource path
13
+ * (`VercelVariable`) hits a Pulumi engine-internal stateful bug on clean-slate
14
+ * first creates ("Unexpected struct type", strictly alternating pass/fail
15
+ * across identical from-zero runs, reproduced with plain-literal inputs β€” zero
16
+ * Outputs/secrets β€” on matched CLI/SDK versions; four structural theories
17
+ * falsified by bisection). The fix is architectural: run the env upserts as an
18
+ * imperative step of the deploy command β€” the same pattern as the Railway
19
+ * deploy monitor owning imperative deploy steps β€” off the dynamic-provider
20
+ * marshal path entirely.
21
+ *
22
+ * Dependency-injected (`log`) so the behavior is unit-tested. The runnable
23
+ * wrapper that wires real IO lives in `./bin/apply-env.ts`.
24
+ */
25
+ /**
26
+ * Upserts every entry of `variables` for the project, targeting production +
27
+ * preview + development (create, or update in place on ENV_CONFLICT β€” the
28
+ * exact semantics `VercelVariable` uses, via the shared `env-var-api`).
29
+ *
30
+ * Logs one line per applied key β€” names only, never values. Throws on the
31
+ * first failure with the offending key named, so the bin exits non-zero and
32
+ * the deploy never runs against a half-applied environment.
33
+ */
34
+ async function applyVercelEnv(input, deps) {
35
+ const client = new require_vercel_client.VercelClient(input.token, input.teamId);
36
+ for (const [key, value] of Object.entries(input.variables)) {
37
+ try {
38
+ await require_vercel_env_var_api.upsertEnvVar(client, input.projectId, key, value, deps.log);
39
+ } catch (error) {
40
+ throw new Error(`failed to apply Vercel env var "${key}": ${error instanceof Error ? error.message : String(error)}`);
41
+ }
42
+ deps.log(`applied Vercel env var "${key}"`);
43
+ }
44
+ }
45
+
46
+ //#endregion
47
+ exports.applyVercelEnv = applyVercelEnv;
48
+ //# sourceMappingURL=env-applier.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-applier.cjs","names":["VercelClient","upsertEnvVar"],"sources":["../../src/vercel/env-applier.ts"],"sourcesContent":["/**\n * Deploy-integrated Vercel env var applier.\n *\n * Applies a key→value map to a Vercel project (production + preview +\n * development) through the plain REST API, inside the deploy COMMAND flow —\n * never as a Pulumi dynamic resource. The dynamic-resource path\n * (`VercelVariable`) hits a Pulumi engine-internal stateful bug on clean-slate\n * first creates (\"Unexpected struct type\", strictly alternating pass/fail\n * across identical from-zero runs, reproduced with plain-literal inputs — zero\n * Outputs/secrets — on matched CLI/SDK versions; four structural theories\n * falsified by bisection). The fix is architectural: run the env upserts as an\n * imperative step of the deploy command — the same pattern as the Railway\n * deploy monitor owning imperative deploy steps — off the dynamic-provider\n * marshal path entirely.\n *\n * Dependency-injected (`log`) so the behavior is unit-tested. The runnable\n * wrapper that wires real IO lives in `./bin/apply-env.ts`.\n */\nimport { VercelClient } from \"./client\";\nimport { upsertEnvVar } from \"./env-var-api\";\n\n/** Everything the applier needs to upsert one project's env vars. */\nexport interface ApplyVercelEnvInput {\n\t/** Vercel API bearer token. */\n\ttoken: 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/** Injected side-effecting collaborators (real impl in the bin, fakes in tests). */\nexport interface ApplyVercelEnvDeps {\n\tlog: (line: string) => void;\n}\n\n/**\n * Upserts every entry of `variables` for the project, targeting production +\n * preview + development (create, or update in place on ENV_CONFLICT — the\n * exact semantics `VercelVariable` uses, via the shared `env-var-api`).\n *\n * Logs one line per applied key — names only, never values. Throws on the\n * first failure with the offending key named, so the bin exits non-zero and\n * the deploy never runs against a half-applied environment.\n */\nexport async function applyVercelEnv(\n\tinput: ApplyVercelEnvInput,\n\tdeps: ApplyVercelEnvDeps,\n): Promise<void> {\n\tconst client = new VercelClient(input.token, input.teamId);\n\n\tfor (const [key, value] of Object.entries(input.variables)) {\n\t\ttry {\n\t\t\tawait upsertEnvVar(client, input.projectId, key, value, deps.log);\n\t\t} catch (error) {\n\t\t\tthrow new Error(\n\t\t\t\t`failed to apply Vercel env var \"${key}\": ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t);\n\t\t}\n\n\t\tdeps.log(`applied Vercel env var \"${key}\"`);\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkDA,eAAsB,eACrB,OACA,MACgB;CAChB,MAAM,SAAS,IAAIA,mCAAa,MAAM,OAAO,MAAM,MAAM;CAEzD,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,SAAS,GAAG;EAC3D,IAAI;GACH,MAAMC,wCAAa,QAAQ,MAAM,WAAW,KAAK,OAAO,KAAK,GAAG;EACjE,SAAS,OAAO;GACf,MAAM,IAAI,MACT,mCAAmC,IAAI,KAAK,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAClG;EACD;EAEA,KAAK,IAAI,2BAA2B,IAAI,EAAE;CAC3C;AACD"}
@@ -0,0 +1,31 @@
1
+ import { t as __name } from "../chunk-OPjESj5l.cjs";
2
+
3
+ //#region src/vercel/env-applier.d.ts
4
+ /** Everything the applier needs to upsert one project's env vars. */
5
+ interface ApplyVercelEnvInput {
6
+ /** Vercel API bearer token. */
7
+ token: string;
8
+ /** Vercel team/org ID. */
9
+ teamId: string;
10
+ /** Vercel project ID. */
11
+ projectId: string;
12
+ /** Key-value map of environment variable names to their values. */
13
+ variables: Record<string, string>;
14
+ }
15
+ /** Injected side-effecting collaborators (real impl in the bin, fakes in tests). */
16
+ interface ApplyVercelEnvDeps {
17
+ log: (line: string) => void;
18
+ }
19
+ /**
20
+ * Upserts every entry of `variables` for the project, targeting production +
21
+ * preview + development (create, or update in place on ENV_CONFLICT β€” the
22
+ * exact semantics `VercelVariable` uses, via the shared `env-var-api`).
23
+ *
24
+ * Logs one line per applied key β€” names only, never values. Throws on the
25
+ * first failure with the offending key named, so the bin exits non-zero and
26
+ * the deploy never runs against a half-applied environment.
27
+ */
28
+ declare function applyVercelEnv(input: ApplyVercelEnvInput, deps: ApplyVercelEnvDeps): Promise<void>;
29
+ //#endregion
30
+ export { ApplyVercelEnvDeps, ApplyVercelEnvInput, applyVercelEnv };
31
+ //# sourceMappingURL=env-applier.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-applier.d.cts","names":[],"sources":["../../src/vercel/env-applier.ts"],"mappings":";;;;UAsBiB,mBAAA;;EAEhB,KAAA;EAFmC;EAKnC,MAAA;EAMiB;EAHjB,SAAA;EAHA;EAMA,SAAA,EAAW,MAAM;AAAA;;UAID,kBAAA;EAChB,GAAA,GAAM,IAAY;AAAA;;;;AAAA;AAYnB;;;;;iBAAsB,cAAA,CACrB,KAAA,EAAO,mBAAA,EACP,IAAA,EAAM,kBAAA,GACJ,OAAA"}
@@ -0,0 +1,31 @@
1
+ import { t as __name } from "../chunk-OPjESj5l.mjs";
2
+
3
+ //#region src/vercel/env-applier.d.ts
4
+ /** Everything the applier needs to upsert one project's env vars. */
5
+ interface ApplyVercelEnvInput {
6
+ /** Vercel API bearer token. */
7
+ token: string;
8
+ /** Vercel team/org ID. */
9
+ teamId: string;
10
+ /** Vercel project ID. */
11
+ projectId: string;
12
+ /** Key-value map of environment variable names to their values. */
13
+ variables: Record<string, string>;
14
+ }
15
+ /** Injected side-effecting collaborators (real impl in the bin, fakes in tests). */
16
+ interface ApplyVercelEnvDeps {
17
+ log: (line: string) => void;
18
+ }
19
+ /**
20
+ * Upserts every entry of `variables` for the project, targeting production +
21
+ * preview + development (create, or update in place on ENV_CONFLICT β€” the
22
+ * exact semantics `VercelVariable` uses, via the shared `env-var-api`).
23
+ *
24
+ * Logs one line per applied key β€” names only, never values. Throws on the
25
+ * first failure with the offending key named, so the bin exits non-zero and
26
+ * the deploy never runs against a half-applied environment.
27
+ */
28
+ declare function applyVercelEnv(input: ApplyVercelEnvInput, deps: ApplyVercelEnvDeps): Promise<void>;
29
+ //#endregion
30
+ export { ApplyVercelEnvDeps, ApplyVercelEnvInput, applyVercelEnv };
31
+ //# sourceMappingURL=env-applier.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-applier.d.mts","names":[],"sources":["../../src/vercel/env-applier.ts"],"mappings":";;;;UAsBiB,mBAAA;;EAEhB,KAAA;EAFmC;EAKnC,MAAA;EAMiB;EAHjB,SAAA;EAHA;EAMA,SAAA,EAAW,MAAM;AAAA;;UAID,kBAAA;EAChB,GAAA,GAAM,IAAY;AAAA;;;;AAAA;AAYnB;;;;;iBAAsB,cAAA,CACrB,KAAA,EAAO,mBAAA,EACP,IAAA,EAAM,kBAAA,GACJ,OAAA"}