effect-pulumi 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +283 -0
- package/dist/index.cjs +247 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +366 -0
- package/dist/index.d.ts +366 -0
- package/dist/index.js +211 -0
- package/dist/index.js.map +1 -0
- package/package.json +90 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
import * as effect_Cause from 'effect/Cause';
|
|
2
|
+
import * as effect_Types from 'effect/Types';
|
|
3
|
+
import * as pulumi from '@pulumi/pulumi';
|
|
4
|
+
import { Effect } from 'effect';
|
|
5
|
+
import { PulumiFn, LocalWorkspaceOptions, ConfigMap, UpOptions, PreviewOptions, Stack, UpResult, PreviewResult, DestroyOptions, DestroyResult, RefreshOptions, RefreshResult, RemoveOptions, OutputMap } from '@pulumi/pulumi/automation/index.js';
|
|
6
|
+
|
|
7
|
+
declare const PulumiError_base: new <A extends Record<string, any> = {}>(args: effect_Types.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => effect_Cause.YieldableError & {
|
|
8
|
+
readonly _tag: "PulumiError";
|
|
9
|
+
} & Readonly<A>;
|
|
10
|
+
/**
|
|
11
|
+
* Any synchronous failure constructing a resource (bad args, provider
|
|
12
|
+
* validation, etc.), or a failure resolving an Output's promise.
|
|
13
|
+
*
|
|
14
|
+
* Tagged `"PulumiError"`, so it can be matched by tag rather than by
|
|
15
|
+
* `instanceof`:
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* program.pipe(
|
|
20
|
+
* Effect.catchTag("PulumiError", (e) => Effect.logError(e.message))
|
|
21
|
+
* );
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
declare class PulumiError extends PulumiError_base<{
|
|
25
|
+
/** The original thrown value or rejection reason, unwrapped and unmodified.
|
|
26
|
+
* Not necessarily an `Error`. */
|
|
27
|
+
readonly cause: unknown;
|
|
28
|
+
}> {
|
|
29
|
+
/** Derived so anything reading `.message` — plain logging, test failure
|
|
30
|
+
* output, non-Effect error handling — sees the underlying failure instead
|
|
31
|
+
* of an empty string. */
|
|
32
|
+
get message(): string;
|
|
33
|
+
}
|
|
34
|
+
declare const AutomationError_base: new <A extends Record<string, any> = {}>(args: effect_Types.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => effect_Cause.YieldableError & {
|
|
35
|
+
readonly _tag: "AutomationError";
|
|
36
|
+
} & Readonly<A>;
|
|
37
|
+
/**
|
|
38
|
+
* Failure from an Automation API lifecycle call (up/preview/destroy/select).
|
|
39
|
+
*
|
|
40
|
+
* `stage` says which call failed, which matters most in the composite
|
|
41
|
+
* operations: a failed `deploy` could have died selecting the stack, applying
|
|
42
|
+
* config, or running the update, and the three want different responses.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```ts
|
|
46
|
+
* deploy(opts).pipe(
|
|
47
|
+
* Effect.catchTag("AutomationError", (e) =>
|
|
48
|
+
* e.stage === "createOrSelectStack"
|
|
49
|
+
* ? Effect.fail(new BackendUnreachable())
|
|
50
|
+
* : Effect.logError(e.message)
|
|
51
|
+
* )
|
|
52
|
+
* );
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
declare class AutomationError extends AutomationError_base<{
|
|
56
|
+
/** Which Automation API call failed. Named for the wrapper that raised it,
|
|
57
|
+
* so `deploy`'s failures still report the underlying stage. */
|
|
58
|
+
readonly stage: "createOrSelectStack" | "setConfig" | "up" | "preview" | "refresh" | "outputs" | "destroy" | "removeStack";
|
|
59
|
+
/** The rejection reason from the Automation API. For a failed update this is
|
|
60
|
+
* usually a `CommandError` carrying the CLI's stdout and stderr. */
|
|
61
|
+
readonly cause: unknown;
|
|
62
|
+
}> {
|
|
63
|
+
/** `"<stage> failed: <cause>"` — the stage is included because the cause
|
|
64
|
+
* alone rarely says which operation produced it. */
|
|
65
|
+
get message(): string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Lift a single Output into an Effect.
|
|
70
|
+
*
|
|
71
|
+
* `withUnknowns: true` matters during `pulumi preview`, where a not-yet-created
|
|
72
|
+
* resource's outputs have no value: it resolves to Pulumi's unknown sentinel
|
|
73
|
+
* instead of throwing, so a program that reads outputs still previews cleanly.
|
|
74
|
+
*
|
|
75
|
+
* @param output - The Output to resolve.
|
|
76
|
+
* @returns An Effect yielding the settled value, failing with
|
|
77
|
+
* {@link PulumiError} if the underlying promise rejects.
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```ts
|
|
81
|
+
* const bucket = yield* eaws.s3.Bucket("assets", {});
|
|
82
|
+
* const id = yield* fromOutput(bucket.id); // string
|
|
83
|
+
* ```
|
|
84
|
+
*
|
|
85
|
+
* @remarks Reading an Output only makes sense inside a running Pulumi program.
|
|
86
|
+
* During `preview` the value may be the unknown sentinel rather than real data,
|
|
87
|
+
* so don't branch on it to decide what to create.
|
|
88
|
+
*/
|
|
89
|
+
declare const fromOutput: <T>(output: pulumi.Output<T>) => Effect.Effect<T, PulumiError>;
|
|
90
|
+
/**
|
|
91
|
+
* Lift a record of Outputs into a single Effect of the resolved record — use
|
|
92
|
+
* this right after constructing a resource to grab several fields at once.
|
|
93
|
+
*
|
|
94
|
+
* Resolves them together via `pulumi.all`, so it costs one await rather than
|
|
95
|
+
* one per field.
|
|
96
|
+
*
|
|
97
|
+
* @param outputs - A record whose values are all Outputs. Interfaces work as
|
|
98
|
+
* well as object literals; the constraint is self-referential rather than
|
|
99
|
+
* `Record<string, Output<any>>` precisely so interface-typed bags are accepted.
|
|
100
|
+
* @returns An Effect yielding the same record with each value unwrapped.
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* ```ts
|
|
104
|
+
* const bucket = yield* eaws.s3.Bucket("assets", {});
|
|
105
|
+
* const { id, arn } = yield* fromOutputs({ id: bucket.id, arn: bucket.arn });
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
108
|
+
declare const fromOutputs: <T extends { [K in keyof T]: pulumi.Output<any>; }>(outputs: T) => Effect.Effect<{ [K in keyof T]: pulumi.Unwrap<T[K]>; }, PulumiError>;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* effectify — auto-wrap a Pulumi provider package (e.g. @pulumi/aws,
|
|
112
|
+
* @pulumi/cloudflare) so every resource constructor becomes an
|
|
113
|
+
* Effect-returning factory, without the caller ever writing `Effect.sync`.
|
|
114
|
+
*
|
|
115
|
+
* Usage:
|
|
116
|
+
*
|
|
117
|
+
* import * as aws from "@pulumi/aws";
|
|
118
|
+
* const eaws = effectify(aws);
|
|
119
|
+
*
|
|
120
|
+
* const program = Effect.gen(function* () {
|
|
121
|
+
* const bucket = yield* eaws.s3.Bucket("my-bucket", { forceDestroy: true });
|
|
122
|
+
* // ^ Effect<aws.s3.Bucket, PulumiError> — no manual wrapping
|
|
123
|
+
* });
|
|
124
|
+
*
|
|
125
|
+
* How it works:
|
|
126
|
+
* - Every Pulumi resource class extends `pulumi.Resource` under the hood
|
|
127
|
+
* (via CustomResource / ComponentResource). That's the runtime marker
|
|
128
|
+
* used to tell "this export is a resource constructor" apart from "this
|
|
129
|
+
* export is a namespace object" (e.g. `aws.s3`) or "this export is an
|
|
130
|
+
* invoke function" (e.g. `aws.s3.getBucket`).
|
|
131
|
+
* - Namespace objects get recursively proxied (lazily, memoized).
|
|
132
|
+
* - CustomResource constructors get wrapped so any field in their args
|
|
133
|
+
* object may *additionally* be an Effect — resolved (concurrently, they
|
|
134
|
+
* are independent by construction) before construction. This is safe
|
|
135
|
+
* because codegen guarantees CustomResource args are always
|
|
136
|
+
* Record<string, Input<T>>.
|
|
137
|
+
* - Wrapped constructors keep their static members: `Bucket.get(...)`,
|
|
138
|
+
* `Bucket.isInstance(...)` and friends forward to the original class, so
|
|
139
|
+
* the wrapped package can be the only import a program needs.
|
|
140
|
+
* - ComponentResource constructors get wrapped with no arg-lifting — args
|
|
141
|
+
* pass through exactly as declared, since component args aren't
|
|
142
|
+
* guaranteed to be Input<T>-shaped (hand-authored, may do synchronous
|
|
143
|
+
* work on a bare primitive inside the constructor).
|
|
144
|
+
* - Invoke functions (`aws.s3.getBucket`) return an Effect instead of a
|
|
145
|
+
* Promise. There is no runtime marker for "this function is async", so
|
|
146
|
+
* the wrapper calls the function and inspects the result: a thenable
|
|
147
|
+
* becomes an Effect, anything else is returned as-is. That means the
|
|
148
|
+
* invoke *starts* at the call site (see the caveat on `wrapInvokeLike`);
|
|
149
|
+
* `*Output` invoke variants return an Output, which is not thenable, so
|
|
150
|
+
* they pass through untouched — matching the type-level mapping, which
|
|
151
|
+
* only rewrites Promise-returning signatures.
|
|
152
|
+
* - Everything else (enums, plain values, non-resource classes) passes
|
|
153
|
+
* through untouched.
|
|
154
|
+
*
|
|
155
|
+
* Resource registration remains synchronous under the hood — Effect.try
|
|
156
|
+
* runs its thunk immediately. This only removes hand-written wrapper
|
|
157
|
+
* boilerplate, not Pulumi's execution model.
|
|
158
|
+
*/
|
|
159
|
+
|
|
160
|
+
/** Allow any field of a CustomResource args object to *additionally* be an
|
|
161
|
+
* Effect. Homomorphic, so optional fields stay optional and plain
|
|
162
|
+
* `Input<T>`/`Output<T>` values keep working untouched. */
|
|
163
|
+
type LiftedArgs<A> = A extends object ? {
|
|
164
|
+
[K in keyof A]: A[K] | Effect.Effect<A[K], PulumiError>;
|
|
165
|
+
} : A;
|
|
166
|
+
/** Apply `LiftedArgs` to the second constructor parameter (the args object),
|
|
167
|
+
* leaving `name` and `opts` alone. Mapping over the parameter tuple
|
|
168
|
+
* homomorphically preserves labels and optionality, so constructors whose
|
|
169
|
+
* args are optional stay callable as `Bucket("name")`. */
|
|
170
|
+
type LiftArgsParam<P extends readonly unknown[]> = {
|
|
171
|
+
[K in keyof P]: K extends "1" ? LiftedArgs<P[K]> : P[K];
|
|
172
|
+
};
|
|
173
|
+
/** The class's static side, minus `prototype`: `keyof` on a constructor type
|
|
174
|
+
* yields exactly the statics (own and inherited, e.g. codegen'd `get` and
|
|
175
|
+
* `isInstance`), which the runtime wrapper forwards to the original class. */
|
|
176
|
+
type StaticMembers<T> = Omit<T, "prototype">;
|
|
177
|
+
/**
|
|
178
|
+
* The type of {@link effectify}'s result: `T` with every resource constructor
|
|
179
|
+
* and every `Promise`-returning invoke rewritten, and everything else left
|
|
180
|
+
* alone.
|
|
181
|
+
*
|
|
182
|
+
* The cases, in the order they are tried:
|
|
183
|
+
*
|
|
184
|
+
* | Input | Becomes |
|
|
185
|
+
* | --------------------------- | ---------------------------------------------- |
|
|
186
|
+
* | `ComponentResource` class | factory returning `Effect`, args **not** lifted |
|
|
187
|
+
* | `CustomResource` class | factory returning `Effect`, args lifted |
|
|
188
|
+
* | other class | unchanged |
|
|
189
|
+
* | `(...) => Promise<R>` | `(...) => Effect<R, PulumiError>` |
|
|
190
|
+
* | other function | unchanged (includes `*Output` invokes) |
|
|
191
|
+
* | object | mapped recursively |
|
|
192
|
+
* | anything else | unchanged |
|
|
193
|
+
*
|
|
194
|
+
* Statics survive on the wrapped constructors, so `Bucket.get` and
|
|
195
|
+
* `Bucket.isInstance` remain callable.
|
|
196
|
+
*/
|
|
197
|
+
type Effectify<T> = T extends abstract new (...params: infer P) => infer R ? R extends pulumi.ComponentResource ? ((...params: P) => Effect.Effect<R, PulumiError>) & StaticMembers<T> : R extends pulumi.CustomResource ? ((...params: LiftArgsParam<P>) => Effect.Effect<R, PulumiError>) & StaticMembers<T> : T : T extends (...args: infer A) => Promise<infer R> ? (...args: A) => Effect.Effect<R, PulumiError> : T extends (...args: any[]) => any ? T : T extends object ? {
|
|
198
|
+
[K in keyof T]: Effectify<T[K]>;
|
|
199
|
+
} : T;
|
|
200
|
+
/**
|
|
201
|
+
* Wrap a provider package (or any namespace) once, turning every resource
|
|
202
|
+
* constructor into an `Effect`-returning factory and every `Promise`-returning
|
|
203
|
+
* invoke into an `Effect`-returning function.
|
|
204
|
+
*
|
|
205
|
+
* Call this once per package at module scope and export the result — the
|
|
206
|
+
* wrapper is cached, so repeated calls and repeated property reads hand back
|
|
207
|
+
* the same objects, but there is no reason to re-wrap.
|
|
208
|
+
*
|
|
209
|
+
* @param mod - The provider package, or any namespace within one. Not mutated;
|
|
210
|
+
* the result is a read-only proxy that forwards to it.
|
|
211
|
+
* @returns A same-shaped view of `mod`, typed by {@link Effectify}.
|
|
212
|
+
*
|
|
213
|
+
* @example Constructing a resource
|
|
214
|
+
* ```ts
|
|
215
|
+
* import * as aws from "@pulumi/aws";
|
|
216
|
+
* const eaws = effectify(aws);
|
|
217
|
+
*
|
|
218
|
+
* const program = Effect.gen(function* () {
|
|
219
|
+
* const bucket = yield* eaws.s3.Bucket("assets", { forceDestroy: true });
|
|
220
|
+
* return bucket.id;
|
|
221
|
+
* });
|
|
222
|
+
* ```
|
|
223
|
+
*
|
|
224
|
+
* @example Passing an Effect as an argument
|
|
225
|
+
* A `CustomResource`'s args may hold Effects, which are resolved concurrently
|
|
226
|
+
* before the resource is constructed:
|
|
227
|
+
* ```ts
|
|
228
|
+
* yield* eaws.s3.BucketObject("readme", {
|
|
229
|
+
* bucket: bucketIdEffect,
|
|
230
|
+
* content: "hello",
|
|
231
|
+
* });
|
|
232
|
+
* ```
|
|
233
|
+
*
|
|
234
|
+
* @throws Nothing. Failures surface in the returned Effect's error channel as
|
|
235
|
+
* {@link PulumiError} — including synchronous throws from the constructor.
|
|
236
|
+
*
|
|
237
|
+
* @see {@link Effectify} for the type-level mapping.
|
|
238
|
+
*/
|
|
239
|
+
declare function effectify<T extends object>(mod: T): Effectify<T>;
|
|
240
|
+
|
|
241
|
+
/** Arguments for an inline program — the Pulumi program is a function in this
|
|
242
|
+
* process, with no `Pulumi.yaml` on disk. */
|
|
243
|
+
interface InlineStackOptions {
|
|
244
|
+
/** Stack to select, created if absent. */
|
|
245
|
+
readonly stackName: string;
|
|
246
|
+
/** Project name to register the stack under. Chosen freely here, since
|
|
247
|
+
* there is no `Pulumi.yaml` to take it from — but it is part of the stack's
|
|
248
|
+
* identity in the backend, so changing it later points at a different
|
|
249
|
+
* stack. */
|
|
250
|
+
readonly projectName: string;
|
|
251
|
+
/** The program itself. Runs in this process, so it needs no separate Node
|
|
252
|
+
* runtime and can close over values from the caller. */
|
|
253
|
+
readonly program: PulumiFn;
|
|
254
|
+
readonly workspaceOptions?: LocalWorkspaceOptions;
|
|
255
|
+
}
|
|
256
|
+
/** Arguments for a local program — an existing Pulumi project on disk. */
|
|
257
|
+
interface LocalStackOptions {
|
|
258
|
+
/** Stack to select, created if absent. */
|
|
259
|
+
readonly stackName: string;
|
|
260
|
+
/** Directory holding the project's `Pulumi.yaml`. Its `name:` supplies the
|
|
261
|
+
* project name, which is why there is no `projectName` here. */
|
|
262
|
+
readonly workDir: string;
|
|
263
|
+
readonly workspaceOptions?: LocalWorkspaceOptions;
|
|
264
|
+
}
|
|
265
|
+
/** Either flavour of stack. Discriminated at runtime by the presence of
|
|
266
|
+
* `workDir`, so the two are not interchangeable: an inline program needs
|
|
267
|
+
* `projectName`, a local one takes it from `Pulumi.yaml`. */
|
|
268
|
+
type StackOptions = InlineStackOptions | LocalStackOptions;
|
|
269
|
+
/** Select the stack, creating it if it does not exist, and return the handle
|
|
270
|
+
* every other operation here takes.
|
|
271
|
+
*
|
|
272
|
+
* Creating the workspace is itself work — it may write files and shell out to
|
|
273
|
+
* the CLI — so hold on to the returned `Stack` rather than re-selecting before
|
|
274
|
+
* each operation. */
|
|
275
|
+
declare const createOrSelectStack: (opts: StackOptions) => Effect.Effect<Stack, AutomationError>;
|
|
276
|
+
/** Apply the whole config map in one `setAllConfig` call — a single CLI
|
|
277
|
+
* round-trip, where per-key `setConfig` costs one `pulumi config set`
|
|
278
|
+
* invocation each. */
|
|
279
|
+
declare const setStackConfig: (stack: Stack, config: ConfigMap | undefined) => Effect.Effect<void, AutomationError>;
|
|
280
|
+
/** Compute the plan without applying it.
|
|
281
|
+
*
|
|
282
|
+
* A preview is a full engine run against the provider, not a cheap check — see
|
|
283
|
+
* {@link DeployOptions.preview} before pairing one with an `up`. */
|
|
284
|
+
declare const previewStack: (stack: Stack, opts?: PreviewOptions) => Effect.Effect<PreviewResult, AutomationError>;
|
|
285
|
+
/** Apply the program: create, update and delete resources to match it.
|
|
286
|
+
*
|
|
287
|
+
* The result carries the stack's outputs and a summary; pass `onOutput` to
|
|
288
|
+
* watch progress while it runs. */
|
|
289
|
+
declare const upStack: (stack: Stack, opts?: UpOptions) => Effect.Effect<UpResult, AutomationError>;
|
|
290
|
+
/** Refresh the stack's state from the actual cloud resources, without
|
|
291
|
+
* changing them — what to run when state may have drifted (manual console
|
|
292
|
+
* edits, a crashed update) before deciding what to do about it. */
|
|
293
|
+
declare const refreshStack: (stack: Stack, opts?: RefreshOptions) => Effect.Effect<RefreshResult, AutomationError>;
|
|
294
|
+
/** Read the stack's current outputs without running an update. */
|
|
295
|
+
declare const stackOutputs: (stack: Stack) => Effect.Effect<OutputMap, AutomationError>;
|
|
296
|
+
/** Destroy the stack's resources. The stack itself remains registered with
|
|
297
|
+
* the backend — see `removeStack` / `teardownStack` to delete it too. */
|
|
298
|
+
declare const destroyStack: (stack: Stack, opts?: DestroyOptions) => Effect.Effect<DestroyResult, AutomationError>;
|
|
299
|
+
/** Delete the stack and its configuration and history from the backend.
|
|
300
|
+
*
|
|
301
|
+
* This does not destroy resources — run `destroyStack` first, or use
|
|
302
|
+
* `teardownStack`. Pulumi refuses to remove a stack that still has resources
|
|
303
|
+
* unless `RemoveOptions.force` is set, and forcing it orphans them: they keep
|
|
304
|
+
* existing and billing with nothing tracking them. */
|
|
305
|
+
declare const removeStack: (stack: Stack, opts?: RemoveOptions) => Effect.Effect<void, AutomationError>;
|
|
306
|
+
/** Full teardown: destroy the resources, then delete the stack.
|
|
307
|
+
*
|
|
308
|
+
* `destroyStack` alone leaves an empty stack behind, so anything creating
|
|
309
|
+
* stacks per-run (ephemeral environments, tests naming stacks by timestamp)
|
|
310
|
+
* accumulates them in the backend. */
|
|
311
|
+
declare const teardownStack: (stack: Stack, opts?: {
|
|
312
|
+
readonly destroy?: DestroyOptions;
|
|
313
|
+
readonly remove?: RemoveOptions;
|
|
314
|
+
}) => Effect.Effect<DestroyResult, AutomationError>;
|
|
315
|
+
/** {@link deploy}'s arguments: the stack to target, plus what to do with it. */
|
|
316
|
+
type DeployOptions = StackOptions & {
|
|
317
|
+
/** Config to apply before the update, in one `setAllConfig` call. Keys are
|
|
318
|
+
* fully qualified (`"my-project:myKey"`). */
|
|
319
|
+
readonly config?: ConfigMap;
|
|
320
|
+
/** Options forwarded to the update — `onOutput` to stream progress,
|
|
321
|
+
* `parallel`, `target`, and so on. */
|
|
322
|
+
readonly up?: UpOptions;
|
|
323
|
+
/** Run `preview` before `up`, returning its result.
|
|
324
|
+
*
|
|
325
|
+
* Off by default: a preview is a full engine run against the provider, so
|
|
326
|
+
* previewing and then immediately upping does the work twice. `up` reports
|
|
327
|
+
* the same failures, so this earns its cost only when you want the plan
|
|
328
|
+
* itself. */
|
|
329
|
+
readonly preview?: PreviewOptions | boolean;
|
|
330
|
+
};
|
|
331
|
+
/** What {@link deploy} hands back. */
|
|
332
|
+
interface DeployResult {
|
|
333
|
+
/** The selected stack, so teardown needs no second `createOrSelectStack`. */
|
|
334
|
+
readonly stack: Stack;
|
|
335
|
+
/** The update's result — `outputs` and `summary` live here. */
|
|
336
|
+
readonly result: UpResult;
|
|
337
|
+
/** Present only when `preview` was requested. */
|
|
338
|
+
readonly preview?: PreviewResult;
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Select or create the stack, apply config, optionally preview, then up.
|
|
342
|
+
*
|
|
343
|
+
* The common path, assembled from the primitives above. Anything more
|
|
344
|
+
* involved — refreshing first, inspecting the plan before deciding, retrying a
|
|
345
|
+
* stage — should compose those directly rather than grow options here.
|
|
346
|
+
*
|
|
347
|
+
* @param opts - Which stack, and what to do with it.
|
|
348
|
+
* @returns The stack handle alongside the results, so callers can tear down
|
|
349
|
+
* afterwards without re-selecting.
|
|
350
|
+
*
|
|
351
|
+
* @example Deploy, use the outputs, then always tear down
|
|
352
|
+
* ```ts
|
|
353
|
+
* Effect.scoped(
|
|
354
|
+
* Effect.gen(function* () {
|
|
355
|
+
* const { result } = yield* Effect.acquireRelease(
|
|
356
|
+
* deploy({ stackName, projectName, program, up: { onOutput } }),
|
|
357
|
+
* ({ stack }) => teardownStack(stack).pipe(Effect.ignore)
|
|
358
|
+
* );
|
|
359
|
+
* return result.outputs.bucketId?.value;
|
|
360
|
+
* })
|
|
361
|
+
* );
|
|
362
|
+
* ```
|
|
363
|
+
*/
|
|
364
|
+
declare const deploy: (opts: DeployOptions) => Effect.Effect<DeployResult, AutomationError>;
|
|
365
|
+
|
|
366
|
+
export { AutomationError, type DeployOptions, type DeployResult, type Effectify, type InlineStackOptions, type LocalStackOptions, PulumiError, type StackOptions, createOrSelectStack, deploy, destroyStack, effectify, fromOutput, fromOutputs, previewStack, refreshStack, removeStack, setStackConfig, stackOutputs, teardownStack, upStack };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { Data, Effect } from 'effect';
|
|
2
|
+
import * as pulumi2 from '@pulumi/pulumi';
|
|
3
|
+
import { LocalWorkspace } from '@pulumi/pulumi/automation/index.js';
|
|
4
|
+
|
|
5
|
+
// src/errors.ts
|
|
6
|
+
var describeCause = (cause) => cause instanceof Error ? cause.message : String(cause);
|
|
7
|
+
var PulumiError = class extends Data.TaggedError("PulumiError") {
|
|
8
|
+
/** Derived so anything reading `.message` — plain logging, test failure
|
|
9
|
+
* output, non-Effect error handling — sees the underlying failure instead
|
|
10
|
+
* of an empty string. */
|
|
11
|
+
get message() {
|
|
12
|
+
return describeCause(this.cause);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
var AutomationError = class extends Data.TaggedError("AutomationError") {
|
|
16
|
+
/** `"<stage> failed: <cause>"` — the stage is included because the cause
|
|
17
|
+
* alone rarely says which operation produced it. */
|
|
18
|
+
get message() {
|
|
19
|
+
return `${this.stage} failed: ${describeCause(this.cause)}`;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
var fromOutput = (output) => Effect.tryPromise({
|
|
23
|
+
try: () => output.promise(true),
|
|
24
|
+
catch: (cause) => new PulumiError({ cause })
|
|
25
|
+
});
|
|
26
|
+
var fromOutputs = (outputs) => fromOutput(pulumi2.all(outputs));
|
|
27
|
+
var isResourceConstructor = (value) => typeof value === "function" && (value === pulumi2.Resource || value.prototype instanceof pulumi2.Resource);
|
|
28
|
+
var isComponentResourceConstructor = (Ctor) => Ctor === pulumi2.ComponentResource || Ctor.prototype instanceof pulumi2.ComponentResource;
|
|
29
|
+
var isNamespace = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
30
|
+
var isArgsObject = (value) => isNamespace(value);
|
|
31
|
+
var isPromiseLike = (value) => typeof value === "object" && value !== null && typeof value.then === "function";
|
|
32
|
+
var proxyCache = /* @__PURE__ */ new WeakMap();
|
|
33
|
+
var isEffect = (value) => Effect.isEffect(value);
|
|
34
|
+
function resolveLiftedArgs(args) {
|
|
35
|
+
if (!isArgsObject(args)) {
|
|
36
|
+
return Effect.succeed(args);
|
|
37
|
+
}
|
|
38
|
+
const effectEntries = Object.entries(args).filter(([, v]) => isEffect(v));
|
|
39
|
+
if (effectEntries.length === 0) {
|
|
40
|
+
return Effect.succeed(args);
|
|
41
|
+
}
|
|
42
|
+
return Effect.map(
|
|
43
|
+
Effect.all(
|
|
44
|
+
effectEntries.map(
|
|
45
|
+
([key, effectValue]) => Effect.map(
|
|
46
|
+
effectValue,
|
|
47
|
+
(resolved) => [key, resolved]
|
|
48
|
+
)
|
|
49
|
+
),
|
|
50
|
+
{ concurrency: "unbounded" }
|
|
51
|
+
),
|
|
52
|
+
(resolved) => ({ ...args, ...Object.fromEntries(resolved) })
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
function wrapCustomResourceCtor(Ctor) {
|
|
56
|
+
return (name, args, opts) => Effect.gen(function* () {
|
|
57
|
+
const resolvedArgs = yield* resolveLiftedArgs(args);
|
|
58
|
+
return yield* Effect.try({
|
|
59
|
+
try: () => new Ctor(name, resolvedArgs, opts),
|
|
60
|
+
catch: (cause) => new PulumiError({ cause })
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
function wrapComponentResourceCtor(Ctor) {
|
|
65
|
+
return (name, args, opts) => Effect.try({
|
|
66
|
+
try: () => new Ctor(name, args, opts),
|
|
67
|
+
catch: (cause) => new PulumiError({ cause })
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
var withStatics = (factory, Ctor) => new Proxy(factory, {
|
|
71
|
+
get: (target, prop, receiver) => Reflect.has(target, prop) ? Reflect.get(target, prop, receiver) : Reflect.get(Ctor, prop),
|
|
72
|
+
has: (target, prop) => Reflect.has(target, prop) || Reflect.has(Ctor, prop)
|
|
73
|
+
});
|
|
74
|
+
function wrapResourceCtor(Ctor) {
|
|
75
|
+
const factory = isComponentResourceConstructor(Ctor) ? wrapComponentResourceCtor(Ctor) : wrapCustomResourceCtor(
|
|
76
|
+
Ctor
|
|
77
|
+
);
|
|
78
|
+
return withStatics(factory, Ctor);
|
|
79
|
+
}
|
|
80
|
+
function wrapInvokeLike(fn) {
|
|
81
|
+
return new Proxy(fn, {
|
|
82
|
+
apply(target, thisArg, args) {
|
|
83
|
+
const result = Reflect.apply(target, thisArg, args);
|
|
84
|
+
if (!isPromiseLike(result)) {
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
result.then(void 0, () => {
|
|
88
|
+
});
|
|
89
|
+
return Effect.tryPromise({
|
|
90
|
+
try: () => result,
|
|
91
|
+
catch: (cause) => new PulumiError({ cause })
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
var wrapperCache = /* @__PURE__ */ new WeakMap();
|
|
97
|
+
function effectifyValue(value) {
|
|
98
|
+
if (isResourceConstructor(value)) {
|
|
99
|
+
const cached = wrapperCache.get(value);
|
|
100
|
+
if (cached) return cached;
|
|
101
|
+
const wrapped = wrapResourceCtor(value);
|
|
102
|
+
wrapperCache.set(value, wrapped);
|
|
103
|
+
return wrapped;
|
|
104
|
+
}
|
|
105
|
+
if (typeof value === "function") {
|
|
106
|
+
const cached = wrapperCache.get(value);
|
|
107
|
+
if (cached) return cached;
|
|
108
|
+
const wrapped = wrapInvokeLike(value);
|
|
109
|
+
wrapperCache.set(value, wrapped);
|
|
110
|
+
return wrapped;
|
|
111
|
+
}
|
|
112
|
+
if (isNamespace(value)) {
|
|
113
|
+
return effectifyInner(value);
|
|
114
|
+
}
|
|
115
|
+
return value;
|
|
116
|
+
}
|
|
117
|
+
function effectifyInner(mod) {
|
|
118
|
+
const cached = proxyCache.get(mod);
|
|
119
|
+
if (cached) return cached;
|
|
120
|
+
const proxy = new Proxy(/* @__PURE__ */ Object.create(null), {
|
|
121
|
+
get(_target, prop) {
|
|
122
|
+
return effectifyValue(Reflect.get(mod, prop));
|
|
123
|
+
},
|
|
124
|
+
has(_target, prop) {
|
|
125
|
+
return Reflect.has(mod, prop);
|
|
126
|
+
},
|
|
127
|
+
ownKeys() {
|
|
128
|
+
return Reflect.ownKeys(mod);
|
|
129
|
+
},
|
|
130
|
+
getOwnPropertyDescriptor(_target, prop) {
|
|
131
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(mod, prop);
|
|
132
|
+
if (!descriptor) return void 0;
|
|
133
|
+
return {
|
|
134
|
+
value: effectifyValue(Reflect.get(mod, prop)),
|
|
135
|
+
enumerable: descriptor.enumerable,
|
|
136
|
+
configurable: true,
|
|
137
|
+
writable: true
|
|
138
|
+
};
|
|
139
|
+
},
|
|
140
|
+
set() {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
proxyCache.set(mod, proxy);
|
|
145
|
+
return proxy;
|
|
146
|
+
}
|
|
147
|
+
function effectify(mod) {
|
|
148
|
+
return effectifyInner(mod);
|
|
149
|
+
}
|
|
150
|
+
var isLocal = (opts) => "workDir" in opts;
|
|
151
|
+
var createOrSelectStack = (opts) => Effect.tryPromise({
|
|
152
|
+
try: () => isLocal(opts) ? LocalWorkspace.createOrSelectStack(
|
|
153
|
+
{ stackName: opts.stackName, workDir: opts.workDir },
|
|
154
|
+
opts.workspaceOptions
|
|
155
|
+
) : LocalWorkspace.createOrSelectStack(
|
|
156
|
+
{
|
|
157
|
+
stackName: opts.stackName,
|
|
158
|
+
projectName: opts.projectName,
|
|
159
|
+
program: opts.program
|
|
160
|
+
},
|
|
161
|
+
opts.workspaceOptions
|
|
162
|
+
),
|
|
163
|
+
catch: (cause) => new AutomationError({ stage: "createOrSelectStack", cause })
|
|
164
|
+
});
|
|
165
|
+
var setStackConfig = (stack, config) => !config || Object.keys(config).length === 0 ? Effect.void : Effect.tryPromise({
|
|
166
|
+
try: () => stack.setAllConfig(config),
|
|
167
|
+
catch: (cause) => new AutomationError({ stage: "setConfig", cause })
|
|
168
|
+
});
|
|
169
|
+
var previewStack = (stack, opts) => Effect.tryPromise({
|
|
170
|
+
try: () => stack.preview(opts),
|
|
171
|
+
catch: (cause) => new AutomationError({ stage: "preview", cause })
|
|
172
|
+
});
|
|
173
|
+
var upStack = (stack, opts) => Effect.tryPromise({
|
|
174
|
+
try: () => stack.up(opts),
|
|
175
|
+
catch: (cause) => new AutomationError({ stage: "up", cause })
|
|
176
|
+
});
|
|
177
|
+
var refreshStack = (stack, opts) => Effect.tryPromise({
|
|
178
|
+
try: () => stack.refresh(opts),
|
|
179
|
+
catch: (cause) => new AutomationError({ stage: "refresh", cause })
|
|
180
|
+
});
|
|
181
|
+
var stackOutputs = (stack) => Effect.tryPromise({
|
|
182
|
+
try: () => stack.outputs(),
|
|
183
|
+
catch: (cause) => new AutomationError({ stage: "outputs", cause })
|
|
184
|
+
});
|
|
185
|
+
var destroyStack = (stack, opts) => Effect.tryPromise({
|
|
186
|
+
try: () => stack.destroy(opts),
|
|
187
|
+
catch: (cause) => new AutomationError({ stage: "destroy", cause })
|
|
188
|
+
});
|
|
189
|
+
var removeStack = (stack, opts) => Effect.tryPromise({
|
|
190
|
+
try: () => stack.workspace.removeStack(stack.name, opts),
|
|
191
|
+
catch: (cause) => new AutomationError({ stage: "removeStack", cause })
|
|
192
|
+
});
|
|
193
|
+
var teardownStack = (stack, opts) => Effect.gen(function* () {
|
|
194
|
+
const result = yield* destroyStack(stack, opts?.destroy);
|
|
195
|
+
yield* removeStack(stack, opts?.remove);
|
|
196
|
+
return result;
|
|
197
|
+
});
|
|
198
|
+
var deploy = (opts) => Effect.gen(function* () {
|
|
199
|
+
const stack = yield* createOrSelectStack(opts);
|
|
200
|
+
yield* setStackConfig(stack, opts.config);
|
|
201
|
+
const preview = opts.preview ? yield* previewStack(
|
|
202
|
+
stack,
|
|
203
|
+
typeof opts.preview === "boolean" ? void 0 : opts.preview
|
|
204
|
+
) : void 0;
|
|
205
|
+
const result = yield* upStack(stack, opts.up);
|
|
206
|
+
return { stack, result, preview };
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
export { AutomationError, PulumiError, createOrSelectStack, deploy, destroyStack, effectify, fromOutput, fromOutputs, previewStack, refreshStack, removeStack, setStackConfig, stackOutputs, teardownStack, upStack };
|
|
210
|
+
//# sourceMappingURL=index.js.map
|
|
211
|
+
//# sourceMappingURL=index.js.map
|