@infracraft/pulumi 1.26.0 → 1.27.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/dynamic/is-resolved-string.cjs +20 -0
- package/dist/dynamic/is-resolved-string.cjs.map +1 -0
- package/dist/dynamic/is-resolved-string.d.cts +14 -0
- package/dist/dynamic/is-resolved-string.d.cts.map +1 -0
- package/dist/dynamic/is-resolved-string.d.mts +14 -0
- package/dist/dynamic/is-resolved-string.d.mts.map +1 -0
- package/dist/dynamic/is-resolved-string.mjs +18 -0
- package/dist/dynamic/is-resolved-string.mjs.map +1 -0
- package/dist/fly/app.cjs.map +1 -1
- package/dist/fly/app.d.cts +5 -1
- package/dist/fly/app.d.cts.map +1 -1
- package/dist/fly/app.d.mts +5 -1
- package/dist/fly/app.d.mts.map +1 -1
- package/dist/fly/app.mjs.map +1 -1
- package/dist/fly/secret.cjs.map +1 -1
- package/dist/fly/secret.d.cts +4 -1
- package/dist/fly/secret.d.cts.map +1 -1
- package/dist/fly/secret.d.mts +4 -1
- package/dist/fly/secret.d.mts.map +1 -1
- package/dist/fly/secret.mjs.map +1 -1
- package/dist/fly/volume.cjs +18 -0
- package/dist/fly/volume.cjs.map +1 -1
- package/dist/fly/volume.d.cts +11 -1
- package/dist/fly/volume.d.cts.map +1 -1
- package/dist/fly/volume.d.mts +11 -1
- package/dist/fly/volume.d.mts.map +1 -1
- package/dist/fly/volume.mjs +18 -0
- package/dist/fly/volume.mjs.map +1 -1
- package/dist/neon/branch.cjs +16 -0
- package/dist/neon/branch.cjs.map +1 -1
- package/dist/neon/branch.d.cts +6 -0
- package/dist/neon/branch.d.cts.map +1 -1
- package/dist/neon/branch.d.mts +6 -0
- package/dist/neon/branch.d.mts.map +1 -1
- package/dist/neon/branch.mjs +16 -0
- package/dist/neon/branch.mjs.map +1 -1
- package/dist/neon/database.cjs.map +1 -1
- package/dist/neon/database.d.cts +1 -1
- package/dist/neon/database.d.mts +1 -1
- package/dist/neon/database.mjs.map +1 -1
- package/dist/neon/endpoint.cjs +1 -0
- package/dist/neon/endpoint.cjs.map +1 -1
- package/dist/neon/endpoint.d.cts +3 -3
- package/dist/neon/endpoint.d.cts.map +1 -1
- package/dist/neon/endpoint.d.mts +3 -3
- package/dist/neon/endpoint.d.mts.map +1 -1
- package/dist/neon/endpoint.mjs +1 -0
- package/dist/neon/endpoint.mjs.map +1 -1
- package/dist/neon/project.cjs +1 -0
- package/dist/neon/project.cjs.map +1 -1
- package/dist/neon/project.d.cts.map +1 -1
- package/dist/neon/project.d.mts.map +1 -1
- package/dist/neon/project.mjs +1 -0
- package/dist/neon/project.mjs.map +1 -1
- package/dist/neon/role.cjs +21 -0
- package/dist/neon/role.cjs.map +1 -1
- package/dist/neon/role.d.cts +5 -0
- package/dist/neon/role.d.cts.map +1 -1
- package/dist/neon/role.d.mts +5 -0
- package/dist/neon/role.d.mts.map +1 -1
- package/dist/neon/role.mjs +21 -0
- package/dist/neon/role.mjs.map +1 -1
- package/dist/railway/domain.cjs.map +1 -1
- package/dist/railway/domain.d.cts +5 -1
- package/dist/railway/domain.d.cts.map +1 -1
- package/dist/railway/domain.d.mts +5 -1
- package/dist/railway/domain.d.mts.map +1 -1
- package/dist/railway/domain.mjs.map +1 -1
- package/dist/railway/environment.cjs.map +1 -1
- package/dist/railway/environment.d.cts +1 -0
- package/dist/railway/environment.d.cts.map +1 -1
- package/dist/railway/environment.d.mts +1 -0
- package/dist/railway/environment.d.mts.map +1 -1
- package/dist/railway/environment.mjs.map +1 -1
- package/dist/railway/project-token.cjs +17 -0
- package/dist/railway/project-token.cjs.map +1 -1
- package/dist/railway/project-token.d.cts +6 -0
- package/dist/railway/project-token.d.cts.map +1 -1
- package/dist/railway/project-token.d.mts +6 -0
- package/dist/railway/project-token.d.mts.map +1 -1
- package/dist/railway/project-token.mjs +17 -0
- package/dist/railway/project-token.mjs.map +1 -1
- package/dist/railway/project.cjs +1 -0
- package/dist/railway/project.cjs.map +1 -1
- package/dist/railway/project.d.cts.map +1 -1
- package/dist/railway/project.d.mts.map +1 -1
- package/dist/railway/project.mjs +1 -0
- package/dist/railway/project.mjs.map +1 -1
- package/dist/railway/service.cjs +17 -0
- package/dist/railway/service.cjs.map +1 -1
- package/dist/railway/service.d.cts +5 -0
- package/dist/railway/service.d.cts.map +1 -1
- package/dist/railway/service.d.mts +5 -0
- package/dist/railway/service.d.mts.map +1 -1
- package/dist/railway/service.mjs +17 -0
- package/dist/railway/service.mjs.map +1 -1
- package/dist/railway/volume.cjs +16 -0
- package/dist/railway/volume.cjs.map +1 -1
- package/dist/railway/volume.d.cts +5 -0
- package/dist/railway/volume.d.cts.map +1 -1
- package/dist/railway/volume.d.mts +5 -0
- package/dist/railway/volume.d.mts.map +1 -1
- package/dist/railway/volume.mjs +16 -0
- package/dist/railway/volume.mjs.map +1 -1
- package/dist/vercel/marketplace-resource.cjs.map +1 -1
- package/dist/vercel/marketplace-resource.d.cts +1 -0
- package/dist/vercel/marketplace-resource.d.cts.map +1 -1
- package/dist/vercel/marketplace-resource.d.mts +1 -0
- package/dist/vercel/marketplace-resource.d.mts.map +1 -1
- package/dist/vercel/marketplace-resource.mjs.map +1 -1
- package/dist/vercel/project.cjs +20 -0
- package/dist/vercel/project.cjs.map +1 -1
- package/dist/vercel/project.d.cts +8 -0
- package/dist/vercel/project.d.cts.map +1 -1
- package/dist/vercel/project.d.mts +8 -0
- package/dist/vercel/project.d.mts.map +1 -1
- package/dist/vercel/project.mjs +20 -0
- package/dist/vercel/project.mjs.map +1 -1
- package/dist/vercel/resource-connection.cjs.map +1 -1
- package/dist/vercel/resource-connection.d.cts +1 -0
- package/dist/vercel/resource-connection.d.cts.map +1 -1
- package/dist/vercel/resource-connection.d.mts +1 -0
- package/dist/vercel/resource-connection.d.mts.map +1 -1
- package/dist/vercel/resource-connection.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
const require_chunk = require('../chunk-BVYJZCqc.cjs');
|
|
3
|
+
let _pulumi_pulumi = require("@pulumi/pulumi");
|
|
4
|
+
_pulumi_pulumi = require_chunk.__toESM(_pulumi_pulumi, 1);
|
|
5
|
+
|
|
6
|
+
//#region src/dynamic/is-resolved-string.ts
|
|
7
|
+
/**
|
|
8
|
+
* True when a dynamic-provider input holds a concrete string — neither
|
|
9
|
+
* `undefined` nor Pulumi's preview "unknown" sentinel. `check()` runs at plan
|
|
10
|
+
* time, when an input fed by another resource's output may still be
|
|
11
|
+
* unresolved; validation must skip those values instead of failing on the
|
|
12
|
+
* sentinel.
|
|
13
|
+
*/
|
|
14
|
+
function isResolvedString(value) {
|
|
15
|
+
return typeof value === "string" && value !== _pulumi_pulumi.runtime.unknownValue;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
//#endregion
|
|
19
|
+
exports.isResolvedString = isResolvedString;
|
|
20
|
+
//# sourceMappingURL=is-resolved-string.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"is-resolved-string.cjs","names":["pulumi"],"sources":["../../src/dynamic/is-resolved-string.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\n\n/**\n * True when a dynamic-provider input holds a concrete string — neither\n * `undefined` nor Pulumi's preview \"unknown\" sentinel. `check()` runs at plan\n * time, when an input fed by another resource's output may still be\n * unresolved; validation must skip those values instead of failing on the\n * sentinel.\n */\nexport function isResolvedString(value: unknown): value is string {\n\treturn typeof value === \"string\" && value !== pulumi.runtime.unknownValue;\n}\n"],"mappings":";;;;;;;;;;;;;AASA,SAAgB,iBAAiB,OAAiC;CACjE,OAAO,OAAO,UAAU,YAAY,UAAUA,eAAO,QAAQ;AAC9D"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { t as __name } from "../chunk-OPjESj5l.cjs";
|
|
2
|
+
|
|
3
|
+
//#region src/dynamic/is-resolved-string.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* True when a dynamic-provider input holds a concrete string — neither
|
|
6
|
+
* `undefined` nor Pulumi's preview "unknown" sentinel. `check()` runs at plan
|
|
7
|
+
* time, when an input fed by another resource's output may still be
|
|
8
|
+
* unresolved; validation must skip those values instead of failing on the
|
|
9
|
+
* sentinel.
|
|
10
|
+
*/
|
|
11
|
+
declare function isResolvedString(value: unknown): value is string;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { isResolvedString };
|
|
14
|
+
//# sourceMappingURL=is-resolved-string.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"is-resolved-string.d.cts","names":[],"sources":["../../src/dynamic/is-resolved-string.ts"],"mappings":";;;;;;AASA;;;;iBAAgB,gBAAA,CAAiB,KAAA,YAAiB,KAAK"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { t as __name } from "../chunk-OPjESj5l.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/dynamic/is-resolved-string.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* True when a dynamic-provider input holds a concrete string — neither
|
|
6
|
+
* `undefined` nor Pulumi's preview "unknown" sentinel. `check()` runs at plan
|
|
7
|
+
* time, when an input fed by another resource's output may still be
|
|
8
|
+
* unresolved; validation must skip those values instead of failing on the
|
|
9
|
+
* sentinel.
|
|
10
|
+
*/
|
|
11
|
+
declare function isResolvedString(value: unknown): value is string;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { isResolvedString };
|
|
14
|
+
//# sourceMappingURL=is-resolved-string.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"is-resolved-string.d.mts","names":[],"sources":["../../src/dynamic/is-resolved-string.ts"],"mappings":";;;;;;AASA;;;;iBAAgB,gBAAA,CAAiB,KAAA,YAAiB,KAAK"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { t as __name } from "../chunk-OPjESj5l.mjs";
|
|
2
|
+
import * as pulumi from "@pulumi/pulumi";
|
|
3
|
+
|
|
4
|
+
//#region src/dynamic/is-resolved-string.ts
|
|
5
|
+
/**
|
|
6
|
+
* True when a dynamic-provider input holds a concrete string — neither
|
|
7
|
+
* `undefined` nor Pulumi's preview "unknown" sentinel. `check()` runs at plan
|
|
8
|
+
* time, when an input fed by another resource's output may still be
|
|
9
|
+
* unresolved; validation must skip those values instead of failing on the
|
|
10
|
+
* sentinel.
|
|
11
|
+
*/
|
|
12
|
+
function isResolvedString(value) {
|
|
13
|
+
return typeof value === "string" && value !== pulumi.runtime.unknownValue;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
//#endregion
|
|
17
|
+
export { isResolvedString };
|
|
18
|
+
//# sourceMappingURL=is-resolved-string.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"is-resolved-string.mjs","names":[],"sources":["../../src/dynamic/is-resolved-string.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\n\n/**\n * True when a dynamic-provider input holds a concrete string — neither\n * `undefined` nor Pulumi's preview \"unknown\" sentinel. `check()` runs at plan\n * time, when an input fed by another resource's output may still be\n * unresolved; validation must skip those values instead of failing on the\n * sentinel.\n */\nexport function isResolvedString(value: unknown): value is string {\n\treturn typeof value === \"string\" && value !== pulumi.runtime.unknownValue;\n}\n"],"mappings":";;;;;;;;;;;AASA,SAAgB,iBAAiB,OAAiC;CACjE,OAAO,OAAO,UAAU,YAAY,UAAU,OAAO,QAAQ;AAC9D"}
|
package/dist/fly/app.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.cjs","names":["FlyClient","pulumi"],"sources":["../../src/fly/app.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\n\nimport { FlyClient } from \"./client\";\nimport type { FlyProvider } from \"./provider\";\n\n/** Resolved inputs for the Fly app dynamic provider. */\ninterface FlyAppInputs {\n\t/** Fly API token. */\n\ttoken: string;\n\n\t/** App name (globally unique). Used as the resource identifier. */\n\tname: string;\n\n\t/** Org slug used only when creating a new app. */\n\torganization?: string;\n}\n\n/** Persisted state for the Fly app. */\ninterface FlyAppOutputs extends FlyAppInputs {\n\t/** App identifier — equals the app name (all child paths key off the name). */\n\tappId: string;\n}\n\n/** Get-app response (only the fields we read). */\ninterface FlyAppResponse {\n\tid: string;\n\tname: string;\n}\n\n/**\n * Dynamic provider implementing adopt-or-create for Fly apps.\n *\n * `create()` does `GET /v1/apps/{name}`; if found it adopts, otherwise it\n * `POST /v1/apps`. `delete()` is a no-op — deleting a Fly app destroys\n * everything in it, so (like Railway/Neon/Vercel top-level resources) Pulumi\n * does not delete apps.\n *\n * @internal Exported only for unit testing; not part of the public API surface.\n */\nexport class FlyAppResourceProvider implements pulumi.dynamic.ResourceProvider {\n\tasync create(inputs: FlyAppInputs): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst client = new FlyClient(inputs.token);\n\n\t\tconst existing = await client.tryGet<FlyAppResponse>(\n\t\t\t`/v1/apps/${inputs.name}`,\n\t\t);\n\n\t\tif (existing) {\n\t\t\tpulumi.log.info(`Adopting existing Fly app \"${inputs.name}\"`);\n\t\t} else {\n\t\t\tif (!inputs.organization) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`FlyApp \"${inputs.name}\": an organization is required to create a new app — set it on FlyProvider or FlyApp args`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tpulumi.log.info(`Fly app \"${inputs.name}\" not found — creating...`);\n\n\t\t\tawait client.post(\"/v1/apps\", {\n\t\t\t\tapp_name: inputs.name,\n\t\t\t\torg_slug: inputs.organization,\n\t\t\t});\n\t\t}\n\n\t\tconst outs: FlyAppOutputs = { ...inputs, appId: inputs.name };\n\n\t\treturn { id: inputs.name, outs };\n\t}\n\n\tasync read(\n\t\tid: string,\n\t\tprops: FlyAppOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\tconst client = new FlyClient(props.token);\n\t\tconst app = await client.tryGet<FlyAppResponse>(`/v1/apps/${id}`);\n\n\t\tif (!app) {\n\t\t\t// Resource gone → blank id lets refresh reconcile the deletion.\n\t\t\treturn {};\n\t\t}\n\n\t\treturn { id, props: { ...props, name: app.name, appId: app.name } };\n\t}\n\n\tasync update(\n\t\tid: string,\n\t\t_olds: FlyAppOutputs,\n\t\tnews: FlyAppInputs,\n\t): Promise<pulumi.dynamic.UpdateResult> {\n\t\treturn { outs: { ...news, appId: id } };\n\t}\n\n\tasync delete(): Promise<void> {\n\t\tpulumi.log.warn(\n\t\t\t\"Fly app deletion skipped — apps are not deleted by Pulumi (would destroy all contained resources)\",\n\t\t);\n\t}\n\n\tasync diff(\n\t\t_id: string,\n\t\tolds: FlyAppOutputs,\n\t\tnews: FlyAppInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst replaces: string[] = [];\n\n\t\tif (olds.name !== news.name) {\n\t\t\treplaces.push(\"name\");\n\t\t}\n\n\t\tif (olds.organization !== news.organization) {\n\t\t\treplaces.push(\"organization\");\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 FlyAppResource extends pulumi.dynamic.Resource {\n\tpublic declare readonly appId: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: {\n\t\t\ttoken: pulumi.Input<string>;\n\t\t\tname: pulumi.Input<string>;\n\t\t\torganization?: pulumi.Input<string | undefined>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\tsuper(\n\t\t\tnew FlyAppResourceProvider(),\n\t\t\tname,\n\t\t\t{ ...args, appId: undefined },\n\t\t\t// The API token flows into dynamic-provider state with the outputs — mark it secret there.\n\t\t\t{ ...opts, additionalSecretOutputs: [\"token\"] },\n\t\t);\n\t}\n}\n\n/** Options type for FlyApp — replaces Pulumi's native `provider` field. */\ntype FlyAppOptions = Omit<pulumi.ComponentResourceOptions, \"provider\"> & {\n\t/** Fly authentication context. */\n\tprovider: FlyProvider;\n};\n\n/** Args for FlyApp. */\nexport interface FlyAppArgs {\n\t
|
|
1
|
+
{"version":3,"file":"app.cjs","names":["FlyClient","pulumi"],"sources":["../../src/fly/app.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\n\nimport { FlyClient } from \"./client\";\nimport type { FlyProvider } from \"./provider\";\n\n/** Resolved inputs for the Fly app dynamic provider. */\ninterface FlyAppInputs {\n\t/** Fly API token. */\n\ttoken: string;\n\n\t/** App name (globally unique). Used as the resource identifier. */\n\tname: string;\n\n\t/** Org slug used only when creating a new app. */\n\torganization?: string;\n}\n\n/** Persisted state for the Fly app. */\ninterface FlyAppOutputs extends FlyAppInputs {\n\t/** App identifier — equals the app name (all child paths key off the name). */\n\tappId: string;\n}\n\n/** Get-app response (only the fields we read). */\ninterface FlyAppResponse {\n\tid: string;\n\tname: string;\n}\n\n/**\n * Dynamic provider implementing adopt-or-create for Fly apps.\n *\n * `create()` does `GET /v1/apps/{name}`; if found it adopts, otherwise it\n * `POST /v1/apps`. `delete()` is a no-op — deleting a Fly app destroys\n * everything in it, so (like Railway/Neon/Vercel top-level resources) Pulumi\n * does not delete apps.\n *\n * @internal Exported only for unit testing; not part of the public API surface.\n */\nexport class FlyAppResourceProvider implements pulumi.dynamic.ResourceProvider {\n\tasync create(inputs: FlyAppInputs): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst client = new FlyClient(inputs.token);\n\n\t\tconst existing = await client.tryGet<FlyAppResponse>(\n\t\t\t`/v1/apps/${inputs.name}`,\n\t\t);\n\n\t\tif (existing) {\n\t\t\tpulumi.log.info(`Adopting existing Fly app \"${inputs.name}\"`);\n\t\t} else {\n\t\t\tif (!inputs.organization) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`FlyApp \"${inputs.name}\": an organization is required to create a new app — set it on FlyProvider or FlyApp args`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tpulumi.log.info(`Fly app \"${inputs.name}\" not found — creating...`);\n\n\t\t\tawait client.post(\"/v1/apps\", {\n\t\t\t\tapp_name: inputs.name,\n\t\t\t\torg_slug: inputs.organization,\n\t\t\t});\n\t\t}\n\n\t\tconst outs: FlyAppOutputs = { ...inputs, appId: inputs.name };\n\n\t\treturn { id: inputs.name, outs };\n\t}\n\n\tasync read(\n\t\tid: string,\n\t\tprops: FlyAppOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\tconst client = new FlyClient(props.token);\n\t\tconst app = await client.tryGet<FlyAppResponse>(`/v1/apps/${id}`);\n\n\t\tif (!app) {\n\t\t\t// Resource gone → blank id lets refresh reconcile the deletion.\n\t\t\treturn {};\n\t\t}\n\n\t\treturn { id, props: { ...props, name: app.name, appId: app.name } };\n\t}\n\n\tasync update(\n\t\tid: string,\n\t\t_olds: FlyAppOutputs,\n\t\tnews: FlyAppInputs,\n\t): Promise<pulumi.dynamic.UpdateResult> {\n\t\treturn { outs: { ...news, appId: id } };\n\t}\n\n\tasync delete(): Promise<void> {\n\t\tpulumi.log.warn(\n\t\t\t\"Fly app deletion skipped — apps are not deleted by Pulumi (would destroy all contained resources)\",\n\t\t);\n\t}\n\n\tasync diff(\n\t\t_id: string,\n\t\tolds: FlyAppOutputs,\n\t\tnews: FlyAppInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst replaces: string[] = [];\n\n\t\tif (olds.name !== news.name) {\n\t\t\treplaces.push(\"name\");\n\t\t}\n\n\t\tif (olds.organization !== news.organization) {\n\t\t\treplaces.push(\"organization\");\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 FlyAppResource extends pulumi.dynamic.Resource {\n\tpublic declare readonly appId: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: {\n\t\t\ttoken: pulumi.Input<string>;\n\t\t\tname: pulumi.Input<string>;\n\t\t\torganization?: pulumi.Input<string | undefined>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\tsuper(\n\t\t\tnew FlyAppResourceProvider(),\n\t\t\tname,\n\t\t\t{ ...args, appId: undefined },\n\t\t\t// The API token flows into dynamic-provider state with the outputs — mark it secret there.\n\t\t\t{ ...opts, additionalSecretOutputs: [\"token\"] },\n\t\t);\n\t}\n}\n\n/** Options type for FlyApp — replaces Pulumi's native `provider` field. */\ntype FlyAppOptions = Omit<pulumi.ComponentResourceOptions, \"provider\"> & {\n\t/** Fly authentication context. */\n\tprovider: FlyProvider;\n};\n\n/** Args for FlyApp. */\nexport interface FlyAppArgs {\n\t/**\n\t * App name (globally unique). Used for adoption lookup and as `.id`.\n\t * Maps to the Fly Machines API field `app_name`.\n\t */\n\tname: pulumi.Input<string>;\n\n\t/**\n\t * Org slug for app creation. Overrides `FlyProvider.organization`.\n\t * Ignored when the app already exists (adoption).\n\t * Maps to the Fly Machines API field `org_slug`.\n\t */\n\torganization?: pulumi.Input<string>;\n}\n\n/**\n * Manages a Fly app with adopt-or-create semantics.\n *\n * @example\n * ```typescript\n * const app = new FlyApp(\"api\", { name: \"rby-api\" }, { provider });\n * ```\n */\nexport class FlyApp extends pulumi.ComponentResource {\n\t/** App identifier (equals the app name). */\n\tpublic readonly id: pulumi.Output<string>;\n\n\tconstructor(name: string, args: FlyAppArgs, opts: FlyAppOptions) {\n\t\tconst { provider, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:fly:App\", name, {}, pulumiOpts);\n\n\t\tconst resource = new FlyAppResource(\n\t\t\t`${name}-resource`,\n\t\t\t{\n\t\t\t\ttoken: provider.token,\n\t\t\t\tname: args.name,\n\t\t\t\torganization: args.organization ?? provider.organization,\n\t\t\t},\n\t\t\t{ parent: this },\n\t\t);\n\n\t\tthis.id = resource.appId;\n\n\t\tthis.registerOutputs({ id: this.id });\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAuCA,IAAa,yBAAb,MAA+E;CAC9E,MAAM,OAAO,QAA4D;EACxE,MAAM,SAAS,IAAIA,6BAAU,OAAO,KAAK;EAMzC,IAAI,MAJmB,OAAO,OAC7B,YAAY,OAAO,MACpB,GAGC,eAAO,IAAI,KAAK,8BAA8B,OAAO,KAAK,EAAE;OACtD;GACN,IAAI,CAAC,OAAO,cACX,MAAM,IAAI,MACT,WAAW,OAAO,KAAK,0FACxB;GAGD,eAAO,IAAI,KAAK,YAAY,OAAO,KAAK,0BAA0B;GAElE,MAAM,OAAO,KAAK,YAAY;IAC7B,UAAU,OAAO;IACjB,UAAU,OAAO;GAClB,CAAC;EACF;EAEA,MAAM,OAAsB;GAAE,GAAG;GAAQ,OAAO,OAAO;EAAK;EAE5D,OAAO;GAAE,IAAI,OAAO;GAAM;EAAK;CAChC;CAEA,MAAM,KACL,IACA,OACqC;EAErC,MAAM,MAAM,MAAM,IADCA,6BAAU,MAAM,KACZ,EAAE,OAAuB,YAAY,IAAI;EAEhE,IAAI,CAAC,KAEJ,OAAO,CAAC;EAGT,OAAO;GAAE;GAAI,OAAO;IAAE,GAAG;IAAO,MAAM,IAAI;IAAM,OAAO,IAAI;GAAK;EAAE;CACnE;CAEA,MAAM,OACL,IACA,OACA,MACuC;EACvC,OAAO,EAAE,MAAM;GAAE,GAAG;GAAM,OAAO;EAAG,EAAE;CACvC;CAEA,MAAM,SAAwB;EAC7B,eAAO,IAAI,KACV,mGACD;CACD;CAEA,MAAM,KACL,KACA,MACA,MACqC;EACrC,MAAM,WAAqB,CAAC;EAE5B,IAAI,KAAK,SAAS,KAAK,MACtB,SAAS,KAAK,MAAM;EAGrB,IAAI,KAAK,iBAAiB,KAAK,cAC9B,SAAS,KAAK,cAAc;EAG7B,OAAO;GACN,SAAS,SAAS,SAAS;GAC3B;GACA,qBAAqB;EACtB;CACD;AACD;;AAGA,IAAM,iBAAN,cAA6BC,eAAO,QAAQ,SAAS;CAGpD,YACC,MACA,MAKA,MACC;EACD,MACC,IAAI,uBAAuB,GAC3B,MACA;GAAE,GAAG;GAAM,OAAO;EAAU,GAE5B;GAAE,GAAG;GAAM,yBAAyB,CAAC,OAAO;EAAE,CAC/C;CACD;AACD;;;;;;;;;AAgCA,IAAa,SAAb,cAA4BA,eAAO,kBAAkB;CAIpD,YAAY,MAAc,MAAkB,MAAqB;EAChE,MAAM,EAAE,UAAU,GAAG,eAAe;EAEpC,MAAM,sBAAsB,MAAM,CAAC,GAAG,UAAU;EAEhD,MAAM,WAAW,IAAI,eACpB,GAAG,KAAK,YACR;GACC,OAAO,SAAS;GAChB,MAAM,KAAK;GACX,cAAc,KAAK,gBAAgB,SAAS;EAC7C,GACA,EAAE,QAAQ,KAAK,CAChB;EAEA,KAAK,KAAK,SAAS;EAEnB,KAAK,gBAAgB,EAAE,IAAI,KAAK,GAAG,CAAC;CACrC;AACD"}
|
package/dist/fly/app.d.cts
CHANGED
|
@@ -40,11 +40,15 @@ type FlyAppOptions = Omit<pulumi.ComponentResourceOptions, "provider"> & {
|
|
|
40
40
|
};
|
|
41
41
|
/** Args for FlyApp. */
|
|
42
42
|
interface FlyAppArgs {
|
|
43
|
-
/**
|
|
43
|
+
/**
|
|
44
|
+
* App name (globally unique). Used for adoption lookup and as `.id`.
|
|
45
|
+
* Maps to the Fly Machines API field `app_name`.
|
|
46
|
+
*/
|
|
44
47
|
name: pulumi.Input<string>;
|
|
45
48
|
/**
|
|
46
49
|
* Org slug for app creation. Overrides `FlyProvider.organization`.
|
|
47
50
|
* Ignored when the app already exists (adoption).
|
|
51
|
+
* Maps to the Fly Machines API field `org_slug`.
|
|
48
52
|
*/
|
|
49
53
|
organization?: pulumi.Input<string>;
|
|
50
54
|
}
|
package/dist/fly/app.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.d.cts","names":[],"sources":["../../src/fly/app.ts"],"mappings":";;;;;;UAMU,YAAA;;EAET,KAAA;EAFqB;EAKrB,IAAA;EALqB;EAQrB,YAAA;AAAA;;UAIS,aAAA,SAAsB,YAAY;EAJ/B;EAMZ,KAAK;AAAA;;;AAAA;AAmBN;;;;;;;cAAa,sBAAA,YAAkC,MAAA,CAAO,OAAA,CAAQ,gBAAA;EACvD,MAAA,CAAO,MAAA,EAAQ,YAAA,GAAe,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EA6BrD,IAAA,CACL,EAAA,UACA,KAAA,EAAO,aAAA,GACL,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;EAYpB,MAAA,CACL,EAAA,UACA,KAAA,EAAO,aAAA,EACP,IAAA,EAAM,YAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EAIpB,MAAA,CAAA,GAAU,OAAA;EAMV,IAAA,CACL,GAAA,UACA,IAAA,EAAM,aAAA,EACN,IAAA,EAAM,YAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;AAAA;;KA2CtB,aAAA,GAAgB,IAAA,CAAK,MAAA,CAAO,wBAAA;EA3CrB,kCA6CX,QAAA,EAAU,WAAA;AAAA;;UAIM,UAAA;EAhH8B
|
|
1
|
+
{"version":3,"file":"app.d.cts","names":[],"sources":["../../src/fly/app.ts"],"mappings":";;;;;;UAMU,YAAA;;EAET,KAAA;EAFqB;EAKrB,IAAA;EALqB;EAQrB,YAAA;AAAA;;UAIS,aAAA,SAAsB,YAAY;EAJ/B;EAMZ,KAAK;AAAA;;;AAAA;AAmBN;;;;;;;cAAa,sBAAA,YAAkC,MAAA,CAAO,OAAA,CAAQ,gBAAA;EACvD,MAAA,CAAO,MAAA,EAAQ,YAAA,GAAe,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EA6BrD,IAAA,CACL,EAAA,UACA,KAAA,EAAO,aAAA,GACL,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;EAYpB,MAAA,CACL,EAAA,UACA,KAAA,EAAO,aAAA,EACP,IAAA,EAAM,YAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EAIpB,MAAA,CAAA,GAAU,OAAA;EAMV,IAAA,CACL,GAAA,UACA,IAAA,EAAM,aAAA,EACN,IAAA,EAAM,YAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;AAAA;;KA2CtB,aAAA,GAAgB,IAAA,CAAK,MAAA,CAAO,wBAAA;EA3CrB,kCA6CX,QAAA,EAAU,WAAA;AAAA;;UAIM,UAAA;EAhH8B;;;;EAqH9C,IAAA,EAAM,MAAA,CAAO,KAAA;EApHA;;;;;EA2Hb,YAAA,GAAe,MAAA,CAAO,KAAK;AAAA;;;;;;;;;cAWf,MAAA,SAAe,MAAA,CAAO,iBAAA;EAxFjC;EAAA,SA0Fe,EAAA,EAAI,MAAA,CAAO,MAAA;cAEf,IAAA,UAAc,IAAA,EAAM,UAAA,EAAY,IAAA,EAAM,aAAA;AAAA"}
|
package/dist/fly/app.d.mts
CHANGED
|
@@ -40,11 +40,15 @@ type FlyAppOptions = Omit<pulumi.ComponentResourceOptions, "provider"> & {
|
|
|
40
40
|
};
|
|
41
41
|
/** Args for FlyApp. */
|
|
42
42
|
interface FlyAppArgs {
|
|
43
|
-
/**
|
|
43
|
+
/**
|
|
44
|
+
* App name (globally unique). Used for adoption lookup and as `.id`.
|
|
45
|
+
* Maps to the Fly Machines API field `app_name`.
|
|
46
|
+
*/
|
|
44
47
|
name: pulumi.Input<string>;
|
|
45
48
|
/**
|
|
46
49
|
* Org slug for app creation. Overrides `FlyProvider.organization`.
|
|
47
50
|
* Ignored when the app already exists (adoption).
|
|
51
|
+
* Maps to the Fly Machines API field `org_slug`.
|
|
48
52
|
*/
|
|
49
53
|
organization?: pulumi.Input<string>;
|
|
50
54
|
}
|
package/dist/fly/app.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.d.mts","names":[],"sources":["../../src/fly/app.ts"],"mappings":";;;;;;UAMU,YAAA;;EAET,KAAA;EAFqB;EAKrB,IAAA;EALqB;EAQrB,YAAA;AAAA;;UAIS,aAAA,SAAsB,YAAY;EAJ/B;EAMZ,KAAK;AAAA;;;AAAA;AAmBN;;;;;;;cAAa,sBAAA,YAAkC,MAAA,CAAO,OAAA,CAAQ,gBAAA;EACvD,MAAA,CAAO,MAAA,EAAQ,YAAA,GAAe,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EA6BrD,IAAA,CACL,EAAA,UACA,KAAA,EAAO,aAAA,GACL,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;EAYpB,MAAA,CACL,EAAA,UACA,KAAA,EAAO,aAAA,EACP,IAAA,EAAM,YAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EAIpB,MAAA,CAAA,GAAU,OAAA;EAMV,IAAA,CACL,GAAA,UACA,IAAA,EAAM,aAAA,EACN,IAAA,EAAM,YAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;AAAA;;KA2CtB,aAAA,GAAgB,IAAA,CAAK,MAAA,CAAO,wBAAA;EA3CrB,kCA6CX,QAAA,EAAU,WAAA;AAAA;;UAIM,UAAA;EAhH8B
|
|
1
|
+
{"version":3,"file":"app.d.mts","names":[],"sources":["../../src/fly/app.ts"],"mappings":";;;;;;UAMU,YAAA;;EAET,KAAA;EAFqB;EAKrB,IAAA;EALqB;EAQrB,YAAA;AAAA;;UAIS,aAAA,SAAsB,YAAY;EAJ/B;EAMZ,KAAK;AAAA;;;AAAA;AAmBN;;;;;;;cAAa,sBAAA,YAAkC,MAAA,CAAO,OAAA,CAAQ,gBAAA;EACvD,MAAA,CAAO,MAAA,EAAQ,YAAA,GAAe,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EA6BrD,IAAA,CACL,EAAA,UACA,KAAA,EAAO,aAAA,GACL,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;EAYpB,MAAA,CACL,EAAA,UACA,KAAA,EAAO,aAAA,EACP,IAAA,EAAM,YAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EAIpB,MAAA,CAAA,GAAU,OAAA;EAMV,IAAA,CACL,GAAA,UACA,IAAA,EAAM,aAAA,EACN,IAAA,EAAM,YAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;AAAA;;KA2CtB,aAAA,GAAgB,IAAA,CAAK,MAAA,CAAO,wBAAA;EA3CrB,kCA6CX,QAAA,EAAU,WAAA;AAAA;;UAIM,UAAA;EAhH8B;;;;EAqH9C,IAAA,EAAM,MAAA,CAAO,KAAA;EApHA;;;;;EA2Hb,YAAA,GAAe,MAAA,CAAO,KAAK;AAAA;;;;;;;;;cAWf,MAAA,SAAe,MAAA,CAAO,iBAAA;EAxFjC;EAAA,SA0Fe,EAAA,EAAI,MAAA,CAAO,MAAA;cAEf,IAAA,UAAc,IAAA,EAAM,UAAA,EAAY,IAAA,EAAM,aAAA;AAAA"}
|
package/dist/fly/app.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.mjs","names":[],"sources":["../../src/fly/app.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\n\nimport { FlyClient } from \"./client\";\nimport type { FlyProvider } from \"./provider\";\n\n/** Resolved inputs for the Fly app dynamic provider. */\ninterface FlyAppInputs {\n\t/** Fly API token. */\n\ttoken: string;\n\n\t/** App name (globally unique). Used as the resource identifier. */\n\tname: string;\n\n\t/** Org slug used only when creating a new app. */\n\torganization?: string;\n}\n\n/** Persisted state for the Fly app. */\ninterface FlyAppOutputs extends FlyAppInputs {\n\t/** App identifier — equals the app name (all child paths key off the name). */\n\tappId: string;\n}\n\n/** Get-app response (only the fields we read). */\ninterface FlyAppResponse {\n\tid: string;\n\tname: string;\n}\n\n/**\n * Dynamic provider implementing adopt-or-create for Fly apps.\n *\n * `create()` does `GET /v1/apps/{name}`; if found it adopts, otherwise it\n * `POST /v1/apps`. `delete()` is a no-op — deleting a Fly app destroys\n * everything in it, so (like Railway/Neon/Vercel top-level resources) Pulumi\n * does not delete apps.\n *\n * @internal Exported only for unit testing; not part of the public API surface.\n */\nexport class FlyAppResourceProvider implements pulumi.dynamic.ResourceProvider {\n\tasync create(inputs: FlyAppInputs): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst client = new FlyClient(inputs.token);\n\n\t\tconst existing = await client.tryGet<FlyAppResponse>(\n\t\t\t`/v1/apps/${inputs.name}`,\n\t\t);\n\n\t\tif (existing) {\n\t\t\tpulumi.log.info(`Adopting existing Fly app \"${inputs.name}\"`);\n\t\t} else {\n\t\t\tif (!inputs.organization) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`FlyApp \"${inputs.name}\": an organization is required to create a new app — set it on FlyProvider or FlyApp args`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tpulumi.log.info(`Fly app \"${inputs.name}\" not found — creating...`);\n\n\t\t\tawait client.post(\"/v1/apps\", {\n\t\t\t\tapp_name: inputs.name,\n\t\t\t\torg_slug: inputs.organization,\n\t\t\t});\n\t\t}\n\n\t\tconst outs: FlyAppOutputs = { ...inputs, appId: inputs.name };\n\n\t\treturn { id: inputs.name, outs };\n\t}\n\n\tasync read(\n\t\tid: string,\n\t\tprops: FlyAppOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\tconst client = new FlyClient(props.token);\n\t\tconst app = await client.tryGet<FlyAppResponse>(`/v1/apps/${id}`);\n\n\t\tif (!app) {\n\t\t\t// Resource gone → blank id lets refresh reconcile the deletion.\n\t\t\treturn {};\n\t\t}\n\n\t\treturn { id, props: { ...props, name: app.name, appId: app.name } };\n\t}\n\n\tasync update(\n\t\tid: string,\n\t\t_olds: FlyAppOutputs,\n\t\tnews: FlyAppInputs,\n\t): Promise<pulumi.dynamic.UpdateResult> {\n\t\treturn { outs: { ...news, appId: id } };\n\t}\n\n\tasync delete(): Promise<void> {\n\t\tpulumi.log.warn(\n\t\t\t\"Fly app deletion skipped — apps are not deleted by Pulumi (would destroy all contained resources)\",\n\t\t);\n\t}\n\n\tasync diff(\n\t\t_id: string,\n\t\tolds: FlyAppOutputs,\n\t\tnews: FlyAppInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst replaces: string[] = [];\n\n\t\tif (olds.name !== news.name) {\n\t\t\treplaces.push(\"name\");\n\t\t}\n\n\t\tif (olds.organization !== news.organization) {\n\t\t\treplaces.push(\"organization\");\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 FlyAppResource extends pulumi.dynamic.Resource {\n\tpublic declare readonly appId: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: {\n\t\t\ttoken: pulumi.Input<string>;\n\t\t\tname: pulumi.Input<string>;\n\t\t\torganization?: pulumi.Input<string | undefined>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\tsuper(\n\t\t\tnew FlyAppResourceProvider(),\n\t\t\tname,\n\t\t\t{ ...args, appId: undefined },\n\t\t\t// The API token flows into dynamic-provider state with the outputs — mark it secret there.\n\t\t\t{ ...opts, additionalSecretOutputs: [\"token\"] },\n\t\t);\n\t}\n}\n\n/** Options type for FlyApp — replaces Pulumi's native `provider` field. */\ntype FlyAppOptions = Omit<pulumi.ComponentResourceOptions, \"provider\"> & {\n\t/** Fly authentication context. */\n\tprovider: FlyProvider;\n};\n\n/** Args for FlyApp. */\nexport interface FlyAppArgs {\n\t
|
|
1
|
+
{"version":3,"file":"app.mjs","names":[],"sources":["../../src/fly/app.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\n\nimport { FlyClient } from \"./client\";\nimport type { FlyProvider } from \"./provider\";\n\n/** Resolved inputs for the Fly app dynamic provider. */\ninterface FlyAppInputs {\n\t/** Fly API token. */\n\ttoken: string;\n\n\t/** App name (globally unique). Used as the resource identifier. */\n\tname: string;\n\n\t/** Org slug used only when creating a new app. */\n\torganization?: string;\n}\n\n/** Persisted state for the Fly app. */\ninterface FlyAppOutputs extends FlyAppInputs {\n\t/** App identifier — equals the app name (all child paths key off the name). */\n\tappId: string;\n}\n\n/** Get-app response (only the fields we read). */\ninterface FlyAppResponse {\n\tid: string;\n\tname: string;\n}\n\n/**\n * Dynamic provider implementing adopt-or-create for Fly apps.\n *\n * `create()` does `GET /v1/apps/{name}`; if found it adopts, otherwise it\n * `POST /v1/apps`. `delete()` is a no-op — deleting a Fly app destroys\n * everything in it, so (like Railway/Neon/Vercel top-level resources) Pulumi\n * does not delete apps.\n *\n * @internal Exported only for unit testing; not part of the public API surface.\n */\nexport class FlyAppResourceProvider implements pulumi.dynamic.ResourceProvider {\n\tasync create(inputs: FlyAppInputs): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst client = new FlyClient(inputs.token);\n\n\t\tconst existing = await client.tryGet<FlyAppResponse>(\n\t\t\t`/v1/apps/${inputs.name}`,\n\t\t);\n\n\t\tif (existing) {\n\t\t\tpulumi.log.info(`Adopting existing Fly app \"${inputs.name}\"`);\n\t\t} else {\n\t\t\tif (!inputs.organization) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`FlyApp \"${inputs.name}\": an organization is required to create a new app — set it on FlyProvider or FlyApp args`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tpulumi.log.info(`Fly app \"${inputs.name}\" not found — creating...`);\n\n\t\t\tawait client.post(\"/v1/apps\", {\n\t\t\t\tapp_name: inputs.name,\n\t\t\t\torg_slug: inputs.organization,\n\t\t\t});\n\t\t}\n\n\t\tconst outs: FlyAppOutputs = { ...inputs, appId: inputs.name };\n\n\t\treturn { id: inputs.name, outs };\n\t}\n\n\tasync read(\n\t\tid: string,\n\t\tprops: FlyAppOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\tconst client = new FlyClient(props.token);\n\t\tconst app = await client.tryGet<FlyAppResponse>(`/v1/apps/${id}`);\n\n\t\tif (!app) {\n\t\t\t// Resource gone → blank id lets refresh reconcile the deletion.\n\t\t\treturn {};\n\t\t}\n\n\t\treturn { id, props: { ...props, name: app.name, appId: app.name } };\n\t}\n\n\tasync update(\n\t\tid: string,\n\t\t_olds: FlyAppOutputs,\n\t\tnews: FlyAppInputs,\n\t): Promise<pulumi.dynamic.UpdateResult> {\n\t\treturn { outs: { ...news, appId: id } };\n\t}\n\n\tasync delete(): Promise<void> {\n\t\tpulumi.log.warn(\n\t\t\t\"Fly app deletion skipped — apps are not deleted by Pulumi (would destroy all contained resources)\",\n\t\t);\n\t}\n\n\tasync diff(\n\t\t_id: string,\n\t\tolds: FlyAppOutputs,\n\t\tnews: FlyAppInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst replaces: string[] = [];\n\n\t\tif (olds.name !== news.name) {\n\t\t\treplaces.push(\"name\");\n\t\t}\n\n\t\tif (olds.organization !== news.organization) {\n\t\t\treplaces.push(\"organization\");\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 FlyAppResource extends pulumi.dynamic.Resource {\n\tpublic declare readonly appId: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: {\n\t\t\ttoken: pulumi.Input<string>;\n\t\t\tname: pulumi.Input<string>;\n\t\t\torganization?: pulumi.Input<string | undefined>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\tsuper(\n\t\t\tnew FlyAppResourceProvider(),\n\t\t\tname,\n\t\t\t{ ...args, appId: undefined },\n\t\t\t// The API token flows into dynamic-provider state with the outputs — mark it secret there.\n\t\t\t{ ...opts, additionalSecretOutputs: [\"token\"] },\n\t\t);\n\t}\n}\n\n/** Options type for FlyApp — replaces Pulumi's native `provider` field. */\ntype FlyAppOptions = Omit<pulumi.ComponentResourceOptions, \"provider\"> & {\n\t/** Fly authentication context. */\n\tprovider: FlyProvider;\n};\n\n/** Args for FlyApp. */\nexport interface FlyAppArgs {\n\t/**\n\t * App name (globally unique). Used for adoption lookup and as `.id`.\n\t * Maps to the Fly Machines API field `app_name`.\n\t */\n\tname: pulumi.Input<string>;\n\n\t/**\n\t * Org slug for app creation. Overrides `FlyProvider.organization`.\n\t * Ignored when the app already exists (adoption).\n\t * Maps to the Fly Machines API field `org_slug`.\n\t */\n\torganization?: pulumi.Input<string>;\n}\n\n/**\n * Manages a Fly app with adopt-or-create semantics.\n *\n * @example\n * ```typescript\n * const app = new FlyApp(\"api\", { name: \"rby-api\" }, { provider });\n * ```\n */\nexport class FlyApp extends pulumi.ComponentResource {\n\t/** App identifier (equals the app name). */\n\tpublic readonly id: pulumi.Output<string>;\n\n\tconstructor(name: string, args: FlyAppArgs, opts: FlyAppOptions) {\n\t\tconst { provider, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:fly:App\", name, {}, pulumiOpts);\n\n\t\tconst resource = new FlyAppResource(\n\t\t\t`${name}-resource`,\n\t\t\t{\n\t\t\t\ttoken: provider.token,\n\t\t\t\tname: args.name,\n\t\t\t\torganization: args.organization ?? provider.organization,\n\t\t\t},\n\t\t\t{ parent: this },\n\t\t);\n\n\t\tthis.id = resource.appId;\n\n\t\tthis.registerOutputs({ id: this.id });\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;AAuCA,IAAa,yBAAb,MAA+E;CAC9E,MAAM,OAAO,QAA4D;EACxE,MAAM,SAAS,IAAI,UAAU,OAAO,KAAK;EAMzC,IAAI,MAJmB,OAAO,OAC7B,YAAY,OAAO,MACpB,GAGC,OAAO,IAAI,KAAK,8BAA8B,OAAO,KAAK,EAAE;OACtD;GACN,IAAI,CAAC,OAAO,cACX,MAAM,IAAI,MACT,WAAW,OAAO,KAAK,0FACxB;GAGD,OAAO,IAAI,KAAK,YAAY,OAAO,KAAK,0BAA0B;GAElE,MAAM,OAAO,KAAK,YAAY;IAC7B,UAAU,OAAO;IACjB,UAAU,OAAO;GAClB,CAAC;EACF;EAEA,MAAM,OAAsB;GAAE,GAAG;GAAQ,OAAO,OAAO;EAAK;EAE5D,OAAO;GAAE,IAAI,OAAO;GAAM;EAAK;CAChC;CAEA,MAAM,KACL,IACA,OACqC;EAErC,MAAM,MAAM,MAAM,IADC,UAAU,MAAM,KACZ,EAAE,OAAuB,YAAY,IAAI;EAEhE,IAAI,CAAC,KAEJ,OAAO,CAAC;EAGT,OAAO;GAAE;GAAI,OAAO;IAAE,GAAG;IAAO,MAAM,IAAI;IAAM,OAAO,IAAI;GAAK;EAAE;CACnE;CAEA,MAAM,OACL,IACA,OACA,MACuC;EACvC,OAAO,EAAE,MAAM;GAAE,GAAG;GAAM,OAAO;EAAG,EAAE;CACvC;CAEA,MAAM,SAAwB;EAC7B,OAAO,IAAI,KACV,mGACD;CACD;CAEA,MAAM,KACL,KACA,MACA,MACqC;EACrC,MAAM,WAAqB,CAAC;EAE5B,IAAI,KAAK,SAAS,KAAK,MACtB,SAAS,KAAK,MAAM;EAGrB,IAAI,KAAK,iBAAiB,KAAK,cAC9B,SAAS,KAAK,cAAc;EAG7B,OAAO;GACN,SAAS,SAAS,SAAS;GAC3B;GACA,qBAAqB;EACtB;CACD;AACD;;AAGA,IAAM,iBAAN,cAA6B,OAAO,QAAQ,SAAS;CAGpD,YACC,MACA,MAKA,MACC;EACD,MACC,IAAI,uBAAuB,GAC3B,MACA;GAAE,GAAG;GAAM,OAAO;EAAU,GAE5B;GAAE,GAAG;GAAM,yBAAyB,CAAC,OAAO;EAAE,CAC/C;CACD;AACD;;;;;;;;;AAgCA,IAAa,SAAb,cAA4B,OAAO,kBAAkB;CAIpD,YAAY,MAAc,MAAkB,MAAqB;EAChE,MAAM,EAAE,UAAU,GAAG,eAAe;EAEpC,MAAM,sBAAsB,MAAM,CAAC,GAAG,UAAU;EAEhD,MAAM,WAAW,IAAI,eACpB,GAAG,KAAK,YACR;GACC,OAAO,SAAS;GAChB,MAAM,KAAK;GACX,cAAc,KAAK,gBAAgB,SAAS;EAC7C,GACA,EAAE,QAAQ,KAAK,CAChB;EAEA,KAAK,KAAK,SAAS;EAEnB,KAAK,gBAAgB,EAAE,IAAI,KAAK,GAAG,CAAC;CACrC;AACD"}
|
package/dist/fly/secret.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"secret.cjs","names":["FlyClient","ApiNotFoundError","pulumi"],"sources":["../../src/fly/secret.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\n\nimport { ApiNotFoundError } from \"../errors/api-not-found-error\";\nimport type { FlyApp } from \"./app\";\nimport { FlyClient } from \"./client\";\nimport type { FlyProvider } from \"./provider\";\n\n/** Resolved inputs for the Fly secret dynamic provider. */\ninterface FlySecretInputs {\n\t/** Fly API token. */\n\ttoken: string;\n\n\t/** App name the secrets belong to. */\n\tappName: string;\n\n\t/** Secret key/value pairs to set on the app. */\n\tsecrets: Record<string, string>;\n}\n\n/** Persisted state for Fly secrets. */\ninterface FlySecretOutputs extends FlySecretInputs {\n\t/** Fly secrets version (uint64, stored as string). Changes on every mutation. */\n\tversion: string;\n}\n\n/** Response shape of the bulk secrets endpoint. */\ninterface UpdateSecretsResponse {\n\tversion: number;\n}\n\n/**\n * POSTs a `values` map to `/v1/apps/{app}/secrets`. Keys with `null` values are\n * deleted; keys with string values are set. Returns the new version as a string.\n */\nasync function applySecrets(\n\tclient: FlyClient,\n\tappName: string,\n\tvalues: Record<string, string | null>,\n): Promise<string> {\n\tconst response = await client.post<UpdateSecretsResponse>(\n\t\t`/v1/apps/${appName}/secrets`,\n\t\t{ values },\n\t);\n\n\treturn String(response.version);\n}\n\n/**\n * Dynamic provider for Fly app secrets via the Machines REST bulk endpoint.\n *\n * Secret values are stored in state (required to diff them) and wrapped with\n * `pulumi.secret()` by the public resource so they are encrypted at rest.\n * Setting secrets only takes effect on the next machine restart — wire\n * `FlySecret.version` into `FlyDeploy.triggers` to force a redeploy on change.\n */\nclass FlySecretResourceProvider implements pulumi.dynamic.ResourceProvider {\n\tasync create(inputs: FlySecretInputs): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst client = new FlyClient(inputs.token);\n\t\tconst version = await applySecrets(client, inputs.appName, inputs.secrets);\n\n\t\tconst outs: FlySecretOutputs = { ...inputs, version };\n\n\t\treturn { id: `${inputs.appName}-secrets`, outs };\n\t}\n\n\tasync read(\n\t\tid: string,\n\t\tprops: FlySecretOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\t// Values are write-only (the API returns digests, not plaintext), so we\n\t\t// keep the desired state as the source of truth on refresh.\n\t\treturn { id, props };\n\t}\n\n\tasync update(\n\t\t_id: string,\n\t\tolds: FlySecretOutputs,\n\t\tnews: FlySecretInputs,\n\t): Promise<pulumi.dynamic.UpdateResult> {\n\t\tconst client = new FlyClient(news.token);\n\n\t\tconst values: Record<string, string | null> = { ...news.secrets };\n\n\t\tfor (const key of Object.keys(olds.secrets)) {\n\t\t\tif (!(key in news.secrets)) {\n\t\t\t\tvalues[key] = null;\n\t\t\t}\n\t\t}\n\n\t\tconst version = await applySecrets(client, news.appName, values);\n\n\t\treturn { outs: { ...news, version } };\n\t}\n\n\tasync delete(_id: string, props: FlySecretOutputs): Promise<void> {\n\t\tconst client = new FlyClient(props.token);\n\n\t\tconst values: Record<string, string | null> = {};\n\n\t\tfor (const key of Object.keys(props.secrets)) {\n\t\t\tvalues[key] = null;\n\t\t}\n\n\t\ttry {\n\t\t\tawait applySecrets(client, props.appName, values);\n\t\t} catch (error) {\n\t\t\t// App (and its secrets) already gone — deletion is idempotent.\n\t\t\tif (error instanceof ApiNotFoundError) {\n\t\t\t\tpulumi.log.warn(\n\t\t\t\t\t`Fly app \"${props.appName}\" already deleted — nothing to unset`,\n\t\t\t\t);\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync diff(\n\t\t_id: string,\n\t\tolds: FlySecretOutputs,\n\t\tnews: FlySecretInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst replaces: string[] = [];\n\n\t\tif (olds.appName !== news.appName) {\n\t\t\treplaces.push(\"appName\");\n\t\t}\n\n\t\tconst oldKeys = Object.keys(olds.secrets).sort().join(\",\");\n\t\tconst newKeys = Object.keys(news.secrets).sort().join(\",\");\n\n\t\tconst valuesChanged = Object.entries(news.secrets).some(\n\t\t\t([key, value]) => olds.secrets[key] !== value,\n\t\t);\n\n\t\treturn {\n\t\t\tchanges: replaces.length > 0 || oldKeys !== newKeys || valuesChanged,\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 FlySecretResource extends pulumi.dynamic.Resource {\n\tpublic declare readonly version: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: {\n\t\t\ttoken: pulumi.Input<string>;\n\t\t\tappName: pulumi.Input<string>;\n\t\t\tsecrets: pulumi.Input<Record<string, string>>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\tsuper(\n\t\t\tnew FlySecretResourceProvider(),\n\t\t\tname,\n\t\t\t{ ...args, version: undefined },\n\t\t\t// The API token flows into dynamic-provider state with the outputs — mark it secret there.\n\t\t\t{ ...opts, additionalSecretOutputs: [\"token\"] },\n\t\t);\n\t}\n}\n\n/** Options type for FlySecret. */\ntype FlySecretOptions = Omit<pulumi.ComponentResourceOptions, \"provider\"> & {\n\t/** Fly authentication context. */\n\tprovider: FlyProvider;\n\n\t/** App the secrets belong to. */\n\tapp: FlyApp;\n};\n\n/** Args for FlySecret. */\nexport interface FlySecretArgs {\n\t
|
|
1
|
+
{"version":3,"file":"secret.cjs","names":["FlyClient","ApiNotFoundError","pulumi"],"sources":["../../src/fly/secret.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\n\nimport { ApiNotFoundError } from \"../errors/api-not-found-error\";\nimport type { FlyApp } from \"./app\";\nimport { FlyClient } from \"./client\";\nimport type { FlyProvider } from \"./provider\";\n\n/** Resolved inputs for the Fly secret dynamic provider. */\ninterface FlySecretInputs {\n\t/** Fly API token. */\n\ttoken: string;\n\n\t/** App name the secrets belong to. */\n\tappName: string;\n\n\t/** Secret key/value pairs to set on the app. */\n\tsecrets: Record<string, string>;\n}\n\n/** Persisted state for Fly secrets. */\ninterface FlySecretOutputs extends FlySecretInputs {\n\t/** Fly secrets version (uint64, stored as string). Changes on every mutation. */\n\tversion: string;\n}\n\n/** Response shape of the bulk secrets endpoint. */\ninterface UpdateSecretsResponse {\n\tversion: number;\n}\n\n/**\n * POSTs a `values` map to `/v1/apps/{app}/secrets`. Keys with `null` values are\n * deleted; keys with string values are set. Returns the new version as a string.\n */\nasync function applySecrets(\n\tclient: FlyClient,\n\tappName: string,\n\tvalues: Record<string, string | null>,\n): Promise<string> {\n\tconst response = await client.post<UpdateSecretsResponse>(\n\t\t`/v1/apps/${appName}/secrets`,\n\t\t{ values },\n\t);\n\n\treturn String(response.version);\n}\n\n/**\n * Dynamic provider for Fly app secrets via the Machines REST bulk endpoint.\n *\n * Secret values are stored in state (required to diff them) and wrapped with\n * `pulumi.secret()` by the public resource so they are encrypted at rest.\n * Setting secrets only takes effect on the next machine restart — wire\n * `FlySecret.version` into `FlyDeploy.triggers` to force a redeploy on change.\n */\nclass FlySecretResourceProvider implements pulumi.dynamic.ResourceProvider {\n\tasync create(inputs: FlySecretInputs): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst client = new FlyClient(inputs.token);\n\t\tconst version = await applySecrets(client, inputs.appName, inputs.secrets);\n\n\t\tconst outs: FlySecretOutputs = { ...inputs, version };\n\n\t\treturn { id: `${inputs.appName}-secrets`, outs };\n\t}\n\n\tasync read(\n\t\tid: string,\n\t\tprops: FlySecretOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\t// Values are write-only (the API returns digests, not plaintext), so we\n\t\t// keep the desired state as the source of truth on refresh.\n\t\treturn { id, props };\n\t}\n\n\tasync update(\n\t\t_id: string,\n\t\tolds: FlySecretOutputs,\n\t\tnews: FlySecretInputs,\n\t): Promise<pulumi.dynamic.UpdateResult> {\n\t\tconst client = new FlyClient(news.token);\n\n\t\tconst values: Record<string, string | null> = { ...news.secrets };\n\n\t\tfor (const key of Object.keys(olds.secrets)) {\n\t\t\tif (!(key in news.secrets)) {\n\t\t\t\tvalues[key] = null;\n\t\t\t}\n\t\t}\n\n\t\tconst version = await applySecrets(client, news.appName, values);\n\n\t\treturn { outs: { ...news, version } };\n\t}\n\n\tasync delete(_id: string, props: FlySecretOutputs): Promise<void> {\n\t\tconst client = new FlyClient(props.token);\n\n\t\tconst values: Record<string, string | null> = {};\n\n\t\tfor (const key of Object.keys(props.secrets)) {\n\t\t\tvalues[key] = null;\n\t\t}\n\n\t\ttry {\n\t\t\tawait applySecrets(client, props.appName, values);\n\t\t} catch (error) {\n\t\t\t// App (and its secrets) already gone — deletion is idempotent.\n\t\t\tif (error instanceof ApiNotFoundError) {\n\t\t\t\tpulumi.log.warn(\n\t\t\t\t\t`Fly app \"${props.appName}\" already deleted — nothing to unset`,\n\t\t\t\t);\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync diff(\n\t\t_id: string,\n\t\tolds: FlySecretOutputs,\n\t\tnews: FlySecretInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst replaces: string[] = [];\n\n\t\tif (olds.appName !== news.appName) {\n\t\t\treplaces.push(\"appName\");\n\t\t}\n\n\t\tconst oldKeys = Object.keys(olds.secrets).sort().join(\",\");\n\t\tconst newKeys = Object.keys(news.secrets).sort().join(\",\");\n\n\t\tconst valuesChanged = Object.entries(news.secrets).some(\n\t\t\t([key, value]) => olds.secrets[key] !== value,\n\t\t);\n\n\t\treturn {\n\t\t\tchanges: replaces.length > 0 || oldKeys !== newKeys || valuesChanged,\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 FlySecretResource extends pulumi.dynamic.Resource {\n\tpublic declare readonly version: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: {\n\t\t\ttoken: pulumi.Input<string>;\n\t\t\tappName: pulumi.Input<string>;\n\t\t\tsecrets: pulumi.Input<Record<string, string>>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\tsuper(\n\t\t\tnew FlySecretResourceProvider(),\n\t\t\tname,\n\t\t\t{ ...args, version: undefined },\n\t\t\t// The API token flows into dynamic-provider state with the outputs — mark it secret there.\n\t\t\t{ ...opts, additionalSecretOutputs: [\"token\"] },\n\t\t);\n\t}\n}\n\n/** Options type for FlySecret. */\ntype FlySecretOptions = Omit<pulumi.ComponentResourceOptions, \"provider\"> & {\n\t/** Fly authentication context. */\n\tprovider: FlyProvider;\n\n\t/** App the secrets belong to. */\n\tapp: FlyApp;\n};\n\n/** Args for FlySecret. */\nexport interface FlySecretArgs {\n\t/**\n\t * Secret key/value pairs to set on the app.\n\t * Maps to the bulk secrets endpoint's `values` map (where a `null` value deletes a key).\n\t */\n\tsecrets: pulumi.Input<Record<string, string>>;\n}\n\n/**\n * Manages an app's Fly secrets as a single resource.\n *\n * Exposes `.version`, which changes only when the secret set changes — feed it\n * into `FlyDeploy.triggers` so a redeploy fires when secrets change.\n *\n * @example\n * ```typescript\n * const secrets = new FlySecret(\"api-secrets\", {\n * secrets: { JWT_SECRET: jwt, DATABASE_URL: dbUrl },\n * }, { provider, app });\n * ```\n */\nexport class FlySecret extends pulumi.ComponentResource {\n\t/** Fly secrets version. Changes only when the secret set changes. */\n\tpublic readonly version: pulumi.Output<string>;\n\n\tconstructor(name: string, args: FlySecretArgs, opts: FlySecretOptions) {\n\t\tconst { provider, app, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:fly:Secret\", name, {}, pulumiOpts);\n\n\t\tconst resource = new FlySecretResource(\n\t\t\t`${name}-resource`,\n\t\t\t{\n\t\t\t\ttoken: provider.token,\n\t\t\t\tappName: app.id,\n\t\t\t\tsecrets: pulumi.secret(args.secrets),\n\t\t\t},\n\t\t\t{ parent: this },\n\t\t);\n\n\t\tthis.version = resource.version;\n\n\t\tthis.registerOutputs({ version: this.version });\n\t}\n}\n"],"mappings":";;;;;;;;;;;;AAkCA,eAAe,aACd,QACA,SACA,QACkB;CAClB,MAAM,WAAW,MAAM,OAAO,KAC7B,YAAY,QAAQ,WACpB,EAAE,OAAO,CACV;CAEA,OAAO,OAAO,SAAS,OAAO;AAC/B;;;;;;;;;AAUA,IAAM,4BAAN,MAA2E;CAC1E,MAAM,OAAO,QAA+D;EAE3E,MAAM,UAAU,MAAM,aAAa,IADhBA,6BAAU,OAAO,KACI,GAAG,OAAO,SAAS,OAAO,OAAO;EAEzE,MAAM,OAAyB;GAAE,GAAG;GAAQ;EAAQ;EAEpD,OAAO;GAAE,IAAI,GAAG,OAAO,QAAQ;GAAW;EAAK;CAChD;CAEA,MAAM,KACL,IACA,OACqC;EAGrC,OAAO;GAAE;GAAI;EAAM;CACpB;CAEA,MAAM,OACL,KACA,MACA,MACuC;EACvC,MAAM,SAAS,IAAIA,6BAAU,KAAK,KAAK;EAEvC,MAAM,SAAwC,EAAE,GAAG,KAAK,QAAQ;EAEhE,KAAK,MAAM,OAAO,OAAO,KAAK,KAAK,OAAO,GACzC,IAAI,EAAE,OAAO,KAAK,UACjB,OAAO,OAAO;EAIhB,MAAM,UAAU,MAAM,aAAa,QAAQ,KAAK,SAAS,MAAM;EAE/D,OAAO,EAAE,MAAM;GAAE,GAAG;GAAM;EAAQ,EAAE;CACrC;CAEA,MAAM,OAAO,KAAa,OAAwC;EACjE,MAAM,SAAS,IAAIA,6BAAU,MAAM,KAAK;EAExC,MAAM,SAAwC,CAAC;EAE/C,KAAK,MAAM,OAAO,OAAO,KAAK,MAAM,OAAO,GAC1C,OAAO,OAAO;EAGf,IAAI;GACH,MAAM,aAAa,QAAQ,MAAM,SAAS,MAAM;EACjD,SAAS,OAAO;GAEf,IAAI,iBAAiBC,qDAAkB;IACtC,eAAO,IAAI,KACV,YAAY,MAAM,QAAQ,qCAC3B;IAEA;GACD;GAEA,MAAM;EACP;CACD;CAEA,MAAM,KACL,KACA,MACA,MACqC;EACrC,MAAM,WAAqB,CAAC;EAE5B,IAAI,KAAK,YAAY,KAAK,SACzB,SAAS,KAAK,SAAS;EAGxB,MAAM,UAAU,OAAO,KAAK,KAAK,OAAO,EAAE,KAAK,EAAE,KAAK,GAAG;EACzD,MAAM,UAAU,OAAO,KAAK,KAAK,OAAO,EAAE,KAAK,EAAE,KAAK,GAAG;EAEzD,MAAM,gBAAgB,OAAO,QAAQ,KAAK,OAAO,EAAE,MACjD,CAAC,KAAK,WAAW,KAAK,QAAQ,SAAS,KACzC;EAEA,OAAO;GACN,SAAS,SAAS,SAAS,KAAK,YAAY,WAAW;GACvD;GACA,qBAAqB;EACtB;CACD;AACD;;AAGA,IAAM,oBAAN,cAAgCC,eAAO,QAAQ,SAAS;CAGvD,YACC,MACA,MAKA,MACC;EACD,MACC,IAAI,0BAA0B,GAC9B,MACA;GAAE,GAAG;GAAM,SAAS;EAAU,GAE9B;GAAE,GAAG;GAAM,yBAAyB,CAAC,OAAO;EAAE,CAC/C;CACD;AACD;;;;;;;;;;;;;;AAiCA,IAAa,YAAb,cAA+BA,eAAO,kBAAkB;CAIvD,YAAY,MAAc,MAAqB,MAAwB;EACtE,MAAM,EAAE,UAAU,KAAK,GAAG,eAAe;EAEzC,MAAM,yBAAyB,MAAM,CAAC,GAAG,UAAU;EAEnD,MAAM,WAAW,IAAI,kBACpB,GAAG,KAAK,YACR;GACC,OAAO,SAAS;GAChB,SAAS,IAAI;GACb,SAASA,eAAO,OAAO,KAAK,OAAO;EACpC,GACA,EAAE,QAAQ,KAAK,CAChB;EAEA,KAAK,UAAU,SAAS;EAExB,KAAK,gBAAgB,EAAE,SAAS,KAAK,QAAQ,CAAC;CAC/C;AACD"}
|
package/dist/fly/secret.d.cts
CHANGED
|
@@ -11,7 +11,10 @@ type FlySecretOptions = Omit<pulumi.ComponentResourceOptions, "provider"> & {
|
|
|
11
11
|
};
|
|
12
12
|
/** Args for FlySecret. */
|
|
13
13
|
interface FlySecretArgs {
|
|
14
|
-
/**
|
|
14
|
+
/**
|
|
15
|
+
* Secret key/value pairs to set on the app.
|
|
16
|
+
* Maps to the bulk secrets endpoint's `values` map (where a `null` value deletes a key).
|
|
17
|
+
*/
|
|
15
18
|
secrets: pulumi.Input<Record<string, string>>;
|
|
16
19
|
}
|
|
17
20
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"secret.d.cts","names":[],"sources":["../../src/fly/secret.ts"],"mappings":";;;;;;;KAyKK,gBAAA,GAAmB,IAAA,CAAK,MAAA,CAAO,wBAAA;oCAEnC,QAAA,EAAU,WAAA,EAFU;EAKpB,GAAA,EAAK,MAAA;AAAA;;UAIW,aAAA;EAJX
|
|
1
|
+
{"version":3,"file":"secret.d.cts","names":[],"sources":["../../src/fly/secret.ts"],"mappings":";;;;;;;KAyKK,gBAAA,GAAmB,IAAA,CAAK,MAAA,CAAO,wBAAA;oCAEnC,QAAA,EAAU,WAAA,EAFU;EAKpB,GAAA,EAAK,MAAA;AAAA;;UAIW,aAAA;EAJX;;;;EASL,OAAA,EAAS,MAAA,CAAO,KAAK,CAAC,MAAA;AAAA;;;;;;AATX;AAIZ;;;;;;;cAqBa,SAAA,SAAkB,MAAA,CAAO,iBAAA;EAhBT;EAAA,SAkBZ,OAAA,EAAS,MAAA,CAAO,MAAA;cAEpB,IAAA,UAAc,IAAA,EAAM,aAAA,EAAe,IAAA,EAAM,gBAAA;AAAA"}
|
package/dist/fly/secret.d.mts
CHANGED
|
@@ -11,7 +11,10 @@ type FlySecretOptions = Omit<pulumi.ComponentResourceOptions, "provider"> & {
|
|
|
11
11
|
};
|
|
12
12
|
/** Args for FlySecret. */
|
|
13
13
|
interface FlySecretArgs {
|
|
14
|
-
/**
|
|
14
|
+
/**
|
|
15
|
+
* Secret key/value pairs to set on the app.
|
|
16
|
+
* Maps to the bulk secrets endpoint's `values` map (where a `null` value deletes a key).
|
|
17
|
+
*/
|
|
15
18
|
secrets: pulumi.Input<Record<string, string>>;
|
|
16
19
|
}
|
|
17
20
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"secret.d.mts","names":[],"sources":["../../src/fly/secret.ts"],"mappings":";;;;;;;KAyKK,gBAAA,GAAmB,IAAA,CAAK,MAAA,CAAO,wBAAA;oCAEnC,QAAA,EAAU,WAAA,EAFU;EAKpB,GAAA,EAAK,MAAA;AAAA;;UAIW,aAAA;EAJX
|
|
1
|
+
{"version":3,"file":"secret.d.mts","names":[],"sources":["../../src/fly/secret.ts"],"mappings":";;;;;;;KAyKK,gBAAA,GAAmB,IAAA,CAAK,MAAA,CAAO,wBAAA;oCAEnC,QAAA,EAAU,WAAA,EAFU;EAKpB,GAAA,EAAK,MAAA;AAAA;;UAIW,aAAA;EAJX;;;;EASL,OAAA,EAAS,MAAA,CAAO,KAAK,CAAC,MAAA;AAAA;;;;;;AATX;AAIZ;;;;;;;cAqBa,SAAA,SAAkB,MAAA,CAAO,iBAAA;EAhBT;EAAA,SAkBZ,OAAA,EAAS,MAAA,CAAO,MAAA;cAEpB,IAAA,UAAc,IAAA,EAAM,aAAA,EAAe,IAAA,EAAM,gBAAA;AAAA"}
|
package/dist/fly/secret.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"secret.mjs","names":[],"sources":["../../src/fly/secret.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\n\nimport { ApiNotFoundError } from \"../errors/api-not-found-error\";\nimport type { FlyApp } from \"./app\";\nimport { FlyClient } from \"./client\";\nimport type { FlyProvider } from \"./provider\";\n\n/** Resolved inputs for the Fly secret dynamic provider. */\ninterface FlySecretInputs {\n\t/** Fly API token. */\n\ttoken: string;\n\n\t/** App name the secrets belong to. */\n\tappName: string;\n\n\t/** Secret key/value pairs to set on the app. */\n\tsecrets: Record<string, string>;\n}\n\n/** Persisted state for Fly secrets. */\ninterface FlySecretOutputs extends FlySecretInputs {\n\t/** Fly secrets version (uint64, stored as string). Changes on every mutation. */\n\tversion: string;\n}\n\n/** Response shape of the bulk secrets endpoint. */\ninterface UpdateSecretsResponse {\n\tversion: number;\n}\n\n/**\n * POSTs a `values` map to `/v1/apps/{app}/secrets`. Keys with `null` values are\n * deleted; keys with string values are set. Returns the new version as a string.\n */\nasync function applySecrets(\n\tclient: FlyClient,\n\tappName: string,\n\tvalues: Record<string, string | null>,\n): Promise<string> {\n\tconst response = await client.post<UpdateSecretsResponse>(\n\t\t`/v1/apps/${appName}/secrets`,\n\t\t{ values },\n\t);\n\n\treturn String(response.version);\n}\n\n/**\n * Dynamic provider for Fly app secrets via the Machines REST bulk endpoint.\n *\n * Secret values are stored in state (required to diff them) and wrapped with\n * `pulumi.secret()` by the public resource so they are encrypted at rest.\n * Setting secrets only takes effect on the next machine restart — wire\n * `FlySecret.version` into `FlyDeploy.triggers` to force a redeploy on change.\n */\nclass FlySecretResourceProvider implements pulumi.dynamic.ResourceProvider {\n\tasync create(inputs: FlySecretInputs): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst client = new FlyClient(inputs.token);\n\t\tconst version = await applySecrets(client, inputs.appName, inputs.secrets);\n\n\t\tconst outs: FlySecretOutputs = { ...inputs, version };\n\n\t\treturn { id: `${inputs.appName}-secrets`, outs };\n\t}\n\n\tasync read(\n\t\tid: string,\n\t\tprops: FlySecretOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\t// Values are write-only (the API returns digests, not plaintext), so we\n\t\t// keep the desired state as the source of truth on refresh.\n\t\treturn { id, props };\n\t}\n\n\tasync update(\n\t\t_id: string,\n\t\tolds: FlySecretOutputs,\n\t\tnews: FlySecretInputs,\n\t): Promise<pulumi.dynamic.UpdateResult> {\n\t\tconst client = new FlyClient(news.token);\n\n\t\tconst values: Record<string, string | null> = { ...news.secrets };\n\n\t\tfor (const key of Object.keys(olds.secrets)) {\n\t\t\tif (!(key in news.secrets)) {\n\t\t\t\tvalues[key] = null;\n\t\t\t}\n\t\t}\n\n\t\tconst version = await applySecrets(client, news.appName, values);\n\n\t\treturn { outs: { ...news, version } };\n\t}\n\n\tasync delete(_id: string, props: FlySecretOutputs): Promise<void> {\n\t\tconst client = new FlyClient(props.token);\n\n\t\tconst values: Record<string, string | null> = {};\n\n\t\tfor (const key of Object.keys(props.secrets)) {\n\t\t\tvalues[key] = null;\n\t\t}\n\n\t\ttry {\n\t\t\tawait applySecrets(client, props.appName, values);\n\t\t} catch (error) {\n\t\t\t// App (and its secrets) already gone — deletion is idempotent.\n\t\t\tif (error instanceof ApiNotFoundError) {\n\t\t\t\tpulumi.log.warn(\n\t\t\t\t\t`Fly app \"${props.appName}\" already deleted — nothing to unset`,\n\t\t\t\t);\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync diff(\n\t\t_id: string,\n\t\tolds: FlySecretOutputs,\n\t\tnews: FlySecretInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst replaces: string[] = [];\n\n\t\tif (olds.appName !== news.appName) {\n\t\t\treplaces.push(\"appName\");\n\t\t}\n\n\t\tconst oldKeys = Object.keys(olds.secrets).sort().join(\",\");\n\t\tconst newKeys = Object.keys(news.secrets).sort().join(\",\");\n\n\t\tconst valuesChanged = Object.entries(news.secrets).some(\n\t\t\t([key, value]) => olds.secrets[key] !== value,\n\t\t);\n\n\t\treturn {\n\t\t\tchanges: replaces.length > 0 || oldKeys !== newKeys || valuesChanged,\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 FlySecretResource extends pulumi.dynamic.Resource {\n\tpublic declare readonly version: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: {\n\t\t\ttoken: pulumi.Input<string>;\n\t\t\tappName: pulumi.Input<string>;\n\t\t\tsecrets: pulumi.Input<Record<string, string>>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\tsuper(\n\t\t\tnew FlySecretResourceProvider(),\n\t\t\tname,\n\t\t\t{ ...args, version: undefined },\n\t\t\t// The API token flows into dynamic-provider state with the outputs — mark it secret there.\n\t\t\t{ ...opts, additionalSecretOutputs: [\"token\"] },\n\t\t);\n\t}\n}\n\n/** Options type for FlySecret. */\ntype FlySecretOptions = Omit<pulumi.ComponentResourceOptions, \"provider\"> & {\n\t/** Fly authentication context. */\n\tprovider: FlyProvider;\n\n\t/** App the secrets belong to. */\n\tapp: FlyApp;\n};\n\n/** Args for FlySecret. */\nexport interface FlySecretArgs {\n\t
|
|
1
|
+
{"version":3,"file":"secret.mjs","names":[],"sources":["../../src/fly/secret.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\n\nimport { ApiNotFoundError } from \"../errors/api-not-found-error\";\nimport type { FlyApp } from \"./app\";\nimport { FlyClient } from \"./client\";\nimport type { FlyProvider } from \"./provider\";\n\n/** Resolved inputs for the Fly secret dynamic provider. */\ninterface FlySecretInputs {\n\t/** Fly API token. */\n\ttoken: string;\n\n\t/** App name the secrets belong to. */\n\tappName: string;\n\n\t/** Secret key/value pairs to set on the app. */\n\tsecrets: Record<string, string>;\n}\n\n/** Persisted state for Fly secrets. */\ninterface FlySecretOutputs extends FlySecretInputs {\n\t/** Fly secrets version (uint64, stored as string). Changes on every mutation. */\n\tversion: string;\n}\n\n/** Response shape of the bulk secrets endpoint. */\ninterface UpdateSecretsResponse {\n\tversion: number;\n}\n\n/**\n * POSTs a `values` map to `/v1/apps/{app}/secrets`. Keys with `null` values are\n * deleted; keys with string values are set. Returns the new version as a string.\n */\nasync function applySecrets(\n\tclient: FlyClient,\n\tappName: string,\n\tvalues: Record<string, string | null>,\n): Promise<string> {\n\tconst response = await client.post<UpdateSecretsResponse>(\n\t\t`/v1/apps/${appName}/secrets`,\n\t\t{ values },\n\t);\n\n\treturn String(response.version);\n}\n\n/**\n * Dynamic provider for Fly app secrets via the Machines REST bulk endpoint.\n *\n * Secret values are stored in state (required to diff them) and wrapped with\n * `pulumi.secret()` by the public resource so they are encrypted at rest.\n * Setting secrets only takes effect on the next machine restart — wire\n * `FlySecret.version` into `FlyDeploy.triggers` to force a redeploy on change.\n */\nclass FlySecretResourceProvider implements pulumi.dynamic.ResourceProvider {\n\tasync create(inputs: FlySecretInputs): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst client = new FlyClient(inputs.token);\n\t\tconst version = await applySecrets(client, inputs.appName, inputs.secrets);\n\n\t\tconst outs: FlySecretOutputs = { ...inputs, version };\n\n\t\treturn { id: `${inputs.appName}-secrets`, outs };\n\t}\n\n\tasync read(\n\t\tid: string,\n\t\tprops: FlySecretOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\t// Values are write-only (the API returns digests, not plaintext), so we\n\t\t// keep the desired state as the source of truth on refresh.\n\t\treturn { id, props };\n\t}\n\n\tasync update(\n\t\t_id: string,\n\t\tolds: FlySecretOutputs,\n\t\tnews: FlySecretInputs,\n\t): Promise<pulumi.dynamic.UpdateResult> {\n\t\tconst client = new FlyClient(news.token);\n\n\t\tconst values: Record<string, string | null> = { ...news.secrets };\n\n\t\tfor (const key of Object.keys(olds.secrets)) {\n\t\t\tif (!(key in news.secrets)) {\n\t\t\t\tvalues[key] = null;\n\t\t\t}\n\t\t}\n\n\t\tconst version = await applySecrets(client, news.appName, values);\n\n\t\treturn { outs: { ...news, version } };\n\t}\n\n\tasync delete(_id: string, props: FlySecretOutputs): Promise<void> {\n\t\tconst client = new FlyClient(props.token);\n\n\t\tconst values: Record<string, string | null> = {};\n\n\t\tfor (const key of Object.keys(props.secrets)) {\n\t\t\tvalues[key] = null;\n\t\t}\n\n\t\ttry {\n\t\t\tawait applySecrets(client, props.appName, values);\n\t\t} catch (error) {\n\t\t\t// App (and its secrets) already gone — deletion is idempotent.\n\t\t\tif (error instanceof ApiNotFoundError) {\n\t\t\t\tpulumi.log.warn(\n\t\t\t\t\t`Fly app \"${props.appName}\" already deleted — nothing to unset`,\n\t\t\t\t);\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync diff(\n\t\t_id: string,\n\t\tolds: FlySecretOutputs,\n\t\tnews: FlySecretInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst replaces: string[] = [];\n\n\t\tif (olds.appName !== news.appName) {\n\t\t\treplaces.push(\"appName\");\n\t\t}\n\n\t\tconst oldKeys = Object.keys(olds.secrets).sort().join(\",\");\n\t\tconst newKeys = Object.keys(news.secrets).sort().join(\",\");\n\n\t\tconst valuesChanged = Object.entries(news.secrets).some(\n\t\t\t([key, value]) => olds.secrets[key] !== value,\n\t\t);\n\n\t\treturn {\n\t\t\tchanges: replaces.length > 0 || oldKeys !== newKeys || valuesChanged,\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 FlySecretResource extends pulumi.dynamic.Resource {\n\tpublic declare readonly version: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: {\n\t\t\ttoken: pulumi.Input<string>;\n\t\t\tappName: pulumi.Input<string>;\n\t\t\tsecrets: pulumi.Input<Record<string, string>>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\tsuper(\n\t\t\tnew FlySecretResourceProvider(),\n\t\t\tname,\n\t\t\t{ ...args, version: undefined },\n\t\t\t// The API token flows into dynamic-provider state with the outputs — mark it secret there.\n\t\t\t{ ...opts, additionalSecretOutputs: [\"token\"] },\n\t\t);\n\t}\n}\n\n/** Options type for FlySecret. */\ntype FlySecretOptions = Omit<pulumi.ComponentResourceOptions, \"provider\"> & {\n\t/** Fly authentication context. */\n\tprovider: FlyProvider;\n\n\t/** App the secrets belong to. */\n\tapp: FlyApp;\n};\n\n/** Args for FlySecret. */\nexport interface FlySecretArgs {\n\t/**\n\t * Secret key/value pairs to set on the app.\n\t * Maps to the bulk secrets endpoint's `values` map (where a `null` value deletes a key).\n\t */\n\tsecrets: pulumi.Input<Record<string, string>>;\n}\n\n/**\n * Manages an app's Fly secrets as a single resource.\n *\n * Exposes `.version`, which changes only when the secret set changes — feed it\n * into `FlyDeploy.triggers` so a redeploy fires when secrets change.\n *\n * @example\n * ```typescript\n * const secrets = new FlySecret(\"api-secrets\", {\n * secrets: { JWT_SECRET: jwt, DATABASE_URL: dbUrl },\n * }, { provider, app });\n * ```\n */\nexport class FlySecret extends pulumi.ComponentResource {\n\t/** Fly secrets version. Changes only when the secret set changes. */\n\tpublic readonly version: pulumi.Output<string>;\n\n\tconstructor(name: string, args: FlySecretArgs, opts: FlySecretOptions) {\n\t\tconst { provider, app, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:fly:Secret\", name, {}, pulumiOpts);\n\n\t\tconst resource = new FlySecretResource(\n\t\t\t`${name}-resource`,\n\t\t\t{\n\t\t\t\ttoken: provider.token,\n\t\t\t\tappName: app.id,\n\t\t\t\tsecrets: pulumi.secret(args.secrets),\n\t\t\t},\n\t\t\t{ parent: this },\n\t\t);\n\n\t\tthis.version = resource.version;\n\n\t\tthis.registerOutputs({ version: this.version });\n\t}\n}\n"],"mappings":";;;;;;;;;;AAkCA,eAAe,aACd,QACA,SACA,QACkB;CAClB,MAAM,WAAW,MAAM,OAAO,KAC7B,YAAY,QAAQ,WACpB,EAAE,OAAO,CACV;CAEA,OAAO,OAAO,SAAS,OAAO;AAC/B;;;;;;;;;AAUA,IAAM,4BAAN,MAA2E;CAC1E,MAAM,OAAO,QAA+D;EAE3E,MAAM,UAAU,MAAM,aAAa,IADhB,UAAU,OAAO,KACI,GAAG,OAAO,SAAS,OAAO,OAAO;EAEzE,MAAM,OAAyB;GAAE,GAAG;GAAQ;EAAQ;EAEpD,OAAO;GAAE,IAAI,GAAG,OAAO,QAAQ;GAAW;EAAK;CAChD;CAEA,MAAM,KACL,IACA,OACqC;EAGrC,OAAO;GAAE;GAAI;EAAM;CACpB;CAEA,MAAM,OACL,KACA,MACA,MACuC;EACvC,MAAM,SAAS,IAAI,UAAU,KAAK,KAAK;EAEvC,MAAM,SAAwC,EAAE,GAAG,KAAK,QAAQ;EAEhE,KAAK,MAAM,OAAO,OAAO,KAAK,KAAK,OAAO,GACzC,IAAI,EAAE,OAAO,KAAK,UACjB,OAAO,OAAO;EAIhB,MAAM,UAAU,MAAM,aAAa,QAAQ,KAAK,SAAS,MAAM;EAE/D,OAAO,EAAE,MAAM;GAAE,GAAG;GAAM;EAAQ,EAAE;CACrC;CAEA,MAAM,OAAO,KAAa,OAAwC;EACjE,MAAM,SAAS,IAAI,UAAU,MAAM,KAAK;EAExC,MAAM,SAAwC,CAAC;EAE/C,KAAK,MAAM,OAAO,OAAO,KAAK,MAAM,OAAO,GAC1C,OAAO,OAAO;EAGf,IAAI;GACH,MAAM,aAAa,QAAQ,MAAM,SAAS,MAAM;EACjD,SAAS,OAAO;GAEf,IAAI,iBAAiB,kBAAkB;IACtC,OAAO,IAAI,KACV,YAAY,MAAM,QAAQ,qCAC3B;IAEA;GACD;GAEA,MAAM;EACP;CACD;CAEA,MAAM,KACL,KACA,MACA,MACqC;EACrC,MAAM,WAAqB,CAAC;EAE5B,IAAI,KAAK,YAAY,KAAK,SACzB,SAAS,KAAK,SAAS;EAGxB,MAAM,UAAU,OAAO,KAAK,KAAK,OAAO,EAAE,KAAK,EAAE,KAAK,GAAG;EACzD,MAAM,UAAU,OAAO,KAAK,KAAK,OAAO,EAAE,KAAK,EAAE,KAAK,GAAG;EAEzD,MAAM,gBAAgB,OAAO,QAAQ,KAAK,OAAO,EAAE,MACjD,CAAC,KAAK,WAAW,KAAK,QAAQ,SAAS,KACzC;EAEA,OAAO;GACN,SAAS,SAAS,SAAS,KAAK,YAAY,WAAW;GACvD;GACA,qBAAqB;EACtB;CACD;AACD;;AAGA,IAAM,oBAAN,cAAgC,OAAO,QAAQ,SAAS;CAGvD,YACC,MACA,MAKA,MACC;EACD,MACC,IAAI,0BAA0B,GAC9B,MACA;GAAE,GAAG;GAAM,SAAS;EAAU,GAE9B;GAAE,GAAG;GAAM,yBAAyB,CAAC,OAAO;EAAE,CAC/C;CACD;AACD;;;;;;;;;;;;;;AAiCA,IAAa,YAAb,cAA+B,OAAO,kBAAkB;CAIvD,YAAY,MAAc,MAAqB,MAAwB;EACtE,MAAM,EAAE,UAAU,KAAK,GAAG,eAAe;EAEzC,MAAM,yBAAyB,MAAM,CAAC,GAAG,UAAU;EAEnD,MAAM,WAAW,IAAI,kBACpB,GAAG,KAAK,YACR;GACC,OAAO,SAAS;GAChB,SAAS,IAAI;GACb,SAAS,OAAO,OAAO,KAAK,OAAO;EACpC,GACA,EAAE,QAAQ,KAAK,CAChB;EAEA,KAAK,UAAU,SAAS;EAExB,KAAK,gBAAgB,EAAE,SAAS,KAAK,QAAQ,CAAC;CAC/C;AACD"}
|
package/dist/fly/volume.cjs
CHANGED
|
@@ -15,6 +15,23 @@ _pulumi_pulumi = require_chunk.__toESM(_pulumi_pulumi, 1);
|
|
|
15
15
|
* @internal Exported only for unit testing; not part of the public API surface.
|
|
16
16
|
*/
|
|
17
17
|
var FlyVolumeResourceProvider = class {
|
|
18
|
+
/**
|
|
19
|
+
* Validates inputs at plan time. A non-positive or fractional `sizeGb`
|
|
20
|
+
* would otherwise fail deep inside the volumes API with an opaque error.
|
|
21
|
+
* A preview-unknown value arrives as Pulumi's string sentinel, so the
|
|
22
|
+
* `typeof` guard skips it.
|
|
23
|
+
*/
|
|
24
|
+
async check(_olds, news) {
|
|
25
|
+
const failures = [];
|
|
26
|
+
if (typeof news.sizeGb === "number" && (!Number.isInteger(news.sizeGb) || news.sizeGb <= 0)) failures.push({
|
|
27
|
+
property: "sizeGb",
|
|
28
|
+
reason: `sizeGb must be a positive integer (whole GB), got ${news.sizeGb}`
|
|
29
|
+
});
|
|
30
|
+
return {
|
|
31
|
+
inputs: news,
|
|
32
|
+
failures
|
|
33
|
+
};
|
|
34
|
+
}
|
|
18
35
|
async create(inputs) {
|
|
19
36
|
const client = new require_fly_client.FlyClient(inputs.token);
|
|
20
37
|
const existing = (await client.get(`/v1/apps/${inputs.appName}/volumes`)).find((volume) => volume.name === inputs.name && volume.state !== "destroyed");
|
|
@@ -79,6 +96,7 @@ var FlyVolumeResourceProvider = class {
|
|
|
79
96
|
return {
|
|
80
97
|
changes: replaces.length > 0 || sizeGrew,
|
|
81
98
|
replaces,
|
|
99
|
+
stables: replaces.length === 0 ? ["volumeId"] : [],
|
|
82
100
|
deleteBeforeReplace: true
|
|
83
101
|
};
|
|
84
102
|
}
|
package/dist/fly/volume.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"volume.cjs","names":["FlyClient","ApiNotFoundError","pulumi"],"sources":["../../src/fly/volume.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\n\nimport { ApiNotFoundError } from \"../errors/api-not-found-error\";\nimport type { FlyApp } from \"./app\";\nimport { FlyClient } from \"./client\";\nimport type { FlyProvider } from \"./provider\";\n\n/** Resolved inputs for the Fly volume dynamic provider. */\ninterface FlyVolumeInputs {\n\t/** Fly API token. */\n\ttoken: string;\n\n\t/** App name the volume belongs to. */\n\tappName: string;\n\n\t/** Volume name (used for adoption lookup). */\n\tname: string;\n\n\t/** Region (IATA code). */\n\tregion: string;\n\n\t/** Volume size in GB. */\n\tsizeGb: number;\n}\n\n/** Persisted state for the Fly volume. */\ninterface FlyVolumeOutputs extends FlyVolumeInputs {\n\t/** Fly-assigned volume ID (`vol_…`). */\n\tvolumeId: string;\n}\n\n/** Volume response (only the fields we read). */\ninterface FlyVolumeResponse {\n\tid: string;\n\tname: string;\n\tstate: string;\n\tsize_gb: number;\n\tregion: string;\n}\n\n/**\n * Dynamic provider for Fly volumes. `create()` lists volumes and adopts one\n * matching the name (volume names are not unique, so it adopts the first\n * non-destroyed match); otherwise it creates a new encrypted volume. Growing\n * `sizeGb` extends in place; shrinking is not supported by Fly.\n *\n * @internal Exported only for unit testing; not part of the public API surface.\n */\nexport class FlyVolumeResourceProvider\n\timplements pulumi.dynamic.ResourceProvider\n{\n\tasync create(inputs: FlyVolumeInputs): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst client = new FlyClient(inputs.token);\n\n\t\tconst volumes = await client.get<FlyVolumeResponse[]>(\n\t\t\t`/v1/apps/${inputs.appName}/volumes`,\n\t\t);\n\n\t\tconst existing = volumes.find(\n\t\t\t(volume) => volume.name === inputs.name && volume.state !== \"destroyed\",\n\t\t);\n\n\t\tlet volumeId: string;\n\n\t\tif (existing) {\n\t\t\tpulumi.log.info(\n\t\t\t\t`Adopting existing Fly volume \"${inputs.name}\" (${existing.id})`,\n\t\t\t);\n\n\t\t\tvolumeId = existing.id;\n\t\t} else {\n\t\t\tconst created = await client.post<FlyVolumeResponse>(\n\t\t\t\t`/v1/apps/${inputs.appName}/volumes`,\n\t\t\t\t{\n\t\t\t\t\tname: inputs.name,\n\t\t\t\t\tregion: inputs.region,\n\t\t\t\t\tsize_gb: inputs.sizeGb,\n\t\t\t\t\tencrypted: true,\n\t\t\t\t},\n\t\t\t);\n\n\t\t\tvolumeId = created.id;\n\t\t}\n\n\t\tconst outs: FlyVolumeOutputs = { ...inputs, volumeId };\n\n\t\treturn { id: volumeId, outs };\n\t}\n\n\tasync read(\n\t\tid: string,\n\t\tprops: FlyVolumeOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\tconst client = new FlyClient(props.token);\n\n\t\tconst volume = await client.tryGet<FlyVolumeResponse>(\n\t\t\t`/v1/apps/${props.appName}/volumes/${id}`,\n\t\t);\n\n\t\tif (!volume) {\n\t\t\t// Resource gone → blank id lets refresh reconcile the deletion.\n\t\t\treturn {};\n\t\t}\n\n\t\treturn {\n\t\t\tid,\n\t\t\tprops: {\n\t\t\t\t...props,\n\t\t\t\tname: volume.name,\n\t\t\t\tregion: volume.region,\n\t\t\t\tsizeGb: volume.size_gb,\n\t\t\t},\n\t\t};\n\t}\n\n\tasync update(\n\t\tid: string,\n\t\tolds: FlyVolumeOutputs,\n\t\tnews: FlyVolumeInputs,\n\t): Promise<pulumi.dynamic.UpdateResult> {\n\t\tif (news.sizeGb > olds.sizeGb) {\n\t\t\tconst client = new FlyClient(news.token);\n\n\t\t\tawait client.put(`/v1/apps/${news.appName}/volumes/${id}/extend`, {\n\t\t\t\tsize_gb: news.sizeGb,\n\t\t\t});\n\t\t}\n\n\t\treturn { outs: { ...news, volumeId: id } };\n\t}\n\n\tasync delete(id: string, props: FlyVolumeOutputs): Promise<void> {\n\t\tconst client = new FlyClient(props.token);\n\n\t\ttry {\n\t\t\tawait client.delete(`/v1/apps/${props.appName}/volumes/${id}`);\n\t\t} catch (error) {\n\t\t\t// Already gone — deletion is idempotent.\n\t\t\tif (error instanceof ApiNotFoundError) {\n\t\t\t\tpulumi.log.warn(`Fly volume \"${id}\" already deleted`);\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync diff(\n\t\t_id: string,\n\t\tolds: FlyVolumeOutputs,\n\t\tnews: FlyVolumeInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst replaces: string[] = [];\n\n\t\tif (olds.appName !== news.appName) {\n\t\t\treplaces.push(\"appName\");\n\t\t}\n\n\t\tif (olds.name !== news.name) {\n\t\t\treplaces.push(\"name\");\n\t\t}\n\n\t\tif (olds.region !== news.region) {\n\t\t\treplaces.push(\"region\");\n\t\t}\n\n\t\tif (news.sizeGb < olds.sizeGb) {\n\t\t\treplaces.push(\"sizeGb\");\n\t\t}\n\n\t\tconst sizeGrew = news.sizeGb > olds.sizeGb;\n\n\t\treturn {\n\t\t\tchanges: replaces.length > 0 || sizeGrew,\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 FlyVolumeResource extends pulumi.dynamic.Resource {\n\tpublic declare readonly volumeId: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: {\n\t\t\ttoken: pulumi.Input<string>;\n\t\t\tappName: pulumi.Input<string>;\n\t\t\tname: pulumi.Input<string>;\n\t\t\tregion: pulumi.Input<string>;\n\t\t\tsizeGb: pulumi.Input<number>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\tsuper(\n\t\t\tnew FlyVolumeResourceProvider(),\n\t\t\tname,\n\t\t\t{ ...args, volumeId: undefined },\n\t\t\t// The API token flows into dynamic-provider state with the outputs — mark it secret there.\n\t\t\t{ ...opts, additionalSecretOutputs: [\"token\"] },\n\t\t);\n\t}\n}\n\n/** Options type for FlyVolume. */\ntype FlyVolumeOptions = Omit<pulumi.ComponentResourceOptions, \"provider\"> & {\n\t/** Fly authentication context. */\n\tprovider: FlyProvider;\n\n\t/** App the volume belongs to. */\n\tapp: FlyApp;\n};\n\n/** Args for FlyVolume. */\nexport interface FlyVolumeArgs {\n\t/** Volume name. */\n\tname: pulumi.Input<string>;\n\n\t/** Region (IATA code, e.g. `\"iad\"`). */\n\tregion: pulumi.Input<string>;\n\n\t/** Volume size in GB. Can be grown (extended) but not shrunk. */\n\tsizeGb: pulumi.Input<number>;\n}\n\n/**\n * Manages a Fly volume with adopt-or-create semantics.\n *\n * @example\n * ```typescript\n * const volume = new FlyVolume(\"api-data\", {\n * name: \"data\",\n * region: \"iad\",\n * sizeGb: 10,\n * }, { provider, app });\n * ```\n */\nexport class FlyVolume extends pulumi.ComponentResource {\n\t/** Fly-assigned volume ID. */\n\tpublic readonly id: pulumi.Output<string>;\n\n\tconstructor(name: string, args: FlyVolumeArgs, opts: FlyVolumeOptions) {\n\t\tconst { provider, app, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:fly:Volume\", name, {}, pulumiOpts);\n\n\t\tconst resource = new FlyVolumeResource(\n\t\t\t`${name}-resource`,\n\t\t\t{\n\t\t\t\ttoken: provider.token,\n\t\t\t\tappName: app.id,\n\t\t\t\t...args,\n\t\t\t},\n\t\t\t{ parent: this },\n\t\t);\n\n\t\tthis.id = resource.volumeId;\n\n\t\tthis.registerOutputs({ id: this.id });\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"volume.cjs","names":["FlyClient","ApiNotFoundError","pulumi"],"sources":["../../src/fly/volume.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\n\nimport { ApiNotFoundError } from \"../errors/api-not-found-error\";\nimport type { FlyApp } from \"./app\";\nimport { FlyClient } from \"./client\";\nimport type { FlyProvider } from \"./provider\";\n\n/** Resolved inputs for the Fly volume dynamic provider. */\ninterface FlyVolumeInputs {\n\t/** Fly API token. */\n\ttoken: string;\n\n\t/** App name the volume belongs to. */\n\tappName: string;\n\n\t/** Volume name (used for adoption lookup). */\n\tname: string;\n\n\t/** Region (IATA code). */\n\tregion: string;\n\n\t/** Volume size in GB. */\n\tsizeGb: number;\n}\n\n/** Persisted state for the Fly volume. */\ninterface FlyVolumeOutputs extends FlyVolumeInputs {\n\t/** Fly-assigned volume ID (`vol_…`). */\n\tvolumeId: string;\n}\n\n/** Volume response (only the fields we read). */\ninterface FlyVolumeResponse {\n\tid: string;\n\tname: string;\n\tstate: string;\n\tsize_gb: number;\n\tregion: string;\n}\n\n/**\n * Dynamic provider for Fly volumes. `create()` lists volumes and adopts one\n * matching the name (volume names are not unique, so it adopts the first\n * non-destroyed match); otherwise it creates a new encrypted volume. Growing\n * `sizeGb` extends in place; shrinking is not supported by Fly.\n *\n * @internal Exported only for unit testing; not part of the public API surface.\n */\nexport class FlyVolumeResourceProvider\n\timplements pulumi.dynamic.ResourceProvider\n{\n\t/**\n\t * Validates inputs at plan time. A non-positive or fractional `sizeGb`\n\t * would otherwise fail deep inside the volumes API with an opaque error.\n\t * A preview-unknown value arrives as Pulumi's string sentinel, so the\n\t * `typeof` guard skips it.\n\t */\n\tasync check(\n\t\t_olds: FlyVolumeInputs,\n\t\tnews: FlyVolumeInputs,\n\t): Promise<pulumi.dynamic.CheckResult<FlyVolumeInputs>> {\n\t\tconst failures: pulumi.dynamic.CheckFailure[] = [];\n\n\t\tif (\n\t\t\ttypeof news.sizeGb === \"number\" &&\n\t\t\t(!Number.isInteger(news.sizeGb) || news.sizeGb <= 0)\n\t\t) {\n\t\t\tfailures.push({\n\t\t\t\tproperty: \"sizeGb\",\n\t\t\t\treason: `sizeGb must be a positive integer (whole GB), got ${news.sizeGb}`,\n\t\t\t});\n\t\t}\n\n\t\treturn { inputs: news, failures };\n\t}\n\n\tasync create(inputs: FlyVolumeInputs): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst client = new FlyClient(inputs.token);\n\n\t\tconst volumes = await client.get<FlyVolumeResponse[]>(\n\t\t\t`/v1/apps/${inputs.appName}/volumes`,\n\t\t);\n\n\t\tconst existing = volumes.find(\n\t\t\t(volume) => volume.name === inputs.name && volume.state !== \"destroyed\",\n\t\t);\n\n\t\tlet volumeId: string;\n\n\t\tif (existing) {\n\t\t\tpulumi.log.info(\n\t\t\t\t`Adopting existing Fly volume \"${inputs.name}\" (${existing.id})`,\n\t\t\t);\n\n\t\t\tvolumeId = existing.id;\n\t\t} else {\n\t\t\tconst created = await client.post<FlyVolumeResponse>(\n\t\t\t\t`/v1/apps/${inputs.appName}/volumes`,\n\t\t\t\t{\n\t\t\t\t\tname: inputs.name,\n\t\t\t\t\tregion: inputs.region,\n\t\t\t\t\tsize_gb: inputs.sizeGb,\n\t\t\t\t\tencrypted: true,\n\t\t\t\t},\n\t\t\t);\n\n\t\t\tvolumeId = created.id;\n\t\t}\n\n\t\tconst outs: FlyVolumeOutputs = { ...inputs, volumeId };\n\n\t\treturn { id: volumeId, outs };\n\t}\n\n\tasync read(\n\t\tid: string,\n\t\tprops: FlyVolumeOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\tconst client = new FlyClient(props.token);\n\n\t\tconst volume = await client.tryGet<FlyVolumeResponse>(\n\t\t\t`/v1/apps/${props.appName}/volumes/${id}`,\n\t\t);\n\n\t\tif (!volume) {\n\t\t\t// Resource gone → blank id lets refresh reconcile the deletion.\n\t\t\treturn {};\n\t\t}\n\n\t\treturn {\n\t\t\tid,\n\t\t\tprops: {\n\t\t\t\t...props,\n\t\t\t\tname: volume.name,\n\t\t\t\tregion: volume.region,\n\t\t\t\tsizeGb: volume.size_gb,\n\t\t\t},\n\t\t};\n\t}\n\n\tasync update(\n\t\tid: string,\n\t\tolds: FlyVolumeOutputs,\n\t\tnews: FlyVolumeInputs,\n\t): Promise<pulumi.dynamic.UpdateResult> {\n\t\tif (news.sizeGb > olds.sizeGb) {\n\t\t\tconst client = new FlyClient(news.token);\n\n\t\t\tawait client.put(`/v1/apps/${news.appName}/volumes/${id}/extend`, {\n\t\t\t\tsize_gb: news.sizeGb,\n\t\t\t});\n\t\t}\n\n\t\treturn { outs: { ...news, volumeId: id } };\n\t}\n\n\tasync delete(id: string, props: FlyVolumeOutputs): Promise<void> {\n\t\tconst client = new FlyClient(props.token);\n\n\t\ttry {\n\t\t\tawait client.delete(`/v1/apps/${props.appName}/volumes/${id}`);\n\t\t} catch (error) {\n\t\t\t// Already gone — deletion is idempotent.\n\t\t\tif (error instanceof ApiNotFoundError) {\n\t\t\t\tpulumi.log.warn(`Fly volume \"${id}\" already deleted`);\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync diff(\n\t\t_id: string,\n\t\tolds: FlyVolumeOutputs,\n\t\tnews: FlyVolumeInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst replaces: string[] = [];\n\n\t\tif (olds.appName !== news.appName) {\n\t\t\treplaces.push(\"appName\");\n\t\t}\n\n\t\tif (olds.name !== news.name) {\n\t\t\treplaces.push(\"name\");\n\t\t}\n\n\t\tif (olds.region !== news.region) {\n\t\t\treplaces.push(\"region\");\n\t\t}\n\n\t\tif (news.sizeGb < olds.sizeGb) {\n\t\t\treplaces.push(\"sizeGb\");\n\t\t}\n\n\t\tconst sizeGrew = news.sizeGb > olds.sizeGb;\n\n\t\treturn {\n\t\t\tchanges: replaces.length > 0 || sizeGrew,\n\t\t\treplaces,\n\t\t\t// volumeId survives an in-place extend (only appName/name/region/shrink\n\t\t\t// replace), so dependents keep a known volumeId during preview.\n\t\t\tstables: replaces.length === 0 ? [\"volumeId\"] : [],\n\t\t\tdeleteBeforeReplace: true,\n\t\t};\n\t}\n}\n\n/** Internal dynamic resource — not part of the public API. */\nclass FlyVolumeResource extends pulumi.dynamic.Resource {\n\tpublic declare readonly volumeId: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: {\n\t\t\ttoken: pulumi.Input<string>;\n\t\t\tappName: pulumi.Input<string>;\n\t\t\tname: pulumi.Input<string>;\n\t\t\tregion: pulumi.Input<string>;\n\t\t\tsizeGb: pulumi.Input<number>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\tsuper(\n\t\t\tnew FlyVolumeResourceProvider(),\n\t\t\tname,\n\t\t\t{ ...args, volumeId: undefined },\n\t\t\t// The API token flows into dynamic-provider state with the outputs — mark it secret there.\n\t\t\t{ ...opts, additionalSecretOutputs: [\"token\"] },\n\t\t);\n\t}\n}\n\n/** Options type for FlyVolume. */\ntype FlyVolumeOptions = Omit<pulumi.ComponentResourceOptions, \"provider\"> & {\n\t/** Fly authentication context. */\n\tprovider: FlyProvider;\n\n\t/** App the volume belongs to. */\n\tapp: FlyApp;\n};\n\n/** Args for FlyVolume. */\nexport interface FlyVolumeArgs {\n\t/** Volume name. */\n\tname: pulumi.Input<string>;\n\n\t/** Region (IATA code, e.g. `\"iad\"`). */\n\tregion: pulumi.Input<string>;\n\n\t/**\n\t * Volume size in GB. Can be grown (extended) but not shrunk.\n\t * Maps to the Fly Machines API field `size_gb`.\n\t */\n\tsizeGb: pulumi.Input<number>;\n}\n\n/**\n * Manages a Fly volume with adopt-or-create semantics.\n *\n * @example\n * ```typescript\n * const volume = new FlyVolume(\"api-data\", {\n * name: \"data\",\n * region: \"iad\",\n * sizeGb: 10,\n * }, { provider, app });\n * ```\n */\nexport class FlyVolume extends pulumi.ComponentResource {\n\t/** Fly-assigned volume ID. */\n\tpublic readonly id: pulumi.Output<string>;\n\n\tconstructor(name: string, args: FlyVolumeArgs, opts: FlyVolumeOptions) {\n\t\tconst { provider, app, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:fly:Volume\", name, {}, pulumiOpts);\n\n\t\tconst resource = new FlyVolumeResource(\n\t\t\t`${name}-resource`,\n\t\t\t{\n\t\t\t\ttoken: provider.token,\n\t\t\t\tappName: app.id,\n\t\t\t\t...args,\n\t\t\t},\n\t\t\t{ parent: this },\n\t\t);\n\n\t\tthis.id = resource.volumeId;\n\n\t\tthis.registerOutputs({ id: this.id });\n\t}\n}\n"],"mappings":"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"}
|
package/dist/fly/volume.d.cts
CHANGED
|
@@ -31,6 +31,13 @@ interface FlyVolumeOutputs extends FlyVolumeInputs {
|
|
|
31
31
|
* @internal Exported only for unit testing; not part of the public API surface.
|
|
32
32
|
*/
|
|
33
33
|
declare class FlyVolumeResourceProvider implements pulumi.dynamic.ResourceProvider {
|
|
34
|
+
/**
|
|
35
|
+
* Validates inputs at plan time. A non-positive or fractional `sizeGb`
|
|
36
|
+
* would otherwise fail deep inside the volumes API with an opaque error.
|
|
37
|
+
* A preview-unknown value arrives as Pulumi's string sentinel, so the
|
|
38
|
+
* `typeof` guard skips it.
|
|
39
|
+
*/
|
|
40
|
+
check(_olds: FlyVolumeInputs, news: FlyVolumeInputs): Promise<pulumi.dynamic.CheckResult<FlyVolumeInputs>>;
|
|
34
41
|
create(inputs: FlyVolumeInputs): Promise<pulumi.dynamic.CreateResult>;
|
|
35
42
|
read(id: string, props: FlyVolumeOutputs): Promise<pulumi.dynamic.ReadResult>;
|
|
36
43
|
update(id: string, olds: FlyVolumeOutputs, news: FlyVolumeInputs): Promise<pulumi.dynamic.UpdateResult>;
|
|
@@ -48,7 +55,10 @@ interface FlyVolumeArgs {
|
|
|
48
55
|
name: pulumi.Input<string>;
|
|
49
56
|
/** Region (IATA code, e.g. `"iad"`). */
|
|
50
57
|
region: pulumi.Input<string>;
|
|
51
|
-
/**
|
|
58
|
+
/**
|
|
59
|
+
* Volume size in GB. Can be grown (extended) but not shrunk.
|
|
60
|
+
* Maps to the Fly Machines API field `size_gb`.
|
|
61
|
+
*/
|
|
52
62
|
sizeGb: pulumi.Input<number>;
|
|
53
63
|
}
|
|
54
64
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"volume.d.cts","names":[],"sources":["../../src/fly/volume.ts"],"mappings":";;;;;;;UAQU,eAAA;;EAET,KAAA;EAFwB;EAKxB,OAAA;EALwB;EAQxB,IAAA;EAHA;EAMA,MAAA;EAAA;EAGA,MAAA;AAAA;AAAM;AAAA,UAIG,gBAAA,SAAyB,eAAe;EAAvB;EAE1B,QAAQ;AAAA;AAAA;AAoBT;;;;;;;AApBS,cAoBI,yBAAA,YACD,MAAA,CAAO,OAAA,CAAQ,gBAAA;
|
|
1
|
+
{"version":3,"file":"volume.d.cts","names":[],"sources":["../../src/fly/volume.ts"],"mappings":";;;;;;;UAQU,eAAA;;EAET,KAAA;EAFwB;EAKxB,OAAA;EALwB;EAQxB,IAAA;EAHA;EAMA,MAAA;EAAA;EAGA,MAAA;AAAA;AAAM;AAAA,UAIG,gBAAA,SAAyB,eAAe;EAAvB;EAE1B,QAAQ;AAAA;AAAA;AAoBT;;;;;;;AApBS,cAoBI,yBAAA,YACD,MAAA,CAAO,OAAA,CAAQ,gBAAA;EA2BL;;;;;;EAnBf,KAAA,CACL,KAAA,EAAO,eAAA,EACP,IAAA,EAAM,eAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,WAAA,CAAY,eAAA;EAgBhC,MAAA,CAAO,MAAA,EAAQ,eAAA,GAAkB,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EAsCxD,IAAA,CACL,EAAA,UACA,KAAA,EAAO,gBAAA,GACL,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;EAuBpB,MAAA,CACL,EAAA,UACA,IAAA,EAAM,gBAAA,EACN,IAAA,EAAM,eAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EAYpB,MAAA,CAAO,EAAA,UAAY,KAAA,EAAO,gBAAA,GAAmB,OAAA;EAiB7C,IAAA,CACL,GAAA,UACA,IAAA,EAAM,gBAAA,EACN,IAAA,EAAM,eAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;AAAA;;KA0DtB,gBAAA,GAAmB,IAAA,CAAK,MAAA,CAAO,wBAAA;EA1DhC,kCA4DH,QAAA,EAAU,WAAA,EA5LgC;EA+L1C,GAAA,EAAK,MAAA;AAAA;;UAIW,aAAA;EA3LV;EA6LN,IAAA,EAAM,MAAA,CAAO,KAAA;EA5LZ;EA+LD,MAAA,EAAQ,MAAA,CAAO,KAAA;EA9Ld;;;;EAoMD,MAAA,EAAQ,MAAA,CAAO,KAAA;AAAA;;;;;;;;;;;;;cAeH,SAAA,SAAkB,MAAA,CAAO,iBAAA;EAzJnB;EAAA,SA2JF,EAAA,EAAI,MAAA,CAAO,MAAA;cAEf,IAAA,UAAc,IAAA,EAAM,aAAA,EAAe,IAAA,EAAM,gBAAA;AAAA"}
|
package/dist/fly/volume.d.mts
CHANGED
|
@@ -31,6 +31,13 @@ interface FlyVolumeOutputs extends FlyVolumeInputs {
|
|
|
31
31
|
* @internal Exported only for unit testing; not part of the public API surface.
|
|
32
32
|
*/
|
|
33
33
|
declare class FlyVolumeResourceProvider implements pulumi.dynamic.ResourceProvider {
|
|
34
|
+
/**
|
|
35
|
+
* Validates inputs at plan time. A non-positive or fractional `sizeGb`
|
|
36
|
+
* would otherwise fail deep inside the volumes API with an opaque error.
|
|
37
|
+
* A preview-unknown value arrives as Pulumi's string sentinel, so the
|
|
38
|
+
* `typeof` guard skips it.
|
|
39
|
+
*/
|
|
40
|
+
check(_olds: FlyVolumeInputs, news: FlyVolumeInputs): Promise<pulumi.dynamic.CheckResult<FlyVolumeInputs>>;
|
|
34
41
|
create(inputs: FlyVolumeInputs): Promise<pulumi.dynamic.CreateResult>;
|
|
35
42
|
read(id: string, props: FlyVolumeOutputs): Promise<pulumi.dynamic.ReadResult>;
|
|
36
43
|
update(id: string, olds: FlyVolumeOutputs, news: FlyVolumeInputs): Promise<pulumi.dynamic.UpdateResult>;
|
|
@@ -48,7 +55,10 @@ interface FlyVolumeArgs {
|
|
|
48
55
|
name: pulumi.Input<string>;
|
|
49
56
|
/** Region (IATA code, e.g. `"iad"`). */
|
|
50
57
|
region: pulumi.Input<string>;
|
|
51
|
-
/**
|
|
58
|
+
/**
|
|
59
|
+
* Volume size in GB. Can be grown (extended) but not shrunk.
|
|
60
|
+
* Maps to the Fly Machines API field `size_gb`.
|
|
61
|
+
*/
|
|
52
62
|
sizeGb: pulumi.Input<number>;
|
|
53
63
|
}
|
|
54
64
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"volume.d.mts","names":[],"sources":["../../src/fly/volume.ts"],"mappings":";;;;;;;UAQU,eAAA;;EAET,KAAA;EAFwB;EAKxB,OAAA;EALwB;EAQxB,IAAA;EAHA;EAMA,MAAA;EAAA;EAGA,MAAA;AAAA;AAAM;AAAA,UAIG,gBAAA,SAAyB,eAAe;EAAvB;EAE1B,QAAQ;AAAA;AAAA;AAoBT;;;;;;;AApBS,cAoBI,yBAAA,YACD,MAAA,CAAO,OAAA,CAAQ,gBAAA;
|
|
1
|
+
{"version":3,"file":"volume.d.mts","names":[],"sources":["../../src/fly/volume.ts"],"mappings":";;;;;;;UAQU,eAAA;;EAET,KAAA;EAFwB;EAKxB,OAAA;EALwB;EAQxB,IAAA;EAHA;EAMA,MAAA;EAAA;EAGA,MAAA;AAAA;AAAM;AAAA,UAIG,gBAAA,SAAyB,eAAe;EAAvB;EAE1B,QAAQ;AAAA;AAAA;AAoBT;;;;;;;AApBS,cAoBI,yBAAA,YACD,MAAA,CAAO,OAAA,CAAQ,gBAAA;EA2BL;;;;;;EAnBf,KAAA,CACL,KAAA,EAAO,eAAA,EACP,IAAA,EAAM,eAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,WAAA,CAAY,eAAA;EAgBhC,MAAA,CAAO,MAAA,EAAQ,eAAA,GAAkB,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EAsCxD,IAAA,CACL,EAAA,UACA,KAAA,EAAO,gBAAA,GACL,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;EAuBpB,MAAA,CACL,EAAA,UACA,IAAA,EAAM,gBAAA,EACN,IAAA,EAAM,eAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EAYpB,MAAA,CAAO,EAAA,UAAY,KAAA,EAAO,gBAAA,GAAmB,OAAA;EAiB7C,IAAA,CACL,GAAA,UACA,IAAA,EAAM,gBAAA,EACN,IAAA,EAAM,eAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;AAAA;;KA0DtB,gBAAA,GAAmB,IAAA,CAAK,MAAA,CAAO,wBAAA;EA1DhC,kCA4DH,QAAA,EAAU,WAAA,EA5LgC;EA+L1C,GAAA,EAAK,MAAA;AAAA;;UAIW,aAAA;EA3LV;EA6LN,IAAA,EAAM,MAAA,CAAO,KAAA;EA5LZ;EA+LD,MAAA,EAAQ,MAAA,CAAO,KAAA;EA9Ld;;;;EAoMD,MAAA,EAAQ,MAAA,CAAO,KAAA;AAAA;;;;;;;;;;;;;cAeH,SAAA,SAAkB,MAAA,CAAO,iBAAA;EAzJnB;EAAA,SA2JF,EAAA,EAAI,MAAA,CAAO,MAAA;cAEf,IAAA,UAAc,IAAA,EAAM,aAAA,EAAe,IAAA,EAAM,gBAAA;AAAA"}
|
package/dist/fly/volume.mjs
CHANGED
|
@@ -13,6 +13,23 @@ import * as pulumi from "@pulumi/pulumi";
|
|
|
13
13
|
* @internal Exported only for unit testing; not part of the public API surface.
|
|
14
14
|
*/
|
|
15
15
|
var FlyVolumeResourceProvider = class {
|
|
16
|
+
/**
|
|
17
|
+
* Validates inputs at plan time. A non-positive or fractional `sizeGb`
|
|
18
|
+
* would otherwise fail deep inside the volumes API with an opaque error.
|
|
19
|
+
* A preview-unknown value arrives as Pulumi's string sentinel, so the
|
|
20
|
+
* `typeof` guard skips it.
|
|
21
|
+
*/
|
|
22
|
+
async check(_olds, news) {
|
|
23
|
+
const failures = [];
|
|
24
|
+
if (typeof news.sizeGb === "number" && (!Number.isInteger(news.sizeGb) || news.sizeGb <= 0)) failures.push({
|
|
25
|
+
property: "sizeGb",
|
|
26
|
+
reason: `sizeGb must be a positive integer (whole GB), got ${news.sizeGb}`
|
|
27
|
+
});
|
|
28
|
+
return {
|
|
29
|
+
inputs: news,
|
|
30
|
+
failures
|
|
31
|
+
};
|
|
32
|
+
}
|
|
16
33
|
async create(inputs) {
|
|
17
34
|
const client = new FlyClient(inputs.token);
|
|
18
35
|
const existing = (await client.get(`/v1/apps/${inputs.appName}/volumes`)).find((volume) => volume.name === inputs.name && volume.state !== "destroyed");
|
|
@@ -77,6 +94,7 @@ var FlyVolumeResourceProvider = class {
|
|
|
77
94
|
return {
|
|
78
95
|
changes: replaces.length > 0 || sizeGrew,
|
|
79
96
|
replaces,
|
|
97
|
+
stables: replaces.length === 0 ? ["volumeId"] : [],
|
|
80
98
|
deleteBeforeReplace: true
|
|
81
99
|
};
|
|
82
100
|
}
|
package/dist/fly/volume.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"volume.mjs","names":[],"sources":["../../src/fly/volume.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\n\nimport { ApiNotFoundError } from \"../errors/api-not-found-error\";\nimport type { FlyApp } from \"./app\";\nimport { FlyClient } from \"./client\";\nimport type { FlyProvider } from \"./provider\";\n\n/** Resolved inputs for the Fly volume dynamic provider. */\ninterface FlyVolumeInputs {\n\t/** Fly API token. */\n\ttoken: string;\n\n\t/** App name the volume belongs to. */\n\tappName: string;\n\n\t/** Volume name (used for adoption lookup). */\n\tname: string;\n\n\t/** Region (IATA code). */\n\tregion: string;\n\n\t/** Volume size in GB. */\n\tsizeGb: number;\n}\n\n/** Persisted state for the Fly volume. */\ninterface FlyVolumeOutputs extends FlyVolumeInputs {\n\t/** Fly-assigned volume ID (`vol_…`). */\n\tvolumeId: string;\n}\n\n/** Volume response (only the fields we read). */\ninterface FlyVolumeResponse {\n\tid: string;\n\tname: string;\n\tstate: string;\n\tsize_gb: number;\n\tregion: string;\n}\n\n/**\n * Dynamic provider for Fly volumes. `create()` lists volumes and adopts one\n * matching the name (volume names are not unique, so it adopts the first\n * non-destroyed match); otherwise it creates a new encrypted volume. Growing\n * `sizeGb` extends in place; shrinking is not supported by Fly.\n *\n * @internal Exported only for unit testing; not part of the public API surface.\n */\nexport class FlyVolumeResourceProvider\n\timplements pulumi.dynamic.ResourceProvider\n{\n\tasync create(inputs: FlyVolumeInputs): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst client = new FlyClient(inputs.token);\n\n\t\tconst volumes = await client.get<FlyVolumeResponse[]>(\n\t\t\t`/v1/apps/${inputs.appName}/volumes`,\n\t\t);\n\n\t\tconst existing = volumes.find(\n\t\t\t(volume) => volume.name === inputs.name && volume.state !== \"destroyed\",\n\t\t);\n\n\t\tlet volumeId: string;\n\n\t\tif (existing) {\n\t\t\tpulumi.log.info(\n\t\t\t\t`Adopting existing Fly volume \"${inputs.name}\" (${existing.id})`,\n\t\t\t);\n\n\t\t\tvolumeId = existing.id;\n\t\t} else {\n\t\t\tconst created = await client.post<FlyVolumeResponse>(\n\t\t\t\t`/v1/apps/${inputs.appName}/volumes`,\n\t\t\t\t{\n\t\t\t\t\tname: inputs.name,\n\t\t\t\t\tregion: inputs.region,\n\t\t\t\t\tsize_gb: inputs.sizeGb,\n\t\t\t\t\tencrypted: true,\n\t\t\t\t},\n\t\t\t);\n\n\t\t\tvolumeId = created.id;\n\t\t}\n\n\t\tconst outs: FlyVolumeOutputs = { ...inputs, volumeId };\n\n\t\treturn { id: volumeId, outs };\n\t}\n\n\tasync read(\n\t\tid: string,\n\t\tprops: FlyVolumeOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\tconst client = new FlyClient(props.token);\n\n\t\tconst volume = await client.tryGet<FlyVolumeResponse>(\n\t\t\t`/v1/apps/${props.appName}/volumes/${id}`,\n\t\t);\n\n\t\tif (!volume) {\n\t\t\t// Resource gone → blank id lets refresh reconcile the deletion.\n\t\t\treturn {};\n\t\t}\n\n\t\treturn {\n\t\t\tid,\n\t\t\tprops: {\n\t\t\t\t...props,\n\t\t\t\tname: volume.name,\n\t\t\t\tregion: volume.region,\n\t\t\t\tsizeGb: volume.size_gb,\n\t\t\t},\n\t\t};\n\t}\n\n\tasync update(\n\t\tid: string,\n\t\tolds: FlyVolumeOutputs,\n\t\tnews: FlyVolumeInputs,\n\t): Promise<pulumi.dynamic.UpdateResult> {\n\t\tif (news.sizeGb > olds.sizeGb) {\n\t\t\tconst client = new FlyClient(news.token);\n\n\t\t\tawait client.put(`/v1/apps/${news.appName}/volumes/${id}/extend`, {\n\t\t\t\tsize_gb: news.sizeGb,\n\t\t\t});\n\t\t}\n\n\t\treturn { outs: { ...news, volumeId: id } };\n\t}\n\n\tasync delete(id: string, props: FlyVolumeOutputs): Promise<void> {\n\t\tconst client = new FlyClient(props.token);\n\n\t\ttry {\n\t\t\tawait client.delete(`/v1/apps/${props.appName}/volumes/${id}`);\n\t\t} catch (error) {\n\t\t\t// Already gone — deletion is idempotent.\n\t\t\tif (error instanceof ApiNotFoundError) {\n\t\t\t\tpulumi.log.warn(`Fly volume \"${id}\" already deleted`);\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync diff(\n\t\t_id: string,\n\t\tolds: FlyVolumeOutputs,\n\t\tnews: FlyVolumeInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst replaces: string[] = [];\n\n\t\tif (olds.appName !== news.appName) {\n\t\t\treplaces.push(\"appName\");\n\t\t}\n\n\t\tif (olds.name !== news.name) {\n\t\t\treplaces.push(\"name\");\n\t\t}\n\n\t\tif (olds.region !== news.region) {\n\t\t\treplaces.push(\"region\");\n\t\t}\n\n\t\tif (news.sizeGb < olds.sizeGb) {\n\t\t\treplaces.push(\"sizeGb\");\n\t\t}\n\n\t\tconst sizeGrew = news.sizeGb > olds.sizeGb;\n\n\t\treturn {\n\t\t\tchanges: replaces.length > 0 || sizeGrew,\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 FlyVolumeResource extends pulumi.dynamic.Resource {\n\tpublic declare readonly volumeId: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: {\n\t\t\ttoken: pulumi.Input<string>;\n\t\t\tappName: pulumi.Input<string>;\n\t\t\tname: pulumi.Input<string>;\n\t\t\tregion: pulumi.Input<string>;\n\t\t\tsizeGb: pulumi.Input<number>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\tsuper(\n\t\t\tnew FlyVolumeResourceProvider(),\n\t\t\tname,\n\t\t\t{ ...args, volumeId: undefined },\n\t\t\t// The API token flows into dynamic-provider state with the outputs — mark it secret there.\n\t\t\t{ ...opts, additionalSecretOutputs: [\"token\"] },\n\t\t);\n\t}\n}\n\n/** Options type for FlyVolume. */\ntype FlyVolumeOptions = Omit<pulumi.ComponentResourceOptions, \"provider\"> & {\n\t/** Fly authentication context. */\n\tprovider: FlyProvider;\n\n\t/** App the volume belongs to. */\n\tapp: FlyApp;\n};\n\n/** Args for FlyVolume. */\nexport interface FlyVolumeArgs {\n\t/** Volume name. */\n\tname: pulumi.Input<string>;\n\n\t/** Region (IATA code, e.g. `\"iad\"`). */\n\tregion: pulumi.Input<string>;\n\n\t/** Volume size in GB. Can be grown (extended) but not shrunk. */\n\tsizeGb: pulumi.Input<number>;\n}\n\n/**\n * Manages a Fly volume with adopt-or-create semantics.\n *\n * @example\n * ```typescript\n * const volume = new FlyVolume(\"api-data\", {\n * name: \"data\",\n * region: \"iad\",\n * sizeGb: 10,\n * }, { provider, app });\n * ```\n */\nexport class FlyVolume extends pulumi.ComponentResource {\n\t/** Fly-assigned volume ID. */\n\tpublic readonly id: pulumi.Output<string>;\n\n\tconstructor(name: string, args: FlyVolumeArgs, opts: FlyVolumeOptions) {\n\t\tconst { provider, app, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:fly:Volume\", name, {}, pulumiOpts);\n\n\t\tconst resource = new FlyVolumeResource(\n\t\t\t`${name}-resource`,\n\t\t\t{\n\t\t\t\ttoken: provider.token,\n\t\t\t\tappName: app.id,\n\t\t\t\t...args,\n\t\t\t},\n\t\t\t{ parent: this },\n\t\t);\n\n\t\tthis.id = resource.volumeId;\n\n\t\tthis.registerOutputs({ id: this.id });\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"volume.mjs","names":[],"sources":["../../src/fly/volume.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\n\nimport { ApiNotFoundError } from \"../errors/api-not-found-error\";\nimport type { FlyApp } from \"./app\";\nimport { FlyClient } from \"./client\";\nimport type { FlyProvider } from \"./provider\";\n\n/** Resolved inputs for the Fly volume dynamic provider. */\ninterface FlyVolumeInputs {\n\t/** Fly API token. */\n\ttoken: string;\n\n\t/** App name the volume belongs to. */\n\tappName: string;\n\n\t/** Volume name (used for adoption lookup). */\n\tname: string;\n\n\t/** Region (IATA code). */\n\tregion: string;\n\n\t/** Volume size in GB. */\n\tsizeGb: number;\n}\n\n/** Persisted state for the Fly volume. */\ninterface FlyVolumeOutputs extends FlyVolumeInputs {\n\t/** Fly-assigned volume ID (`vol_…`). */\n\tvolumeId: string;\n}\n\n/** Volume response (only the fields we read). */\ninterface FlyVolumeResponse {\n\tid: string;\n\tname: string;\n\tstate: string;\n\tsize_gb: number;\n\tregion: string;\n}\n\n/**\n * Dynamic provider for Fly volumes. `create()` lists volumes and adopts one\n * matching the name (volume names are not unique, so it adopts the first\n * non-destroyed match); otherwise it creates a new encrypted volume. Growing\n * `sizeGb` extends in place; shrinking is not supported by Fly.\n *\n * @internal Exported only for unit testing; not part of the public API surface.\n */\nexport class FlyVolumeResourceProvider\n\timplements pulumi.dynamic.ResourceProvider\n{\n\t/**\n\t * Validates inputs at plan time. A non-positive or fractional `sizeGb`\n\t * would otherwise fail deep inside the volumes API with an opaque error.\n\t * A preview-unknown value arrives as Pulumi's string sentinel, so the\n\t * `typeof` guard skips it.\n\t */\n\tasync check(\n\t\t_olds: FlyVolumeInputs,\n\t\tnews: FlyVolumeInputs,\n\t): Promise<pulumi.dynamic.CheckResult<FlyVolumeInputs>> {\n\t\tconst failures: pulumi.dynamic.CheckFailure[] = [];\n\n\t\tif (\n\t\t\ttypeof news.sizeGb === \"number\" &&\n\t\t\t(!Number.isInteger(news.sizeGb) || news.sizeGb <= 0)\n\t\t) {\n\t\t\tfailures.push({\n\t\t\t\tproperty: \"sizeGb\",\n\t\t\t\treason: `sizeGb must be a positive integer (whole GB), got ${news.sizeGb}`,\n\t\t\t});\n\t\t}\n\n\t\treturn { inputs: news, failures };\n\t}\n\n\tasync create(inputs: FlyVolumeInputs): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst client = new FlyClient(inputs.token);\n\n\t\tconst volumes = await client.get<FlyVolumeResponse[]>(\n\t\t\t`/v1/apps/${inputs.appName}/volumes`,\n\t\t);\n\n\t\tconst existing = volumes.find(\n\t\t\t(volume) => volume.name === inputs.name && volume.state !== \"destroyed\",\n\t\t);\n\n\t\tlet volumeId: string;\n\n\t\tif (existing) {\n\t\t\tpulumi.log.info(\n\t\t\t\t`Adopting existing Fly volume \"${inputs.name}\" (${existing.id})`,\n\t\t\t);\n\n\t\t\tvolumeId = existing.id;\n\t\t} else {\n\t\t\tconst created = await client.post<FlyVolumeResponse>(\n\t\t\t\t`/v1/apps/${inputs.appName}/volumes`,\n\t\t\t\t{\n\t\t\t\t\tname: inputs.name,\n\t\t\t\t\tregion: inputs.region,\n\t\t\t\t\tsize_gb: inputs.sizeGb,\n\t\t\t\t\tencrypted: true,\n\t\t\t\t},\n\t\t\t);\n\n\t\t\tvolumeId = created.id;\n\t\t}\n\n\t\tconst outs: FlyVolumeOutputs = { ...inputs, volumeId };\n\n\t\treturn { id: volumeId, outs };\n\t}\n\n\tasync read(\n\t\tid: string,\n\t\tprops: FlyVolumeOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\tconst client = new FlyClient(props.token);\n\n\t\tconst volume = await client.tryGet<FlyVolumeResponse>(\n\t\t\t`/v1/apps/${props.appName}/volumes/${id}`,\n\t\t);\n\n\t\tif (!volume) {\n\t\t\t// Resource gone → blank id lets refresh reconcile the deletion.\n\t\t\treturn {};\n\t\t}\n\n\t\treturn {\n\t\t\tid,\n\t\t\tprops: {\n\t\t\t\t...props,\n\t\t\t\tname: volume.name,\n\t\t\t\tregion: volume.region,\n\t\t\t\tsizeGb: volume.size_gb,\n\t\t\t},\n\t\t};\n\t}\n\n\tasync update(\n\t\tid: string,\n\t\tolds: FlyVolumeOutputs,\n\t\tnews: FlyVolumeInputs,\n\t): Promise<pulumi.dynamic.UpdateResult> {\n\t\tif (news.sizeGb > olds.sizeGb) {\n\t\t\tconst client = new FlyClient(news.token);\n\n\t\t\tawait client.put(`/v1/apps/${news.appName}/volumes/${id}/extend`, {\n\t\t\t\tsize_gb: news.sizeGb,\n\t\t\t});\n\t\t}\n\n\t\treturn { outs: { ...news, volumeId: id } };\n\t}\n\n\tasync delete(id: string, props: FlyVolumeOutputs): Promise<void> {\n\t\tconst client = new FlyClient(props.token);\n\n\t\ttry {\n\t\t\tawait client.delete(`/v1/apps/${props.appName}/volumes/${id}`);\n\t\t} catch (error) {\n\t\t\t// Already gone — deletion is idempotent.\n\t\t\tif (error instanceof ApiNotFoundError) {\n\t\t\t\tpulumi.log.warn(`Fly volume \"${id}\" already deleted`);\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync diff(\n\t\t_id: string,\n\t\tolds: FlyVolumeOutputs,\n\t\tnews: FlyVolumeInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst replaces: string[] = [];\n\n\t\tif (olds.appName !== news.appName) {\n\t\t\treplaces.push(\"appName\");\n\t\t}\n\n\t\tif (olds.name !== news.name) {\n\t\t\treplaces.push(\"name\");\n\t\t}\n\n\t\tif (olds.region !== news.region) {\n\t\t\treplaces.push(\"region\");\n\t\t}\n\n\t\tif (news.sizeGb < olds.sizeGb) {\n\t\t\treplaces.push(\"sizeGb\");\n\t\t}\n\n\t\tconst sizeGrew = news.sizeGb > olds.sizeGb;\n\n\t\treturn {\n\t\t\tchanges: replaces.length > 0 || sizeGrew,\n\t\t\treplaces,\n\t\t\t// volumeId survives an in-place extend (only appName/name/region/shrink\n\t\t\t// replace), so dependents keep a known volumeId during preview.\n\t\t\tstables: replaces.length === 0 ? [\"volumeId\"] : [],\n\t\t\tdeleteBeforeReplace: true,\n\t\t};\n\t}\n}\n\n/** Internal dynamic resource — not part of the public API. */\nclass FlyVolumeResource extends pulumi.dynamic.Resource {\n\tpublic declare readonly volumeId: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: {\n\t\t\ttoken: pulumi.Input<string>;\n\t\t\tappName: pulumi.Input<string>;\n\t\t\tname: pulumi.Input<string>;\n\t\t\tregion: pulumi.Input<string>;\n\t\t\tsizeGb: pulumi.Input<number>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\tsuper(\n\t\t\tnew FlyVolumeResourceProvider(),\n\t\t\tname,\n\t\t\t{ ...args, volumeId: undefined },\n\t\t\t// The API token flows into dynamic-provider state with the outputs — mark it secret there.\n\t\t\t{ ...opts, additionalSecretOutputs: [\"token\"] },\n\t\t);\n\t}\n}\n\n/** Options type for FlyVolume. */\ntype FlyVolumeOptions = Omit<pulumi.ComponentResourceOptions, \"provider\"> & {\n\t/** Fly authentication context. */\n\tprovider: FlyProvider;\n\n\t/** App the volume belongs to. */\n\tapp: FlyApp;\n};\n\n/** Args for FlyVolume. */\nexport interface FlyVolumeArgs {\n\t/** Volume name. */\n\tname: pulumi.Input<string>;\n\n\t/** Region (IATA code, e.g. `\"iad\"`). */\n\tregion: pulumi.Input<string>;\n\n\t/**\n\t * Volume size in GB. Can be grown (extended) but not shrunk.\n\t * Maps to the Fly Machines API field `size_gb`.\n\t */\n\tsizeGb: pulumi.Input<number>;\n}\n\n/**\n * Manages a Fly volume with adopt-or-create semantics.\n *\n * @example\n * ```typescript\n * const volume = new FlyVolume(\"api-data\", {\n * name: \"data\",\n * region: \"iad\",\n * sizeGb: 10,\n * }, { provider, app });\n * ```\n */\nexport class FlyVolume extends pulumi.ComponentResource {\n\t/** Fly-assigned volume ID. */\n\tpublic readonly id: pulumi.Output<string>;\n\n\tconstructor(name: string, args: FlyVolumeArgs, opts: FlyVolumeOptions) {\n\t\tconst { provider, app, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:fly:Volume\", name, {}, pulumiOpts);\n\n\t\tconst resource = new FlyVolumeResource(\n\t\t\t`${name}-resource`,\n\t\t\t{\n\t\t\t\ttoken: provider.token,\n\t\t\t\tappName: app.id,\n\t\t\t\t...args,\n\t\t\t},\n\t\t\t{ parent: this },\n\t\t);\n\n\t\tthis.id = resource.volumeId;\n\n\t\tthis.registerOutputs({ id: this.id });\n\t}\n}\n"],"mappings":"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"}
|