@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,266 @@
1
+ import { t as __name } from "../chunk-OPjESj5l.mjs";
2
+ import { RailwayClient } from "./client.mjs";
3
+ import * as pulumi from "@pulumi/pulumi";
4
+
5
+ //#region src/railway/service.ts
6
+ const SERVICES_QUERY = `
7
+ query($projectId: String!) {
8
+ project(id: $projectId) {
9
+ services {
10
+ edges {
11
+ node {
12
+ id
13
+ name
14
+ }
15
+ }
16
+ }
17
+ }
18
+ }
19
+ `;
20
+ const SERVICE_QUERY = `
21
+ query($serviceId: String!) {
22
+ service(id: $serviceId) {
23
+ id
24
+ name
25
+ }
26
+ }
27
+ `;
28
+ const SERVICE_CREATE = `
29
+ mutation($input: ServiceCreateInput!) {
30
+ serviceCreate(input: $input) {
31
+ id
32
+ name
33
+ }
34
+ }
35
+ `;
36
+ const SERVICE_UPDATE = `
37
+ mutation($id: String!, $input: ServiceUpdateInput!) {
38
+ serviceUpdate(id: $id, input: $input) {
39
+ id
40
+ name
41
+ }
42
+ }
43
+ `;
44
+ const SERVICE_INSTANCE_UPDATE = `
45
+ mutation(
46
+ $serviceId: String!
47
+ $environmentId: String!
48
+ $input: ServiceInstanceUpdateInput!
49
+ ) {
50
+ serviceInstanceUpdate(
51
+ serviceId: $serviceId
52
+ environmentId: $environmentId
53
+ input: $input
54
+ )
55
+ }
56
+ `;
57
+ const SERVICE_CONNECT = `
58
+ mutation($id: String!, $input: ServiceConnectInput!) {
59
+ serviceConnect(id: $id, input: $input) {
60
+ id
61
+ }
62
+ }
63
+ `;
64
+ const SERVICE_DELETE = `
65
+ mutation($id: String!) { serviceDelete(id: $id) }
66
+ `;
67
+ /**
68
+ * Applies service instance configuration (builder, commands, healthcheck).
69
+ * Retries without healthcheck fields if the first call fails.
70
+ */
71
+ async function applyInstanceConfig(client, serviceId, environmentId, inputs) {
72
+ const instanceInput = {};
73
+ if (inputs.builder) instanceInput.builder = inputs.builder;
74
+ if (inputs.buildCommand) instanceInput.buildCommand = inputs.buildCommand;
75
+ if (inputs.startCommand) instanceInput.startCommand = inputs.startCommand;
76
+ if (inputs.restartPolicyType) instanceInput.restartPolicyType = inputs.restartPolicyType;
77
+ if (inputs.healthcheckPath) instanceInput.healthcheckPath = inputs.healthcheckPath;
78
+ if (inputs.healthcheckTimeout) instanceInput.healthcheckTimeout = inputs.healthcheckTimeout;
79
+ if (inputs.preDeployCommand) instanceInput.preDeployCommand = inputs.preDeployCommand;
80
+ if (Object.keys(instanceInput).length === 0) return;
81
+ try {
82
+ await client.query(SERVICE_INSTANCE_UPDATE, {
83
+ serviceId,
84
+ environmentId,
85
+ input: instanceInput
86
+ });
87
+ } catch (error) {
88
+ pulumi.log.warn(`serviceInstanceUpdate failed, retrying without healthcheck fields: ${error}`);
89
+ delete instanceInput.healthcheckPath;
90
+ delete instanceInput.healthcheckTimeout;
91
+ if (Object.keys(instanceInput).length > 0) await client.query(SERVICE_INSTANCE_UPDATE, {
92
+ serviceId,
93
+ environmentId,
94
+ input: instanceInput
95
+ });
96
+ }
97
+ }
98
+ /**
99
+ * Dynamic provider implementing CRUD for Railway services.
100
+ *
101
+ * Uses adopt-or-create on `create()`: queries services by project ID and name
102
+ * before creating a new one. Service instance configuration (builder, commands,
103
+ * healthcheck) is applied via `serviceInstanceUpdate` after create or update.
104
+ */
105
+ var RailwayServiceProvider = class {
106
+ /**
107
+ * Creates or adopts a Railway service by name, then applies instance config.
108
+ *
109
+ * @param inputs Resolved service configuration
110
+ * @returns The Railway service UUID as the resource ID
111
+ */
112
+ async create(inputs) {
113
+ const client = new RailwayClient(inputs.token);
114
+ let serviceId = (await client.query(SERVICES_QUERY, { projectId: inputs.projectId })).project.services.edges.find((edge) => edge.node.name === inputs.name)?.node.id;
115
+ if (serviceId) pulumi.log.info(`Adopted existing Railway service "${inputs.name}" (${serviceId})`);
116
+ else {
117
+ const createInput = {
118
+ projectId: inputs.projectId,
119
+ name: inputs.name
120
+ };
121
+ if (inputs.source) createInput.source = { image: inputs.source.image };
122
+ serviceId = (await client.query(SERVICE_CREATE, { input: createInput })).serviceCreate.id;
123
+ pulumi.log.info(`Created Railway service "${inputs.name}" (${serviceId})`);
124
+ if (inputs.source) await client.query(SERVICE_CONNECT, {
125
+ id: serviceId,
126
+ input: { image: inputs.source.image }
127
+ });
128
+ if (inputs.icon) await client.query(SERVICE_UPDATE, {
129
+ id: serviceId,
130
+ input: { icon: inputs.icon }
131
+ });
132
+ }
133
+ await applyInstanceConfig(client, serviceId, inputs.environmentId, inputs);
134
+ const outs = {
135
+ ...inputs,
136
+ serviceId
137
+ };
138
+ return {
139
+ id: serviceId,
140
+ outs
141
+ };
142
+ }
143
+ /**
144
+ * Updates service name/icon and re-applies instance configuration.
145
+ *
146
+ * @param id Railway service UUID
147
+ * @param _olds Previous persisted state
148
+ * @param news New desired configuration
149
+ */
150
+ async update(id, _olds, news) {
151
+ const client = new RailwayClient(news.token);
152
+ if (news.icon) await client.query(SERVICE_UPDATE, {
153
+ id,
154
+ input: { icon: news.icon }
155
+ });
156
+ await applyInstanceConfig(client, id, news.environmentId, news);
157
+ return { outs: {
158
+ ...news,
159
+ serviceId: id
160
+ } };
161
+ }
162
+ /**
163
+ * Reads current state for `pulumi refresh` by querying the service by ID.
164
+ *
165
+ * @param id Railway service UUID
166
+ * @param props Last known persisted state
167
+ */
168
+ async read(id, props) {
169
+ const client = new RailwayClient(props.token);
170
+ try {
171
+ await client.query(SERVICE_QUERY, { serviceId: id });
172
+ } catch {
173
+ throw new Error(`Railway service "${props.name}" (${id}) not found`);
174
+ }
175
+ return {
176
+ id,
177
+ props: {
178
+ ...props,
179
+ serviceId: id
180
+ }
181
+ };
182
+ }
183
+ /**
184
+ * Deletes the Railway service. Silently succeeds if already deleted.
185
+ *
186
+ * @param id Railway service UUID to delete
187
+ * @param props Last known persisted state (used for token and logging)
188
+ */
189
+ async delete(id, props) {
190
+ const client = new RailwayClient(props.token);
191
+ try {
192
+ await client.query(SERVICE_DELETE, { id });
193
+ pulumi.log.info(`Deleted Railway service "${props.name}" (${id})`);
194
+ } catch {
195
+ pulumi.log.warn(`Failed to delete Railway service "${props.name}" (${id}) — may already be deleted`);
196
+ }
197
+ }
198
+ /**
199
+ * Compares old and new inputs to determine what changed.
200
+ *
201
+ * Name, projectId, and environmentId changes trigger replacement.
202
+ * Builder, commands, and healthcheck changes trigger in-place update.
203
+ *
204
+ * @param _id Current resource ID (unused)
205
+ * @param olds Previous persisted state
206
+ * @param news New desired configuration
207
+ */
208
+ async diff(_id, olds, news) {
209
+ const replaces = [];
210
+ const changes = [];
211
+ if (olds.name !== news.name) replaces.push("name");
212
+ if (olds.projectId !== news.projectId) replaces.push("projectId");
213
+ if (olds.environmentId !== news.environmentId) replaces.push("environmentId");
214
+ if (olds.builder !== news.builder) changes.push("builder");
215
+ if (olds.buildCommand !== news.buildCommand) changes.push("buildCommand");
216
+ if (olds.startCommand !== news.startCommand) changes.push("startCommand");
217
+ if (olds.restartPolicyType !== news.restartPolicyType) changes.push("restartPolicyType");
218
+ if (olds.healthcheckPath !== news.healthcheckPath) changes.push("healthcheckPath");
219
+ if (olds.healthcheckTimeout !== news.healthcheckTimeout) changes.push("healthcheckTimeout");
220
+ if (olds.preDeployCommand !== news.preDeployCommand) changes.push("preDeployCommand");
221
+ if (olds.icon !== news.icon) changes.push("icon");
222
+ return {
223
+ changes: replaces.length > 0 || changes.length > 0,
224
+ replaces,
225
+ deleteBeforeReplace: true
226
+ };
227
+ }
228
+ };
229
+ /**
230
+ * Manages a Railway service with adopt-or-create semantics.
231
+ *
232
+ * Queries existing services by project ID and name before creating new ones.
233
+ * Service instance configuration (builder, start command, healthcheck) is
234
+ * applied after creation via `serviceInstanceUpdate`.
235
+ *
236
+ * @example
237
+ * ```typescript
238
+ * const service = new RailwayService("railway-service-api", {
239
+ * token: project.projectToken,
240
+ * projectId: project.projectId,
241
+ * environmentId: project.productionEnvironmentId,
242
+ * name: "@my-app/api",
243
+ * builder: "RAILPACK",
244
+ * startCommand: "node dist/index.js",
245
+ * healthcheckPath: "/health",
246
+ * });
247
+ *
248
+ * // Use serviceId downstream
249
+ * new RailwayVariable("railway-variable-api", {
250
+ * serviceId: service.serviceId,
251
+ * ...
252
+ * });
253
+ * ```
254
+ */
255
+ var RailwayService = class extends pulumi.dynamic.Resource {
256
+ constructor(name, args, opts) {
257
+ super(new RailwayServiceProvider(), name, {
258
+ ...args,
259
+ serviceId: void 0
260
+ }, opts);
261
+ }
262
+ };
263
+
264
+ //#endregion
265
+ export { RailwayService };
266
+ //# sourceMappingURL=service.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.mjs","names":[],"sources":["../../src/railway/service.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\nimport { RailwayClient } from \"./client.js\";\n\n/** Docker image source for a Railway service (e.g. `redis:8-alpine`). */\ninterface RailwayServiceSource {\n\t/** Full Docker image reference including tag. */\n\timage: string;\n}\n\n/** Resolved inputs for the Railway service dynamic provider. */\nexport interface RailwayServiceInputs {\n\t/** Railway API bearer token. */\n\ttoken: string;\n\n\t/** Railway project UUID. */\n\tprojectId: string;\n\n\t/** Railway environment UUID (e.g. production). */\n\tenvironmentId: string;\n\n\t/** Human-readable service name used for adopt-or-create matching. */\n\tname: string;\n\n\t/** SVG icon URL displayed in the Railway dashboard. */\n\ticon?: string;\n\n\t/** Docker image source for image-based services. */\n\tsource?: RailwayServiceSource;\n\n\t/** Build system: `\"RAILPACK\"`, `\"NIXPACKS\"`, or `\"DOCKERFILE\"`. */\n\tbuilder?: string;\n\n\t/** Shell command executed during the build phase. */\n\tbuildCommand?: string;\n\n\t/** Shell command executed to start the service at runtime. */\n\tstartCommand?: string;\n\n\t/** Restart behavior: `\"ON_FAILURE\"`, `\"ALWAYS\"`, or `\"NEVER\"`. */\n\trestartPolicyType?: string;\n\n\t/** HTTP path polled for health checks (e.g. `\"/health-check\"`). */\n\thealthcheckPath?: string;\n\n\t/** Seconds to wait for a healthy response before marking unhealthy. */\n\thealthcheckTimeout?: number;\n\n\t/** Shell command executed before the main deploy (e.g. migrations). */\n\tpreDeployCommand?: string;\n}\n\n/** Persisted state for the Railway service, extending inputs with the Railway-assigned ID. */\ninterface RailwayServiceOutputs extends RailwayServiceInputs {\n\t/** Railway-assigned service UUID. */\n\tserviceId: string;\n}\n\nconst SERVICES_QUERY = `\n query($projectId: String!) {\n project(id: $projectId) {\n services {\n edges {\n node {\n id\n name\n }\n }\n }\n }\n }\n`;\n\nconst SERVICE_QUERY = `\n query($serviceId: String!) {\n service(id: $serviceId) {\n id\n name\n }\n }\n`;\n\nconst SERVICE_CREATE = `\n mutation($input: ServiceCreateInput!) {\n serviceCreate(input: $input) {\n id\n name\n }\n }\n`;\n\nconst SERVICE_UPDATE = `\n mutation($id: String!, $input: ServiceUpdateInput!) {\n serviceUpdate(id: $id, input: $input) {\n id\n name\n }\n }\n`;\n\nconst SERVICE_INSTANCE_UPDATE = `\n mutation(\n $serviceId: String!\n $environmentId: String!\n $input: ServiceInstanceUpdateInput!\n ) {\n serviceInstanceUpdate(\n serviceId: $serviceId\n environmentId: $environmentId\n input: $input\n )\n }\n`;\n\nconst SERVICE_CONNECT = `\n mutation($id: String!, $input: ServiceConnectInput!) {\n serviceConnect(id: $id, input: $input) {\n id\n }\n }\n`;\n\nconst SERVICE_DELETE = `\n mutation($id: String!) { serviceDelete(id: $id) }\n`;\n\n/**\n * Applies service instance configuration (builder, commands, healthcheck).\n * Retries without healthcheck fields if the first call fails.\n */\nasync function applyInstanceConfig(\n\tclient: RailwayClient,\n\tserviceId: string,\n\tenvironmentId: string,\n\tinputs: RailwayServiceInputs,\n): Promise<void> {\n\tconst instanceInput: Record<string, unknown> = {};\n\n\tif (inputs.builder) {\n\t\tinstanceInput.builder = inputs.builder;\n\t}\n\n\tif (inputs.buildCommand) {\n\t\tinstanceInput.buildCommand = inputs.buildCommand;\n\t}\n\n\tif (inputs.startCommand) {\n\t\tinstanceInput.startCommand = inputs.startCommand;\n\t}\n\n\tif (inputs.restartPolicyType) {\n\t\tinstanceInput.restartPolicyType = inputs.restartPolicyType;\n\t}\n\n\tif (inputs.healthcheckPath) {\n\t\tinstanceInput.healthcheckPath = inputs.healthcheckPath;\n\t}\n\n\tif (inputs.healthcheckTimeout) {\n\t\tinstanceInput.healthcheckTimeout = inputs.healthcheckTimeout;\n\t}\n\n\tif (inputs.preDeployCommand) {\n\t\tinstanceInput.preDeployCommand = inputs.preDeployCommand;\n\t}\n\n\tif (Object.keys(instanceInput).length === 0) {\n\t\treturn;\n\t}\n\n\ttry {\n\t\tawait client.query(SERVICE_INSTANCE_UPDATE, {\n\t\t\tserviceId,\n\t\t\tenvironmentId,\n\t\t\tinput: instanceInput,\n\t\t});\n\t} catch (error) {\n\t\tpulumi.log.warn(\n\t\t\t`serviceInstanceUpdate failed, retrying without healthcheck fields: ${error}`,\n\t\t);\n\n\t\tdelete instanceInput.healthcheckPath;\n\t\tdelete instanceInput.healthcheckTimeout;\n\n\t\tif (Object.keys(instanceInput).length > 0) {\n\t\t\tawait client.query(SERVICE_INSTANCE_UPDATE, {\n\t\t\t\tserviceId,\n\t\t\t\tenvironmentId,\n\t\t\t\tinput: instanceInput,\n\t\t\t});\n\t\t}\n\t}\n}\n\n/**\n * Dynamic provider implementing CRUD for Railway services.\n *\n * Uses adopt-or-create on `create()`: queries services by project ID and name\n * before creating a new one. Service instance configuration (builder, commands,\n * healthcheck) is applied via `serviceInstanceUpdate` after create or update.\n */\nclass RailwayServiceProvider implements pulumi.dynamic.ResourceProvider {\n\t/**\n\t * Creates or adopts a Railway service by name, then applies instance config.\n\t *\n\t * @param inputs Resolved service configuration\n\t * @returns The Railway service UUID as the resource ID\n\t */\n\tasync create(\n\t\tinputs: RailwayServiceInputs,\n\t): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst client = new RailwayClient(inputs.token);\n\n\t\tconst result = await client.query<{\n\t\t\tproject: {\n\t\t\t\tservices: { edges: Array<{ node: { id: string; name: string } }> };\n\t\t\t};\n\t\t}>(SERVICES_QUERY, { projectId: inputs.projectId });\n\n\t\tlet serviceId = result.project.services.edges.find(\n\t\t\t(edge) => edge.node.name === inputs.name,\n\t\t)?.node.id;\n\n\t\tif (serviceId) {\n\t\t\tpulumi.log.info(\n\t\t\t\t`Adopted existing Railway service \"${inputs.name}\" (${serviceId})`,\n\t\t\t);\n\t\t} else {\n\t\t\tconst createInput: Record<string, unknown> = {\n\t\t\t\tprojectId: inputs.projectId,\n\t\t\t\tname: inputs.name,\n\t\t\t};\n\n\t\t\tif (inputs.source) {\n\t\t\t\tcreateInput.source = { image: inputs.source.image };\n\t\t\t}\n\n\t\t\tconst created = await client.query<{\n\t\t\t\tserviceCreate: { id: string; name: string };\n\t\t\t}>(SERVICE_CREATE, { input: createInput });\n\n\t\t\tserviceId = created.serviceCreate.id;\n\n\t\t\tpulumi.log.info(\n\t\t\t\t`Created Railway service \"${inputs.name}\" (${serviceId})`,\n\t\t\t);\n\n\t\t\tif (inputs.source) {\n\t\t\t\tawait client.query(SERVICE_CONNECT, {\n\t\t\t\t\tid: serviceId,\n\t\t\t\t\tinput: { image: inputs.source.image },\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (inputs.icon) {\n\t\t\t\tawait client.query(SERVICE_UPDATE, {\n\t\t\t\t\tid: serviceId,\n\t\t\t\t\tinput: { icon: inputs.icon },\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tawait applyInstanceConfig(client, serviceId, inputs.environmentId, inputs);\n\n\t\tconst outs: RailwayServiceOutputs = { ...inputs, serviceId };\n\n\t\treturn { id: serviceId, outs };\n\t}\n\n\t/**\n\t * Updates service name/icon and re-applies instance configuration.\n\t *\n\t * @param id Railway service UUID\n\t * @param _olds Previous persisted state\n\t * @param news New desired configuration\n\t */\n\tasync update(\n\t\tid: string,\n\t\t_olds: RailwayServiceOutputs,\n\t\tnews: RailwayServiceInputs,\n\t): Promise<pulumi.dynamic.UpdateResult> {\n\t\tconst client = new RailwayClient(news.token);\n\n\t\tif (news.icon) {\n\t\t\tawait client.query(SERVICE_UPDATE, {\n\t\t\t\tid,\n\t\t\t\tinput: { icon: news.icon },\n\t\t\t});\n\t\t}\n\n\t\tawait applyInstanceConfig(client, id, news.environmentId, news);\n\n\t\tconst outs: RailwayServiceOutputs = { ...news, serviceId: id };\n\n\t\treturn { outs };\n\t}\n\n\t/**\n\t * Reads current state for `pulumi refresh` by querying the service by ID.\n\t *\n\t * @param id Railway service UUID\n\t * @param props Last known persisted state\n\t */\n\tasync read(\n\t\tid: string,\n\t\tprops: RailwayServiceOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\tconst client = new RailwayClient(props.token);\n\n\t\ttry {\n\t\t\tawait client.query<{ service: { id: string; name: string } }>(\n\t\t\t\tSERVICE_QUERY,\n\t\t\t\t{ serviceId: id },\n\t\t\t);\n\t\t} catch {\n\t\t\tthrow new Error(`Railway service \"${props.name}\" (${id}) not found`);\n\t\t}\n\n\t\treturn { id, props: { ...props, serviceId: id } };\n\t}\n\n\t/**\n\t * Deletes the Railway service. Silently succeeds if already deleted.\n\t *\n\t * @param id Railway service UUID to delete\n\t * @param props Last known persisted state (used for token and logging)\n\t */\n\tasync delete(id: string, props: RailwayServiceOutputs): Promise<void> {\n\t\tconst client = new RailwayClient(props.token);\n\n\t\ttry {\n\t\t\tawait client.query(SERVICE_DELETE, { id });\n\n\t\t\tpulumi.log.info(`Deleted Railway service \"${props.name}\" (${id})`);\n\t\t} catch {\n\t\t\tpulumi.log.warn(\n\t\t\t\t`Failed to delete Railway service \"${props.name}\" (${id}) — 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 * Name, projectId, and environmentId changes trigger replacement.\n\t * Builder, commands, and healthcheck changes trigger in-place update.\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 */\n\tasync diff(\n\t\t_id: string,\n\t\tolds: RailwayServiceOutputs,\n\t\tnews: RailwayServiceInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst replaces: string[] = [];\n\t\tconst changes: string[] = [];\n\n\t\tif (olds.name !== news.name) {\n\t\t\treplaces.push(\"name\");\n\t\t}\n\n\t\tif (olds.projectId !== news.projectId) {\n\t\t\treplaces.push(\"projectId\");\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.builder !== news.builder) {\n\t\t\tchanges.push(\"builder\");\n\t\t}\n\n\t\tif (olds.buildCommand !== news.buildCommand) {\n\t\t\tchanges.push(\"buildCommand\");\n\t\t}\n\n\t\tif (olds.startCommand !== news.startCommand) {\n\t\t\tchanges.push(\"startCommand\");\n\t\t}\n\n\t\tif (olds.restartPolicyType !== news.restartPolicyType) {\n\t\t\tchanges.push(\"restartPolicyType\");\n\t\t}\n\n\t\tif (olds.healthcheckPath !== news.healthcheckPath) {\n\t\t\tchanges.push(\"healthcheckPath\");\n\t\t}\n\n\t\tif (olds.healthcheckTimeout !== news.healthcheckTimeout) {\n\t\t\tchanges.push(\"healthcheckTimeout\");\n\t\t}\n\n\t\tif (olds.preDeployCommand !== news.preDeployCommand) {\n\t\t\tchanges.push(\"preDeployCommand\");\n\t\t}\n\n\t\tif (olds.icon !== news.icon) {\n\t\t\tchanges.push(\"icon\");\n\t\t}\n\n\t\treturn {\n\t\t\tchanges: replaces.length > 0 || changes.length > 0,\n\t\t\treplaces,\n\t\t\tdeleteBeforeReplace: true,\n\t\t};\n\t}\n}\n\n/**\n * Manages a Railway service with adopt-or-create semantics.\n *\n * Queries existing services by project ID and name before creating new ones.\n * Service instance configuration (builder, start command, healthcheck) is\n * applied after creation via `serviceInstanceUpdate`.\n *\n * @example\n * ```typescript\n * const service = new RailwayService(\"railway-service-api\", {\n * token: project.projectToken,\n * projectId: project.projectId,\n * environmentId: project.productionEnvironmentId,\n * name: \"@my-app/api\",\n * builder: \"RAILPACK\",\n * startCommand: \"node dist/index.js\",\n * healthcheckPath: \"/health\",\n * });\n *\n * // Use serviceId downstream\n * new RailwayVariable(\"railway-variable-api\", {\n * serviceId: service.serviceId,\n * ...\n * });\n * ```\n */\nexport class RailwayService extends pulumi.dynamic.Resource {\n\t/** Railway service UUID. */\n\tpublic declare readonly serviceId: pulumi.Output<string>;\n\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 environment UUID (e.g. production). */\n\t\t\tenvironmentId: pulumi.Input<string>;\n\n\t\t\t/** Human-readable service name used for adopt-or-create matching. */\n\t\t\tname: pulumi.Input<string>;\n\n\t\t\t/** SVG icon URL displayed in the Railway dashboard. */\n\t\t\ticon?: pulumi.Input<string>;\n\n\t\t\t/** Docker image source for image-based services. */\n\t\t\tsource?: pulumi.Input<{ image: pulumi.Input<string> }>;\n\n\t\t\t/** Build system: `\"RAILPACK\"`, `\"NIXPACKS\"`, or `\"DOCKERFILE\"`. */\n\t\t\tbuilder?: pulumi.Input<string>;\n\n\t\t\t/** Shell command executed during the build phase. */\n\t\t\tbuildCommand?: pulumi.Input<string>;\n\n\t\t\t/** Shell command executed to start the service at runtime. */\n\t\t\tstartCommand?: pulumi.Input<string>;\n\n\t\t\t/** Restart behavior: `\"ON_FAILURE\"`, `\"ALWAYS\"`, or `\"NEVER\"`. */\n\t\t\trestartPolicyType?: pulumi.Input<string>;\n\n\t\t\t/** HTTP path polled for health checks (e.g. `\"/health-check\"`). */\n\t\t\thealthcheckPath?: pulumi.Input<string>;\n\n\t\t\t/** Seconds to wait for a healthy response before marking unhealthy. */\n\t\t\thealthcheckTimeout?: pulumi.Input<number>;\n\n\t\t\t/** Shell command executed before the main deploy (e.g. migrations). */\n\t\t\tpreDeployCommand?: pulumi.Input<string>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\tsuper(\n\t\t\tnew RailwayServiceProvider(),\n\t\t\tname,\n\t\t\t{ ...args, serviceId: undefined },\n\t\t\topts,\n\t\t);\n\t}\n}\n"],"mappings":"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"}
@@ -0,0 +1,149 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ const require_chunk = require('../chunk-BVYJZCqc.cjs');
3
+ const require_railway_client = require('./client.cjs');
4
+ let _pulumi_pulumi = require("@pulumi/pulumi");
5
+ _pulumi_pulumi = require_chunk.__toESM(_pulumi_pulumi, 1);
6
+
7
+ //#region src/railway/variable.ts
8
+ const VARIABLE_UPSERT = `
9
+ mutation($input: VariableCollectionUpsertInput!) {
10
+ variableCollectionUpsert(input: $input)
11
+ }
12
+ `;
13
+ const VARIABLE_DELETE = `
14
+ mutation($input: VariableDeleteInput!) {
15
+ variableDelete(input: $input)
16
+ }
17
+ `;
18
+ /**
19
+ * Dynamic provider implementing CRUD for Railway service environment variables.
20
+ *
21
+ * Uses `skipDeploys: true` on all upsert operations to prevent
22
+ * "Cannot redeploy without snapshot" errors on newly created services.
23
+ */
24
+ var RailwayVariableProvider = class {
25
+ /**
26
+ * Creates all variables on the target service via batch upsert.
27
+ *
28
+ * @param inputs Resolved variable configuration
29
+ * @returns Composite ID in the form `{serviceId}:variables`
30
+ */
31
+ async create(inputs) {
32
+ await new require_railway_client.RailwayClient(inputs.token).query(VARIABLE_UPSERT, { input: {
33
+ projectId: inputs.projectId,
34
+ serviceId: inputs.serviceId,
35
+ environmentId: inputs.environmentId,
36
+ variables: inputs.variables,
37
+ skipDeploys: true
38
+ } });
39
+ return {
40
+ id: `${inputs.serviceId}:variables`,
41
+ outs: inputs
42
+ };
43
+ }
44
+ /**
45
+ * Updates variables by deleting removed keys individually, then upserting the rest.
46
+ *
47
+ * @param _id Current resource ID (unused)
48
+ * @param olds Previous persisted variable state
49
+ * @param news New desired variable configuration
50
+ * @returns Updated outputs
51
+ */
52
+ async update(_id, olds, news) {
53
+ const client = new require_railway_client.RailwayClient(news.token);
54
+ const removedKeys = Object.keys(olds.variables).filter((key) => !(key in news.variables));
55
+ for (const key of removedKeys) await client.query(VARIABLE_DELETE, { input: {
56
+ projectId: news.projectId,
57
+ serviceId: news.serviceId,
58
+ environmentId: news.environmentId,
59
+ name: key
60
+ } });
61
+ if (Object.keys(news.variables).length > 0) await client.query(VARIABLE_UPSERT, { input: {
62
+ projectId: news.projectId,
63
+ serviceId: news.serviceId,
64
+ environmentId: news.environmentId,
65
+ variables: news.variables,
66
+ skipDeploys: true
67
+ } });
68
+ return { outs: news };
69
+ }
70
+ /**
71
+ * Reads current state for `pulumi refresh`.
72
+ * Returns persisted props since Railway has no single-call variable read API.
73
+ *
74
+ * @param id Current resource ID
75
+ * @param props Last known persisted state
76
+ * @returns Unchanged resource ID and properties
77
+ */
78
+ async read(id, props) {
79
+ return {
80
+ id,
81
+ props
82
+ };
83
+ }
84
+ /**
85
+ * Deletes all variables one by one. Silently succeeds if already deleted.
86
+ *
87
+ * @param _id Current resource ID (unused)
88
+ * @param props Last known persisted state
89
+ */
90
+ async delete(_id, props) {
91
+ const client = new require_railway_client.RailwayClient(props.token);
92
+ for (const key of Object.keys(props.variables)) try {
93
+ await client.query(VARIABLE_DELETE, { input: {
94
+ projectId: props.projectId,
95
+ serviceId: props.serviceId,
96
+ environmentId: props.environmentId,
97
+ name: key
98
+ } });
99
+ } catch {
100
+ _pulumi_pulumi.log.warn(`Failed to delete Railway variable "${key}" (may already be deleted)`);
101
+ }
102
+ }
103
+ /**
104
+ * Compares old and new variable maps by key set and value equality.
105
+ *
106
+ * @param _id Current resource ID (unused)
107
+ * @param olds Previous persisted state
108
+ * @param news New desired configuration
109
+ * @returns Whether any keys or values changed
110
+ */
111
+ async diff(_id, olds, news) {
112
+ const oldKeys = Object.keys(olds.variables).sort().join(",");
113
+ const newKeys = Object.keys(news.variables).sort().join(",");
114
+ const valuesChanged = Object.entries(news.variables).some(([key, value]) => olds.variables[key] !== value);
115
+ return { changes: oldKeys !== newKeys || valuesChanged };
116
+ }
117
+ };
118
+ /**
119
+ * Manages Railway service environment variables with `skipDeploys` to prevent snapshot errors.
120
+ *
121
+ * Handles batch upsert on create/update and per-key deletion when variables are removed.
122
+ * All mutations use `skipDeploys: true` to avoid triggering deploys before the service
123
+ * has a build snapshot.
124
+ *
125
+ * @example
126
+ * ```typescript
127
+ * new RailwayVariable("railway-variable-api", {
128
+ * token: project.projectToken,
129
+ * projectId: project.projectId,
130
+ * serviceId: service.serviceId,
131
+ * environmentId: project.productionEnvironmentId,
132
+ * variables: { DATABASE_URL: databaseUrl, NODE_ENV: "production" },
133
+ * });
134
+ * ```
135
+ */
136
+ var RailwayVariable = class extends _pulumi_pulumi.dynamic.Resource {
137
+ /**
138
+ * @param name Pulumi resource name (logical identifier in state)
139
+ * @param args Variable configuration inputs
140
+ * @param opts Standard Pulumi resource options (e.g. `dependsOn`, `parent`)
141
+ */
142
+ constructor(name, args, opts) {
143
+ super(new RailwayVariableProvider(), name, { ...args }, opts);
144
+ }
145
+ };
146
+
147
+ //#endregion
148
+ exports.RailwayVariable = RailwayVariable;
149
+ //# sourceMappingURL=variable.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"variable.cjs","names":["RailwayClient","pulumi"],"sources":["../../src/railway/variable.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\nimport { RailwayClient } from \"./client.js\";\n\n/** Resolved inputs for the Railway variable dynamic provider. */\nexport interface RailwayVariableInputs {\n\t/** Railway API bearer token. */\n\ttoken: string;\n\n\t/** Railway project UUID. */\n\tprojectId: string;\n\n\t/** Railway service UUID that owns these variables. */\n\tserviceId: string;\n\n\t/** Railway environment UUID (e.g. production). */\n\tenvironmentId: 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 Railway variables (identical to inputs). */\ninterface RailwayVariableOutputs extends RailwayVariableInputs {}\n\nconst VARIABLE_UPSERT = `\n mutation($input: VariableCollectionUpsertInput!) {\n variableCollectionUpsert(input: $input)\n }\n`;\n\nconst VARIABLE_DELETE = `\n mutation($input: VariableDeleteInput!) {\n variableDelete(input: $input)\n }\n`;\n\n/**\n * Dynamic provider implementing CRUD for Railway service environment variables.\n *\n * Uses `skipDeploys: true` on all upsert operations to prevent\n * \"Cannot redeploy without snapshot\" errors on newly created services.\n */\nclass RailwayVariableProvider implements pulumi.dynamic.ResourceProvider {\n\t/**\n\t * Creates all variables on the target service via batch upsert.\n\t *\n\t * @param inputs Resolved variable configuration\n\t * @returns Composite ID in the form `{serviceId}:variables`\n\t */\n\tasync create(\n\t\tinputs: RailwayVariableInputs,\n\t): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst client = new RailwayClient(inputs.token);\n\n\t\tawait client.query(VARIABLE_UPSERT, {\n\t\t\tinput: {\n\t\t\t\tprojectId: inputs.projectId,\n\t\t\t\tserviceId: inputs.serviceId,\n\t\t\t\tenvironmentId: inputs.environmentId,\n\t\t\t\tvariables: inputs.variables,\n\t\t\t\tskipDeploys: true,\n\t\t\t},\n\t\t});\n\n\t\tconst id = `${inputs.serviceId}:variables`;\n\n\t\treturn { id, outs: inputs };\n\t}\n\n\t/**\n\t * Updates variables by deleting removed keys individually, then upserting the rest.\n\t *\n\t * @param _id Current resource ID (unused)\n\t * @param olds Previous persisted variable state\n\t * @param news New desired variable configuration\n\t * @returns Updated outputs\n\t */\n\tasync update(\n\t\t_id: string,\n\t\tolds: RailwayVariableOutputs,\n\t\tnews: RailwayVariableInputs,\n\t): Promise<pulumi.dynamic.UpdateResult> {\n\t\tconst client = new RailwayClient(news.token);\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\tawait client.query(VARIABLE_DELETE, {\n\t\t\t\tinput: {\n\t\t\t\t\tprojectId: news.projectId,\n\t\t\t\t\tserviceId: news.serviceId,\n\t\t\t\t\tenvironmentId: news.environmentId,\n\t\t\t\t\tname: key,\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\n\t\tif (Object.keys(news.variables).length > 0) {\n\t\t\tawait client.query(VARIABLE_UPSERT, {\n\t\t\t\tinput: {\n\t\t\t\t\tprojectId: news.projectId,\n\t\t\t\t\tserviceId: news.serviceId,\n\t\t\t\t\tenvironmentId: news.environmentId,\n\t\t\t\t\tvariables: news.variables,\n\t\t\t\t\tskipDeploys: true,\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\n\t\treturn { outs: news };\n\t}\n\n\t/**\n\t * Reads current state for `pulumi refresh`.\n\t * Returns persisted props since Railway has no single-call variable read API.\n\t *\n\t * @param id Current resource ID\n\t * @param props Last known persisted state\n\t * @returns Unchanged resource ID and properties\n\t */\n\tasync read(\n\t\tid: string,\n\t\tprops: RailwayVariableOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\treturn { id, props };\n\t}\n\n\t/**\n\t * Deletes all variables one by one. Silently succeeds if already deleted.\n\t *\n\t * @param _id Current resource ID (unused)\n\t * @param props Last known persisted state\n\t */\n\tasync delete(_id: string, props: RailwayVariableOutputs): Promise<void> {\n\t\tconst client = new RailwayClient(props.token);\n\n\t\tfor (const key of Object.keys(props.variables)) {\n\t\t\ttry {\n\t\t\t\tawait client.query(VARIABLE_DELETE, {\n\t\t\t\t\tinput: {\n\t\t\t\t\t\tprojectId: props.projectId,\n\t\t\t\t\t\tserviceId: props.serviceId,\n\t\t\t\t\t\tenvironmentId: props.environmentId,\n\t\t\t\t\t\tname: key,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t} catch {\n\t\t\t\tpulumi.log.warn(\n\t\t\t\t\t`Failed to delete Railway variable \"${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 (unused)\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: RailwayVariableOutputs,\n\t\tnews: RailwayVariableInputs,\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 Railway service environment variables with `skipDeploys` to prevent snapshot errors.\n *\n * Handles batch upsert on create/update and per-key deletion when variables are removed.\n * All mutations use `skipDeploys: true` to avoid triggering deploys before the service\n * has a build snapshot.\n *\n * @example\n * ```typescript\n * new RailwayVariable(\"railway-variable-api\", {\n * token: project.projectToken,\n * projectId: project.projectId,\n * serviceId: service.serviceId,\n * environmentId: project.productionEnvironmentId,\n * variables: { DATABASE_URL: databaseUrl, NODE_ENV: \"production\" },\n * });\n * ```\n */\nexport class RailwayVariable extends pulumi.dynamic.Resource {\n\t/**\n\t * @param name Pulumi resource name (logical identifier in state)\n\t * @param args Variable 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 that owns these variables. */\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/** 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(new RailwayVariableProvider(), name, { ...args }, opts);\n\t}\n}\n"],"mappings":";;;;;;;AAwBA,MAAM,kBAAkB;;;;;AAMxB,MAAM,kBAAkB;;;;;;;;;;;AAYxB,IAAM,0BAAN,MAAyE;;;;;;;CAOxE,MAAM,OACL,QACuC;EAGvC,MAAM,IAFaA,qCAAc,OAAO,KAE7B,EAAE,MAAM,iBAAiB,EACnC,OAAO;GACN,WAAW,OAAO;GAClB,WAAW,OAAO;GAClB,eAAe,OAAO;GACtB,WAAW,OAAO;GAClB,aAAa;EACd,EACD,CAAC;EAID,OAAO;GAAE,OAFK,OAAO,UAAU;GAElB,MAAM;EAAO;CAC3B;;;;;;;;;CAUA,MAAM,OACL,KACA,MACA,MACuC;EACvC,MAAM,SAAS,IAAIA,qCAAc,KAAK,KAAK;EAE3C,MAAM,cAAc,OAAO,KAAK,KAAK,SAAS,EAAE,QAC9C,QAAQ,EAAE,OAAO,KAAK,UACxB;EAEA,KAAK,MAAM,OAAO,aACjB,MAAM,OAAO,MAAM,iBAAiB,EACnC,OAAO;GACN,WAAW,KAAK;GAChB,WAAW,KAAK;GAChB,eAAe,KAAK;GACpB,MAAM;EACP,EACD,CAAC;EAGF,IAAI,OAAO,KAAK,KAAK,SAAS,EAAE,SAAS,GACxC,MAAM,OAAO,MAAM,iBAAiB,EACnC,OAAO;GACN,WAAW,KAAK;GAChB,WAAW,KAAK;GAChB,eAAe,KAAK;GACpB,WAAW,KAAK;GAChB,aAAa;EACd,EACD,CAAC;EAGF,OAAO,EAAE,MAAM,KAAK;CACrB;;;;;;;;;CAUA,MAAM,KACL,IACA,OACqC;EACrC,OAAO;GAAE;GAAI;EAAM;CACpB;;;;;;;CAQA,MAAM,OAAO,KAAa,OAA8C;EACvE,MAAM,SAAS,IAAIA,qCAAc,MAAM,KAAK;EAE5C,KAAK,MAAM,OAAO,OAAO,KAAK,MAAM,SAAS,GAC5C,IAAI;GACH,MAAM,OAAO,MAAM,iBAAiB,EACnC,OAAO;IACN,WAAW,MAAM;IACjB,WAAW,MAAM;IACjB,eAAe,MAAM;IACrB,MAAM;GACP,EACD,CAAC;EACF,QAAQ;GACP,eAAO,IAAI,KACV,sCAAsC,IAAI,2BAC3C;EACD;CAEF;;;;;;;;;CAUA,MAAM,KACL,KACA,MACA,MACqC;EACrC,MAAM,UAAU,OAAO,KAAK,KAAK,SAAS,EAAE,KAAK,EAAE,KAAK,GAAG;EAC3D,MAAM,UAAU,OAAO,KAAK,KAAK,SAAS,EAAE,KAAK,EAAE,KAAK,GAAG;EAE3D,MAAM,gBAAgB,OAAO,QAAQ,KAAK,SAAS,EAAE,MACnD,CAAC,KAAK,WAAW,KAAK,UAAU,SAAS,KAC3C;EAEA,OAAO,EACN,SAAS,YAAY,WAAW,cACjC;CACD;AACD;;;;;;;;;;;;;;;;;;;AAoBA,IAAa,kBAAb,cAAqCC,eAAO,QAAQ,SAAS;;;;;;CAM5D,YACC,MACA,MAgBA,MACC;EACD,MAAM,IAAI,wBAAwB,GAAG,MAAM,EAAE,GAAG,KAAK,GAAG,IAAI;CAC7D;AACD"}
@@ -0,0 +1,52 @@
1
+ import { t as __name } from "../chunk-OPjESj5l.cjs";
2
+ import * as pulumi from "@pulumi/pulumi";
3
+
4
+ //#region src/railway/variable.d.ts
5
+ /** Resolved inputs for the Railway variable dynamic provider. */
6
+ interface RailwayVariableInputs {
7
+ /** Railway API bearer token. */
8
+ token: string;
9
+ /** Railway project UUID. */
10
+ projectId: string;
11
+ /** Railway service UUID that owns these variables. */
12
+ serviceId: string;
13
+ /** Railway environment UUID (e.g. production). */
14
+ environmentId: string;
15
+ /** Key-value map of environment variable names to their values. */
16
+ variables: Record<string, string>;
17
+ }
18
+ /**
19
+ * Manages Railway service environment variables with `skipDeploys` to prevent snapshot errors.
20
+ *
21
+ * Handles batch upsert on create/update and per-key deletion when variables are removed.
22
+ * All mutations use `skipDeploys: true` to avoid triggering deploys before the service
23
+ * has a build snapshot.
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * new RailwayVariable("railway-variable-api", {
28
+ * token: project.projectToken,
29
+ * projectId: project.projectId,
30
+ * serviceId: service.serviceId,
31
+ * environmentId: project.productionEnvironmentId,
32
+ * variables: { DATABASE_URL: databaseUrl, NODE_ENV: "production" },
33
+ * });
34
+ * ```
35
+ */
36
+ declare class RailwayVariable extends pulumi.dynamic.Resource {
37
+ /**
38
+ * @param name Pulumi resource name (logical identifier in state)
39
+ * @param args Variable configuration inputs
40
+ * @param opts Standard Pulumi resource options (e.g. `dependsOn`, `parent`)
41
+ */
42
+ constructor(name: string, args: {
43
+ /** Railway API bearer token. */token: pulumi.Input<string>; /** Railway project UUID. */
44
+ projectId: pulumi.Input<string>; /** Railway service UUID that owns these variables. */
45
+ serviceId: pulumi.Input<string>; /** Railway environment UUID (e.g. production). */
46
+ environmentId: pulumi.Input<string>; /** Key-value map of environment variable names to their values. */
47
+ variables: pulumi.Input<Record<string, pulumi.Input<string>>>;
48
+ }, opts?: pulumi.CustomResourceOptions);
49
+ }
50
+ //#endregion
51
+ export { RailwayVariable, RailwayVariableInputs };
52
+ //# sourceMappingURL=variable.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"variable.d.cts","names":[],"sources":["../../src/railway/variable.ts"],"mappings":";;;;;UAIiB,qBAAA;;EAEhB,KAAA;EAFqC;EAKrC,SAAA;EASiB;EANjB,SAAA;EAHA;EAMA,aAAA;EAAA;EAGA,SAAA,EAAW,MAAM;AAAA;;AAAA;AAsLlB;;;;;;;;;;;;;;;;cAAa,eAAA,SAAwB,MAAA,CAAO,OAAA,CAAQ,QAAA;;;;;;cAOlD,IAAA,UACA,IAAA;IAKY,gCAHX,KAAA,EAAO,MAAA,CAAO,KAAA,UAMd;IAHA,SAAA,EAAW,MAAA,CAAO,KAAA,UAGA;IAAlB,SAAA,EAAW,MAAA,CAAO,KAAA,UAGH;IAAf,aAAA,EAAe,MAAA,CAAO,KAAA,UAGtB;IAAA,SAAA,EAAW,MAAA,CAAO,KAAA,CAAM,MAAA,SAAe,MAAA,CAAO,KAAA;EAAA,GAE/C,IAAA,GAAO,MAAA,CAAO,qBAAA;AAAA"}
@@ -0,0 +1,52 @@
1
+ import { t as __name } from "../chunk-OPjESj5l.mjs";
2
+ import * as pulumi from "@pulumi/pulumi";
3
+
4
+ //#region src/railway/variable.d.ts
5
+ /** Resolved inputs for the Railway variable dynamic provider. */
6
+ interface RailwayVariableInputs {
7
+ /** Railway API bearer token. */
8
+ token: string;
9
+ /** Railway project UUID. */
10
+ projectId: string;
11
+ /** Railway service UUID that owns these variables. */
12
+ serviceId: string;
13
+ /** Railway environment UUID (e.g. production). */
14
+ environmentId: string;
15
+ /** Key-value map of environment variable names to their values. */
16
+ variables: Record<string, string>;
17
+ }
18
+ /**
19
+ * Manages Railway service environment variables with `skipDeploys` to prevent snapshot errors.
20
+ *
21
+ * Handles batch upsert on create/update and per-key deletion when variables are removed.
22
+ * All mutations use `skipDeploys: true` to avoid triggering deploys before the service
23
+ * has a build snapshot.
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * new RailwayVariable("railway-variable-api", {
28
+ * token: project.projectToken,
29
+ * projectId: project.projectId,
30
+ * serviceId: service.serviceId,
31
+ * environmentId: project.productionEnvironmentId,
32
+ * variables: { DATABASE_URL: databaseUrl, NODE_ENV: "production" },
33
+ * });
34
+ * ```
35
+ */
36
+ declare class RailwayVariable extends pulumi.dynamic.Resource {
37
+ /**
38
+ * @param name Pulumi resource name (logical identifier in state)
39
+ * @param args Variable configuration inputs
40
+ * @param opts Standard Pulumi resource options (e.g. `dependsOn`, `parent`)
41
+ */
42
+ constructor(name: string, args: {
43
+ /** Railway API bearer token. */token: pulumi.Input<string>; /** Railway project UUID. */
44
+ projectId: pulumi.Input<string>; /** Railway service UUID that owns these variables. */
45
+ serviceId: pulumi.Input<string>; /** Railway environment UUID (e.g. production). */
46
+ environmentId: pulumi.Input<string>; /** Key-value map of environment variable names to their values. */
47
+ variables: pulumi.Input<Record<string, pulumi.Input<string>>>;
48
+ }, opts?: pulumi.CustomResourceOptions);
49
+ }
50
+ //#endregion
51
+ export { RailwayVariable, RailwayVariableInputs };
52
+ //# sourceMappingURL=variable.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"variable.d.mts","names":[],"sources":["../../src/railway/variable.ts"],"mappings":";;;;;UAIiB,qBAAA;;EAEhB,KAAA;EAFqC;EAKrC,SAAA;EASiB;EANjB,SAAA;EAHA;EAMA,aAAA;EAAA;EAGA,SAAA,EAAW,MAAM;AAAA;;AAAA;AAsLlB;;;;;;;;;;;;;;;;cAAa,eAAA,SAAwB,MAAA,CAAO,OAAA,CAAQ,QAAA;;;;;;cAOlD,IAAA,UACA,IAAA;IAKY,gCAHX,KAAA,EAAO,MAAA,CAAO,KAAA,UAMd;IAHA,SAAA,EAAW,MAAA,CAAO,KAAA,UAGA;IAAlB,SAAA,EAAW,MAAA,CAAO,KAAA,UAGH;IAAf,aAAA,EAAe,MAAA,CAAO,KAAA,UAGtB;IAAA,SAAA,EAAW,MAAA,CAAO,KAAA,CAAM,MAAA,SAAe,MAAA,CAAO,KAAA;EAAA,GAE/C,IAAA,GAAO,MAAA,CAAO,qBAAA;AAAA"}