@infracraft/pulumi 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of @infracraft/pulumi might be problematic. Click here for more details.

Files changed (151) hide show
  1. package/dist/chunk-BVYJZCqc.cjs +44 -0
  2. package/dist/chunk-OPjESj5l.mjs +9 -0
  3. package/dist/git-guard.cjs +62 -0
  4. package/dist/git-guard.cjs.map +1 -0
  5. package/dist/git-guard.d.cts +13 -0
  6. package/dist/git-guard.d.cts.map +1 -0
  7. package/dist/git-guard.d.mts +13 -0
  8. package/dist/git-guard.d.mts.map +1 -0
  9. package/dist/git-guard.mjs +56 -0
  10. package/dist/git-guard.mjs.map +1 -0
  11. package/dist/hash.cjs +40 -0
  12. package/dist/hash.cjs.map +1 -0
  13. package/dist/hash.d.cts +10 -0
  14. package/dist/hash.d.cts.map +1 -0
  15. package/dist/hash.d.mts +10 -0
  16. package/dist/hash.d.mts.map +1 -0
  17. package/dist/hash.mjs +36 -0
  18. package/dist/hash.mjs.map +1 -0
  19. package/dist/neon/branch.cjs +113 -0
  20. package/dist/neon/branch.cjs.map +1 -0
  21. package/dist/neon/branch.d.cts +40 -0
  22. package/dist/neon/branch.d.cts.map +1 -0
  23. package/dist/neon/branch.d.mts +40 -0
  24. package/dist/neon/branch.d.mts.map +1 -0
  25. package/dist/neon/branch.mjs +111 -0
  26. package/dist/neon/branch.mjs.map +1 -0
  27. package/dist/neon/client.cjs +85 -0
  28. package/dist/neon/client.cjs.map +1 -0
  29. package/dist/neon/client.d.cts +59 -0
  30. package/dist/neon/client.d.cts.map +1 -0
  31. package/dist/neon/client.d.mts +59 -0
  32. package/dist/neon/client.d.mts.map +1 -0
  33. package/dist/neon/client.mjs +84 -0
  34. package/dist/neon/client.mjs.map +1 -0
  35. package/dist/neon/database.cjs +117 -0
  36. package/dist/neon/database.cjs.map +1 -0
  37. package/dist/neon/database.d.cts +48 -0
  38. package/dist/neon/database.d.cts.map +1 -0
  39. package/dist/neon/database.d.mts +48 -0
  40. package/dist/neon/database.d.mts.map +1 -0
  41. package/dist/neon/database.mjs +115 -0
  42. package/dist/neon/database.mjs.map +1 -0
  43. package/dist/neon/endpoint.cjs +167 -0
  44. package/dist/neon/endpoint.cjs.map +1 -0
  45. package/dist/neon/endpoint.d.cts +57 -0
  46. package/dist/neon/endpoint.d.cts.map +1 -0
  47. package/dist/neon/endpoint.d.mts +57 -0
  48. package/dist/neon/endpoint.d.mts.map +1 -0
  49. package/dist/neon/endpoint.mjs +165 -0
  50. package/dist/neon/endpoint.mjs.map +1 -0
  51. package/dist/neon/index.cjs +14 -0
  52. package/dist/neon/index.d.cts +7 -0
  53. package/dist/neon/index.d.mts +7 -0
  54. package/dist/neon/index.mjs +8 -0
  55. package/dist/neon/project.cjs +120 -0
  56. package/dist/neon/project.cjs.map +1 -0
  57. package/dist/neon/project.d.cts +52 -0
  58. package/dist/neon/project.d.cts.map +1 -0
  59. package/dist/neon/project.d.mts +52 -0
  60. package/dist/neon/project.d.mts.map +1 -0
  61. package/dist/neon/project.mjs +118 -0
  62. package/dist/neon/project.mjs.map +1 -0
  63. package/dist/neon/role.cjs +135 -0
  64. package/dist/neon/role.cjs.map +1 -0
  65. package/dist/neon/role.d.cts +49 -0
  66. package/dist/neon/role.d.cts.map +1 -0
  67. package/dist/neon/role.d.mts +49 -0
  68. package/dist/neon/role.d.mts.map +1 -0
  69. package/dist/neon/role.mjs +133 -0
  70. package/dist/neon/role.mjs.map +1 -0
  71. package/dist/railway/client.cjs +59 -0
  72. package/dist/railway/client.cjs.map +1 -0
  73. package/dist/railway/client.d.cts +39 -0
  74. package/dist/railway/client.d.cts.map +1 -0
  75. package/dist/railway/client.d.mts +39 -0
  76. package/dist/railway/client.d.mts.map +1 -0
  77. package/dist/railway/client.mjs +58 -0
  78. package/dist/railway/client.mjs.map +1 -0
  79. package/dist/railway/deploy.cjs +40 -0
  80. package/dist/railway/deploy.cjs.map +1 -0
  81. package/dist/railway/deploy.d.cts +47 -0
  82. package/dist/railway/deploy.d.cts.map +1 -0
  83. package/dist/railway/deploy.d.mts +47 -0
  84. package/dist/railway/deploy.d.mts.map +1 -0
  85. package/dist/railway/deploy.mjs +37 -0
  86. package/dist/railway/deploy.mjs.map +1 -0
  87. package/dist/railway/domain.cjs +236 -0
  88. package/dist/railway/domain.cjs.map +1 -0
  89. package/dist/railway/domain.d.cts +52 -0
  90. package/dist/railway/domain.d.cts.map +1 -0
  91. package/dist/railway/domain.d.mts +52 -0
  92. package/dist/railway/domain.d.mts.map +1 -0
  93. package/dist/railway/domain.mjs +234 -0
  94. package/dist/railway/domain.mjs.map +1 -0
  95. package/dist/railway/index.cjs +16 -0
  96. package/dist/railway/index.d.cts +8 -0
  97. package/dist/railway/index.d.mts +8 -0
  98. package/dist/railway/index.mjs +9 -0
  99. package/dist/railway/project.cjs +231 -0
  100. package/dist/railway/project.cjs.map +1 -0
  101. package/dist/railway/project.d.cts +52 -0
  102. package/dist/railway/project.d.cts.map +1 -0
  103. package/dist/railway/project.d.mts +52 -0
  104. package/dist/railway/project.d.mts.map +1 -0
  105. package/dist/railway/project.mjs +229 -0
  106. package/dist/railway/project.mjs.map +1 -0
  107. package/dist/railway/service.cjs +268 -0
  108. package/dist/railway/service.cjs.map +1 -0
  109. package/dist/railway/service.d.cts +88 -0
  110. package/dist/railway/service.d.cts.map +1 -0
  111. package/dist/railway/service.d.mts +88 -0
  112. package/dist/railway/service.d.mts.map +1 -0
  113. package/dist/railway/service.mjs +266 -0
  114. package/dist/railway/service.mjs.map +1 -0
  115. package/dist/railway/variable.cjs +149 -0
  116. package/dist/railway/variable.cjs.map +1 -0
  117. package/dist/railway/variable.d.cts +52 -0
  118. package/dist/railway/variable.d.cts.map +1 -0
  119. package/dist/railway/variable.d.mts +52 -0
  120. package/dist/railway/variable.d.mts.map +1 -0
  121. package/dist/railway/variable.mjs +147 -0
  122. package/dist/railway/variable.mjs.map +1 -0
  123. package/dist/railway/volume.cjs +179 -0
  124. package/dist/railway/volume.cjs.map +1 -0
  125. package/dist/railway/volume.d.cts +52 -0
  126. package/dist/railway/volume.d.cts.map +1 -0
  127. package/dist/railway/volume.d.mts +52 -0
  128. package/dist/railway/volume.d.mts.map +1 -0
  129. package/dist/railway/volume.mjs +177 -0
  130. package/dist/railway/volume.mjs.map +1 -0
  131. package/dist/vercel/deploy.cjs +65 -0
  132. package/dist/vercel/deploy.cjs.map +1 -0
  133. package/dist/vercel/deploy.d.cts +54 -0
  134. package/dist/vercel/deploy.d.cts.map +1 -0
  135. package/dist/vercel/deploy.d.mts +54 -0
  136. package/dist/vercel/deploy.d.mts.map +1 -0
  137. package/dist/vercel/deploy.mjs +61 -0
  138. package/dist/vercel/deploy.mjs.map +1 -0
  139. package/dist/vercel/index.cjs +6 -0
  140. package/dist/vercel/index.d.cts +3 -0
  141. package/dist/vercel/index.d.mts +3 -0
  142. package/dist/vercel/index.mjs +4 -0
  143. package/dist/vercel/variable.cjs +280 -0
  144. package/dist/vercel/variable.cjs.map +1 -0
  145. package/dist/vercel/variable.d.cts +55 -0
  146. package/dist/vercel/variable.d.cts.map +1 -0
  147. package/dist/vercel/variable.d.mts +55 -0
  148. package/dist/vercel/variable.d.mts.map +1 -0
  149. package/dist/vercel/variable.mjs +278 -0
  150. package/dist/vercel/variable.mjs.map +1 -0
  151. package/package.json +62 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"volume.mjs","names":[],"sources":["../../src/railway/volume.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\nimport { RailwayClient } from \"./client.js\";\n\n/** Resolved inputs for the Railway volume dynamic provider. */\nexport interface RailwayVolumeInputs {\n\t/** Railway API bearer token. */\n\ttoken: string;\n\n\t/** Railway project UUID. */\n\tprojectId: string;\n\n\t/** Railway service UUID to attach the volume to. */\n\tserviceId: string;\n\n\t/** Railway environment UUID (e.g. production). */\n\tenvironmentId: string;\n\n\t/** Absolute path inside the container where the volume is mounted (e.g. `\"/data\"`). */\n\tmountPath: string;\n}\n\n/** Persisted state for the Railway volume, extending inputs with the Railway-assigned ID. */\ninterface RailwayVolumeOutputs extends RailwayVolumeInputs {\n\t/** Railway-assigned volume UUID (set after create or adopt). */\n\tvolumeId: string;\n}\n\nconst VOLUME_CREATE = `\n mutation($input: VolumeCreateInput!) {\n volumeCreate(input: $input) {\n id\n name\n }\n }\n`;\n\nconst VOLUME_DELETE = `\n mutation($volumeId: String!) {\n volumeDelete(volumeId: $volumeId)\n }\n`;\n\nconst VOLUMES_QUERY = `\n query($projectId: String!) {\n project(id: $projectId) {\n volumes {\n edges {\n node {\n id\n name\n volumeInstances {\n edges {\n node {\n id\n mountPath\n serviceId\n }\n }\n }\n }\n }\n }\n }\n }\n`;\n\n/**\n * Finds an existing volume attached to a specific service within a project.\n *\n * @param client Authenticated Railway API client\n * @param projectId Railway project UUID to search within\n * @param serviceId Railway service UUID to match volume instances against\n * @returns The volume UUID if found, `undefined` otherwise\n */\nasync function findVolumeByService(\n\tclient: RailwayClient,\n\tprojectId: string,\n\tserviceId: string,\n): Promise<string | undefined> {\n\tconst result = await client.query<{\n\t\tproject: {\n\t\t\tvolumes: {\n\t\t\t\tedges: Array<{\n\t\t\t\t\tnode: {\n\t\t\t\t\t\tid: string;\n\t\t\t\t\t\tvolumeInstances: {\n\t\t\t\t\t\t\tedges: Array<{\n\t\t\t\t\t\t\t\tnode: { serviceId: string };\n\t\t\t\t\t\t\t}>;\n\t\t\t\t\t\t};\n\t\t\t\t\t};\n\t\t\t\t}>;\n\t\t\t};\n\t\t};\n\t}>(VOLUMES_QUERY, { projectId });\n\n\tconst match = result.project.volumes.edges.find((edge) =>\n\t\tedge.node.volumeInstances.edges.some(\n\t\t\t(vi) => vi.node.serviceId === serviceId,\n\t\t),\n\t);\n\n\treturn match?.node.id;\n}\n\n/**\n * Dynamic provider implementing CRUD for Railway persistent volumes.\n *\n * Uses adopt-or-create on `create()`: finds an existing volume by service\n * before creating a new one, making `pulumi up` idempotent from zero.\n * Volumes are immutable after creation — changing `serviceId`, `mountPath`,\n * `environmentId`, or `projectId` triggers replacement (delete + create).\n */\nclass RailwayVolumeProvider implements pulumi.dynamic.ResourceProvider {\n\t/**\n\t * Creates or adopts a Railway volume for the target service.\n\t *\n\t * @param inputs Resolved volume configuration\n\t * @returns The Railway volume UUID as the resource ID\n\t */\n\tasync create(\n\t\tinputs: RailwayVolumeInputs,\n\t): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst client = new RailwayClient(inputs.token);\n\n\t\tlet volumeId = await findVolumeByService(\n\t\t\tclient,\n\t\t\tinputs.projectId,\n\t\t\tinputs.serviceId,\n\t\t);\n\n\t\tif (volumeId) {\n\t\t\tpulumi.log.info(`Adopting existing volume for service (${volumeId})`);\n\t\t} else {\n\t\t\tconst result = await client.query<{\n\t\t\t\tvolumeCreate: { id: string };\n\t\t\t}>(VOLUME_CREATE, {\n\t\t\t\tinput: {\n\t\t\t\t\tprojectId: inputs.projectId,\n\t\t\t\t\tserviceId: inputs.serviceId,\n\t\t\t\t\tenvironmentId: inputs.environmentId,\n\t\t\t\t\tmountPath: inputs.mountPath,\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tvolumeId = result.volumeCreate.id;\n\t\t}\n\n\t\treturn {\n\t\t\tid: volumeId,\n\t\t\touts: { ...inputs, volumeId },\n\t\t};\n\t}\n\n\t/**\n\t * Reads current state for `pulumi refresh` by looking up the volume.\n\t *\n\t * @param id Current Railway volume UUID\n\t * @param props Last known persisted state\n\t * @returns Refreshed resource ID and properties\n\t * @throws {Error} If the volume no longer exists in Railway\n\t */\n\tasync read(\n\t\t_id: string,\n\t\tprops: RailwayVolumeOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\tconst client = new RailwayClient(props.token);\n\n\t\tconst volumeId = await findVolumeByService(\n\t\t\tclient,\n\t\t\tprops.projectId,\n\t\t\tprops.serviceId,\n\t\t);\n\n\t\tif (!volumeId) {\n\t\t\tthrow new Error(\"Railway volume not found during refresh\");\n\t\t}\n\n\t\treturn { id: volumeId, props: { ...props, volumeId } };\n\t}\n\n\t/**\n\t * Deletes the Railway volume. Silently succeeds if already deleted.\n\t *\n\t * @param id Railway volume UUID to delete\n\t * @param props Last known persisted state (used for token)\n\t */\n\tasync delete(id: string, props: RailwayVolumeOutputs): Promise<void> {\n\t\tconst client = new RailwayClient(props.token);\n\n\t\ttry {\n\t\t\tawait client.query(VOLUME_DELETE, { volumeId: id });\n\t\t} catch {\n\t\t\tpulumi.log.warn(\n\t\t\t\t\"Failed to delete Railway volume (may already be deleted)\",\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Compares old and new inputs to determine what changed.\n\t *\n\t * All mutable properties trigger replacement since Railway volumes\n\t * cannot be moved between services, mount paths, environments, or projects.\n\t *\n\t * @param _id Current resource ID (unused)\n\t * @param olds Previous persisted state\n\t * @param news New desired configuration\n\t * @returns Diff result with replacement triggers\n\t */\n\tasync diff(\n\t\t_id: string,\n\t\tolds: RailwayVolumeOutputs,\n\t\tnews: RailwayVolumeInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst replaces: string[] = [];\n\n\t\tif (olds.serviceId !== news.serviceId) {\n\t\t\treplaces.push(\"serviceId\");\n\t\t}\n\n\t\tif (olds.mountPath !== news.mountPath) {\n\t\t\treplaces.push(\"mountPath\");\n\t\t}\n\n\t\tif (olds.environmentId !== news.environmentId) {\n\t\t\treplaces.push(\"environmentId\");\n\t\t}\n\n\t\tif (olds.projectId !== news.projectId) {\n\t\t\treplaces.push(\"projectId\");\n\t\t}\n\n\t\treturn {\n\t\t\tchanges: replaces.length > 0,\n\t\t\treplaces,\n\t\t\tdeleteBeforeReplace: true,\n\t\t};\n\t}\n}\n\n/**\n * Manages a Railway persistent volume with adopt-or-create semantics.\n *\n * Finds existing volumes attached to the target service before creating\n * new ones. Changing `serviceId`, `mountPath`, `environmentId`, or `projectId`\n * triggers replacement (delete + create with new settings).\n *\n * @example\n * ```typescript\n * new RailwayVolume(\"railway-volume-redis\", {\n * token: project.projectToken,\n * projectId: project.projectId,\n * serviceId: service.serviceId,\n * environmentId: project.productionEnvironmentId,\n * mountPath: \"/data\",\n * });\n * ```\n */\nexport class RailwayVolume extends pulumi.dynamic.Resource {\n\t/**\n\t * @param name Pulumi resource name (logical identifier in state)\n\t * @param args Volume configuration inputs\n\t * @param opts Standard Pulumi resource options (e.g. `dependsOn`, `parent`)\n\t */\n\tconstructor(\n\t\tname: string,\n\t\targs: {\n\t\t\t/** Railway API bearer token. */\n\t\t\ttoken: pulumi.Input<string>;\n\n\t\t\t/** Railway project UUID. */\n\t\t\tprojectId: pulumi.Input<string>;\n\n\t\t\t/** Railway service UUID to attach the volume to. */\n\t\t\tserviceId: pulumi.Input<string>;\n\n\t\t\t/** Railway environment UUID (e.g. production). */\n\t\t\tenvironmentId: pulumi.Input<string>;\n\n\t\t\t/** Absolute path inside the container where the volume is mounted. */\n\t\t\tmountPath: pulumi.Input<string>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\tsuper(\n\t\t\tnew RailwayVolumeProvider(),\n\t\t\tname,\n\t\t\t{ ...args, volumeId: undefined },\n\t\t\topts,\n\t\t);\n\t}\n}\n"],"mappings":";;;;;AA2BA,MAAM,gBAAgB;;;;;;;;AAStB,MAAM,gBAAgB;;;;;AAMtB,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCtB,eAAe,oBACd,QACA,WACA,WAC8B;CAwB9B,QANc,MAjBO,OAAO,MAezB,eAAe,EAAE,UAAU,CAAC,GAEV,QAAQ,QAAQ,MAAM,MAAM,SAChD,KAAK,KAAK,gBAAgB,MAAM,MAC9B,OAAO,GAAG,KAAK,cAAc,SAC/B,CAGU,GAAG,KAAK;AACpB;;;;;;;;;AAUA,IAAM,wBAAN,MAAuE;;;;;;;CAOtE,MAAM,OACL,QACuC;EACvC,MAAM,SAAS,IAAI,cAAc,OAAO,KAAK;EAE7C,IAAI,WAAW,MAAM,oBACpB,QACA,OAAO,WACP,OAAO,SACR;EAEA,IAAI,UACH,OAAO,IAAI,KAAK,yCAAyC,SAAS,EAAE;OAapE,YAAW,MAXU,OAAO,MAEzB,eAAe,EACjB,OAAO;GACN,WAAW,OAAO;GAClB,WAAW,OAAO;GAClB,eAAe,OAAO;GACtB,WAAW,OAAO;EACnB,EACD,CAAC,GAEiB,aAAa;EAGhC,OAAO;GACN,IAAI;GACJ,MAAM;IAAE,GAAG;IAAQ;GAAS;EAC7B;CACD;;;;;;;;;CAUA,MAAM,KACL,KACA,OACqC;EAGrC,MAAM,WAAW,MAAM,oBACtB,IAHkB,cAAc,MAAM,KAGjC,GACL,MAAM,WACN,MAAM,SACP;EAEA,IAAI,CAAC,UACJ,MAAM,IAAI,MAAM,yCAAyC;EAG1D,OAAO;GAAE,IAAI;GAAU,OAAO;IAAE,GAAG;IAAO;GAAS;EAAE;CACtD;;;;;;;CAQA,MAAM,OAAO,IAAY,OAA4C;EACpE,MAAM,SAAS,IAAI,cAAc,MAAM,KAAK;EAE5C,IAAI;GACH,MAAM,OAAO,MAAM,eAAe,EAAE,UAAU,GAAG,CAAC;EACnD,QAAQ;GACP,OAAO,IAAI,KACV,0DACD;EACD;CACD;;;;;;;;;;;;CAaA,MAAM,KACL,KACA,MACA,MACqC;EACrC,MAAM,WAAqB,CAAC;EAE5B,IAAI,KAAK,cAAc,KAAK,WAC3B,SAAS,KAAK,WAAW;EAG1B,IAAI,KAAK,cAAc,KAAK,WAC3B,SAAS,KAAK,WAAW;EAG1B,IAAI,KAAK,kBAAkB,KAAK,eAC/B,SAAS,KAAK,eAAe;EAG9B,IAAI,KAAK,cAAc,KAAK,WAC3B,SAAS,KAAK,WAAW;EAG1B,OAAO;GACN,SAAS,SAAS,SAAS;GAC3B;GACA,qBAAqB;EACtB;CACD;AACD;;;;;;;;;;;;;;;;;;;AAoBA,IAAa,gBAAb,cAAmC,OAAO,QAAQ,SAAS;;;;;;CAM1D,YACC,MACA,MAgBA,MACC;EACD,MACC,IAAI,sBAAsB,GAC1B,MACA;GAAE,GAAG;GAAM,UAAU;EAAU,GAC/B,IACD;CACD;AACD"}
@@ -0,0 +1,65 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ const require_chunk = require('../chunk-BVYJZCqc.cjs');
3
+ const require_hash = require('../hash.cjs');
4
+ let node_path = require("node:path");
5
+ node_path = require_chunk.__toESM(node_path, 1);
6
+ let _pulumi_command = require("@pulumi/command");
7
+ _pulumi_command = require_chunk.__toESM(_pulumi_command, 1);
8
+ let _pulumi_pulumi = require("@pulumi/pulumi");
9
+ _pulumi_pulumi = require_chunk.__toESM(_pulumi_pulumi, 1);
10
+
11
+ //#region src/vercel/deploy.ts
12
+ /**
13
+ * Deploys a Vercel project via `vercel deploy --prod --yes` CLI.
14
+ *
15
+ * Triggers on source hash (computed from the app directory) and env content hash
16
+ * (from `VercelVariable.contentHash`). When an env value changes — whether from
17
+ * code, a new mesh URL, or a drift fix after `pulumi refresh` — the hash changes
18
+ * and a redeploy is triggered.
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * new VercelDeploy("vercel-deploy-nexus", {
23
+ * token: vercelConfig.token,
24
+ * projectId: project.id,
25
+ * rootDirectory: "apps/nexus",
26
+ * monorepoRoot,
27
+ * teamId: vercelConfig.teamId,
28
+ * env: { NEXT_PUBLIC_API_URL: meshUrl },
29
+ * });
30
+ * ```
31
+ */
32
+ var VercelDeploy = class extends _pulumi_pulumi.ComponentResource {
33
+ /**
34
+ * @param name Pulumi resource name (logical identifier in state)
35
+ * @param args Deploy configuration
36
+ * @param opts Component resource options
37
+ */
38
+ constructor(name, args, opts) {
39
+ super("infrakit:vercel:Deploy", name, {}, opts);
40
+ const hashParts = [require_hash.hashDirectory(node_path.join(args.monorepoRoot, args.rootDirectory))];
41
+ for (const dir of args.additionalSourceDirs ?? []) hashParts.push(require_hash.hashDirectory(node_path.join(args.monorepoRoot, dir)));
42
+ const sourceHash = hashParts.join(",");
43
+ const commandOpts = { parent: this };
44
+ if (args.dependsOn && args.dependsOn.length > 0) commandOpts.dependsOn = args.dependsOn;
45
+ const envHash = _pulumi_pulumi.all(Object.entries(args.env).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => _pulumi_pulumi.output(v).apply((val) => `${k}=${val}`))).apply((parts) => parts.join(","));
46
+ new _pulumi_command.local.Command(`${name}-deploy`, {
47
+ create: "vercel deploy --prod --yes",
48
+ triggers: [sourceHash, envHash],
49
+ dir: args.monorepoRoot,
50
+ environment: {
51
+ VERCEL_TOKEN: args.token,
52
+ VERCEL_ORG_ID: args.teamId,
53
+ VERCEL_PROJECT_ID: args.projectId
54
+ }
55
+ }, {
56
+ parent: this,
57
+ ...commandOpts.dependsOn ? { dependsOn: commandOpts.dependsOn } : {}
58
+ });
59
+ this.registerOutputs({});
60
+ }
61
+ };
62
+
63
+ //#endregion
64
+ exports.VercelDeploy = VercelDeploy;
65
+ //# sourceMappingURL=deploy.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deploy.cjs","names":["pulumi","hashDirectory","path","command"],"sources":["../../src/vercel/deploy.ts"],"sourcesContent":["import * as path from \"node:path\";\nimport * as command from \"@pulumi/command\";\nimport * as pulumi from \"@pulumi/pulumi\";\nimport { hashDirectory } from \"../hash.js\";\n\n/** Configuration for a Vercel project deploy via `vercel deploy` CLI. */\nexport interface VercelDeployArgs {\n\t/** Vercel API bearer token. */\n\ttoken: pulumi.Input<string>;\n\n\t/** Vercel project ID (Output from `vercel.Project`). */\n\tprojectId: pulumi.Input<string>;\n\n\t/** Relative path from monorepo root to the app directory (e.g. `\"apps/nexus\"`). */\n\trootDirectory: string;\n\n\t/** Absolute path to the monorepo root (working directory for `vercel deploy`). */\n\tmonorepoRoot: string;\n\n\t/** Vercel team/org ID. */\n\tteamId: string;\n\n\t/** Env var map used as deploy trigger. Hashes both keys and resolved values so value changes trigger redeploy. */\n\tenv: Record<string, pulumi.Input<string>>;\n\n\t/** Additional directories (relative to monorepo root) to include in source hash. Changes in these trigger redeploy. */\n\tadditionalSourceDirs?: string[];\n\n\t/** Resources that must complete before the deploy runs. */\n\tdependsOn?: pulumi.Resource[];\n}\n\n/**\n * Deploys a Vercel project via `vercel deploy --prod --yes` CLI.\n *\n * Triggers on source hash (computed from the app directory) and env content hash\n * (from `VercelVariable.contentHash`). When an env value changes — whether from\n * code, a new mesh URL, or a drift fix after `pulumi refresh` — the hash changes\n * and a redeploy is triggered.\n *\n * @example\n * ```typescript\n * new VercelDeploy(\"vercel-deploy-nexus\", {\n * token: vercelConfig.token,\n * projectId: project.id,\n * rootDirectory: \"apps/nexus\",\n * monorepoRoot,\n * teamId: vercelConfig.teamId,\n * env: { NEXT_PUBLIC_API_URL: meshUrl },\n * });\n * ```\n */\nexport class VercelDeploy extends pulumi.ComponentResource {\n\t/**\n\t * @param name Pulumi resource name (logical identifier in state)\n\t * @param args Deploy configuration\n\t * @param opts Component resource options\n\t */\n\tconstructor(\n\t\tname: string,\n\t\targs: VercelDeployArgs,\n\t\topts?: pulumi.ComponentResourceOptions,\n\t) {\n\t\tsuper(\"infrakit:vercel:Deploy\", name, {}, opts);\n\n\t\tconst appDir = path.join(args.monorepoRoot, args.rootDirectory);\n\n\t\tconst hashParts = [hashDirectory(appDir)];\n\n\t\tfor (const dir of args.additionalSourceDirs ?? []) {\n\t\t\thashParts.push(hashDirectory(path.join(args.monorepoRoot, dir)));\n\t\t}\n\n\t\tconst sourceHash = hashParts.join(\",\");\n\n\t\tconst commandOpts: pulumi.ResourceOptions = { parent: this };\n\n\t\tif (args.dependsOn && args.dependsOn.length > 0) {\n\t\t\tcommandOpts.dependsOn = args.dependsOn;\n\t\t}\n\n\t\tconst envHash = pulumi\n\t\t\t.all(\n\t\t\t\tObject.entries(args.env)\n\t\t\t\t\t.sort(([a], [b]) => a.localeCompare(b))\n\t\t\t\t\t.map(([k, v]) => pulumi.output(v).apply((val) => `${k}=${val}`)),\n\t\t\t)\n\t\t\t.apply((parts) => parts.join(\",\"));\n\n\t\tnew command.local.Command(\n\t\t\t`${name}-deploy`,\n\t\t\t{\n\t\t\t\tcreate: \"vercel deploy --prod --yes\",\n\t\t\t\ttriggers: [sourceHash, envHash],\n\t\t\t\tdir: args.monorepoRoot,\n\t\t\t\tenvironment: {\n\t\t\t\t\tVERCEL_TOKEN: args.token,\n\t\t\t\t\tVERCEL_ORG_ID: args.teamId,\n\t\t\t\t\tVERCEL_PROJECT_ID: args.projectId,\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tparent: this,\n\t\t\t\t...(commandOpts.dependsOn ? { dependsOn: commandOpts.dependsOn } : {}),\n\t\t\t},\n\t\t);\n\n\t\tthis.registerOutputs({});\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoDA,IAAa,eAAb,cAAkCA,eAAO,kBAAkB;;;;;;CAM1D,YACC,MACA,MACA,MACC;EACD,MAAM,0BAA0B,MAAM,CAAC,GAAG,IAAI;EAI9C,MAAM,YAAY,CAACC,2BAFJC,UAAK,KAAK,KAAK,cAAc,KAAK,aAEX,CAAC,CAAC;EAExC,KAAK,MAAM,OAAO,KAAK,wBAAwB,CAAC,GAC/C,UAAU,KAAKD,2BAAcC,UAAK,KAAK,KAAK,cAAc,GAAG,CAAC,CAAC;EAGhE,MAAM,aAAa,UAAU,KAAK,GAAG;EAErC,MAAM,cAAsC,EAAE,QAAQ,KAAK;EAE3D,IAAI,KAAK,aAAa,KAAK,UAAU,SAAS,GAC7C,YAAY,YAAY,KAAK;EAG9B,MAAM,UAAUF,eACd,IACA,OAAO,QAAQ,KAAK,GAAG,EACrB,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC,EACrC,KAAK,CAAC,GAAG,OAAOA,eAAO,OAAO,CAAC,EAAE,OAAO,QAAQ,GAAG,EAAE,GAAG,KAAK,CAAC,CACjE,EACC,OAAO,UAAU,MAAM,KAAK,GAAG,CAAC;EAElC,IAAIG,gBAAQ,MAAM,QACjB,GAAG,KAAK,UACR;GACC,QAAQ;GACR,UAAU,CAAC,YAAY,OAAO;GAC9B,KAAK,KAAK;GACV,aAAa;IACZ,cAAc,KAAK;IACnB,eAAe,KAAK;IACpB,mBAAmB,KAAK;GACzB;EACD,GACA;GACC,QAAQ;GACR,GAAI,YAAY,YAAY,EAAE,WAAW,YAAY,UAAU,IAAI,CAAC;EACrE,CACD;EAEA,KAAK,gBAAgB,CAAC,CAAC;CACxB;AACD"}
@@ -0,0 +1,54 @@
1
+ import { t as __name } from "../chunk-OPjESj5l.cjs";
2
+ import * as pulumi from "@pulumi/pulumi";
3
+
4
+ //#region src/vercel/deploy.d.ts
5
+ /** Configuration for a Vercel project deploy via `vercel deploy` CLI. */
6
+ interface VercelDeployArgs {
7
+ /** Vercel API bearer token. */
8
+ token: pulumi.Input<string>;
9
+ /** Vercel project ID (Output from `vercel.Project`). */
10
+ projectId: pulumi.Input<string>;
11
+ /** Relative path from monorepo root to the app directory (e.g. `"apps/nexus"`). */
12
+ rootDirectory: string;
13
+ /** Absolute path to the monorepo root (working directory for `vercel deploy`). */
14
+ monorepoRoot: string;
15
+ /** Vercel team/org ID. */
16
+ teamId: string;
17
+ /** Env var map used as deploy trigger. Hashes both keys and resolved values so value changes trigger redeploy. */
18
+ env: Record<string, pulumi.Input<string>>;
19
+ /** Additional directories (relative to monorepo root) to include in source hash. Changes in these trigger redeploy. */
20
+ additionalSourceDirs?: string[];
21
+ /** Resources that must complete before the deploy runs. */
22
+ dependsOn?: pulumi.Resource[];
23
+ }
24
+ /**
25
+ * Deploys a Vercel project via `vercel deploy --prod --yes` CLI.
26
+ *
27
+ * Triggers on source hash (computed from the app directory) and env content hash
28
+ * (from `VercelVariable.contentHash`). When an env value changes — whether from
29
+ * code, a new mesh URL, or a drift fix after `pulumi refresh` — the hash changes
30
+ * and a redeploy is triggered.
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * new VercelDeploy("vercel-deploy-nexus", {
35
+ * token: vercelConfig.token,
36
+ * projectId: project.id,
37
+ * rootDirectory: "apps/nexus",
38
+ * monorepoRoot,
39
+ * teamId: vercelConfig.teamId,
40
+ * env: { NEXT_PUBLIC_API_URL: meshUrl },
41
+ * });
42
+ * ```
43
+ */
44
+ declare class VercelDeploy extends pulumi.ComponentResource {
45
+ /**
46
+ * @param name Pulumi resource name (logical identifier in state)
47
+ * @param args Deploy configuration
48
+ * @param opts Component resource options
49
+ */
50
+ constructor(name: string, args: VercelDeployArgs, opts?: pulumi.ComponentResourceOptions);
51
+ }
52
+ //#endregion
53
+ export { VercelDeploy, VercelDeployArgs };
54
+ //# sourceMappingURL=deploy.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deploy.d.cts","names":[],"sources":["../../src/vercel/deploy.ts"],"mappings":";;;;;UAMiB,gBAAA;;EAEhB,KAAA,EAAO,MAAA,CAAO,KAAA;EAFkB;EAKhC,SAAA,EAAW,MAAA,CAAO,KAAA;EAHX;EAMP,aAAA;EASoB;EANpB,YAAA;EAYY;EATZ,MAAA;EAS2B;EAN3B,GAAA,EAAK,MAAA,SAAe,MAAA,CAAO,KAAA;EAfpB;EAkBP,oBAAA;EAfA;EAkBA,SAAA,GAAY,MAAA,CAAO,QAAA;AAAA;;;;;;;;;;;;;AAAQ;AAuB5B;;;;;;;cAAa,YAAA,SAAqB,MAAA,CAAO,iBAAA;EAAP;;;;;cAOhC,IAAA,UACA,IAAA,EAAM,gBAAA,EACN,IAAA,GAAO,MAAA,CAAO,wBAAA;AAAA"}
@@ -0,0 +1,54 @@
1
+ import { t as __name } from "../chunk-OPjESj5l.mjs";
2
+ import * as pulumi from "@pulumi/pulumi";
3
+
4
+ //#region src/vercel/deploy.d.ts
5
+ /** Configuration for a Vercel project deploy via `vercel deploy` CLI. */
6
+ interface VercelDeployArgs {
7
+ /** Vercel API bearer token. */
8
+ token: pulumi.Input<string>;
9
+ /** Vercel project ID (Output from `vercel.Project`). */
10
+ projectId: pulumi.Input<string>;
11
+ /** Relative path from monorepo root to the app directory (e.g. `"apps/nexus"`). */
12
+ rootDirectory: string;
13
+ /** Absolute path to the monorepo root (working directory for `vercel deploy`). */
14
+ monorepoRoot: string;
15
+ /** Vercel team/org ID. */
16
+ teamId: string;
17
+ /** Env var map used as deploy trigger. Hashes both keys and resolved values so value changes trigger redeploy. */
18
+ env: Record<string, pulumi.Input<string>>;
19
+ /** Additional directories (relative to monorepo root) to include in source hash. Changes in these trigger redeploy. */
20
+ additionalSourceDirs?: string[];
21
+ /** Resources that must complete before the deploy runs. */
22
+ dependsOn?: pulumi.Resource[];
23
+ }
24
+ /**
25
+ * Deploys a Vercel project via `vercel deploy --prod --yes` CLI.
26
+ *
27
+ * Triggers on source hash (computed from the app directory) and env content hash
28
+ * (from `VercelVariable.contentHash`). When an env value changes — whether from
29
+ * code, a new mesh URL, or a drift fix after `pulumi refresh` — the hash changes
30
+ * and a redeploy is triggered.
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * new VercelDeploy("vercel-deploy-nexus", {
35
+ * token: vercelConfig.token,
36
+ * projectId: project.id,
37
+ * rootDirectory: "apps/nexus",
38
+ * monorepoRoot,
39
+ * teamId: vercelConfig.teamId,
40
+ * env: { NEXT_PUBLIC_API_URL: meshUrl },
41
+ * });
42
+ * ```
43
+ */
44
+ declare class VercelDeploy extends pulumi.ComponentResource {
45
+ /**
46
+ * @param name Pulumi resource name (logical identifier in state)
47
+ * @param args Deploy configuration
48
+ * @param opts Component resource options
49
+ */
50
+ constructor(name: string, args: VercelDeployArgs, opts?: pulumi.ComponentResourceOptions);
51
+ }
52
+ //#endregion
53
+ export { VercelDeploy, VercelDeployArgs };
54
+ //# sourceMappingURL=deploy.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deploy.d.mts","names":[],"sources":["../../src/vercel/deploy.ts"],"mappings":";;;;;UAMiB,gBAAA;;EAEhB,KAAA,EAAO,MAAA,CAAO,KAAA;EAFkB;EAKhC,SAAA,EAAW,MAAA,CAAO,KAAA;EAHX;EAMP,aAAA;EASoB;EANpB,YAAA;EAYY;EATZ,MAAA;EAS2B;EAN3B,GAAA,EAAK,MAAA,SAAe,MAAA,CAAO,KAAA;EAfpB;EAkBP,oBAAA;EAfA;EAkBA,SAAA,GAAY,MAAA,CAAO,QAAA;AAAA;;;;;;;;;;;;;AAAQ;AAuB5B;;;;;;;cAAa,YAAA,SAAqB,MAAA,CAAO,iBAAA;EAAP;;;;;cAOhC,IAAA,UACA,IAAA,EAAM,gBAAA,EACN,IAAA,GAAO,MAAA,CAAO,wBAAA;AAAA"}
@@ -0,0 +1,61 @@
1
+ import { t as __name } from "../chunk-OPjESj5l.mjs";
2
+ import { hashDirectory } from "../hash.mjs";
3
+ import * as path from "node:path";
4
+ import * as command from "@pulumi/command";
5
+ import * as pulumi from "@pulumi/pulumi";
6
+
7
+ //#region src/vercel/deploy.ts
8
+ /**
9
+ * Deploys a Vercel project via `vercel deploy --prod --yes` CLI.
10
+ *
11
+ * Triggers on source hash (computed from the app directory) and env content hash
12
+ * (from `VercelVariable.contentHash`). When an env value changes — whether from
13
+ * code, a new mesh URL, or a drift fix after `pulumi refresh` — the hash changes
14
+ * and a redeploy is triggered.
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * new VercelDeploy("vercel-deploy-nexus", {
19
+ * token: vercelConfig.token,
20
+ * projectId: project.id,
21
+ * rootDirectory: "apps/nexus",
22
+ * monorepoRoot,
23
+ * teamId: vercelConfig.teamId,
24
+ * env: { NEXT_PUBLIC_API_URL: meshUrl },
25
+ * });
26
+ * ```
27
+ */
28
+ var VercelDeploy = class extends pulumi.ComponentResource {
29
+ /**
30
+ * @param name Pulumi resource name (logical identifier in state)
31
+ * @param args Deploy configuration
32
+ * @param opts Component resource options
33
+ */
34
+ constructor(name, args, opts) {
35
+ super("infrakit:vercel:Deploy", name, {}, opts);
36
+ const hashParts = [hashDirectory(path.join(args.monorepoRoot, args.rootDirectory))];
37
+ for (const dir of args.additionalSourceDirs ?? []) hashParts.push(hashDirectory(path.join(args.monorepoRoot, dir)));
38
+ const sourceHash = hashParts.join(",");
39
+ const commandOpts = { parent: this };
40
+ if (args.dependsOn && args.dependsOn.length > 0) commandOpts.dependsOn = args.dependsOn;
41
+ const envHash = pulumi.all(Object.entries(args.env).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => pulumi.output(v).apply((val) => `${k}=${val}`))).apply((parts) => parts.join(","));
42
+ new command.local.Command(`${name}-deploy`, {
43
+ create: "vercel deploy --prod --yes",
44
+ triggers: [sourceHash, envHash],
45
+ dir: args.monorepoRoot,
46
+ environment: {
47
+ VERCEL_TOKEN: args.token,
48
+ VERCEL_ORG_ID: args.teamId,
49
+ VERCEL_PROJECT_ID: args.projectId
50
+ }
51
+ }, {
52
+ parent: this,
53
+ ...commandOpts.dependsOn ? { dependsOn: commandOpts.dependsOn } : {}
54
+ });
55
+ this.registerOutputs({});
56
+ }
57
+ };
58
+
59
+ //#endregion
60
+ export { VercelDeploy };
61
+ //# sourceMappingURL=deploy.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deploy.mjs","names":[],"sources":["../../src/vercel/deploy.ts"],"sourcesContent":["import * as path from \"node:path\";\nimport * as command from \"@pulumi/command\";\nimport * as pulumi from \"@pulumi/pulumi\";\nimport { hashDirectory } from \"../hash.js\";\n\n/** Configuration for a Vercel project deploy via `vercel deploy` CLI. */\nexport interface VercelDeployArgs {\n\t/** Vercel API bearer token. */\n\ttoken: pulumi.Input<string>;\n\n\t/** Vercel project ID (Output from `vercel.Project`). */\n\tprojectId: pulumi.Input<string>;\n\n\t/** Relative path from monorepo root to the app directory (e.g. `\"apps/nexus\"`). */\n\trootDirectory: string;\n\n\t/** Absolute path to the monorepo root (working directory for `vercel deploy`). */\n\tmonorepoRoot: string;\n\n\t/** Vercel team/org ID. */\n\tteamId: string;\n\n\t/** Env var map used as deploy trigger. Hashes both keys and resolved values so value changes trigger redeploy. */\n\tenv: Record<string, pulumi.Input<string>>;\n\n\t/** Additional directories (relative to monorepo root) to include in source hash. Changes in these trigger redeploy. */\n\tadditionalSourceDirs?: string[];\n\n\t/** Resources that must complete before the deploy runs. */\n\tdependsOn?: pulumi.Resource[];\n}\n\n/**\n * Deploys a Vercel project via `vercel deploy --prod --yes` CLI.\n *\n * Triggers on source hash (computed from the app directory) and env content hash\n * (from `VercelVariable.contentHash`). When an env value changes — whether from\n * code, a new mesh URL, or a drift fix after `pulumi refresh` — the hash changes\n * and a redeploy is triggered.\n *\n * @example\n * ```typescript\n * new VercelDeploy(\"vercel-deploy-nexus\", {\n * token: vercelConfig.token,\n * projectId: project.id,\n * rootDirectory: \"apps/nexus\",\n * monorepoRoot,\n * teamId: vercelConfig.teamId,\n * env: { NEXT_PUBLIC_API_URL: meshUrl },\n * });\n * ```\n */\nexport class VercelDeploy extends pulumi.ComponentResource {\n\t/**\n\t * @param name Pulumi resource name (logical identifier in state)\n\t * @param args Deploy configuration\n\t * @param opts Component resource options\n\t */\n\tconstructor(\n\t\tname: string,\n\t\targs: VercelDeployArgs,\n\t\topts?: pulumi.ComponentResourceOptions,\n\t) {\n\t\tsuper(\"infrakit:vercel:Deploy\", name, {}, opts);\n\n\t\tconst appDir = path.join(args.monorepoRoot, args.rootDirectory);\n\n\t\tconst hashParts = [hashDirectory(appDir)];\n\n\t\tfor (const dir of args.additionalSourceDirs ?? []) {\n\t\t\thashParts.push(hashDirectory(path.join(args.monorepoRoot, dir)));\n\t\t}\n\n\t\tconst sourceHash = hashParts.join(\",\");\n\n\t\tconst commandOpts: pulumi.ResourceOptions = { parent: this };\n\n\t\tif (args.dependsOn && args.dependsOn.length > 0) {\n\t\t\tcommandOpts.dependsOn = args.dependsOn;\n\t\t}\n\n\t\tconst envHash = pulumi\n\t\t\t.all(\n\t\t\t\tObject.entries(args.env)\n\t\t\t\t\t.sort(([a], [b]) => a.localeCompare(b))\n\t\t\t\t\t.map(([k, v]) => pulumi.output(v).apply((val) => `${k}=${val}`)),\n\t\t\t)\n\t\t\t.apply((parts) => parts.join(\",\"));\n\n\t\tnew command.local.Command(\n\t\t\t`${name}-deploy`,\n\t\t\t{\n\t\t\t\tcreate: \"vercel deploy --prod --yes\",\n\t\t\t\ttriggers: [sourceHash, envHash],\n\t\t\t\tdir: args.monorepoRoot,\n\t\t\t\tenvironment: {\n\t\t\t\t\tVERCEL_TOKEN: args.token,\n\t\t\t\t\tVERCEL_ORG_ID: args.teamId,\n\t\t\t\t\tVERCEL_PROJECT_ID: args.projectId,\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tparent: this,\n\t\t\t\t...(commandOpts.dependsOn ? { dependsOn: commandOpts.dependsOn } : {}),\n\t\t\t},\n\t\t);\n\n\t\tthis.registerOutputs({});\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAoDA,IAAa,eAAb,cAAkC,OAAO,kBAAkB;;;;;;CAM1D,YACC,MACA,MACA,MACC;EACD,MAAM,0BAA0B,MAAM,CAAC,GAAG,IAAI;EAI9C,MAAM,YAAY,CAAC,cAFJ,KAAK,KAAK,KAAK,cAAc,KAAK,aAEX,CAAC,CAAC;EAExC,KAAK,MAAM,OAAO,KAAK,wBAAwB,CAAC,GAC/C,UAAU,KAAK,cAAc,KAAK,KAAK,KAAK,cAAc,GAAG,CAAC,CAAC;EAGhE,MAAM,aAAa,UAAU,KAAK,GAAG;EAErC,MAAM,cAAsC,EAAE,QAAQ,KAAK;EAE3D,IAAI,KAAK,aAAa,KAAK,UAAU,SAAS,GAC7C,YAAY,YAAY,KAAK;EAG9B,MAAM,UAAU,OACd,IACA,OAAO,QAAQ,KAAK,GAAG,EACrB,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC,EACrC,KAAK,CAAC,GAAG,OAAO,OAAO,OAAO,CAAC,EAAE,OAAO,QAAQ,GAAG,EAAE,GAAG,KAAK,CAAC,CACjE,EACC,OAAO,UAAU,MAAM,KAAK,GAAG,CAAC;EAElC,IAAI,QAAQ,MAAM,QACjB,GAAG,KAAK,UACR;GACC,QAAQ;GACR,UAAU,CAAC,YAAY,OAAO;GAC9B,KAAK,KAAK;GACV,aAAa;IACZ,cAAc,KAAK;IACnB,eAAe,KAAK;IACpB,mBAAmB,KAAK;GACzB;EACD,GACA;GACC,QAAQ;GACR,GAAI,YAAY,YAAY,EAAE,WAAW,YAAY,UAAU,IAAI,CAAC;EACrE,CACD;EAEA,KAAK,gBAAgB,CAAC,CAAC;CACxB;AACD"}
@@ -0,0 +1,6 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ const require_vercel_deploy = require('./deploy.cjs');
3
+ const require_vercel_variable = require('./variable.cjs');
4
+
5
+ exports.VercelDeploy = require_vercel_deploy.VercelDeploy;
6
+ exports.VercelVariable = require_vercel_variable.VercelVariable;
@@ -0,0 +1,3 @@
1
+ import { VercelDeploy, VercelDeployArgs } from "./deploy.cjs";
2
+ import { VercelVariable, VercelVariableInputs } from "./variable.cjs";
3
+ export { VercelDeploy, type VercelDeployArgs, VercelVariable, type VercelVariableInputs };
@@ -0,0 +1,3 @@
1
+ import { VercelDeploy, VercelDeployArgs } from "./deploy.mjs";
2
+ import { VercelVariable, VercelVariableInputs } from "./variable.mjs";
3
+ export { VercelDeploy, type VercelDeployArgs, VercelVariable, type VercelVariableInputs };
@@ -0,0 +1,4 @@
1
+ import { VercelDeploy } from "./deploy.mjs";
2
+ import { VercelVariable } from "./variable.mjs";
3
+
4
+ export { VercelDeploy, VercelVariable };
@@ -0,0 +1,280 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ const require_chunk = require('../chunk-BVYJZCqc.cjs');
3
+ let _pulumi_pulumi = require("@pulumi/pulumi");
4
+ _pulumi_pulumi = require_chunk.__toESM(_pulumi_pulumi, 1);
5
+
6
+ //#region src/vercel/variable.ts
7
+ const VERCEL_API_URL = "https://api.vercel.com";
8
+ /**
9
+ * Computes a deterministic content hash of a variables map.
10
+ * Uses dynamic import to avoid capturing `crypto` in the provider closure.
11
+ *
12
+ * @param variables Key-value map of environment variables
13
+ * @returns Hex-encoded SHA-256 hash
14
+ */
15
+ async function hashVariables(variables) {
16
+ const hash = (await import("node:crypto")).createHash("sha256");
17
+ const sorted = Object.entries(variables).sort(([a], [b]) => a.localeCompare(b));
18
+ for (const [key, value] of sorted) {
19
+ hash.update(key);
20
+ hash.update(value);
21
+ }
22
+ return hash.digest("hex");
23
+ }
24
+ /**
25
+ * Fetches all environment variables for a Vercel project with decrypted values.
26
+ *
27
+ * @param token Vercel API token
28
+ * @param teamId Vercel team ID
29
+ * @param projectId Vercel project ID
30
+ * @returns Array of env vars with decrypted values
31
+ */
32
+ async function fetchEnvVars(token, teamId, projectId) {
33
+ const listResponse = await fetch(`${VERCEL_API_URL}/v9/projects/${projectId}/env?teamId=${teamId}`, { headers: { Authorization: `Bearer ${token}` } });
34
+ if (!listResponse.ok) throw new Error(`Vercel API error (${listResponse.status}): ${await listResponse.text()}`);
35
+ const list = await listResponse.json();
36
+ const decrypted = [];
37
+ for (const env of list.envs) {
38
+ const response = await fetch(`${VERCEL_API_URL}/v9/projects/${projectId}/env/${env.id}?teamId=${teamId}&decrypt=true`, { headers: { Authorization: `Bearer ${token}` } });
39
+ if (response.ok) decrypted.push(await response.json());
40
+ }
41
+ return decrypted;
42
+ }
43
+ /**
44
+ * Creates a single Vercel env var targeting all environments.
45
+ *
46
+ * @param token Vercel API token
47
+ * @param teamId Vercel team ID
48
+ * @param projectId Vercel project ID
49
+ * @param key Env var name
50
+ * @param value Env var value
51
+ * @returns The created env var with its Vercel-assigned ID
52
+ */
53
+ async function createEnvVar(token, teamId, projectId, key, value) {
54
+ const response = await fetch(`${VERCEL_API_URL}/v10/projects/${projectId}/env?teamId=${teamId}`, {
55
+ method: "POST",
56
+ headers: {
57
+ Authorization: `Bearer ${token}`,
58
+ "Content-Type": "application/json"
59
+ },
60
+ body: JSON.stringify({
61
+ key,
62
+ value,
63
+ type: "encrypted",
64
+ target: [
65
+ "production",
66
+ "preview",
67
+ "development"
68
+ ]
69
+ })
70
+ });
71
+ if (!response.ok) {
72
+ const errorText = await response.text();
73
+ if (errorText.includes("ENV_CONFLICT")) {
74
+ _pulumi_pulumi.log.info(`Vercel env var "${key}" already exists, will update instead`);
75
+ const match = (await fetchEnvVars(token, teamId, projectId)).find((e) => e.key === key);
76
+ if (match) {
77
+ await updateEnvVar(token, teamId, projectId, match.id, value);
78
+ return {
79
+ ...match,
80
+ value
81
+ };
82
+ }
83
+ }
84
+ throw new Error(`Vercel API error creating "${key}" (${response.status}): ${errorText}`);
85
+ }
86
+ return await response.json();
87
+ }
88
+ /**
89
+ * Updates a single Vercel env var value.
90
+ *
91
+ * @param token Vercel API token
92
+ * @param teamId Vercel team ID
93
+ * @param projectId Vercel project ID
94
+ * @param envId Vercel-assigned env var ID
95
+ * @param value New value
96
+ */
97
+ async function updateEnvVar(token, teamId, projectId, envId, value) {
98
+ const response = await fetch(`${VERCEL_API_URL}/v9/projects/${projectId}/env/${envId}?teamId=${teamId}`, {
99
+ method: "PATCH",
100
+ headers: {
101
+ Authorization: `Bearer ${token}`,
102
+ "Content-Type": "application/json"
103
+ },
104
+ body: JSON.stringify({ value })
105
+ });
106
+ if (!response.ok) throw new Error(`Vercel API error updating env var (${response.status}): ${await response.text()}`);
107
+ }
108
+ /**
109
+ * Deletes a single Vercel env var.
110
+ *
111
+ * @param token Vercel API token
112
+ * @param teamId Vercel team ID
113
+ * @param projectId Vercel project ID
114
+ * @param envId Vercel-assigned env var ID
115
+ */
116
+ async function deleteEnvVar(token, teamId, projectId, envId) {
117
+ const response = await fetch(`${VERCEL_API_URL}/v9/projects/${projectId}/env/${envId}?teamId=${teamId}`, {
118
+ method: "DELETE",
119
+ headers: { Authorization: `Bearer ${token}` }
120
+ });
121
+ if (!response.ok && response.status !== 404) throw new Error(`Vercel API error deleting env var (${response.status}): ${await response.text()}`);
122
+ }
123
+ /**
124
+ * Dynamic provider implementing CRUD for Vercel project environment variables.
125
+ *
126
+ * Manages all env vars for a project as a batch (like RailwayVariable).
127
+ * The `read()` method fetches decrypted values from Vercel's API,
128
+ * enabling drift detection during `pulumi refresh`.
129
+ */
130
+ var VercelVariableProvider = class {
131
+ /**
132
+ * Creates all env vars on the target Vercel project.
133
+ * Handles ENV_CONFLICT by updating existing vars instead of failing.
134
+ *
135
+ * @param inputs Resolved variable configuration
136
+ * @returns Composite ID in the form `{projectId}:variables`
137
+ */
138
+ async create(inputs) {
139
+ const envIds = {};
140
+ for (const [key, value] of Object.entries(inputs.variables)) envIds[key] = (await createEnvVar(inputs.token, inputs.teamId, inputs.projectId, key, value)).id;
141
+ return {
142
+ id: `${inputs.projectId}:variables`,
143
+ outs: {
144
+ ...inputs,
145
+ envIds,
146
+ contentHash: await hashVariables(inputs.variables)
147
+ }
148
+ };
149
+ }
150
+ /**
151
+ * Updates variables by deleting removed keys, creating new keys,
152
+ * and patching changed values.
153
+ *
154
+ * @param _id Current resource ID
155
+ * @param olds Previous persisted state
156
+ * @param news New desired configuration
157
+ * @returns Updated outputs with current envIds
158
+ */
159
+ async update(_id, olds, news) {
160
+ const envIds = { ...olds.envIds };
161
+ const removedKeys = Object.keys(olds.variables).filter((key) => !(key in news.variables));
162
+ for (const key of removedKeys) {
163
+ const envId = envIds[key];
164
+ if (envId) {
165
+ try {
166
+ await deleteEnvVar(news.token, news.teamId, news.projectId, envId);
167
+ } catch {
168
+ _pulumi_pulumi.log.warn(`Failed to delete Vercel env var "${key}" (may already be deleted)`);
169
+ }
170
+ delete envIds[key];
171
+ }
172
+ }
173
+ for (const [key, value] of Object.entries(news.variables)) {
174
+ const existingId = envIds[key];
175
+ if (existingId && olds.variables[key] !== value) await updateEnvVar(news.token, news.teamId, news.projectId, existingId, value);
176
+ else if (!existingId) envIds[key] = (await createEnvVar(news.token, news.teamId, news.projectId, key, value)).id;
177
+ }
178
+ return { outs: {
179
+ ...news,
180
+ envIds,
181
+ contentHash: await hashVariables(news.variables)
182
+ } };
183
+ }
184
+ /**
185
+ * Reads current state from Vercel API with decrypted values.
186
+ * Enables drift detection during `pulumi refresh`.
187
+ *
188
+ * @param id Current resource ID
189
+ * @param props Last known persisted state
190
+ * @returns Refreshed properties with actual values from Vercel
191
+ */
192
+ async read(id, props) {
193
+ const actual = await fetchEnvVars(props.token, props.teamId, props.projectId);
194
+ const actualVariables = {};
195
+ const actualEnvIds = {};
196
+ for (const [key] of Object.entries(props.variables)) {
197
+ const match = actual.find((e) => e.key === key);
198
+ if (match) {
199
+ actualVariables[key] = match.value;
200
+ actualEnvIds[key] = match.id;
201
+ }
202
+ }
203
+ return {
204
+ id,
205
+ props: {
206
+ ...props,
207
+ variables: actualVariables,
208
+ envIds: actualEnvIds,
209
+ contentHash: await hashVariables(actualVariables)
210
+ }
211
+ };
212
+ }
213
+ /**
214
+ * Deletes all managed env vars from Vercel.
215
+ *
216
+ * @param _id Current resource ID
217
+ * @param props Last known persisted state
218
+ */
219
+ async delete(_id, props) {
220
+ for (const [key, envId] of Object.entries(props.envIds)) try {
221
+ await deleteEnvVar(props.token, props.teamId, props.projectId, envId);
222
+ } catch {
223
+ _pulumi_pulumi.log.warn(`Failed to delete Vercel env var "${key}" (may already be deleted)`);
224
+ }
225
+ }
226
+ /**
227
+ * Compares old and new variable maps by key set and value equality.
228
+ *
229
+ * @param _id Current resource ID
230
+ * @param olds Previous persisted state
231
+ * @param news New desired configuration
232
+ * @returns Whether any keys or values changed
233
+ */
234
+ async diff(_id, olds, news) {
235
+ const oldKeys = Object.keys(olds.variables).sort().join(",");
236
+ const newKeys = Object.keys(news.variables).sort().join(",");
237
+ const valuesChanged = Object.entries(news.variables).some(([key, value]) => olds.variables[key] !== value);
238
+ return { changes: oldKeys !== newKeys || valuesChanged };
239
+ }
240
+ };
241
+ /**
242
+ * Manages Vercel project environment variables as a batch with drift detection.
243
+ *
244
+ * Unlike `@pulumiverse/vercel`'s `ProjectEnvironmentVariable`, this provider
245
+ * reads decrypted values from Vercel's API during `pulumi refresh`, enabling
246
+ * detection of manually changed env var values.
247
+ *
248
+ * All variables target production, preview, and development environments.
249
+ *
250
+ * @example
251
+ * ```typescript
252
+ * new VercelVariable("vercel-variable-nexus", {
253
+ * token: vercelConfig.token,
254
+ * teamId: vercelConfig.teamId,
255
+ * projectId: project.id,
256
+ * variables: {
257
+ * NEXT_PUBLIC_API_URL: meshUrl,
258
+ * NEXTAUTH_SECRET: config.requireSecret("nextauthSecret"),
259
+ * },
260
+ * });
261
+ * ```
262
+ */
263
+ var VercelVariable = class extends _pulumi_pulumi.dynamic.Resource {
264
+ /**
265
+ * @param name Pulumi resource name
266
+ * @param args Variable configuration inputs
267
+ * @param opts Standard Pulumi resource options
268
+ */
269
+ constructor(name, args, opts) {
270
+ super(new VercelVariableProvider(), name, {
271
+ ...args,
272
+ envIds: void 0,
273
+ contentHash: void 0
274
+ }, opts);
275
+ }
276
+ };
277
+
278
+ //#endregion
279
+ exports.VercelVariable = VercelVariable;
280
+ //# sourceMappingURL=variable.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"variable.cjs","names":["pulumi"],"sources":["../../src/vercel/variable.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\n\nconst VERCEL_API_URL = \"https://api.vercel.com\";\n\n/** Resolved inputs for the Vercel variable dynamic provider. */\nexport interface VercelVariableInputs {\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/** 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 * Uses dynamic import to avoid capturing `crypto` in the provider closure.\n *\n * @param variables Key-value map of environment variables\n * @returns Hex-encoded SHA-256 hash\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 *\n * @param token Vercel API token\n * @param teamId Vercel team ID\n * @param projectId Vercel project ID\n * @returns Array of env vars with decrypted values\n */\nasync function fetchEnvVars(\n\ttoken: string,\n\tteamId: string,\n\tprojectId: string,\n): Promise<VercelEnvVar[]> {\n\tconst listResponse = await fetch(\n\t\t`${VERCEL_API_URL}/v9/projects/${projectId}/env?teamId=${teamId}`,\n\t\t{ headers: { Authorization: `Bearer ${token}` } },\n\t);\n\n\tif (!listResponse.ok) {\n\t\tthrow new Error(\n\t\t\t`Vercel API error (${listResponse.status}): ${await listResponse.text()}`,\n\t\t);\n\t}\n\n\tconst list = (await listResponse.json()) as { envs: VercelEnvVar[] };\n\n\tconst decrypted: VercelEnvVar[] = [];\n\n\tfor (const env of list.envs) {\n\t\tconst response = await fetch(\n\t\t\t`${VERCEL_API_URL}/v9/projects/${projectId}/env/${env.id}?teamId=${teamId}&decrypt=true`,\n\t\t\t{ headers: { Authorization: `Bearer ${token}` } },\n\t\t);\n\n\t\tif (response.ok) {\n\t\t\tdecrypted.push((await response.json()) as VercelEnvVar);\n\t\t}\n\t}\n\n\treturn decrypted;\n}\n\n/**\n * Creates a single Vercel env var targeting all environments.\n *\n * @param token Vercel API token\n * @param teamId Vercel team ID\n * @param projectId Vercel project ID\n * @param key Env var name\n * @param value Env var value\n * @returns The created env var with its Vercel-assigned ID\n */\nasync function createEnvVar(\n\ttoken: string,\n\tteamId: string,\n\tprojectId: string,\n\tkey: string,\n\tvalue: string,\n): Promise<VercelEnvVar> {\n\tconst response = await fetch(\n\t\t`${VERCEL_API_URL}/v10/projects/${projectId}/env?teamId=${teamId}`,\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\tAuthorization: `Bearer ${token}`,\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t},\n\t\t\tbody: JSON.stringify({\n\t\t\t\tkey,\n\t\t\t\tvalue,\n\t\t\t\ttype: \"encrypted\",\n\t\t\t\ttarget: [\"production\", \"preview\", \"development\"],\n\t\t\t}),\n\t\t},\n\t);\n\n\tif (!response.ok) {\n\t\tconst errorText = await response.text();\n\n\t\tif (errorText.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(token, teamId, projectId);\n\t\t\tconst match = existing.find((e) => e.key === key);\n\n\t\t\tif (match) {\n\t\t\t\tawait updateEnvVar(token, teamId, projectId, match.id, value);\n\n\t\t\t\treturn { ...match, value };\n\t\t\t}\n\t\t}\n\n\t\tthrow new Error(\n\t\t\t`Vercel API error creating \"${key}\" (${response.status}): ${errorText}`,\n\t\t);\n\t}\n\n\treturn (await response.json()) as VercelEnvVar;\n}\n\n/**\n * Updates a single Vercel env var value.\n *\n * @param token Vercel API token\n * @param teamId Vercel team ID\n * @param projectId Vercel project ID\n * @param envId Vercel-assigned env var ID\n * @param value New value\n */\nasync function updateEnvVar(\n\ttoken: string,\n\tteamId: string,\n\tprojectId: string,\n\tenvId: string,\n\tvalue: string,\n): Promise<void> {\n\tconst response = await fetch(\n\t\t`${VERCEL_API_URL}/v9/projects/${projectId}/env/${envId}?teamId=${teamId}`,\n\t\t{\n\t\t\tmethod: \"PATCH\",\n\t\t\theaders: {\n\t\t\t\tAuthorization: `Bearer ${token}`,\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t},\n\t\t\tbody: JSON.stringify({ value }),\n\t\t},\n\t);\n\n\tif (!response.ok) {\n\t\tthrow new Error(\n\t\t\t`Vercel API error updating env var (${response.status}): ${await response.text()}`,\n\t\t);\n\t}\n}\n\n/**\n * Deletes a single Vercel env var.\n *\n * @param token Vercel API token\n * @param teamId Vercel team ID\n * @param projectId Vercel project ID\n * @param envId Vercel-assigned env var ID\n */\nasync function deleteEnvVar(\n\ttoken: string,\n\tteamId: string,\n\tprojectId: string,\n\tenvId: string,\n): Promise<void> {\n\tconst response = await fetch(\n\t\t`${VERCEL_API_URL}/v9/projects/${projectId}/env/${envId}?teamId=${teamId}`,\n\t\t{\n\t\t\tmethod: \"DELETE\",\n\t\t\theaders: { Authorization: `Bearer ${token}` },\n\t\t},\n\t);\n\n\tif (!response.ok && response.status !== 404) {\n\t\tthrow new Error(\n\t\t\t`Vercel API error deleting env var (${response.status}): ${await response.text()}`,\n\t\t);\n\t}\n}\n\n/**\n * Dynamic provider implementing CRUD for Vercel project environment variables.\n *\n * Manages all env vars for a project as a batch (like RailwayVariable).\n * The `read()` method fetches decrypted values from Vercel's API,\n * enabling drift detection during `pulumi refresh`.\n */\nclass VercelVariableProvider implements pulumi.dynamic.ResourceProvider {\n\t/**\n\t * Creates all env vars on the target Vercel project.\n\t * Handles ENV_CONFLICT by updating existing vars instead of failing.\n\t *\n\t * @param inputs Resolved variable configuration\n\t * @returns Composite ID in the form `{projectId}:variables`\n\t */\n\tasync create(\n\t\tinputs: VercelVariableInputs,\n\t): Promise<pulumi.dynamic.CreateResult> {\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(\n\t\t\t\tinputs.token,\n\t\t\t\tinputs.teamId,\n\t\t\t\tinputs.projectId,\n\t\t\t\tkey,\n\t\t\t\tvalue,\n\t\t\t);\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\t/**\n\t * Updates variables by deleting removed keys, creating new keys,\n\t * and patching changed values.\n\t *\n\t * @param _id Current resource ID\n\t * @param olds Previous persisted state\n\t * @param news New desired configuration\n\t * @returns Updated outputs with current envIds\n\t */\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 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(news.token, news.teamId, 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(\n\t\t\t\t\tnews.token,\n\t\t\t\t\tnews.teamId,\n\t\t\t\t\tnews.projectId,\n\t\t\t\t\texistingId,\n\t\t\t\t\tvalue,\n\t\t\t\t);\n\t\t\t} else if (!existingId) {\n\t\t\t\tconst result = await createEnvVar(\n\t\t\t\t\tnews.token,\n\t\t\t\t\tnews.teamId,\n\t\t\t\t\tnews.projectId,\n\t\t\t\t\tkey,\n\t\t\t\t\tvalue,\n\t\t\t\t);\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\t/**\n\t * Reads current state from Vercel API with decrypted values.\n\t * Enables drift detection during `pulumi refresh`.\n\t *\n\t * @param id Current resource ID\n\t * @param props Last known persisted state\n\t * @returns Refreshed properties with actual values from Vercel\n\t */\n\tasync read(\n\t\tid: string,\n\t\tprops: VercelVariableOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\tconst actual = await fetchEnvVars(\n\t\t\tprops.token,\n\t\t\tprops.teamId,\n\t\t\tprops.projectId,\n\t\t);\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\t/**\n\t * Deletes all managed env vars from Vercel.\n\t *\n\t * @param _id Current resource ID\n\t * @param props Last known persisted state\n\t */\n\tasync delete(_id: string, props: VercelVariableOutputs): Promise<void> {\n\t\tfor (const [key, envId] of Object.entries(props.envIds)) {\n\t\t\ttry {\n\t\t\t\tawait deleteEnvVar(props.token, props.teamId, 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\t/**\n\t * Compares old and new variable maps by key set and value equality.\n\t *\n\t * @param _id Current resource ID\n\t * @param olds Previous persisted state\n\t * @param news New desired configuration\n\t * @returns Whether any keys or values changed\n\t */\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/**\n * Manages Vercel project environment variables as a batch with drift detection.\n *\n * Unlike `@pulumiverse/vercel`'s `ProjectEnvironmentVariable`, this provider\n * reads decrypted values from Vercel's API during `pulumi refresh`, enabling\n * detection of manually changed env var values.\n *\n * All variables target production, preview, and development environments.\n *\n * @example\n * ```typescript\n * new VercelVariable(\"vercel-variable-nexus\", {\n * token: vercelConfig.token,\n * teamId: vercelConfig.teamId,\n * projectId: project.id,\n * variables: {\n * NEXT_PUBLIC_API_URL: meshUrl,\n * NEXTAUTH_SECRET: config.requireSecret(\"nextauthSecret\"),\n * },\n * });\n * ```\n */\nexport class VercelVariable extends pulumi.dynamic.Resource {\n\t/** SHA-256 hash of all key-value pairs. Use as a deploy trigger for drift-aware redeployment. */\n\tpublic declare readonly contentHash: pulumi.Output<string>;\n\n\t/**\n\t * @param name Pulumi resource name\n\t * @param args Variable configuration inputs\n\t * @param opts Standard Pulumi resource options\n\t */\n\tconstructor(\n\t\tname: string,\n\t\targs: {\n\t\t\t/** Vercel API bearer token. */\n\t\t\ttoken: pulumi.Input<string>;\n\n\t\t\t/** Vercel team/org ID. */\n\t\t\tteamId: pulumi.Input<string>;\n\n\t\t\t/** Vercel project ID. */\n\t\t\tprojectId: pulumi.Input<string>;\n\n\t\t\t/** Key-value map of environment variable names to their values. */\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 VercelVariableProvider(),\n\t\t\tname,\n\t\t\t{ ...args, envIds: undefined, contentHash: undefined },\n\t\t\topts,\n\t\t);\n\t}\n}\n"],"mappings":"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"}