@infracraft/pulumi 1.24.0 → 1.25.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.
- package/dist/railway/project-token.cjs +10 -8
- package/dist/railway/project-token.cjs.map +1 -1
- package/dist/railway/project-token.d.cts +13 -0
- package/dist/railway/project-token.d.cts.map +1 -1
- package/dist/railway/project-token.d.mts +13 -0
- package/dist/railway/project-token.d.mts.map +1 -1
- package/dist/railway/project-token.mjs +10 -8
- package/dist/railway/project-token.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -89,14 +89,15 @@ var RailwayProjectTokenResourceProvider = class {
|
|
|
89
89
|
* @param news Newly resolved inputs.
|
|
90
90
|
*/
|
|
91
91
|
async diff(_id, olds, news) {
|
|
92
|
-
const
|
|
93
|
-
if (olds.projectId !== news.projectId)
|
|
94
|
-
if (olds.environmentId !== news.environmentId)
|
|
95
|
-
if (olds.name !== news.name)
|
|
92
|
+
const identityReplaces = [];
|
|
93
|
+
if (olds.projectId !== news.projectId) identityReplaces.push("projectId");
|
|
94
|
+
if (olds.environmentId !== news.environmentId) identityReplaces.push("environmentId");
|
|
95
|
+
if (olds.name !== news.name) identityReplaces.push("name");
|
|
96
|
+
const rotates = olds.tokenVersion !== news.tokenVersion;
|
|
96
97
|
return {
|
|
97
|
-
changes:
|
|
98
|
-
replaces,
|
|
99
|
-
deleteBeforeReplace:
|
|
98
|
+
changes: identityReplaces.length > 0 || rotates,
|
|
99
|
+
replaces: rotates ? [...identityReplaces, "tokenVersion"] : identityReplaces,
|
|
100
|
+
deleteBeforeReplace: identityReplaces.length > 0
|
|
100
101
|
};
|
|
101
102
|
}
|
|
102
103
|
};
|
|
@@ -143,7 +144,8 @@ var RailwayProjectToken = class extends _pulumi_pulumi.ComponentResource {
|
|
|
143
144
|
token: provider.token,
|
|
144
145
|
projectId: project.id,
|
|
145
146
|
environmentId: environment.id,
|
|
146
|
-
name: args.name
|
|
147
|
+
name: args.name,
|
|
148
|
+
tokenVersion: args.tokenVersion
|
|
147
149
|
}, { parent: this });
|
|
148
150
|
this.token = resource.value;
|
|
149
151
|
this.registerOutputs({ token: this.token });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-token.cjs","names":["RailwayClient","pulumi"],"sources":["../../src/railway/project-token.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\nimport { RailwayClient } from \"./client\";\nimport type { RailwayEnvironment } from \"./environment\";\nimport type { RailwayProject } from \"./project\";\nimport type { RailwayProvider } from \"./provider\";\n\n/** Resolved inputs for the Railway project token dynamic provider. */\ninterface RailwayProjectTokenInputs {\n\t/** Railway API bearer token (account-scoped, used for API calls). */\n\ttoken: string;\n\n\t/** Railway project UUID. */\n\tprojectId: string;\n\n\t/** Railway environment UUID this deploy token is scoped to. */\n\tenvironmentId: string;\n\n\t/** Distinct token name, e.g. `\"pulumi-staging\"`. Must be unique per environment. */\n\tname: string;\n}\n\n/** Persisted state for the Railway project token. */\ninterface RailwayProjectTokenOutputs extends RailwayProjectTokenInputs {\n\t/** Minted deploy token secret value. */\n\tvalue: string;\n\n\t/** Railway-assigned token UUID (used for clean teardown on delete). */\n\ttokenId: string;\n}\n\nconst PROJECT_TOKENS_QUERY = `\n query($projectId: String!) {\n projectTokens(projectId: $projectId) {\n edges { node { id name } }\n }\n }\n`;\n\nconst PROJECT_TOKEN_CREATE = `\n mutation($input: ProjectTokenCreateInput!) {\n projectTokenCreate(input: $input)\n }\n`;\n\nconst PROJECT_TOKEN_DELETE = `\n mutation($id: String!) { projectTokenDelete(id: $id) }\n`;\n\n/**\n * Dynamic provider that mints a Railway environment-scoped deploy token.\n *\n * On create, it deletes any existing tokens with the same name (stale tokens\n * from previous runs), mints a fresh one scoped to the target environment, then\n * re-lists tokens to capture the Railway-assigned ID for later teardown.\n *\n * @internal Exported only for unit testing; not part of the public API surface.\n */\nexport class RailwayProjectTokenResourceProvider\n\timplements pulumi.dynamic.ResourceProvider\n{\n\t/**\n\t * Deletes stale same-named tokens, mints a new environment-scoped token,\n\t * and captures its ID via a follow-up list query.\n\t *\n\t * @param inputs Resolved provider inputs.\n\t * @returns Pulumi dynamic create result with `value` (secret) and `tokenId`.\n\t */\n\tasync create(\n\t\tinputs: RailwayProjectTokenInputs,\n\t): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst client = new RailwayClient(inputs.token);\n\n\t\tconst tokensResult = await client.query<{\n\t\t\tprojectTokens: {\n\t\t\t\tedges: Array<{ node: { id: string; name: string } }>;\n\t\t\t};\n\t\t}>(PROJECT_TOKENS_QUERY, { projectId: inputs.projectId });\n\n\t\tconst stale = tokensResult.projectTokens.edges.filter(\n\t\t\t(edge) => edge.node.name === inputs.name,\n\t\t);\n\n\t\tfor (const entry of stale) {\n\t\t\tawait client.query(PROJECT_TOKEN_DELETE, { id: entry.node.id });\n\t\t}\n\n\t\tconst createResult = await client.query<{ projectTokenCreate: string }>(\n\t\t\tPROJECT_TOKEN_CREATE,\n\t\t\t{\n\t\t\t\tinput: {\n\t\t\t\t\tprojectId: inputs.projectId,\n\t\t\t\t\tenvironmentId: inputs.environmentId,\n\t\t\t\t\tname: inputs.name,\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\n\t\tconst value = createResult.projectTokenCreate;\n\n\t\t// Re-list tokens to capture the Railway-assigned ID: the create mutation\n\t\t// returns only the token value, not its UUID.\n\t\tconst refreshResult = await client.query<{\n\t\t\tprojectTokens: {\n\t\t\t\tedges: Array<{ node: { id: string; name: string } }>;\n\t\t\t};\n\t\t}>(PROJECT_TOKENS_QUERY, { projectId: inputs.projectId });\n\n\t\t// After the delete sweep above, exactly one token with this name exists (the one we just\n\t\t// created). Resolve its id from the re-list so delete() can revoke it later.\n\t\tconst found = refreshResult.projectTokens.edges.find(\n\t\t\t(edge) => edge.node.name === inputs.name,\n\t\t);\n\n\t\tif (!found) {\n\t\t\tthrow new Error(\n\t\t\t\t`Could not resolve token id for newly created token \"${inputs.name}\" in project ${inputs.projectId}`,\n\t\t\t);\n\t\t}\n\n\t\tconst tokenId = found.node.id;\n\n\t\tconst outs: RailwayProjectTokenOutputs = {\n\t\t\t...inputs,\n\t\t\tvalue,\n\t\t\ttokenId,\n\t\t};\n\n\t\treturn { id: `${inputs.projectId}:${inputs.name}`, outs };\n\t}\n\n\t/**\n\t * Pass-through read — the token value is a write-once secret that Railway\n\t * never re-exposes via API, so the stored state is the only source of truth.\n\t *\n\t * @param id Resource ID.\n\t * @param props Currently stored outputs.\n\t */\n\tasync read(\n\t\tid: string,\n\t\tprops: RailwayProjectTokenOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\treturn { id, props };\n\t}\n\n\t/**\n\t * Deletes the Railway token by its stored UUID for clean teardown.\n\t *\n\t * @param _id Resource ID (unused).\n\t * @param props Currently stored outputs containing the tokenId.\n\t */\n\tasync delete(_id: string, props: RailwayProjectTokenOutputs): Promise<void> {\n\t\tif (props.tokenId) {\n\t\t\tconst client = new RailwayClient(props.token);\n\n\t\t\tawait client.query(PROJECT_TOKEN_DELETE, { id: props.tokenId });\n\t\t}\n\t}\n\n\t/**\n\t * Triggers replacement when the project, environment, or token name changes.\n\t *\n\t * @param _id Resource ID (unused).\n\t * @param olds Previously stored outputs.\n\t * @param news Newly resolved inputs.\n\t */\n\tasync diff(\n\t\t_id: string,\n\t\tolds: RailwayProjectTokenOutputs,\n\t\tnews: RailwayProjectTokenInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst replaces: string[] = [];\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.name !== news.name) {\n\t\t\treplaces.push(\"name\");\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/** Internal dynamic resource — not part of the public API. */\nclass RailwayProjectTokenResource extends pulumi.dynamic.Resource {\n\tpublic declare readonly value: pulumi.Output<string>;\n\tpublic declare readonly tokenId: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: {\n\t\t\ttoken: pulumi.Input<string>;\n\t\t\tprojectId: pulumi.Input<string>;\n\t\t\tenvironmentId: pulumi.Input<string>;\n\t\t\tname: pulumi.Input<string>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\t// `value` is an output-only secret. Declaring it via additionalSecretOutputs\n\t\t// (rather than a pulumi.secret(undefined) input placeholder) is the only reliable\n\t\t// way to mark a dynamic-provider output secret: the placeholder gives the property\n\t\t// both a secret-undefined input and a real output, so a downstream dynamic resource\n\t\t// consuming the token can race onto the undefined (\"malformed RPC secret: missing\n\t\t// value\" / \"Unexpected struct type\"). `tokenId` is a plain (non-secret) output. See\n\t\t// Pulumi #16041, #3012.\n\t\tsuper(\n\t\t\tnew RailwayProjectTokenResourceProvider(),\n\t\t\tname,\n\t\t\t{\n\t\t\t\t...args,\n\t\t\t\tvalue: undefined,\n\t\t\t\ttokenId: undefined,\n\t\t\t},\n\t\t\t{ ...opts, additionalSecretOutputs: [\"value\"] },\n\t\t);\n\t}\n}\n\n/** Options type for RailwayProjectToken — replaces Pulumi's native `provider` field. */\ntype RailwayProjectTokenOptions = Omit<\n\tpulumi.ComponentResourceOptions,\n\t\"provider\"\n> & {\n\t/** Railway authentication context. */\n\tprovider: RailwayProvider;\n\n\t/** Railway project this token belongs to. */\n\tproject: RailwayProject;\n\n\t/** Railway environment this deploy token is scoped to. */\n\tenvironment: RailwayEnvironment;\n};\n\n/** Args for RailwayProjectToken. */\nexport interface RailwayProjectTokenArgs {\n\t/**\n\t * Distinct token name, e.g. `\"pulumi-staging\"`.\n\t * Each environment should use a unique name so multiple stacks sharing\n\t * a project never collide on token ownership.\n\t */\n\tname: pulumi.Input<string>;\n}\n\n/**\n * Provisions an environment-scoped Railway deploy token.\n *\n * Each environment gets its own correctly-scoped token with a distinct name,\n * so multiple stacks sharing the same Railway project never collide.\n * The token value is exposed as a secret output for use in {@link RailwayDeploy}.\n *\n * @example\n * ```typescript\n * const project = new RailwayProject(\"my-project\", { name: \"my-app\" }, { provider });\n * const staging = new RailwayEnvironment(\"staging\", { name: \"staging\" }, { provider, project });\n *\n * const stagingToken = new RailwayProjectToken(\"staging-token\", {\n * name: \"pulumi-staging\",\n * }, { provider, project, environment: staging });\n *\n * new RailwayDeploy(\"api-deploy\", {\n * directory: monorepoRoot,\n * triggers: [sourceHash],\n * }, { provider, project, environment: staging, service, projectToken: stagingToken.token });\n * ```\n */\nexport class RailwayProjectToken extends pulumi.ComponentResource {\n\t/**\n\t * Environment-scoped Railway deploy token value (secret).\n\t * Pass this to `RailwayDeployOptions.projectToken`.\n\t */\n\tpublic readonly token: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: RailwayProjectTokenArgs,\n\t\topts: RailwayProjectTokenOptions,\n\t) {\n\t\tconst { provider, project, environment, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:railway:ProjectToken\", name, {}, pulumiOpts);\n\n\t\tconst resource = new RailwayProjectTokenResource(\n\t\t\t`${name}-resource`,\n\t\t\t{\n\t\t\t\ttoken: provider.token,\n\t\t\t\tprojectId: project.id,\n\t\t\t\tenvironmentId: environment.id,\n\t\t\t\tname: args.name,\n\t\t\t},\n\t\t\t{ parent: this },\n\t\t);\n\n\t\tthis.token = resource.value;\n\n\t\tthis.registerOutputs({ token: this.token });\n\t}\n}\n"],"mappings":";;;;;;;AA8BA,MAAM,uBAAuB;;;;;;;AAQ7B,MAAM,uBAAuB;;;;;AAM7B,MAAM,uBAAuB;;;;;;;;;;;;AAa7B,IAAa,sCAAb,MAEA;;;;;;;;CAQC,MAAM,OACL,QACuC;EACvC,MAAM,SAAS,IAAIA,qCAAc,OAAO,KAAK;EAQ7C,MAAM,SAAQ,MANa,OAAO,MAI/B,sBAAsB,EAAE,WAAW,OAAO,UAAU,CAAC,GAE7B,cAAc,MAAM,QAC7C,SAAS,KAAK,KAAK,SAAS,OAAO,IACrC;EAEA,KAAK,MAAM,SAAS,OACnB,MAAM,OAAO,MAAM,sBAAsB,EAAE,IAAI,MAAM,KAAK,GAAG,CAAC;EAc/D,MAAM,SAAQ,MAXa,OAAO,MACjC,sBACA,EACC,OAAO;GACN,WAAW,OAAO;GAClB,eAAe,OAAO;GACtB,MAAM,OAAO;EACd,EACD,CACD,GAE2B;EAY3B,MAAM,SAAQ,MARc,OAAO,MAIhC,sBAAsB,EAAE,WAAW,OAAO,UAAU,CAAC,GAI5B,cAAc,MAAM,MAC9C,SAAS,KAAK,KAAK,SAAS,OAAO,IACrC;EAEA,IAAI,CAAC,OACJ,MAAM,IAAI,MACT,uDAAuD,OAAO,KAAK,eAAe,OAAO,WAC1F;EAGD,MAAM,UAAU,MAAM,KAAK;EAE3B,MAAM,OAAmC;GACxC,GAAG;GACH;GACA;EACD;EAEA,OAAO;GAAE,IAAI,GAAG,OAAO,UAAU,GAAG,OAAO;GAAQ;EAAK;CACzD;;;;;;;;CASA,MAAM,KACL,IACA,OACqC;EACrC,OAAO;GAAE;GAAI;EAAM;CACpB;;;;;;;CAQA,MAAM,OAAO,KAAa,OAAkD;EAC3E,IAAI,MAAM,SAGT,MAAM,IAFaA,qCAAc,MAAM,KAE5B,EAAE,MAAM,sBAAsB,EAAE,IAAI,MAAM,QAAQ,CAAC;CAEhE;;;;;;;;CASA,MAAM,KACL,KACA,MACA,MACqC;EACrC,MAAM,WAAqB,CAAC;EAE5B,IAAI,KAAK,cAAc,KAAK,WAC3B,SAAS,KAAK,WAAW;EAG1B,IAAI,KAAK,kBAAkB,KAAK,eAC/B,SAAS,KAAK,eAAe;EAG9B,IAAI,KAAK,SAAS,KAAK,MACtB,SAAS,KAAK,MAAM;EAGrB,OAAO;GACN,SAAS,SAAS,SAAS;GAC3B;GACA,qBAAqB;EACtB;CACD;AACD;;AAGA,IAAM,8BAAN,cAA0CC,eAAO,QAAQ,SAAS;CAIjE,YACC,MACA,MAMA,MACC;EAQD,MACC,IAAI,oCAAoC,GACxC,MACA;GACC,GAAG;GACH,OAAO;GACP,SAAS;EACV,GACA;GAAE,GAAG;GAAM,yBAAyB,CAAC,OAAO;EAAE,CAC/C;CACD;AACD;;;;;;;;;;;;;;;;;;;;;;;AAiDA,IAAa,sBAAb,cAAyCA,eAAO,kBAAkB;CAOjE,YACC,MACA,MACA,MACC;EACD,MAAM,EAAE,UAAU,SAAS,aAAa,GAAG,eAAe;EAE1D,MAAM,mCAAmC,MAAM,CAAC,GAAG,UAAU;EAE7D,MAAM,WAAW,IAAI,4BACpB,GAAG,KAAK,YACR;GACC,OAAO,SAAS;GAChB,WAAW,QAAQ;GACnB,eAAe,YAAY;GAC3B,MAAM,KAAK;EACZ,GACA,EAAE,QAAQ,KAAK,CAChB;EAEA,KAAK,QAAQ,SAAS;EAEtB,KAAK,gBAAgB,EAAE,OAAO,KAAK,MAAM,CAAC;CAC3C;AACD"}
|
|
1
|
+
{"version":3,"file":"project-token.cjs","names":["RailwayClient","pulumi"],"sources":["../../src/railway/project-token.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\nimport { RailwayClient } from \"./client\";\nimport type { RailwayEnvironment } from \"./environment\";\nimport type { RailwayProject } from \"./project\";\nimport type { RailwayProvider } from \"./provider\";\n\n/** Resolved inputs for the Railway project token dynamic provider. */\ninterface RailwayProjectTokenInputs {\n\t/** Railway API bearer token (account-scoped, used for API calls). */\n\ttoken: string;\n\n\t/** Railway project UUID. */\n\tprojectId: string;\n\n\t/** Railway environment UUID this deploy token is scoped to. */\n\tenvironmentId: string;\n\n\t/** Distinct token name, e.g. `\"pulumi-staging\"`. Must be unique per environment. */\n\tname: string;\n\n\t/**\n\t * Rotation handle: bumping this number replaces the token on the next `up` —\n\t * the new token is minted BEFORE the old one is revoked (create-before-delete,\n\t * unlike identity changes), so there is never a tokenless window. Leave unset\n\t * until the first rotation is needed.\n\t */\n\ttokenVersion?: number;\n}\n\n/** Persisted state for the Railway project token. */\ninterface RailwayProjectTokenOutputs extends RailwayProjectTokenInputs {\n\t/** Minted deploy token secret value. */\n\tvalue: string;\n\n\t/** Railway-assigned token UUID (used for clean teardown on delete). */\n\ttokenId: string;\n}\n\nconst PROJECT_TOKENS_QUERY = `\n query($projectId: String!) {\n projectTokens(projectId: $projectId) {\n edges { node { id name } }\n }\n }\n`;\n\nconst PROJECT_TOKEN_CREATE = `\n mutation($input: ProjectTokenCreateInput!) {\n projectTokenCreate(input: $input)\n }\n`;\n\nconst PROJECT_TOKEN_DELETE = `\n mutation($id: String!) { projectTokenDelete(id: $id) }\n`;\n\n/**\n * Dynamic provider that mints a Railway environment-scoped deploy token.\n *\n * On create, it deletes any existing tokens with the same name (stale tokens\n * from previous runs), mints a fresh one scoped to the target environment, then\n * re-lists tokens to capture the Railway-assigned ID for later teardown.\n *\n * @internal Exported only for unit testing; not part of the public API surface.\n */\nexport class RailwayProjectTokenResourceProvider\n\timplements pulumi.dynamic.ResourceProvider\n{\n\t/**\n\t * Deletes stale same-named tokens, mints a new environment-scoped token,\n\t * and captures its ID via a follow-up list query.\n\t *\n\t * @param inputs Resolved provider inputs.\n\t * @returns Pulumi dynamic create result with `value` (secret) and `tokenId`.\n\t */\n\tasync create(\n\t\tinputs: RailwayProjectTokenInputs,\n\t): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst client = new RailwayClient(inputs.token);\n\n\t\tconst tokensResult = await client.query<{\n\t\t\tprojectTokens: {\n\t\t\t\tedges: Array<{ node: { id: string; name: string } }>;\n\t\t\t};\n\t\t}>(PROJECT_TOKENS_QUERY, { projectId: inputs.projectId });\n\n\t\tconst stale = tokensResult.projectTokens.edges.filter(\n\t\t\t(edge) => edge.node.name === inputs.name,\n\t\t);\n\n\t\tfor (const entry of stale) {\n\t\t\tawait client.query(PROJECT_TOKEN_DELETE, { id: entry.node.id });\n\t\t}\n\n\t\tconst createResult = await client.query<{ projectTokenCreate: string }>(\n\t\t\tPROJECT_TOKEN_CREATE,\n\t\t\t{\n\t\t\t\tinput: {\n\t\t\t\t\tprojectId: inputs.projectId,\n\t\t\t\t\tenvironmentId: inputs.environmentId,\n\t\t\t\t\tname: inputs.name,\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\n\t\tconst value = createResult.projectTokenCreate;\n\n\t\t// Re-list tokens to capture the Railway-assigned ID: the create mutation\n\t\t// returns only the token value, not its UUID.\n\t\tconst refreshResult = await client.query<{\n\t\t\tprojectTokens: {\n\t\t\t\tedges: Array<{ node: { id: string; name: string } }>;\n\t\t\t};\n\t\t}>(PROJECT_TOKENS_QUERY, { projectId: inputs.projectId });\n\n\t\t// After the delete sweep above, exactly one token with this name exists (the one we just\n\t\t// created). Resolve its id from the re-list so delete() can revoke it later.\n\t\tconst found = refreshResult.projectTokens.edges.find(\n\t\t\t(edge) => edge.node.name === inputs.name,\n\t\t);\n\n\t\tif (!found) {\n\t\t\tthrow new Error(\n\t\t\t\t`Could not resolve token id for newly created token \"${inputs.name}\" in project ${inputs.projectId}`,\n\t\t\t);\n\t\t}\n\n\t\tconst tokenId = found.node.id;\n\n\t\tconst outs: RailwayProjectTokenOutputs = {\n\t\t\t...inputs,\n\t\t\tvalue,\n\t\t\ttokenId,\n\t\t};\n\n\t\treturn { id: `${inputs.projectId}:${inputs.name}`, outs };\n\t}\n\n\t/**\n\t * Pass-through read — the token value is a write-once secret that Railway\n\t * never re-exposes via API, so the stored state is the only source of truth.\n\t *\n\t * @param id Resource ID.\n\t * @param props Currently stored outputs.\n\t */\n\tasync read(\n\t\tid: string,\n\t\tprops: RailwayProjectTokenOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\treturn { id, props };\n\t}\n\n\t/**\n\t * Deletes the Railway token by its stored UUID for clean teardown.\n\t *\n\t * @param _id Resource ID (unused).\n\t * @param props Currently stored outputs containing the tokenId.\n\t */\n\tasync delete(_id: string, props: RailwayProjectTokenOutputs): Promise<void> {\n\t\tif (props.tokenId) {\n\t\t\tconst client = new RailwayClient(props.token);\n\n\t\t\tawait client.query(PROJECT_TOKEN_DELETE, { id: props.tokenId });\n\t\t}\n\t}\n\n\t/**\n\t * Triggers replacement when the project, environment, or token name changes.\n\t *\n\t * @param _id Resource ID (unused).\n\t * @param olds Previously stored outputs.\n\t * @param news Newly resolved inputs.\n\t */\n\tasync diff(\n\t\t_id: string,\n\t\tolds: RailwayProjectTokenOutputs,\n\t\tnews: RailwayProjectTokenInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst identityReplaces: string[] = [];\n\n\t\tif (olds.projectId !== news.projectId) {\n\t\t\tidentityReplaces.push(\"projectId\");\n\t\t}\n\n\t\tif (olds.environmentId !== news.environmentId) {\n\t\t\tidentityReplaces.push(\"environmentId\");\n\t\t}\n\n\t\tif (olds.name !== news.name) {\n\t\t\tidentityReplaces.push(\"name\");\n\t\t}\n\n\t\tconst rotates = olds.tokenVersion !== news.tokenVersion;\n\n\t\treturn {\n\t\t\tchanges: identityReplaces.length > 0 || rotates,\n\t\t\treplaces: rotates\n\t\t\t\t? [...identityReplaces, \"tokenVersion\"]\n\t\t\t\t: identityReplaces,\n\t\t\t// Identity changes revoke the old token first (names must stay unique);\n\t\t\t// a pure rotation mints the new token BEFORE revoking the old so there\n\t\t\t// is never a tokenless window — `create()` already cleans up stale\n\t\t\t// same-name tokens, so the transient name collision is handled.\n\t\t\tdeleteBeforeReplace: identityReplaces.length > 0,\n\t\t};\n\t}\n}\n\n/** Internal dynamic resource — not part of the public API. */\nclass RailwayProjectTokenResource extends pulumi.dynamic.Resource {\n\tpublic declare readonly value: pulumi.Output<string>;\n\tpublic declare readonly tokenId: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: {\n\t\t\ttoken: pulumi.Input<string>;\n\t\t\tprojectId: pulumi.Input<string>;\n\t\t\tenvironmentId: pulumi.Input<string>;\n\t\t\tname: pulumi.Input<string>;\n\t\t\ttokenVersion?: pulumi.Input<number>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\t// `value` is an output-only secret. Declaring it via additionalSecretOutputs\n\t\t// (rather than a pulumi.secret(undefined) input placeholder) is the only reliable\n\t\t// way to mark a dynamic-provider output secret: the placeholder gives the property\n\t\t// both a secret-undefined input and a real output, so a downstream dynamic resource\n\t\t// consuming the token can race onto the undefined (\"malformed RPC secret: missing\n\t\t// value\" / \"Unexpected struct type\"). `tokenId` is a plain (non-secret) output. See\n\t\t// Pulumi #16041, #3012.\n\t\tsuper(\n\t\t\tnew RailwayProjectTokenResourceProvider(),\n\t\t\tname,\n\t\t\t{\n\t\t\t\t...args,\n\t\t\t\tvalue: undefined,\n\t\t\t\ttokenId: undefined,\n\t\t\t},\n\t\t\t{ ...opts, additionalSecretOutputs: [\"value\"] },\n\t\t);\n\t}\n}\n\n/** Options type for RailwayProjectToken — replaces Pulumi's native `provider` field. */\ntype RailwayProjectTokenOptions = Omit<\n\tpulumi.ComponentResourceOptions,\n\t\"provider\"\n> & {\n\t/** Railway authentication context. */\n\tprovider: RailwayProvider;\n\n\t/** Railway project this token belongs to. */\n\tproject: RailwayProject;\n\n\t/** Railway environment this deploy token is scoped to. */\n\tenvironment: RailwayEnvironment;\n};\n\n/** Args for RailwayProjectToken. */\nexport interface RailwayProjectTokenArgs {\n\t/**\n\t * Distinct token name, e.g. `\"pulumi-staging\"`.\n\t * Each environment should use a unique name so multiple stacks sharing\n\t * a project never collide on token ownership.\n\t */\n\tname: pulumi.Input<string>;\n\n\t/**\n\t * Rotation handle: bump to mint a fresh token on the next `up` (the old one\n\t * is revoked only after the new one exists). Consumers of `token` cascade\n\t * automatically.\n\t */\n\ttokenVersion?: pulumi.Input<number>;\n}\n\n/**\n * Provisions an environment-scoped Railway deploy token.\n *\n * Each environment gets its own correctly-scoped token with a distinct name,\n * so multiple stacks sharing the same Railway project never collide.\n * The token value is exposed as a secret output for use in {@link RailwayDeploy}.\n *\n * @example\n * ```typescript\n * const project = new RailwayProject(\"my-project\", { name: \"my-app\" }, { provider });\n * const staging = new RailwayEnvironment(\"staging\", { name: \"staging\" }, { provider, project });\n *\n * const stagingToken = new RailwayProjectToken(\"staging-token\", {\n * name: \"pulumi-staging\",\n * }, { provider, project, environment: staging });\n *\n * new RailwayDeploy(\"api-deploy\", {\n * directory: monorepoRoot,\n * triggers: [sourceHash],\n * }, { provider, project, environment: staging, service, projectToken: stagingToken.token });\n * ```\n */\nexport class RailwayProjectToken extends pulumi.ComponentResource {\n\t/**\n\t * Environment-scoped Railway deploy token value (secret).\n\t * Pass this to `RailwayDeployOptions.projectToken`.\n\t */\n\tpublic readonly token: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: RailwayProjectTokenArgs,\n\t\topts: RailwayProjectTokenOptions,\n\t) {\n\t\tconst { provider, project, environment, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:railway:ProjectToken\", name, {}, pulumiOpts);\n\n\t\tconst resource = new RailwayProjectTokenResource(\n\t\t\t`${name}-resource`,\n\t\t\t{\n\t\t\t\ttoken: provider.token,\n\t\t\t\tprojectId: project.id,\n\t\t\t\tenvironmentId: environment.id,\n\t\t\t\tname: args.name,\n\t\t\t\ttokenVersion: args.tokenVersion,\n\t\t\t},\n\t\t\t{ parent: this },\n\t\t);\n\n\t\tthis.token = resource.value;\n\n\t\tthis.registerOutputs({ token: this.token });\n\t}\n}\n"],"mappings":";;;;;;;AAsCA,MAAM,uBAAuB;;;;;;;AAQ7B,MAAM,uBAAuB;;;;;AAM7B,MAAM,uBAAuB;;;;;;;;;;;;AAa7B,IAAa,sCAAb,MAEA;;;;;;;;CAQC,MAAM,OACL,QACuC;EACvC,MAAM,SAAS,IAAIA,qCAAc,OAAO,KAAK;EAQ7C,MAAM,SAAQ,MANa,OAAO,MAI/B,sBAAsB,EAAE,WAAW,OAAO,UAAU,CAAC,GAE7B,cAAc,MAAM,QAC7C,SAAS,KAAK,KAAK,SAAS,OAAO,IACrC;EAEA,KAAK,MAAM,SAAS,OACnB,MAAM,OAAO,MAAM,sBAAsB,EAAE,IAAI,MAAM,KAAK,GAAG,CAAC;EAc/D,MAAM,SAAQ,MAXa,OAAO,MACjC,sBACA,EACC,OAAO;GACN,WAAW,OAAO;GAClB,eAAe,OAAO;GACtB,MAAM,OAAO;EACd,EACD,CACD,GAE2B;EAY3B,MAAM,SAAQ,MARc,OAAO,MAIhC,sBAAsB,EAAE,WAAW,OAAO,UAAU,CAAC,GAI5B,cAAc,MAAM,MAC9C,SAAS,KAAK,KAAK,SAAS,OAAO,IACrC;EAEA,IAAI,CAAC,OACJ,MAAM,IAAI,MACT,uDAAuD,OAAO,KAAK,eAAe,OAAO,WAC1F;EAGD,MAAM,UAAU,MAAM,KAAK;EAE3B,MAAM,OAAmC;GACxC,GAAG;GACH;GACA;EACD;EAEA,OAAO;GAAE,IAAI,GAAG,OAAO,UAAU,GAAG,OAAO;GAAQ;EAAK;CACzD;;;;;;;;CASA,MAAM,KACL,IACA,OACqC;EACrC,OAAO;GAAE;GAAI;EAAM;CACpB;;;;;;;CAQA,MAAM,OAAO,KAAa,OAAkD;EAC3E,IAAI,MAAM,SAGT,MAAM,IAFaA,qCAAc,MAAM,KAE5B,EAAE,MAAM,sBAAsB,EAAE,IAAI,MAAM,QAAQ,CAAC;CAEhE;;;;;;;;CASA,MAAM,KACL,KACA,MACA,MACqC;EACrC,MAAM,mBAA6B,CAAC;EAEpC,IAAI,KAAK,cAAc,KAAK,WAC3B,iBAAiB,KAAK,WAAW;EAGlC,IAAI,KAAK,kBAAkB,KAAK,eAC/B,iBAAiB,KAAK,eAAe;EAGtC,IAAI,KAAK,SAAS,KAAK,MACtB,iBAAiB,KAAK,MAAM;EAG7B,MAAM,UAAU,KAAK,iBAAiB,KAAK;EAE3C,OAAO;GACN,SAAS,iBAAiB,SAAS,KAAK;GACxC,UAAU,UACP,CAAC,GAAG,kBAAkB,cAAc,IACpC;GAKH,qBAAqB,iBAAiB,SAAS;EAChD;CACD;AACD;;AAGA,IAAM,8BAAN,cAA0CC,eAAO,QAAQ,SAAS;CAIjE,YACC,MACA,MAOA,MACC;EAQD,MACC,IAAI,oCAAoC,GACxC,MACA;GACC,GAAG;GACH,OAAO;GACP,SAAS;EACV,GACA;GAAE,GAAG;GAAM,yBAAyB,CAAC,OAAO;EAAE,CAC/C;CACD;AACD;;;;;;;;;;;;;;;;;;;;;;;AAwDA,IAAa,sBAAb,cAAyCA,eAAO,kBAAkB;CAOjE,YACC,MACA,MACA,MACC;EACD,MAAM,EAAE,UAAU,SAAS,aAAa,GAAG,eAAe;EAE1D,MAAM,mCAAmC,MAAM,CAAC,GAAG,UAAU;EAE7D,MAAM,WAAW,IAAI,4BACpB,GAAG,KAAK,YACR;GACC,OAAO,SAAS;GAChB,WAAW,QAAQ;GACnB,eAAe,YAAY;GAC3B,MAAM,KAAK;GACX,cAAc,KAAK;EACpB,GACA,EAAE,QAAQ,KAAK,CAChB;EAEA,KAAK,QAAQ,SAAS;EAEtB,KAAK,gBAAgB,EAAE,OAAO,KAAK,MAAM,CAAC;CAC3C;AACD"}
|
|
@@ -15,6 +15,13 @@ interface RailwayProjectTokenInputs {
|
|
|
15
15
|
environmentId: string;
|
|
16
16
|
/** Distinct token name, e.g. `"pulumi-staging"`. Must be unique per environment. */
|
|
17
17
|
name: string;
|
|
18
|
+
/**
|
|
19
|
+
* Rotation handle: bumping this number replaces the token on the next `up` —
|
|
20
|
+
* the new token is minted BEFORE the old one is revoked (create-before-delete,
|
|
21
|
+
* unlike identity changes), so there is never a tokenless window. Leave unset
|
|
22
|
+
* until the first rotation is needed.
|
|
23
|
+
*/
|
|
24
|
+
tokenVersion?: number;
|
|
18
25
|
}
|
|
19
26
|
/** Persisted state for the Railway project token. */
|
|
20
27
|
interface RailwayProjectTokenOutputs extends RailwayProjectTokenInputs {
|
|
@@ -79,6 +86,12 @@ interface RailwayProjectTokenArgs {
|
|
|
79
86
|
* a project never collide on token ownership.
|
|
80
87
|
*/
|
|
81
88
|
name: pulumi.Input<string>;
|
|
89
|
+
/**
|
|
90
|
+
* Rotation handle: bump to mint a fresh token on the next `up` (the old one
|
|
91
|
+
* is revoked only after the new one exists). Consumers of `token` cascade
|
|
92
|
+
* automatically.
|
|
93
|
+
*/
|
|
94
|
+
tokenVersion?: pulumi.Input<number>;
|
|
82
95
|
}
|
|
83
96
|
/**
|
|
84
97
|
* Provisions an environment-scoped Railway deploy token.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-token.d.cts","names":[],"sources":["../../src/railway/project-token.ts"],"mappings":";;;;;;;;UAOU,yBAAA;;EAET,KAAA;EAFkC;EAKlC,SAAA;EALkC;EAQlC,aAAA;EAHA;EAMA,IAAA;AAAA;;UAIS,0BAAA,SAAmC,yBAAyB;
|
|
1
|
+
{"version":3,"file":"project-token.d.cts","names":[],"sources":["../../src/railway/project-token.ts"],"mappings":";;;;;;;;UAOU,yBAAA;;EAET,KAAA;EAFkC;EAKlC,SAAA;EALkC;EAQlC,aAAA;EAHA;EAMA,IAAA;EAAA;;;AAQY;AAAA;;EAAZ,YAAA;AAAA;;UAIS,0BAAA,SAAmC,yBAAyB;EAKrE;EAHA,KAAA;EAGO;EAAP,OAAA;AAAA;;;;;;;;;;cA8BY,mCAAA,YACD,MAAA,CAAO,OAAA,CAAQ,gBAAA;EA8GnB;;;;;;;EArGD,MAAA,CACL,MAAA,EAAQ,yBAAA,GACN,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EAXA;;;;;;;EA+EpB,IAAA,CACL,EAAA,UACA,KAAA,EAAO,0BAAA,GACL,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;EAHpB;;;;;;EAaA,MAAA,CAAO,GAAA,UAAa,KAAA,EAAO,0BAAA,GAA6B,OAAA;EAVpC;;;;;;;EAyBpB,IAAA,CACL,GAAA,UACA,IAAA,EAAM,0BAAA,EACN,IAAA,EAAM,yBAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;AAAA;;KAoEtB,0BAAA,GAA6B,IAAA,CACjC,MAAA,CAAO,wBAAA;EAtEN,sCA0ED,QAAA,EAAU,eAAA,EAzEC;EA4EX,OAAA,EAAS,cAAA,EA5EiB;EA+E1B,WAAA,EAAa,kBAAA;AAAA;AAlDb;AAAA,UAsDgB,uBAAA;;;;;;EAMhB,IAAA,EAAM,MAAA,CAAO,KAAA;EAVkB;;;;;EAiB/B,YAAA,GAAe,MAAA,CAAO,KAAK;AAAA;;;;;;AAjBI;AAIhC;;;;;;;;;;;AAa4B;AAyB5B;;;;cAAa,mBAAA,SAA4B,MAAA,CAAO,iBAAA;EAUxC;;;;EAAA,SALS,KAAA,EAAO,MAAA,CAAO,MAAA;cAG7B,IAAA,UACA,IAAA,EAAM,uBAAA,EACN,IAAA,EAAM,0BAAA;AAAA"}
|
|
@@ -15,6 +15,13 @@ interface RailwayProjectTokenInputs {
|
|
|
15
15
|
environmentId: string;
|
|
16
16
|
/** Distinct token name, e.g. `"pulumi-staging"`. Must be unique per environment. */
|
|
17
17
|
name: string;
|
|
18
|
+
/**
|
|
19
|
+
* Rotation handle: bumping this number replaces the token on the next `up` —
|
|
20
|
+
* the new token is minted BEFORE the old one is revoked (create-before-delete,
|
|
21
|
+
* unlike identity changes), so there is never a tokenless window. Leave unset
|
|
22
|
+
* until the first rotation is needed.
|
|
23
|
+
*/
|
|
24
|
+
tokenVersion?: number;
|
|
18
25
|
}
|
|
19
26
|
/** Persisted state for the Railway project token. */
|
|
20
27
|
interface RailwayProjectTokenOutputs extends RailwayProjectTokenInputs {
|
|
@@ -79,6 +86,12 @@ interface RailwayProjectTokenArgs {
|
|
|
79
86
|
* a project never collide on token ownership.
|
|
80
87
|
*/
|
|
81
88
|
name: pulumi.Input<string>;
|
|
89
|
+
/**
|
|
90
|
+
* Rotation handle: bump to mint a fresh token on the next `up` (the old one
|
|
91
|
+
* is revoked only after the new one exists). Consumers of `token` cascade
|
|
92
|
+
* automatically.
|
|
93
|
+
*/
|
|
94
|
+
tokenVersion?: pulumi.Input<number>;
|
|
82
95
|
}
|
|
83
96
|
/**
|
|
84
97
|
* Provisions an environment-scoped Railway deploy token.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-token.d.mts","names":[],"sources":["../../src/railway/project-token.ts"],"mappings":";;;;;;;;UAOU,yBAAA;;EAET,KAAA;EAFkC;EAKlC,SAAA;EALkC;EAQlC,aAAA;EAHA;EAMA,IAAA;AAAA;;UAIS,0BAAA,SAAmC,yBAAyB;
|
|
1
|
+
{"version":3,"file":"project-token.d.mts","names":[],"sources":["../../src/railway/project-token.ts"],"mappings":";;;;;;;;UAOU,yBAAA;;EAET,KAAA;EAFkC;EAKlC,SAAA;EALkC;EAQlC,aAAA;EAHA;EAMA,IAAA;EAAA;;;AAQY;AAAA;;EAAZ,YAAA;AAAA;;UAIS,0BAAA,SAAmC,yBAAyB;EAKrE;EAHA,KAAA;EAGO;EAAP,OAAA;AAAA;;;;;;;;;;cA8BY,mCAAA,YACD,MAAA,CAAO,OAAA,CAAQ,gBAAA;EA8GnB;;;;;;;EArGD,MAAA,CACL,MAAA,EAAQ,yBAAA,GACN,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EAXA;;;;;;;EA+EpB,IAAA,CACL,EAAA,UACA,KAAA,EAAO,0BAAA,GACL,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;EAHpB;;;;;;EAaA,MAAA,CAAO,GAAA,UAAa,KAAA,EAAO,0BAAA,GAA6B,OAAA;EAVpC;;;;;;;EAyBpB,IAAA,CACL,GAAA,UACA,IAAA,EAAM,0BAAA,EACN,IAAA,EAAM,yBAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;AAAA;;KAoEtB,0BAAA,GAA6B,IAAA,CACjC,MAAA,CAAO,wBAAA;EAtEN,sCA0ED,QAAA,EAAU,eAAA,EAzEC;EA4EX,OAAA,EAAS,cAAA,EA5EiB;EA+E1B,WAAA,EAAa,kBAAA;AAAA;AAlDb;AAAA,UAsDgB,uBAAA;;;;;;EAMhB,IAAA,EAAM,MAAA,CAAO,KAAA;EAVkB;;;;;EAiB/B,YAAA,GAAe,MAAA,CAAO,KAAK;AAAA;;;;;;AAjBI;AAIhC;;;;;;;;;;;AAa4B;AAyB5B;;;;cAAa,mBAAA,SAA4B,MAAA,CAAO,iBAAA;EAUxC;;;;EAAA,SALS,KAAA,EAAO,MAAA,CAAO,MAAA;cAG7B,IAAA,UACA,IAAA,EAAM,uBAAA,EACN,IAAA,EAAM,0BAAA;AAAA"}
|
|
@@ -87,14 +87,15 @@ var RailwayProjectTokenResourceProvider = class {
|
|
|
87
87
|
* @param news Newly resolved inputs.
|
|
88
88
|
*/
|
|
89
89
|
async diff(_id, olds, news) {
|
|
90
|
-
const
|
|
91
|
-
if (olds.projectId !== news.projectId)
|
|
92
|
-
if (olds.environmentId !== news.environmentId)
|
|
93
|
-
if (olds.name !== news.name)
|
|
90
|
+
const identityReplaces = [];
|
|
91
|
+
if (olds.projectId !== news.projectId) identityReplaces.push("projectId");
|
|
92
|
+
if (olds.environmentId !== news.environmentId) identityReplaces.push("environmentId");
|
|
93
|
+
if (olds.name !== news.name) identityReplaces.push("name");
|
|
94
|
+
const rotates = olds.tokenVersion !== news.tokenVersion;
|
|
94
95
|
return {
|
|
95
|
-
changes:
|
|
96
|
-
replaces,
|
|
97
|
-
deleteBeforeReplace:
|
|
96
|
+
changes: identityReplaces.length > 0 || rotates,
|
|
97
|
+
replaces: rotates ? [...identityReplaces, "tokenVersion"] : identityReplaces,
|
|
98
|
+
deleteBeforeReplace: identityReplaces.length > 0
|
|
98
99
|
};
|
|
99
100
|
}
|
|
100
101
|
};
|
|
@@ -141,7 +142,8 @@ var RailwayProjectToken = class extends pulumi.ComponentResource {
|
|
|
141
142
|
token: provider.token,
|
|
142
143
|
projectId: project.id,
|
|
143
144
|
environmentId: environment.id,
|
|
144
|
-
name: args.name
|
|
145
|
+
name: args.name,
|
|
146
|
+
tokenVersion: args.tokenVersion
|
|
145
147
|
}, { parent: this });
|
|
146
148
|
this.token = resource.value;
|
|
147
149
|
this.registerOutputs({ token: this.token });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-token.mjs","names":[],"sources":["../../src/railway/project-token.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\nimport { RailwayClient } from \"./client\";\nimport type { RailwayEnvironment } from \"./environment\";\nimport type { RailwayProject } from \"./project\";\nimport type { RailwayProvider } from \"./provider\";\n\n/** Resolved inputs for the Railway project token dynamic provider. */\ninterface RailwayProjectTokenInputs {\n\t/** Railway API bearer token (account-scoped, used for API calls). */\n\ttoken: string;\n\n\t/** Railway project UUID. */\n\tprojectId: string;\n\n\t/** Railway environment UUID this deploy token is scoped to. */\n\tenvironmentId: string;\n\n\t/** Distinct token name, e.g. `\"pulumi-staging\"`. Must be unique per environment. */\n\tname: string;\n}\n\n/** Persisted state for the Railway project token. */\ninterface RailwayProjectTokenOutputs extends RailwayProjectTokenInputs {\n\t/** Minted deploy token secret value. */\n\tvalue: string;\n\n\t/** Railway-assigned token UUID (used for clean teardown on delete). */\n\ttokenId: string;\n}\n\nconst PROJECT_TOKENS_QUERY = `\n query($projectId: String!) {\n projectTokens(projectId: $projectId) {\n edges { node { id name } }\n }\n }\n`;\n\nconst PROJECT_TOKEN_CREATE = `\n mutation($input: ProjectTokenCreateInput!) {\n projectTokenCreate(input: $input)\n }\n`;\n\nconst PROJECT_TOKEN_DELETE = `\n mutation($id: String!) { projectTokenDelete(id: $id) }\n`;\n\n/**\n * Dynamic provider that mints a Railway environment-scoped deploy token.\n *\n * On create, it deletes any existing tokens with the same name (stale tokens\n * from previous runs), mints a fresh one scoped to the target environment, then\n * re-lists tokens to capture the Railway-assigned ID for later teardown.\n *\n * @internal Exported only for unit testing; not part of the public API surface.\n */\nexport class RailwayProjectTokenResourceProvider\n\timplements pulumi.dynamic.ResourceProvider\n{\n\t/**\n\t * Deletes stale same-named tokens, mints a new environment-scoped token,\n\t * and captures its ID via a follow-up list query.\n\t *\n\t * @param inputs Resolved provider inputs.\n\t * @returns Pulumi dynamic create result with `value` (secret) and `tokenId`.\n\t */\n\tasync create(\n\t\tinputs: RailwayProjectTokenInputs,\n\t): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst client = new RailwayClient(inputs.token);\n\n\t\tconst tokensResult = await client.query<{\n\t\t\tprojectTokens: {\n\t\t\t\tedges: Array<{ node: { id: string; name: string } }>;\n\t\t\t};\n\t\t}>(PROJECT_TOKENS_QUERY, { projectId: inputs.projectId });\n\n\t\tconst stale = tokensResult.projectTokens.edges.filter(\n\t\t\t(edge) => edge.node.name === inputs.name,\n\t\t);\n\n\t\tfor (const entry of stale) {\n\t\t\tawait client.query(PROJECT_TOKEN_DELETE, { id: entry.node.id });\n\t\t}\n\n\t\tconst createResult = await client.query<{ projectTokenCreate: string }>(\n\t\t\tPROJECT_TOKEN_CREATE,\n\t\t\t{\n\t\t\t\tinput: {\n\t\t\t\t\tprojectId: inputs.projectId,\n\t\t\t\t\tenvironmentId: inputs.environmentId,\n\t\t\t\t\tname: inputs.name,\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\n\t\tconst value = createResult.projectTokenCreate;\n\n\t\t// Re-list tokens to capture the Railway-assigned ID: the create mutation\n\t\t// returns only the token value, not its UUID.\n\t\tconst refreshResult = await client.query<{\n\t\t\tprojectTokens: {\n\t\t\t\tedges: Array<{ node: { id: string; name: string } }>;\n\t\t\t};\n\t\t}>(PROJECT_TOKENS_QUERY, { projectId: inputs.projectId });\n\n\t\t// After the delete sweep above, exactly one token with this name exists (the one we just\n\t\t// created). Resolve its id from the re-list so delete() can revoke it later.\n\t\tconst found = refreshResult.projectTokens.edges.find(\n\t\t\t(edge) => edge.node.name === inputs.name,\n\t\t);\n\n\t\tif (!found) {\n\t\t\tthrow new Error(\n\t\t\t\t`Could not resolve token id for newly created token \"${inputs.name}\" in project ${inputs.projectId}`,\n\t\t\t);\n\t\t}\n\n\t\tconst tokenId = found.node.id;\n\n\t\tconst outs: RailwayProjectTokenOutputs = {\n\t\t\t...inputs,\n\t\t\tvalue,\n\t\t\ttokenId,\n\t\t};\n\n\t\treturn { id: `${inputs.projectId}:${inputs.name}`, outs };\n\t}\n\n\t/**\n\t * Pass-through read — the token value is a write-once secret that Railway\n\t * never re-exposes via API, so the stored state is the only source of truth.\n\t *\n\t * @param id Resource ID.\n\t * @param props Currently stored outputs.\n\t */\n\tasync read(\n\t\tid: string,\n\t\tprops: RailwayProjectTokenOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\treturn { id, props };\n\t}\n\n\t/**\n\t * Deletes the Railway token by its stored UUID for clean teardown.\n\t *\n\t * @param _id Resource ID (unused).\n\t * @param props Currently stored outputs containing the tokenId.\n\t */\n\tasync delete(_id: string, props: RailwayProjectTokenOutputs): Promise<void> {\n\t\tif (props.tokenId) {\n\t\t\tconst client = new RailwayClient(props.token);\n\n\t\t\tawait client.query(PROJECT_TOKEN_DELETE, { id: props.tokenId });\n\t\t}\n\t}\n\n\t/**\n\t * Triggers replacement when the project, environment, or token name changes.\n\t *\n\t * @param _id Resource ID (unused).\n\t * @param olds Previously stored outputs.\n\t * @param news Newly resolved inputs.\n\t */\n\tasync diff(\n\t\t_id: string,\n\t\tolds: RailwayProjectTokenOutputs,\n\t\tnews: RailwayProjectTokenInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst replaces: string[] = [];\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.name !== news.name) {\n\t\t\treplaces.push(\"name\");\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/** Internal dynamic resource — not part of the public API. */\nclass RailwayProjectTokenResource extends pulumi.dynamic.Resource {\n\tpublic declare readonly value: pulumi.Output<string>;\n\tpublic declare readonly tokenId: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: {\n\t\t\ttoken: pulumi.Input<string>;\n\t\t\tprojectId: pulumi.Input<string>;\n\t\t\tenvironmentId: pulumi.Input<string>;\n\t\t\tname: pulumi.Input<string>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\t// `value` is an output-only secret. Declaring it via additionalSecretOutputs\n\t\t// (rather than a pulumi.secret(undefined) input placeholder) is the only reliable\n\t\t// way to mark a dynamic-provider output secret: the placeholder gives the property\n\t\t// both a secret-undefined input and a real output, so a downstream dynamic resource\n\t\t// consuming the token can race onto the undefined (\"malformed RPC secret: missing\n\t\t// value\" / \"Unexpected struct type\"). `tokenId` is a plain (non-secret) output. See\n\t\t// Pulumi #16041, #3012.\n\t\tsuper(\n\t\t\tnew RailwayProjectTokenResourceProvider(),\n\t\t\tname,\n\t\t\t{\n\t\t\t\t...args,\n\t\t\t\tvalue: undefined,\n\t\t\t\ttokenId: undefined,\n\t\t\t},\n\t\t\t{ ...opts, additionalSecretOutputs: [\"value\"] },\n\t\t);\n\t}\n}\n\n/** Options type for RailwayProjectToken — replaces Pulumi's native `provider` field. */\ntype RailwayProjectTokenOptions = Omit<\n\tpulumi.ComponentResourceOptions,\n\t\"provider\"\n> & {\n\t/** Railway authentication context. */\n\tprovider: RailwayProvider;\n\n\t/** Railway project this token belongs to. */\n\tproject: RailwayProject;\n\n\t/** Railway environment this deploy token is scoped to. */\n\tenvironment: RailwayEnvironment;\n};\n\n/** Args for RailwayProjectToken. */\nexport interface RailwayProjectTokenArgs {\n\t/**\n\t * Distinct token name, e.g. `\"pulumi-staging\"`.\n\t * Each environment should use a unique name so multiple stacks sharing\n\t * a project never collide on token ownership.\n\t */\n\tname: pulumi.Input<string>;\n}\n\n/**\n * Provisions an environment-scoped Railway deploy token.\n *\n * Each environment gets its own correctly-scoped token with a distinct name,\n * so multiple stacks sharing the same Railway project never collide.\n * The token value is exposed as a secret output for use in {@link RailwayDeploy}.\n *\n * @example\n * ```typescript\n * const project = new RailwayProject(\"my-project\", { name: \"my-app\" }, { provider });\n * const staging = new RailwayEnvironment(\"staging\", { name: \"staging\" }, { provider, project });\n *\n * const stagingToken = new RailwayProjectToken(\"staging-token\", {\n * name: \"pulumi-staging\",\n * }, { provider, project, environment: staging });\n *\n * new RailwayDeploy(\"api-deploy\", {\n * directory: monorepoRoot,\n * triggers: [sourceHash],\n * }, { provider, project, environment: staging, service, projectToken: stagingToken.token });\n * ```\n */\nexport class RailwayProjectToken extends pulumi.ComponentResource {\n\t/**\n\t * Environment-scoped Railway deploy token value (secret).\n\t * Pass this to `RailwayDeployOptions.projectToken`.\n\t */\n\tpublic readonly token: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: RailwayProjectTokenArgs,\n\t\topts: RailwayProjectTokenOptions,\n\t) {\n\t\tconst { provider, project, environment, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:railway:ProjectToken\", name, {}, pulumiOpts);\n\n\t\tconst resource = new RailwayProjectTokenResource(\n\t\t\t`${name}-resource`,\n\t\t\t{\n\t\t\t\ttoken: provider.token,\n\t\t\t\tprojectId: project.id,\n\t\t\t\tenvironmentId: environment.id,\n\t\t\t\tname: args.name,\n\t\t\t},\n\t\t\t{ parent: this },\n\t\t);\n\n\t\tthis.token = resource.value;\n\n\t\tthis.registerOutputs({ token: this.token });\n\t}\n}\n"],"mappings":";;;;;AA8BA,MAAM,uBAAuB;;;;;;;AAQ7B,MAAM,uBAAuB;;;;;AAM7B,MAAM,uBAAuB;;;;;;;;;;;;AAa7B,IAAa,sCAAb,MAEA;;;;;;;;CAQC,MAAM,OACL,QACuC;EACvC,MAAM,SAAS,IAAI,cAAc,OAAO,KAAK;EAQ7C,MAAM,SAAQ,MANa,OAAO,MAI/B,sBAAsB,EAAE,WAAW,OAAO,UAAU,CAAC,GAE7B,cAAc,MAAM,QAC7C,SAAS,KAAK,KAAK,SAAS,OAAO,IACrC;EAEA,KAAK,MAAM,SAAS,OACnB,MAAM,OAAO,MAAM,sBAAsB,EAAE,IAAI,MAAM,KAAK,GAAG,CAAC;EAc/D,MAAM,SAAQ,MAXa,OAAO,MACjC,sBACA,EACC,OAAO;GACN,WAAW,OAAO;GAClB,eAAe,OAAO;GACtB,MAAM,OAAO;EACd,EACD,CACD,GAE2B;EAY3B,MAAM,SAAQ,MARc,OAAO,MAIhC,sBAAsB,EAAE,WAAW,OAAO,UAAU,CAAC,GAI5B,cAAc,MAAM,MAC9C,SAAS,KAAK,KAAK,SAAS,OAAO,IACrC;EAEA,IAAI,CAAC,OACJ,MAAM,IAAI,MACT,uDAAuD,OAAO,KAAK,eAAe,OAAO,WAC1F;EAGD,MAAM,UAAU,MAAM,KAAK;EAE3B,MAAM,OAAmC;GACxC,GAAG;GACH;GACA;EACD;EAEA,OAAO;GAAE,IAAI,GAAG,OAAO,UAAU,GAAG,OAAO;GAAQ;EAAK;CACzD;;;;;;;;CASA,MAAM,KACL,IACA,OACqC;EACrC,OAAO;GAAE;GAAI;EAAM;CACpB;;;;;;;CAQA,MAAM,OAAO,KAAa,OAAkD;EAC3E,IAAI,MAAM,SAGT,MAAM,IAFa,cAAc,MAAM,KAE5B,EAAE,MAAM,sBAAsB,EAAE,IAAI,MAAM,QAAQ,CAAC;CAEhE;;;;;;;;CASA,MAAM,KACL,KACA,MACA,MACqC;EACrC,MAAM,WAAqB,CAAC;EAE5B,IAAI,KAAK,cAAc,KAAK,WAC3B,SAAS,KAAK,WAAW;EAG1B,IAAI,KAAK,kBAAkB,KAAK,eAC/B,SAAS,KAAK,eAAe;EAG9B,IAAI,KAAK,SAAS,KAAK,MACtB,SAAS,KAAK,MAAM;EAGrB,OAAO;GACN,SAAS,SAAS,SAAS;GAC3B;GACA,qBAAqB;EACtB;CACD;AACD;;AAGA,IAAM,8BAAN,cAA0C,OAAO,QAAQ,SAAS;CAIjE,YACC,MACA,MAMA,MACC;EAQD,MACC,IAAI,oCAAoC,GACxC,MACA;GACC,GAAG;GACH,OAAO;GACP,SAAS;EACV,GACA;GAAE,GAAG;GAAM,yBAAyB,CAAC,OAAO;EAAE,CAC/C;CACD;AACD;;;;;;;;;;;;;;;;;;;;;;;AAiDA,IAAa,sBAAb,cAAyC,OAAO,kBAAkB;CAOjE,YACC,MACA,MACA,MACC;EACD,MAAM,EAAE,UAAU,SAAS,aAAa,GAAG,eAAe;EAE1D,MAAM,mCAAmC,MAAM,CAAC,GAAG,UAAU;EAE7D,MAAM,WAAW,IAAI,4BACpB,GAAG,KAAK,YACR;GACC,OAAO,SAAS;GAChB,WAAW,QAAQ;GACnB,eAAe,YAAY;GAC3B,MAAM,KAAK;EACZ,GACA,EAAE,QAAQ,KAAK,CAChB;EAEA,KAAK,QAAQ,SAAS;EAEtB,KAAK,gBAAgB,EAAE,OAAO,KAAK,MAAM,CAAC;CAC3C;AACD"}
|
|
1
|
+
{"version":3,"file":"project-token.mjs","names":[],"sources":["../../src/railway/project-token.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\nimport { RailwayClient } from \"./client\";\nimport type { RailwayEnvironment } from \"./environment\";\nimport type { RailwayProject } from \"./project\";\nimport type { RailwayProvider } from \"./provider\";\n\n/** Resolved inputs for the Railway project token dynamic provider. */\ninterface RailwayProjectTokenInputs {\n\t/** Railway API bearer token (account-scoped, used for API calls). */\n\ttoken: string;\n\n\t/** Railway project UUID. */\n\tprojectId: string;\n\n\t/** Railway environment UUID this deploy token is scoped to. */\n\tenvironmentId: string;\n\n\t/** Distinct token name, e.g. `\"pulumi-staging\"`. Must be unique per environment. */\n\tname: string;\n\n\t/**\n\t * Rotation handle: bumping this number replaces the token on the next `up` —\n\t * the new token is minted BEFORE the old one is revoked (create-before-delete,\n\t * unlike identity changes), so there is never a tokenless window. Leave unset\n\t * until the first rotation is needed.\n\t */\n\ttokenVersion?: number;\n}\n\n/** Persisted state for the Railway project token. */\ninterface RailwayProjectTokenOutputs extends RailwayProjectTokenInputs {\n\t/** Minted deploy token secret value. */\n\tvalue: string;\n\n\t/** Railway-assigned token UUID (used for clean teardown on delete). */\n\ttokenId: string;\n}\n\nconst PROJECT_TOKENS_QUERY = `\n query($projectId: String!) {\n projectTokens(projectId: $projectId) {\n edges { node { id name } }\n }\n }\n`;\n\nconst PROJECT_TOKEN_CREATE = `\n mutation($input: ProjectTokenCreateInput!) {\n projectTokenCreate(input: $input)\n }\n`;\n\nconst PROJECT_TOKEN_DELETE = `\n mutation($id: String!) { projectTokenDelete(id: $id) }\n`;\n\n/**\n * Dynamic provider that mints a Railway environment-scoped deploy token.\n *\n * On create, it deletes any existing tokens with the same name (stale tokens\n * from previous runs), mints a fresh one scoped to the target environment, then\n * re-lists tokens to capture the Railway-assigned ID for later teardown.\n *\n * @internal Exported only for unit testing; not part of the public API surface.\n */\nexport class RailwayProjectTokenResourceProvider\n\timplements pulumi.dynamic.ResourceProvider\n{\n\t/**\n\t * Deletes stale same-named tokens, mints a new environment-scoped token,\n\t * and captures its ID via a follow-up list query.\n\t *\n\t * @param inputs Resolved provider inputs.\n\t * @returns Pulumi dynamic create result with `value` (secret) and `tokenId`.\n\t */\n\tasync create(\n\t\tinputs: RailwayProjectTokenInputs,\n\t): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst client = new RailwayClient(inputs.token);\n\n\t\tconst tokensResult = await client.query<{\n\t\t\tprojectTokens: {\n\t\t\t\tedges: Array<{ node: { id: string; name: string } }>;\n\t\t\t};\n\t\t}>(PROJECT_TOKENS_QUERY, { projectId: inputs.projectId });\n\n\t\tconst stale = tokensResult.projectTokens.edges.filter(\n\t\t\t(edge) => edge.node.name === inputs.name,\n\t\t);\n\n\t\tfor (const entry of stale) {\n\t\t\tawait client.query(PROJECT_TOKEN_DELETE, { id: entry.node.id });\n\t\t}\n\n\t\tconst createResult = await client.query<{ projectTokenCreate: string }>(\n\t\t\tPROJECT_TOKEN_CREATE,\n\t\t\t{\n\t\t\t\tinput: {\n\t\t\t\t\tprojectId: inputs.projectId,\n\t\t\t\t\tenvironmentId: inputs.environmentId,\n\t\t\t\t\tname: inputs.name,\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\n\t\tconst value = createResult.projectTokenCreate;\n\n\t\t// Re-list tokens to capture the Railway-assigned ID: the create mutation\n\t\t// returns only the token value, not its UUID.\n\t\tconst refreshResult = await client.query<{\n\t\t\tprojectTokens: {\n\t\t\t\tedges: Array<{ node: { id: string; name: string } }>;\n\t\t\t};\n\t\t}>(PROJECT_TOKENS_QUERY, { projectId: inputs.projectId });\n\n\t\t// After the delete sweep above, exactly one token with this name exists (the one we just\n\t\t// created). Resolve its id from the re-list so delete() can revoke it later.\n\t\tconst found = refreshResult.projectTokens.edges.find(\n\t\t\t(edge) => edge.node.name === inputs.name,\n\t\t);\n\n\t\tif (!found) {\n\t\t\tthrow new Error(\n\t\t\t\t`Could not resolve token id for newly created token \"${inputs.name}\" in project ${inputs.projectId}`,\n\t\t\t);\n\t\t}\n\n\t\tconst tokenId = found.node.id;\n\n\t\tconst outs: RailwayProjectTokenOutputs = {\n\t\t\t...inputs,\n\t\t\tvalue,\n\t\t\ttokenId,\n\t\t};\n\n\t\treturn { id: `${inputs.projectId}:${inputs.name}`, outs };\n\t}\n\n\t/**\n\t * Pass-through read — the token value is a write-once secret that Railway\n\t * never re-exposes via API, so the stored state is the only source of truth.\n\t *\n\t * @param id Resource ID.\n\t * @param props Currently stored outputs.\n\t */\n\tasync read(\n\t\tid: string,\n\t\tprops: RailwayProjectTokenOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\treturn { id, props };\n\t}\n\n\t/**\n\t * Deletes the Railway token by its stored UUID for clean teardown.\n\t *\n\t * @param _id Resource ID (unused).\n\t * @param props Currently stored outputs containing the tokenId.\n\t */\n\tasync delete(_id: string, props: RailwayProjectTokenOutputs): Promise<void> {\n\t\tif (props.tokenId) {\n\t\t\tconst client = new RailwayClient(props.token);\n\n\t\t\tawait client.query(PROJECT_TOKEN_DELETE, { id: props.tokenId });\n\t\t}\n\t}\n\n\t/**\n\t * Triggers replacement when the project, environment, or token name changes.\n\t *\n\t * @param _id Resource ID (unused).\n\t * @param olds Previously stored outputs.\n\t * @param news Newly resolved inputs.\n\t */\n\tasync diff(\n\t\t_id: string,\n\t\tolds: RailwayProjectTokenOutputs,\n\t\tnews: RailwayProjectTokenInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst identityReplaces: string[] = [];\n\n\t\tif (olds.projectId !== news.projectId) {\n\t\t\tidentityReplaces.push(\"projectId\");\n\t\t}\n\n\t\tif (olds.environmentId !== news.environmentId) {\n\t\t\tidentityReplaces.push(\"environmentId\");\n\t\t}\n\n\t\tif (olds.name !== news.name) {\n\t\t\tidentityReplaces.push(\"name\");\n\t\t}\n\n\t\tconst rotates = olds.tokenVersion !== news.tokenVersion;\n\n\t\treturn {\n\t\t\tchanges: identityReplaces.length > 0 || rotates,\n\t\t\treplaces: rotates\n\t\t\t\t? [...identityReplaces, \"tokenVersion\"]\n\t\t\t\t: identityReplaces,\n\t\t\t// Identity changes revoke the old token first (names must stay unique);\n\t\t\t// a pure rotation mints the new token BEFORE revoking the old so there\n\t\t\t// is never a tokenless window — `create()` already cleans up stale\n\t\t\t// same-name tokens, so the transient name collision is handled.\n\t\t\tdeleteBeforeReplace: identityReplaces.length > 0,\n\t\t};\n\t}\n}\n\n/** Internal dynamic resource — not part of the public API. */\nclass RailwayProjectTokenResource extends pulumi.dynamic.Resource {\n\tpublic declare readonly value: pulumi.Output<string>;\n\tpublic declare readonly tokenId: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: {\n\t\t\ttoken: pulumi.Input<string>;\n\t\t\tprojectId: pulumi.Input<string>;\n\t\t\tenvironmentId: pulumi.Input<string>;\n\t\t\tname: pulumi.Input<string>;\n\t\t\ttokenVersion?: pulumi.Input<number>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\t// `value` is an output-only secret. Declaring it via additionalSecretOutputs\n\t\t// (rather than a pulumi.secret(undefined) input placeholder) is the only reliable\n\t\t// way to mark a dynamic-provider output secret: the placeholder gives the property\n\t\t// both a secret-undefined input and a real output, so a downstream dynamic resource\n\t\t// consuming the token can race onto the undefined (\"malformed RPC secret: missing\n\t\t// value\" / \"Unexpected struct type\"). `tokenId` is a plain (non-secret) output. See\n\t\t// Pulumi #16041, #3012.\n\t\tsuper(\n\t\t\tnew RailwayProjectTokenResourceProvider(),\n\t\t\tname,\n\t\t\t{\n\t\t\t\t...args,\n\t\t\t\tvalue: undefined,\n\t\t\t\ttokenId: undefined,\n\t\t\t},\n\t\t\t{ ...opts, additionalSecretOutputs: [\"value\"] },\n\t\t);\n\t}\n}\n\n/** Options type for RailwayProjectToken — replaces Pulumi's native `provider` field. */\ntype RailwayProjectTokenOptions = Omit<\n\tpulumi.ComponentResourceOptions,\n\t\"provider\"\n> & {\n\t/** Railway authentication context. */\n\tprovider: RailwayProvider;\n\n\t/** Railway project this token belongs to. */\n\tproject: RailwayProject;\n\n\t/** Railway environment this deploy token is scoped to. */\n\tenvironment: RailwayEnvironment;\n};\n\n/** Args for RailwayProjectToken. */\nexport interface RailwayProjectTokenArgs {\n\t/**\n\t * Distinct token name, e.g. `\"pulumi-staging\"`.\n\t * Each environment should use a unique name so multiple stacks sharing\n\t * a project never collide on token ownership.\n\t */\n\tname: pulumi.Input<string>;\n\n\t/**\n\t * Rotation handle: bump to mint a fresh token on the next `up` (the old one\n\t * is revoked only after the new one exists). Consumers of `token` cascade\n\t * automatically.\n\t */\n\ttokenVersion?: pulumi.Input<number>;\n}\n\n/**\n * Provisions an environment-scoped Railway deploy token.\n *\n * Each environment gets its own correctly-scoped token with a distinct name,\n * so multiple stacks sharing the same Railway project never collide.\n * The token value is exposed as a secret output for use in {@link RailwayDeploy}.\n *\n * @example\n * ```typescript\n * const project = new RailwayProject(\"my-project\", { name: \"my-app\" }, { provider });\n * const staging = new RailwayEnvironment(\"staging\", { name: \"staging\" }, { provider, project });\n *\n * const stagingToken = new RailwayProjectToken(\"staging-token\", {\n * name: \"pulumi-staging\",\n * }, { provider, project, environment: staging });\n *\n * new RailwayDeploy(\"api-deploy\", {\n * directory: monorepoRoot,\n * triggers: [sourceHash],\n * }, { provider, project, environment: staging, service, projectToken: stagingToken.token });\n * ```\n */\nexport class RailwayProjectToken extends pulumi.ComponentResource {\n\t/**\n\t * Environment-scoped Railway deploy token value (secret).\n\t * Pass this to `RailwayDeployOptions.projectToken`.\n\t */\n\tpublic readonly token: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: RailwayProjectTokenArgs,\n\t\topts: RailwayProjectTokenOptions,\n\t) {\n\t\tconst { provider, project, environment, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:railway:ProjectToken\", name, {}, pulumiOpts);\n\n\t\tconst resource = new RailwayProjectTokenResource(\n\t\t\t`${name}-resource`,\n\t\t\t{\n\t\t\t\ttoken: provider.token,\n\t\t\t\tprojectId: project.id,\n\t\t\t\tenvironmentId: environment.id,\n\t\t\t\tname: args.name,\n\t\t\t\ttokenVersion: args.tokenVersion,\n\t\t\t},\n\t\t\t{ parent: this },\n\t\t);\n\n\t\tthis.token = resource.value;\n\n\t\tthis.registerOutputs({ token: this.token });\n\t}\n}\n"],"mappings":";;;;;AAsCA,MAAM,uBAAuB;;;;;;;AAQ7B,MAAM,uBAAuB;;;;;AAM7B,MAAM,uBAAuB;;;;;;;;;;;;AAa7B,IAAa,sCAAb,MAEA;;;;;;;;CAQC,MAAM,OACL,QACuC;EACvC,MAAM,SAAS,IAAI,cAAc,OAAO,KAAK;EAQ7C,MAAM,SAAQ,MANa,OAAO,MAI/B,sBAAsB,EAAE,WAAW,OAAO,UAAU,CAAC,GAE7B,cAAc,MAAM,QAC7C,SAAS,KAAK,KAAK,SAAS,OAAO,IACrC;EAEA,KAAK,MAAM,SAAS,OACnB,MAAM,OAAO,MAAM,sBAAsB,EAAE,IAAI,MAAM,KAAK,GAAG,CAAC;EAc/D,MAAM,SAAQ,MAXa,OAAO,MACjC,sBACA,EACC,OAAO;GACN,WAAW,OAAO;GAClB,eAAe,OAAO;GACtB,MAAM,OAAO;EACd,EACD,CACD,GAE2B;EAY3B,MAAM,SAAQ,MARc,OAAO,MAIhC,sBAAsB,EAAE,WAAW,OAAO,UAAU,CAAC,GAI5B,cAAc,MAAM,MAC9C,SAAS,KAAK,KAAK,SAAS,OAAO,IACrC;EAEA,IAAI,CAAC,OACJ,MAAM,IAAI,MACT,uDAAuD,OAAO,KAAK,eAAe,OAAO,WAC1F;EAGD,MAAM,UAAU,MAAM,KAAK;EAE3B,MAAM,OAAmC;GACxC,GAAG;GACH;GACA;EACD;EAEA,OAAO;GAAE,IAAI,GAAG,OAAO,UAAU,GAAG,OAAO;GAAQ;EAAK;CACzD;;;;;;;;CASA,MAAM,KACL,IACA,OACqC;EACrC,OAAO;GAAE;GAAI;EAAM;CACpB;;;;;;;CAQA,MAAM,OAAO,KAAa,OAAkD;EAC3E,IAAI,MAAM,SAGT,MAAM,IAFa,cAAc,MAAM,KAE5B,EAAE,MAAM,sBAAsB,EAAE,IAAI,MAAM,QAAQ,CAAC;CAEhE;;;;;;;;CASA,MAAM,KACL,KACA,MACA,MACqC;EACrC,MAAM,mBAA6B,CAAC;EAEpC,IAAI,KAAK,cAAc,KAAK,WAC3B,iBAAiB,KAAK,WAAW;EAGlC,IAAI,KAAK,kBAAkB,KAAK,eAC/B,iBAAiB,KAAK,eAAe;EAGtC,IAAI,KAAK,SAAS,KAAK,MACtB,iBAAiB,KAAK,MAAM;EAG7B,MAAM,UAAU,KAAK,iBAAiB,KAAK;EAE3C,OAAO;GACN,SAAS,iBAAiB,SAAS,KAAK;GACxC,UAAU,UACP,CAAC,GAAG,kBAAkB,cAAc,IACpC;GAKH,qBAAqB,iBAAiB,SAAS;EAChD;CACD;AACD;;AAGA,IAAM,8BAAN,cAA0C,OAAO,QAAQ,SAAS;CAIjE,YACC,MACA,MAOA,MACC;EAQD,MACC,IAAI,oCAAoC,GACxC,MACA;GACC,GAAG;GACH,OAAO;GACP,SAAS;EACV,GACA;GAAE,GAAG;GAAM,yBAAyB,CAAC,OAAO;EAAE,CAC/C;CACD;AACD;;;;;;;;;;;;;;;;;;;;;;;AAwDA,IAAa,sBAAb,cAAyC,OAAO,kBAAkB;CAOjE,YACC,MACA,MACA,MACC;EACD,MAAM,EAAE,UAAU,SAAS,aAAa,GAAG,eAAe;EAE1D,MAAM,mCAAmC,MAAM,CAAC,GAAG,UAAU;EAE7D,MAAM,WAAW,IAAI,4BACpB,GAAG,KAAK,YACR;GACC,OAAO,SAAS;GAChB,WAAW,QAAQ;GACnB,eAAe,YAAY;GAC3B,MAAM,KAAK;GACX,cAAc,KAAK;EACpB,GACA,EAAE,QAAQ,KAAK,CAChB;EAEA,KAAK,QAAQ,SAAS;EAEtB,KAAK,gBAAgB,EAAE,OAAO,KAAK,MAAM,CAAC;CAC3C;AACD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@infracraft/pulumi",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.25.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Native Pulumi providers for Railway, Neon, Vercel, and Fly.io with adopt-or-create semantics and deploy orchestration. No Terraform bridge.",
|