@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.
- package/dist/chunk-BVYJZCqc.cjs +44 -0
- package/dist/chunk-OPjESj5l.mjs +9 -0
- package/dist/git-guard.cjs +62 -0
- package/dist/git-guard.cjs.map +1 -0
- package/dist/git-guard.d.cts +13 -0
- package/dist/git-guard.d.cts.map +1 -0
- package/dist/git-guard.d.mts +13 -0
- package/dist/git-guard.d.mts.map +1 -0
- package/dist/git-guard.mjs +56 -0
- package/dist/git-guard.mjs.map +1 -0
- package/dist/hash.cjs +40 -0
- package/dist/hash.cjs.map +1 -0
- package/dist/hash.d.cts +10 -0
- package/dist/hash.d.cts.map +1 -0
- package/dist/hash.d.mts +10 -0
- package/dist/hash.d.mts.map +1 -0
- package/dist/hash.mjs +36 -0
- package/dist/hash.mjs.map +1 -0
- package/dist/neon/branch.cjs +113 -0
- package/dist/neon/branch.cjs.map +1 -0
- package/dist/neon/branch.d.cts +40 -0
- package/dist/neon/branch.d.cts.map +1 -0
- package/dist/neon/branch.d.mts +40 -0
- package/dist/neon/branch.d.mts.map +1 -0
- package/dist/neon/branch.mjs +111 -0
- package/dist/neon/branch.mjs.map +1 -0
- package/dist/neon/client.cjs +85 -0
- package/dist/neon/client.cjs.map +1 -0
- package/dist/neon/client.d.cts +59 -0
- package/dist/neon/client.d.cts.map +1 -0
- package/dist/neon/client.d.mts +59 -0
- package/dist/neon/client.d.mts.map +1 -0
- package/dist/neon/client.mjs +84 -0
- package/dist/neon/client.mjs.map +1 -0
- package/dist/neon/database.cjs +117 -0
- package/dist/neon/database.cjs.map +1 -0
- package/dist/neon/database.d.cts +48 -0
- package/dist/neon/database.d.cts.map +1 -0
- package/dist/neon/database.d.mts +48 -0
- package/dist/neon/database.d.mts.map +1 -0
- package/dist/neon/database.mjs +115 -0
- package/dist/neon/database.mjs.map +1 -0
- package/dist/neon/endpoint.cjs +167 -0
- package/dist/neon/endpoint.cjs.map +1 -0
- package/dist/neon/endpoint.d.cts +57 -0
- package/dist/neon/endpoint.d.cts.map +1 -0
- package/dist/neon/endpoint.d.mts +57 -0
- package/dist/neon/endpoint.d.mts.map +1 -0
- package/dist/neon/endpoint.mjs +165 -0
- package/dist/neon/endpoint.mjs.map +1 -0
- package/dist/neon/index.cjs +14 -0
- package/dist/neon/index.d.cts +7 -0
- package/dist/neon/index.d.mts +7 -0
- package/dist/neon/index.mjs +8 -0
- package/dist/neon/project.cjs +120 -0
- package/dist/neon/project.cjs.map +1 -0
- package/dist/neon/project.d.cts +52 -0
- package/dist/neon/project.d.cts.map +1 -0
- package/dist/neon/project.d.mts +52 -0
- package/dist/neon/project.d.mts.map +1 -0
- package/dist/neon/project.mjs +118 -0
- package/dist/neon/project.mjs.map +1 -0
- package/dist/neon/role.cjs +135 -0
- package/dist/neon/role.cjs.map +1 -0
- package/dist/neon/role.d.cts +49 -0
- package/dist/neon/role.d.cts.map +1 -0
- package/dist/neon/role.d.mts +49 -0
- package/dist/neon/role.d.mts.map +1 -0
- package/dist/neon/role.mjs +133 -0
- package/dist/neon/role.mjs.map +1 -0
- package/dist/railway/client.cjs +59 -0
- package/dist/railway/client.cjs.map +1 -0
- package/dist/railway/client.d.cts +39 -0
- package/dist/railway/client.d.cts.map +1 -0
- package/dist/railway/client.d.mts +39 -0
- package/dist/railway/client.d.mts.map +1 -0
- package/dist/railway/client.mjs +58 -0
- package/dist/railway/client.mjs.map +1 -0
- package/dist/railway/deploy.cjs +40 -0
- package/dist/railway/deploy.cjs.map +1 -0
- package/dist/railway/deploy.d.cts +47 -0
- package/dist/railway/deploy.d.cts.map +1 -0
- package/dist/railway/deploy.d.mts +47 -0
- package/dist/railway/deploy.d.mts.map +1 -0
- package/dist/railway/deploy.mjs +37 -0
- package/dist/railway/deploy.mjs.map +1 -0
- package/dist/railway/domain.cjs +236 -0
- package/dist/railway/domain.cjs.map +1 -0
- package/dist/railway/domain.d.cts +52 -0
- package/dist/railway/domain.d.cts.map +1 -0
- package/dist/railway/domain.d.mts +52 -0
- package/dist/railway/domain.d.mts.map +1 -0
- package/dist/railway/domain.mjs +234 -0
- package/dist/railway/domain.mjs.map +1 -0
- package/dist/railway/index.cjs +16 -0
- package/dist/railway/index.d.cts +8 -0
- package/dist/railway/index.d.mts +8 -0
- package/dist/railway/index.mjs +9 -0
- package/dist/railway/project.cjs +231 -0
- package/dist/railway/project.cjs.map +1 -0
- package/dist/railway/project.d.cts +52 -0
- package/dist/railway/project.d.cts.map +1 -0
- package/dist/railway/project.d.mts +52 -0
- package/dist/railway/project.d.mts.map +1 -0
- package/dist/railway/project.mjs +229 -0
- package/dist/railway/project.mjs.map +1 -0
- package/dist/railway/service.cjs +268 -0
- package/dist/railway/service.cjs.map +1 -0
- package/dist/railway/service.d.cts +88 -0
- package/dist/railway/service.d.cts.map +1 -0
- package/dist/railway/service.d.mts +88 -0
- package/dist/railway/service.d.mts.map +1 -0
- package/dist/railway/service.mjs +266 -0
- package/dist/railway/service.mjs.map +1 -0
- package/dist/railway/variable.cjs +149 -0
- package/dist/railway/variable.cjs.map +1 -0
- package/dist/railway/variable.d.cts +52 -0
- package/dist/railway/variable.d.cts.map +1 -0
- package/dist/railway/variable.d.mts +52 -0
- package/dist/railway/variable.d.mts.map +1 -0
- package/dist/railway/variable.mjs +147 -0
- package/dist/railway/variable.mjs.map +1 -0
- package/dist/railway/volume.cjs +179 -0
- package/dist/railway/volume.cjs.map +1 -0
- package/dist/railway/volume.d.cts +52 -0
- package/dist/railway/volume.d.cts.map +1 -0
- package/dist/railway/volume.d.mts +52 -0
- package/dist/railway/volume.d.mts.map +1 -0
- package/dist/railway/volume.mjs +177 -0
- package/dist/railway/volume.mjs.map +1 -0
- package/dist/vercel/deploy.cjs +65 -0
- package/dist/vercel/deploy.cjs.map +1 -0
- package/dist/vercel/deploy.d.cts +54 -0
- package/dist/vercel/deploy.d.cts.map +1 -0
- package/dist/vercel/deploy.d.mts +54 -0
- package/dist/vercel/deploy.d.mts.map +1 -0
- package/dist/vercel/deploy.mjs +61 -0
- package/dist/vercel/deploy.mjs.map +1 -0
- package/dist/vercel/index.cjs +6 -0
- package/dist/vercel/index.d.cts +3 -0
- package/dist/vercel/index.d.mts +3 -0
- package/dist/vercel/index.mjs +4 -0
- package/dist/vercel/variable.cjs +280 -0
- package/dist/vercel/variable.cjs.map +1 -0
- package/dist/vercel/variable.d.cts +55 -0
- package/dist/vercel/variable.d.cts.map +1 -0
- package/dist/vercel/variable.d.mts +55 -0
- package/dist/vercel/variable.d.mts.map +1 -0
- package/dist/vercel/variable.mjs +278 -0
- package/dist/vercel/variable.mjs.map +1 -0
- package/package.json +62 -0
|
@@ -0,0 +1,147 @@
|
|
|
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/variable.ts
|
|
6
|
+
const VARIABLE_UPSERT = `
|
|
7
|
+
mutation($input: VariableCollectionUpsertInput!) {
|
|
8
|
+
variableCollectionUpsert(input: $input)
|
|
9
|
+
}
|
|
10
|
+
`;
|
|
11
|
+
const VARIABLE_DELETE = `
|
|
12
|
+
mutation($input: VariableDeleteInput!) {
|
|
13
|
+
variableDelete(input: $input)
|
|
14
|
+
}
|
|
15
|
+
`;
|
|
16
|
+
/**
|
|
17
|
+
* Dynamic provider implementing CRUD for Railway service environment variables.
|
|
18
|
+
*
|
|
19
|
+
* Uses `skipDeploys: true` on all upsert operations to prevent
|
|
20
|
+
* "Cannot redeploy without snapshot" errors on newly created services.
|
|
21
|
+
*/
|
|
22
|
+
var RailwayVariableProvider = class {
|
|
23
|
+
/**
|
|
24
|
+
* Creates all variables on the target service via batch upsert.
|
|
25
|
+
*
|
|
26
|
+
* @param inputs Resolved variable configuration
|
|
27
|
+
* @returns Composite ID in the form `{serviceId}:variables`
|
|
28
|
+
*/
|
|
29
|
+
async create(inputs) {
|
|
30
|
+
await new RailwayClient(inputs.token).query(VARIABLE_UPSERT, { input: {
|
|
31
|
+
projectId: inputs.projectId,
|
|
32
|
+
serviceId: inputs.serviceId,
|
|
33
|
+
environmentId: inputs.environmentId,
|
|
34
|
+
variables: inputs.variables,
|
|
35
|
+
skipDeploys: true
|
|
36
|
+
} });
|
|
37
|
+
return {
|
|
38
|
+
id: `${inputs.serviceId}:variables`,
|
|
39
|
+
outs: inputs
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Updates variables by deleting removed keys individually, then upserting the rest.
|
|
44
|
+
*
|
|
45
|
+
* @param _id Current resource ID (unused)
|
|
46
|
+
* @param olds Previous persisted variable state
|
|
47
|
+
* @param news New desired variable configuration
|
|
48
|
+
* @returns Updated outputs
|
|
49
|
+
*/
|
|
50
|
+
async update(_id, olds, news) {
|
|
51
|
+
const client = new RailwayClient(news.token);
|
|
52
|
+
const removedKeys = Object.keys(olds.variables).filter((key) => !(key in news.variables));
|
|
53
|
+
for (const key of removedKeys) await client.query(VARIABLE_DELETE, { input: {
|
|
54
|
+
projectId: news.projectId,
|
|
55
|
+
serviceId: news.serviceId,
|
|
56
|
+
environmentId: news.environmentId,
|
|
57
|
+
name: key
|
|
58
|
+
} });
|
|
59
|
+
if (Object.keys(news.variables).length > 0) await client.query(VARIABLE_UPSERT, { input: {
|
|
60
|
+
projectId: news.projectId,
|
|
61
|
+
serviceId: news.serviceId,
|
|
62
|
+
environmentId: news.environmentId,
|
|
63
|
+
variables: news.variables,
|
|
64
|
+
skipDeploys: true
|
|
65
|
+
} });
|
|
66
|
+
return { outs: news };
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Reads current state for `pulumi refresh`.
|
|
70
|
+
* Returns persisted props since Railway has no single-call variable read API.
|
|
71
|
+
*
|
|
72
|
+
* @param id Current resource ID
|
|
73
|
+
* @param props Last known persisted state
|
|
74
|
+
* @returns Unchanged resource ID and properties
|
|
75
|
+
*/
|
|
76
|
+
async read(id, props) {
|
|
77
|
+
return {
|
|
78
|
+
id,
|
|
79
|
+
props
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Deletes all variables one by one. Silently succeeds if already deleted.
|
|
84
|
+
*
|
|
85
|
+
* @param _id Current resource ID (unused)
|
|
86
|
+
* @param props Last known persisted state
|
|
87
|
+
*/
|
|
88
|
+
async delete(_id, props) {
|
|
89
|
+
const client = new RailwayClient(props.token);
|
|
90
|
+
for (const key of Object.keys(props.variables)) try {
|
|
91
|
+
await client.query(VARIABLE_DELETE, { input: {
|
|
92
|
+
projectId: props.projectId,
|
|
93
|
+
serviceId: props.serviceId,
|
|
94
|
+
environmentId: props.environmentId,
|
|
95
|
+
name: key
|
|
96
|
+
} });
|
|
97
|
+
} catch {
|
|
98
|
+
pulumi.log.warn(`Failed to delete Railway variable "${key}" (may already be deleted)`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Compares old and new variable maps by key set and value equality.
|
|
103
|
+
*
|
|
104
|
+
* @param _id Current resource ID (unused)
|
|
105
|
+
* @param olds Previous persisted state
|
|
106
|
+
* @param news New desired configuration
|
|
107
|
+
* @returns Whether any keys or values changed
|
|
108
|
+
*/
|
|
109
|
+
async diff(_id, olds, news) {
|
|
110
|
+
const oldKeys = Object.keys(olds.variables).sort().join(",");
|
|
111
|
+
const newKeys = Object.keys(news.variables).sort().join(",");
|
|
112
|
+
const valuesChanged = Object.entries(news.variables).some(([key, value]) => olds.variables[key] !== value);
|
|
113
|
+
return { changes: oldKeys !== newKeys || valuesChanged };
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
/**
|
|
117
|
+
* Manages Railway service environment variables with `skipDeploys` to prevent snapshot errors.
|
|
118
|
+
*
|
|
119
|
+
* Handles batch upsert on create/update and per-key deletion when variables are removed.
|
|
120
|
+
* All mutations use `skipDeploys: true` to avoid triggering deploys before the service
|
|
121
|
+
* has a build snapshot.
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* ```typescript
|
|
125
|
+
* new RailwayVariable("railway-variable-api", {
|
|
126
|
+
* token: project.projectToken,
|
|
127
|
+
* projectId: project.projectId,
|
|
128
|
+
* serviceId: service.serviceId,
|
|
129
|
+
* environmentId: project.productionEnvironmentId,
|
|
130
|
+
* variables: { DATABASE_URL: databaseUrl, NODE_ENV: "production" },
|
|
131
|
+
* });
|
|
132
|
+
* ```
|
|
133
|
+
*/
|
|
134
|
+
var RailwayVariable = class extends pulumi.dynamic.Resource {
|
|
135
|
+
/**
|
|
136
|
+
* @param name Pulumi resource name (logical identifier in state)
|
|
137
|
+
* @param args Variable configuration inputs
|
|
138
|
+
* @param opts Standard Pulumi resource options (e.g. `dependsOn`, `parent`)
|
|
139
|
+
*/
|
|
140
|
+
constructor(name, args, opts) {
|
|
141
|
+
super(new RailwayVariableProvider(), name, { ...args }, opts);
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
//#endregion
|
|
146
|
+
export { RailwayVariable };
|
|
147
|
+
//# sourceMappingURL=variable.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"variable.mjs","names":[],"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,IAFa,cAAc,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,IAAI,cAAc,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,IAAI,cAAc,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,OAAO,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,cAAqC,OAAO,QAAQ,SAAS;;;;;;CAM5D,YACC,MACA,MAgBA,MACC;EACD,MAAM,IAAI,wBAAwB,GAAG,MAAM,EAAE,GAAG,KAAK,GAAG,IAAI;CAC7D;AACD"}
|
|
@@ -0,0 +1,179 @@
|
|
|
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/volume.ts
|
|
8
|
+
const VOLUME_CREATE = `
|
|
9
|
+
mutation($input: VolumeCreateInput!) {
|
|
10
|
+
volumeCreate(input: $input) {
|
|
11
|
+
id
|
|
12
|
+
name
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
`;
|
|
16
|
+
const VOLUME_DELETE = `
|
|
17
|
+
mutation($volumeId: String!) {
|
|
18
|
+
volumeDelete(volumeId: $volumeId)
|
|
19
|
+
}
|
|
20
|
+
`;
|
|
21
|
+
const VOLUMES_QUERY = `
|
|
22
|
+
query($projectId: String!) {
|
|
23
|
+
project(id: $projectId) {
|
|
24
|
+
volumes {
|
|
25
|
+
edges {
|
|
26
|
+
node {
|
|
27
|
+
id
|
|
28
|
+
name
|
|
29
|
+
volumeInstances {
|
|
30
|
+
edges {
|
|
31
|
+
node {
|
|
32
|
+
id
|
|
33
|
+
mountPath
|
|
34
|
+
serviceId
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
`;
|
|
44
|
+
/**
|
|
45
|
+
* Finds an existing volume attached to a specific service within a project.
|
|
46
|
+
*
|
|
47
|
+
* @param client Authenticated Railway API client
|
|
48
|
+
* @param projectId Railway project UUID to search within
|
|
49
|
+
* @param serviceId Railway service UUID to match volume instances against
|
|
50
|
+
* @returns The volume UUID if found, `undefined` otherwise
|
|
51
|
+
*/
|
|
52
|
+
async function findVolumeByService(client, projectId, serviceId) {
|
|
53
|
+
return (await client.query(VOLUMES_QUERY, { projectId })).project.volumes.edges.find((edge) => edge.node.volumeInstances.edges.some((vi) => vi.node.serviceId === serviceId))?.node.id;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Dynamic provider implementing CRUD for Railway persistent volumes.
|
|
57
|
+
*
|
|
58
|
+
* Uses adopt-or-create on `create()`: finds an existing volume by service
|
|
59
|
+
* before creating a new one, making `pulumi up` idempotent from zero.
|
|
60
|
+
* Volumes are immutable after creation — changing `serviceId`, `mountPath`,
|
|
61
|
+
* `environmentId`, or `projectId` triggers replacement (delete + create).
|
|
62
|
+
*/
|
|
63
|
+
var RailwayVolumeProvider = class {
|
|
64
|
+
/**
|
|
65
|
+
* Creates or adopts a Railway volume for the target service.
|
|
66
|
+
*
|
|
67
|
+
* @param inputs Resolved volume configuration
|
|
68
|
+
* @returns The Railway volume UUID as the resource ID
|
|
69
|
+
*/
|
|
70
|
+
async create(inputs) {
|
|
71
|
+
const client = new require_railway_client.RailwayClient(inputs.token);
|
|
72
|
+
let volumeId = await findVolumeByService(client, inputs.projectId, inputs.serviceId);
|
|
73
|
+
if (volumeId) _pulumi_pulumi.log.info(`Adopting existing volume for service (${volumeId})`);
|
|
74
|
+
else volumeId = (await client.query(VOLUME_CREATE, { input: {
|
|
75
|
+
projectId: inputs.projectId,
|
|
76
|
+
serviceId: inputs.serviceId,
|
|
77
|
+
environmentId: inputs.environmentId,
|
|
78
|
+
mountPath: inputs.mountPath
|
|
79
|
+
} })).volumeCreate.id;
|
|
80
|
+
return {
|
|
81
|
+
id: volumeId,
|
|
82
|
+
outs: {
|
|
83
|
+
...inputs,
|
|
84
|
+
volumeId
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Reads current state for `pulumi refresh` by looking up the volume.
|
|
90
|
+
*
|
|
91
|
+
* @param id Current Railway volume UUID
|
|
92
|
+
* @param props Last known persisted state
|
|
93
|
+
* @returns Refreshed resource ID and properties
|
|
94
|
+
* @throws {Error} If the volume no longer exists in Railway
|
|
95
|
+
*/
|
|
96
|
+
async read(_id, props) {
|
|
97
|
+
const volumeId = await findVolumeByService(new require_railway_client.RailwayClient(props.token), props.projectId, props.serviceId);
|
|
98
|
+
if (!volumeId) throw new Error("Railway volume not found during refresh");
|
|
99
|
+
return {
|
|
100
|
+
id: volumeId,
|
|
101
|
+
props: {
|
|
102
|
+
...props,
|
|
103
|
+
volumeId
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Deletes the Railway volume. Silently succeeds if already deleted.
|
|
109
|
+
*
|
|
110
|
+
* @param id Railway volume UUID to delete
|
|
111
|
+
* @param props Last known persisted state (used for token)
|
|
112
|
+
*/
|
|
113
|
+
async delete(id, props) {
|
|
114
|
+
const client = new require_railway_client.RailwayClient(props.token);
|
|
115
|
+
try {
|
|
116
|
+
await client.query(VOLUME_DELETE, { volumeId: id });
|
|
117
|
+
} catch {
|
|
118
|
+
_pulumi_pulumi.log.warn("Failed to delete Railway volume (may already be deleted)");
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Compares old and new inputs to determine what changed.
|
|
123
|
+
*
|
|
124
|
+
* All mutable properties trigger replacement since Railway volumes
|
|
125
|
+
* cannot be moved between services, mount paths, environments, or projects.
|
|
126
|
+
*
|
|
127
|
+
* @param _id Current resource ID (unused)
|
|
128
|
+
* @param olds Previous persisted state
|
|
129
|
+
* @param news New desired configuration
|
|
130
|
+
* @returns Diff result with replacement triggers
|
|
131
|
+
*/
|
|
132
|
+
async diff(_id, olds, news) {
|
|
133
|
+
const replaces = [];
|
|
134
|
+
if (olds.serviceId !== news.serviceId) replaces.push("serviceId");
|
|
135
|
+
if (olds.mountPath !== news.mountPath) replaces.push("mountPath");
|
|
136
|
+
if (olds.environmentId !== news.environmentId) replaces.push("environmentId");
|
|
137
|
+
if (olds.projectId !== news.projectId) replaces.push("projectId");
|
|
138
|
+
return {
|
|
139
|
+
changes: replaces.length > 0,
|
|
140
|
+
replaces,
|
|
141
|
+
deleteBeforeReplace: true
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
/**
|
|
146
|
+
* Manages a Railway persistent volume with adopt-or-create semantics.
|
|
147
|
+
*
|
|
148
|
+
* Finds existing volumes attached to the target service before creating
|
|
149
|
+
* new ones. Changing `serviceId`, `mountPath`, `environmentId`, or `projectId`
|
|
150
|
+
* triggers replacement (delete + create with new settings).
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* ```typescript
|
|
154
|
+
* new RailwayVolume("railway-volume-redis", {
|
|
155
|
+
* token: project.projectToken,
|
|
156
|
+
* projectId: project.projectId,
|
|
157
|
+
* serviceId: service.serviceId,
|
|
158
|
+
* environmentId: project.productionEnvironmentId,
|
|
159
|
+
* mountPath: "/data",
|
|
160
|
+
* });
|
|
161
|
+
* ```
|
|
162
|
+
*/
|
|
163
|
+
var RailwayVolume = class extends _pulumi_pulumi.dynamic.Resource {
|
|
164
|
+
/**
|
|
165
|
+
* @param name Pulumi resource name (logical identifier in state)
|
|
166
|
+
* @param args Volume configuration inputs
|
|
167
|
+
* @param opts Standard Pulumi resource options (e.g. `dependsOn`, `parent`)
|
|
168
|
+
*/
|
|
169
|
+
constructor(name, args, opts) {
|
|
170
|
+
super(new RailwayVolumeProvider(), name, {
|
|
171
|
+
...args,
|
|
172
|
+
volumeId: void 0
|
|
173
|
+
}, opts);
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
//#endregion
|
|
178
|
+
exports.RailwayVolume = RailwayVolume;
|
|
179
|
+
//# sourceMappingURL=volume.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"volume.cjs","names":["RailwayClient","pulumi"],"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,IAAIA,qCAAc,OAAO,KAAK;EAE7C,IAAI,WAAW,MAAM,oBACpB,QACA,OAAO,WACP,OAAO,SACR;EAEA,IAAI,UACH,eAAO,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,IAHkBA,qCAAc,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,IAAIA,qCAAc,MAAM,KAAK;EAE5C,IAAI;GACH,MAAM,OAAO,MAAM,eAAe,EAAE,UAAU,GAAG,CAAC;EACnD,QAAQ;GACP,eAAO,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,cAAmCC,eAAO,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,52 @@
|
|
|
1
|
+
import { t as __name } from "../chunk-OPjESj5l.cjs";
|
|
2
|
+
import * as pulumi from "@pulumi/pulumi";
|
|
3
|
+
|
|
4
|
+
//#region src/railway/volume.d.ts
|
|
5
|
+
/** Resolved inputs for the Railway volume dynamic provider. */
|
|
6
|
+
interface RailwayVolumeInputs {
|
|
7
|
+
/** Railway API bearer token. */
|
|
8
|
+
token: string;
|
|
9
|
+
/** Railway project UUID. */
|
|
10
|
+
projectId: string;
|
|
11
|
+
/** Railway service UUID to attach the volume to. */
|
|
12
|
+
serviceId: string;
|
|
13
|
+
/** Railway environment UUID (e.g. production). */
|
|
14
|
+
environmentId: string;
|
|
15
|
+
/** Absolute path inside the container where the volume is mounted (e.g. `"/data"`). */
|
|
16
|
+
mountPath: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Manages a Railway persistent volume with adopt-or-create semantics.
|
|
20
|
+
*
|
|
21
|
+
* Finds existing volumes attached to the target service before creating
|
|
22
|
+
* new ones. Changing `serviceId`, `mountPath`, `environmentId`, or `projectId`
|
|
23
|
+
* triggers replacement (delete + create with new settings).
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```typescript
|
|
27
|
+
* new RailwayVolume("railway-volume-redis", {
|
|
28
|
+
* token: project.projectToken,
|
|
29
|
+
* projectId: project.projectId,
|
|
30
|
+
* serviceId: service.serviceId,
|
|
31
|
+
* environmentId: project.productionEnvironmentId,
|
|
32
|
+
* mountPath: "/data",
|
|
33
|
+
* });
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
declare class RailwayVolume extends pulumi.dynamic.Resource {
|
|
37
|
+
/**
|
|
38
|
+
* @param name Pulumi resource name (logical identifier in state)
|
|
39
|
+
* @param args Volume 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 to attach the volume to. */
|
|
45
|
+
serviceId: pulumi.Input<string>; /** Railway environment UUID (e.g. production). */
|
|
46
|
+
environmentId: pulumi.Input<string>; /** Absolute path inside the container where the volume is mounted. */
|
|
47
|
+
mountPath: pulumi.Input<string>;
|
|
48
|
+
}, opts?: pulumi.CustomResourceOptions);
|
|
49
|
+
}
|
|
50
|
+
//#endregion
|
|
51
|
+
export { RailwayVolume, RailwayVolumeInputs };
|
|
52
|
+
//# sourceMappingURL=volume.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"volume.d.cts","names":[],"sources":["../../src/railway/volume.ts"],"mappings":";;;;;UAIiB,mBAAA;;EAEhB,KAAA;EAFmC;EAKnC,SAAA;EALmC;EAQnC,SAAA;EAHA;EAMA,aAAA;EAAA;EAGA,SAAA;AAAA;AAAS;AAiPV;;;;;;;;;;;;;;;;;AAjPU,cAiPG,aAAA,SAAsB,MAAA,CAAO,OAAA,CAAQ,QAAA;EAUxC;;;;;cAHR,IAAA,UACA,IAAA;IAQY,gCANX,KAAA,EAAO,MAAA,CAAO,KAAA,UASd;IANA,SAAA,EAAW,MAAA,CAAO,KAAA,UAMI;IAHtB,SAAA,EAAW,MAAA,CAAO,KAAA,UAMP;IAHX,aAAA,EAAe,MAAA,CAAO,KAAA,UAXvB;IAcC,SAAA,EAAW,MAAA,CAAO,KAAA;EAAA,GAEnB,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/volume.d.ts
|
|
5
|
+
/** Resolved inputs for the Railway volume dynamic provider. */
|
|
6
|
+
interface RailwayVolumeInputs {
|
|
7
|
+
/** Railway API bearer token. */
|
|
8
|
+
token: string;
|
|
9
|
+
/** Railway project UUID. */
|
|
10
|
+
projectId: string;
|
|
11
|
+
/** Railway service UUID to attach the volume to. */
|
|
12
|
+
serviceId: string;
|
|
13
|
+
/** Railway environment UUID (e.g. production). */
|
|
14
|
+
environmentId: string;
|
|
15
|
+
/** Absolute path inside the container where the volume is mounted (e.g. `"/data"`). */
|
|
16
|
+
mountPath: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Manages a Railway persistent volume with adopt-or-create semantics.
|
|
20
|
+
*
|
|
21
|
+
* Finds existing volumes attached to the target service before creating
|
|
22
|
+
* new ones. Changing `serviceId`, `mountPath`, `environmentId`, or `projectId`
|
|
23
|
+
* triggers replacement (delete + create with new settings).
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```typescript
|
|
27
|
+
* new RailwayVolume("railway-volume-redis", {
|
|
28
|
+
* token: project.projectToken,
|
|
29
|
+
* projectId: project.projectId,
|
|
30
|
+
* serviceId: service.serviceId,
|
|
31
|
+
* environmentId: project.productionEnvironmentId,
|
|
32
|
+
* mountPath: "/data",
|
|
33
|
+
* });
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
declare class RailwayVolume extends pulumi.dynamic.Resource {
|
|
37
|
+
/**
|
|
38
|
+
* @param name Pulumi resource name (logical identifier in state)
|
|
39
|
+
* @param args Volume 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 to attach the volume to. */
|
|
45
|
+
serviceId: pulumi.Input<string>; /** Railway environment UUID (e.g. production). */
|
|
46
|
+
environmentId: pulumi.Input<string>; /** Absolute path inside the container where the volume is mounted. */
|
|
47
|
+
mountPath: pulumi.Input<string>;
|
|
48
|
+
}, opts?: pulumi.CustomResourceOptions);
|
|
49
|
+
}
|
|
50
|
+
//#endregion
|
|
51
|
+
export { RailwayVolume, RailwayVolumeInputs };
|
|
52
|
+
//# sourceMappingURL=volume.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"volume.d.mts","names":[],"sources":["../../src/railway/volume.ts"],"mappings":";;;;;UAIiB,mBAAA;;EAEhB,KAAA;EAFmC;EAKnC,SAAA;EALmC;EAQnC,SAAA;EAHA;EAMA,aAAA;EAAA;EAGA,SAAA;AAAA;AAAS;AAiPV;;;;;;;;;;;;;;;;;AAjPU,cAiPG,aAAA,SAAsB,MAAA,CAAO,OAAA,CAAQ,QAAA;EAUxC;;;;;cAHR,IAAA,UACA,IAAA;IAQY,gCANX,KAAA,EAAO,MAAA,CAAO,KAAA,UASd;IANA,SAAA,EAAW,MAAA,CAAO,KAAA,UAMI;IAHtB,SAAA,EAAW,MAAA,CAAO,KAAA,UAMP;IAHX,aAAA,EAAe,MAAA,CAAO,KAAA,UAXvB;IAcC,SAAA,EAAW,MAAA,CAAO,KAAA;EAAA,GAEnB,IAAA,GAAO,MAAA,CAAO,qBAAA;AAAA"}
|
|
@@ -0,0 +1,177 @@
|
|
|
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/volume.ts
|
|
6
|
+
const VOLUME_CREATE = `
|
|
7
|
+
mutation($input: VolumeCreateInput!) {
|
|
8
|
+
volumeCreate(input: $input) {
|
|
9
|
+
id
|
|
10
|
+
name
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
`;
|
|
14
|
+
const VOLUME_DELETE = `
|
|
15
|
+
mutation($volumeId: String!) {
|
|
16
|
+
volumeDelete(volumeId: $volumeId)
|
|
17
|
+
}
|
|
18
|
+
`;
|
|
19
|
+
const VOLUMES_QUERY = `
|
|
20
|
+
query($projectId: String!) {
|
|
21
|
+
project(id: $projectId) {
|
|
22
|
+
volumes {
|
|
23
|
+
edges {
|
|
24
|
+
node {
|
|
25
|
+
id
|
|
26
|
+
name
|
|
27
|
+
volumeInstances {
|
|
28
|
+
edges {
|
|
29
|
+
node {
|
|
30
|
+
id
|
|
31
|
+
mountPath
|
|
32
|
+
serviceId
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
`;
|
|
42
|
+
/**
|
|
43
|
+
* Finds an existing volume attached to a specific service within a project.
|
|
44
|
+
*
|
|
45
|
+
* @param client Authenticated Railway API client
|
|
46
|
+
* @param projectId Railway project UUID to search within
|
|
47
|
+
* @param serviceId Railway service UUID to match volume instances against
|
|
48
|
+
* @returns The volume UUID if found, `undefined` otherwise
|
|
49
|
+
*/
|
|
50
|
+
async function findVolumeByService(client, projectId, serviceId) {
|
|
51
|
+
return (await client.query(VOLUMES_QUERY, { projectId })).project.volumes.edges.find((edge) => edge.node.volumeInstances.edges.some((vi) => vi.node.serviceId === serviceId))?.node.id;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Dynamic provider implementing CRUD for Railway persistent volumes.
|
|
55
|
+
*
|
|
56
|
+
* Uses adopt-or-create on `create()`: finds an existing volume by service
|
|
57
|
+
* before creating a new one, making `pulumi up` idempotent from zero.
|
|
58
|
+
* Volumes are immutable after creation — changing `serviceId`, `mountPath`,
|
|
59
|
+
* `environmentId`, or `projectId` triggers replacement (delete + create).
|
|
60
|
+
*/
|
|
61
|
+
var RailwayVolumeProvider = class {
|
|
62
|
+
/**
|
|
63
|
+
* Creates or adopts a Railway volume for the target service.
|
|
64
|
+
*
|
|
65
|
+
* @param inputs Resolved volume configuration
|
|
66
|
+
* @returns The Railway volume UUID as the resource ID
|
|
67
|
+
*/
|
|
68
|
+
async create(inputs) {
|
|
69
|
+
const client = new RailwayClient(inputs.token);
|
|
70
|
+
let volumeId = await findVolumeByService(client, inputs.projectId, inputs.serviceId);
|
|
71
|
+
if (volumeId) pulumi.log.info(`Adopting existing volume for service (${volumeId})`);
|
|
72
|
+
else volumeId = (await client.query(VOLUME_CREATE, { input: {
|
|
73
|
+
projectId: inputs.projectId,
|
|
74
|
+
serviceId: inputs.serviceId,
|
|
75
|
+
environmentId: inputs.environmentId,
|
|
76
|
+
mountPath: inputs.mountPath
|
|
77
|
+
} })).volumeCreate.id;
|
|
78
|
+
return {
|
|
79
|
+
id: volumeId,
|
|
80
|
+
outs: {
|
|
81
|
+
...inputs,
|
|
82
|
+
volumeId
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Reads current state for `pulumi refresh` by looking up the volume.
|
|
88
|
+
*
|
|
89
|
+
* @param id Current Railway volume UUID
|
|
90
|
+
* @param props Last known persisted state
|
|
91
|
+
* @returns Refreshed resource ID and properties
|
|
92
|
+
* @throws {Error} If the volume no longer exists in Railway
|
|
93
|
+
*/
|
|
94
|
+
async read(_id, props) {
|
|
95
|
+
const volumeId = await findVolumeByService(new RailwayClient(props.token), props.projectId, props.serviceId);
|
|
96
|
+
if (!volumeId) throw new Error("Railway volume not found during refresh");
|
|
97
|
+
return {
|
|
98
|
+
id: volumeId,
|
|
99
|
+
props: {
|
|
100
|
+
...props,
|
|
101
|
+
volumeId
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Deletes the Railway volume. Silently succeeds if already deleted.
|
|
107
|
+
*
|
|
108
|
+
* @param id Railway volume UUID to delete
|
|
109
|
+
* @param props Last known persisted state (used for token)
|
|
110
|
+
*/
|
|
111
|
+
async delete(id, props) {
|
|
112
|
+
const client = new RailwayClient(props.token);
|
|
113
|
+
try {
|
|
114
|
+
await client.query(VOLUME_DELETE, { volumeId: id });
|
|
115
|
+
} catch {
|
|
116
|
+
pulumi.log.warn("Failed to delete Railway volume (may already be deleted)");
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Compares old and new inputs to determine what changed.
|
|
121
|
+
*
|
|
122
|
+
* All mutable properties trigger replacement since Railway volumes
|
|
123
|
+
* cannot be moved between services, mount paths, environments, or projects.
|
|
124
|
+
*
|
|
125
|
+
* @param _id Current resource ID (unused)
|
|
126
|
+
* @param olds Previous persisted state
|
|
127
|
+
* @param news New desired configuration
|
|
128
|
+
* @returns Diff result with replacement triggers
|
|
129
|
+
*/
|
|
130
|
+
async diff(_id, olds, news) {
|
|
131
|
+
const replaces = [];
|
|
132
|
+
if (olds.serviceId !== news.serviceId) replaces.push("serviceId");
|
|
133
|
+
if (olds.mountPath !== news.mountPath) replaces.push("mountPath");
|
|
134
|
+
if (olds.environmentId !== news.environmentId) replaces.push("environmentId");
|
|
135
|
+
if (olds.projectId !== news.projectId) replaces.push("projectId");
|
|
136
|
+
return {
|
|
137
|
+
changes: replaces.length > 0,
|
|
138
|
+
replaces,
|
|
139
|
+
deleteBeforeReplace: true
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
/**
|
|
144
|
+
* Manages a Railway persistent volume with adopt-or-create semantics.
|
|
145
|
+
*
|
|
146
|
+
* Finds existing volumes attached to the target service before creating
|
|
147
|
+
* new ones. Changing `serviceId`, `mountPath`, `environmentId`, or `projectId`
|
|
148
|
+
* triggers replacement (delete + create with new settings).
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* ```typescript
|
|
152
|
+
* new RailwayVolume("railway-volume-redis", {
|
|
153
|
+
* token: project.projectToken,
|
|
154
|
+
* projectId: project.projectId,
|
|
155
|
+
* serviceId: service.serviceId,
|
|
156
|
+
* environmentId: project.productionEnvironmentId,
|
|
157
|
+
* mountPath: "/data",
|
|
158
|
+
* });
|
|
159
|
+
* ```
|
|
160
|
+
*/
|
|
161
|
+
var RailwayVolume = class extends pulumi.dynamic.Resource {
|
|
162
|
+
/**
|
|
163
|
+
* @param name Pulumi resource name (logical identifier in state)
|
|
164
|
+
* @param args Volume configuration inputs
|
|
165
|
+
* @param opts Standard Pulumi resource options (e.g. `dependsOn`, `parent`)
|
|
166
|
+
*/
|
|
167
|
+
constructor(name, args, opts) {
|
|
168
|
+
super(new RailwayVolumeProvider(), name, {
|
|
169
|
+
...args,
|
|
170
|
+
volumeId: void 0
|
|
171
|
+
}, opts);
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
//#endregion
|
|
176
|
+
export { RailwayVolume };
|
|
177
|
+
//# sourceMappingURL=volume.mjs.map
|