effect-pulumi 0.1.1 → 0.2.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/README.md +46 -176
- package/dist/index.cjs +460 -199
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +93 -148
- package/dist/index.d.ts +93 -148
- package/dist/index.js +436 -177
- package/dist/index.js.map +1 -1
- package/package.json +12 -13
package/dist/index.cjs
CHANGED
|
@@ -1,233 +1,494 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
var
|
|
4
|
-
var
|
|
5
|
-
var
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region \0rolldown/runtime.js
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
11
|
+
key = keys[i];
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
13
|
+
get: ((k) => from[k]).bind(null, key),
|
|
14
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
|
|
20
|
+
value: mod,
|
|
21
|
+
enumerable: true
|
|
22
|
+
}) : target, mod));
|
|
23
|
+
//#endregion
|
|
24
|
+
let effect = require("effect");
|
|
25
|
+
let _pulumi_pulumi = require("@pulumi/pulumi");
|
|
26
|
+
_pulumi_pulumi = __toESM(_pulumi_pulumi, 1);
|
|
27
|
+
let _pulumi_pulumi_automation_index_js = require("@pulumi/pulumi/automation/index.js");
|
|
28
|
+
//#region src/errors.ts
|
|
29
|
+
const describeCause = (cause) => cause instanceof Error ? cause.message : String(cause);
|
|
30
|
+
/**
|
|
31
|
+
* Any synchronous failure constructing a resource (bad args, provider
|
|
32
|
+
* validation, etc.), or a failure resolving an Output's promise.
|
|
33
|
+
*
|
|
34
|
+
* Tagged `"PulumiError"`, so it can be matched by tag rather than by
|
|
35
|
+
* `instanceof`:
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* program.pipe(
|
|
40
|
+
* Effect.catchTag("PulumiError", (e) => Effect.logError(e.message))
|
|
41
|
+
* );
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
29
44
|
var PulumiError = class extends effect.Data.TaggedError("PulumiError") {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
45
|
+
/** Derived so anything reading `.message` - plain logging, test failure
|
|
46
|
+
* output, non-Effect error handling - sees the underlying failure instead
|
|
47
|
+
* of an empty string. */
|
|
48
|
+
get message() {
|
|
49
|
+
return describeCause(this.cause);
|
|
50
|
+
}
|
|
36
51
|
};
|
|
52
|
+
/**
|
|
53
|
+
* Failure from an Automation API lifecycle call (up/preview/destroy/select).
|
|
54
|
+
*
|
|
55
|
+
* `stage` says which call failed, which matters most in the composite
|
|
56
|
+
* operations: a failed `deploy` could have died selecting the stack, applying
|
|
57
|
+
* config, or running the update, and the three want different responses.
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```ts
|
|
61
|
+
* deploy(opts).pipe(
|
|
62
|
+
* Effect.catchTag("AutomationError", (e) =>
|
|
63
|
+
* e.stage === "createOrSelectStack"
|
|
64
|
+
* ? Effect.fail(new BackendUnreachable())
|
|
65
|
+
* : Effect.logError(e.message)
|
|
66
|
+
* )
|
|
67
|
+
* );
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
37
70
|
var AutomationError = class extends effect.Data.TaggedError("AutomationError") {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
71
|
+
/** `"<stage> failed: <cause>"` - the stage is included because the cause
|
|
72
|
+
* alone rarely says which operation produced it. */
|
|
73
|
+
get message() {
|
|
74
|
+
return `${this.stage} failed: ${describeCause(this.cause)}`;
|
|
75
|
+
}
|
|
43
76
|
};
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
77
|
+
//#endregion
|
|
78
|
+
//#region src/output-bridge.ts
|
|
79
|
+
/**
|
|
80
|
+
* Lift a single Output into an Effect.
|
|
81
|
+
*
|
|
82
|
+
* `withUnknowns: true` matters during `pulumi preview`, where a not-yet-created
|
|
83
|
+
* resource's outputs have no value: it resolves to Pulumi's unknown sentinel
|
|
84
|
+
* instead of throwing, so a program that reads outputs still previews cleanly.
|
|
85
|
+
*
|
|
86
|
+
* @param output - The Output to resolve.
|
|
87
|
+
* @returns An Effect yielding the settled value, failing with
|
|
88
|
+
* {@link PulumiError} if the underlying promise rejects.
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```ts
|
|
92
|
+
* const bucket = yield* eaws.s3.Bucket("assets", {});
|
|
93
|
+
* const id = yield* fromOutput(bucket.id); // string
|
|
94
|
+
* ```
|
|
95
|
+
*
|
|
96
|
+
* @remarks Reading an Output only makes sense inside a running Pulumi program.
|
|
97
|
+
* During `preview` the value may be the unknown sentinel rather than real data,
|
|
98
|
+
* so don't branch on it to decide what to create.
|
|
99
|
+
*/
|
|
100
|
+
const fromOutput = (output) => effect.Effect.tryPromise({
|
|
101
|
+
try: () => output.promise(true),
|
|
102
|
+
catch: (cause) => new PulumiError({ cause })
|
|
47
103
|
});
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
104
|
+
/**
|
|
105
|
+
* Lift a record of Outputs into a single Effect of the resolved record - use
|
|
106
|
+
* this right after constructing a resource to grab several fields at once.
|
|
107
|
+
*
|
|
108
|
+
* Resolves them together via `pulumi.all`, so it costs one await rather than
|
|
109
|
+
* one per field.
|
|
110
|
+
*
|
|
111
|
+
* @param outputs - A record whose values are all Outputs. Interfaces work as
|
|
112
|
+
* well as object literals; the constraint is self-referential rather than
|
|
113
|
+
* `Record<string, Output<any>>` precisely so interface-typed bags are accepted.
|
|
114
|
+
* @returns An Effect yielding the same record with each value unwrapped.
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* ```ts
|
|
118
|
+
* const bucket = yield* eaws.s3.Bucket("assets", {});
|
|
119
|
+
* const { id, arn } = yield* fromOutputs({ id: bucket.id, arn: bucket.arn });
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
122
|
+
const fromOutputs = (outputs) => fromOutput(_pulumi_pulumi.all(outputs));
|
|
123
|
+
//#endregion
|
|
124
|
+
//#region src/effectify.ts
|
|
125
|
+
/**
|
|
126
|
+
* effectify - auto-wrap a Pulumi provider package (e.g. @pulumi/aws,
|
|
127
|
+
* @pulumi/cloudflare) so every resource constructor becomes an
|
|
128
|
+
* Effect-returning factory, without the caller ever writing `Effect.sync`.
|
|
129
|
+
*
|
|
130
|
+
* Usage:
|
|
131
|
+
*
|
|
132
|
+
* import * as aws from "@pulumi/aws";
|
|
133
|
+
* const eaws = effectify(aws);
|
|
134
|
+
*
|
|
135
|
+
* const program = Effect.gen(function* () {
|
|
136
|
+
* const bucket = yield* eaws.s3.Bucket("my-bucket", { forceDestroy: true });
|
|
137
|
+
* // ^ Effect<aws.s3.Bucket, PulumiError> - no manual wrapping
|
|
138
|
+
* });
|
|
139
|
+
*
|
|
140
|
+
* How it works:
|
|
141
|
+
* - Every Pulumi resource class extends `pulumi.Resource` under the hood
|
|
142
|
+
* (via CustomResource / ComponentResource). That's the runtime marker
|
|
143
|
+
* used to tell "this export is a resource constructor" apart from "this
|
|
144
|
+
* export is a namespace object" (e.g. `aws.s3`) or "this export is an
|
|
145
|
+
* invoke function" (e.g. `aws.s3.getBucket`).
|
|
146
|
+
* - Namespace objects get recursively proxied (lazily, memoized).
|
|
147
|
+
* - CustomResource constructors get wrapped so any field in their args
|
|
148
|
+
* object may *additionally* be an Effect - resolved (concurrently, they
|
|
149
|
+
* are independent by construction) before construction. This is safe
|
|
150
|
+
* because codegen guarantees CustomResource args are always
|
|
151
|
+
* Record<string, Input<T>>.
|
|
152
|
+
* - Wrapped constructors keep their static members: `Bucket.get(...)`,
|
|
153
|
+
* `Bucket.isInstance(...)` and friends forward to the original class, so
|
|
154
|
+
* the wrapped package can be the only import a program needs.
|
|
155
|
+
* - ComponentResource constructors get wrapped with no arg-lifting - args
|
|
156
|
+
* pass through exactly as declared, since component args aren't
|
|
157
|
+
* guaranteed to be Input<T>-shaped (hand-authored, may do synchronous
|
|
158
|
+
* work on a bare primitive inside the constructor).
|
|
159
|
+
* - Invoke functions (`aws.s3.getBucket`) return an Effect instead of a
|
|
160
|
+
* Promise. There is no runtime marker for "this function is async", so
|
|
161
|
+
* the wrapper calls the function and inspects the result: a thenable
|
|
162
|
+
* becomes an Effect, anything else is returned as-is. That means the
|
|
163
|
+
* invoke *starts* at the call site (see the caveat on `wrapInvokeLike`);
|
|
164
|
+
* `*Output` invoke variants return an Output, which is not thenable, so
|
|
165
|
+
* they pass through untouched - matching the type-level mapping, which
|
|
166
|
+
* only rewrites Promise-returning signatures.
|
|
167
|
+
* - Everything else (enums, plain values, non-resource classes) passes
|
|
168
|
+
* through untouched.
|
|
169
|
+
*
|
|
170
|
+
* Resource registration remains synchronous under the hood - Effect.try
|
|
171
|
+
* runs its thunk immediately. This only removes hand-written wrapper
|
|
172
|
+
* boilerplate, not Pulumi's execution model.
|
|
173
|
+
*/
|
|
174
|
+
const isResourceConstructor = (value) => typeof value === "function" && (value === _pulumi_pulumi.Resource || value.prototype instanceof _pulumi_pulumi.Resource);
|
|
175
|
+
const isComponentResourceConstructor = (Ctor) => Ctor === _pulumi_pulumi.ComponentResource || Ctor.prototype instanceof _pulumi_pulumi.ComponentResource;
|
|
176
|
+
/** Is this a namespace to recurse into? The narrowed type is only ever handed
|
|
177
|
+
* to `effectifyInner`, which takes `object` - so claim exactly that and no
|
|
178
|
+
* more. */
|
|
179
|
+
const isNamespace = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
180
|
+
/** Is this an args object whose entries we can scan for Effects? Same runtime
|
|
181
|
+
* check as `isNamespace`, but a different question, so it gets the narrowing
|
|
182
|
+
* that question needs: `object` would make `Object.entries` infer `any` values
|
|
183
|
+
* and silently drop the `isEffect` filter's type safety. */
|
|
184
|
+
const isArgsObject = (value) => isNamespace(value);
|
|
185
|
+
/** Note: `pulumi.Output` is deliberately not thenable, so `*Output` invoke
|
|
186
|
+
* variants fail this check and pass through unwrapped - keeping the runtime
|
|
187
|
+
* behaviour aligned with the type mapping, which only rewrites
|
|
188
|
+
* Promise-returning signatures. */
|
|
189
|
+
const isPromiseLike = (value) => typeof value === "object" && value !== null && typeof value.then === "function";
|
|
190
|
+
const proxyCache = /* @__PURE__ */ new WeakMap();
|
|
191
|
+
const isEffect = (value) => effect.Effect.isEffect(value);
|
|
56
192
|
function resolveLiftedArgs(args) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
return effect.Effect.map(
|
|
65
|
-
effect.Effect.all(
|
|
66
|
-
effectEntries.map(
|
|
67
|
-
([key, effectValue]) => effect.Effect.map(
|
|
68
|
-
effectValue,
|
|
69
|
-
(resolved) => [key, resolved]
|
|
70
|
-
)
|
|
71
|
-
),
|
|
72
|
-
{ concurrency: "unbounded" }
|
|
73
|
-
),
|
|
74
|
-
(resolved) => ({ ...args, ...Object.fromEntries(resolved) })
|
|
75
|
-
);
|
|
193
|
+
if (!isArgsObject(args)) return effect.Effect.succeed(args);
|
|
194
|
+
const effectEntries = Object.entries(args).filter(([, v]) => isEffect(v));
|
|
195
|
+
if (effectEntries.length === 0) return effect.Effect.succeed(args);
|
|
196
|
+
return effect.Effect.map(effect.Effect.all(effectEntries.map(([key, effectValue]) => effect.Effect.map(effectValue, (resolved) => [key, resolved])), { concurrency: "unbounded" }), (resolved) => ({
|
|
197
|
+
...args,
|
|
198
|
+
...Object.fromEntries(resolved)
|
|
199
|
+
}));
|
|
76
200
|
}
|
|
77
201
|
function wrapCustomResourceCtor(Ctor) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
202
|
+
return (name, args, opts) => effect.Effect.gen(function* () {
|
|
203
|
+
const resolvedArgs = yield* resolveLiftedArgs(args);
|
|
204
|
+
return yield* effect.Effect.try({
|
|
205
|
+
try: () => new Ctor(name, resolvedArgs, opts),
|
|
206
|
+
catch: (cause) => new PulumiError({ cause })
|
|
207
|
+
});
|
|
208
|
+
});
|
|
85
209
|
}
|
|
86
210
|
function wrapComponentResourceCtor(Ctor) {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
211
|
+
return (name, args, opts) => effect.Effect.try({
|
|
212
|
+
try: () => new Ctor(name, args, opts),
|
|
213
|
+
catch: (cause) => new PulumiError({ cause })
|
|
214
|
+
});
|
|
91
215
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
216
|
+
/** Forward static members (codegen'd `get`, `isInstance`, …) from the class
|
|
217
|
+
* onto the factory. The factory's own and inherited properties (`name`,
|
|
218
|
+
* `length`, `call`, …) win; anything else falls through to the class, so new
|
|
219
|
+
* statics keep working without being enumerated here. A bonus of forwarding
|
|
220
|
+
* `prototype` is that `x instanceof wrapped` still works. */
|
|
221
|
+
const withStatics = (factory, Ctor) => new Proxy(factory, {
|
|
222
|
+
get: (target, prop, receiver) => Reflect.has(target, prop) ? Reflect.get(target, prop, receiver) : Reflect.get(Ctor, prop),
|
|
223
|
+
has: (target, prop) => Reflect.has(target, prop) || Reflect.has(Ctor, prop)
|
|
95
224
|
});
|
|
96
225
|
function wrapResourceCtor(Ctor) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
);
|
|
100
|
-
return withStatics(factory, Ctor);
|
|
226
|
+
const factory = isComponentResourceConstructor(Ctor) ? wrapComponentResourceCtor(Ctor) : wrapCustomResourceCtor(Ctor);
|
|
227
|
+
return withStatics(factory, Ctor);
|
|
101
228
|
}
|
|
229
|
+
/** Turn a Promise-returning invoke into an Effect-returning one, leaving
|
|
230
|
+
* synchronous functions' behaviour untouched.
|
|
231
|
+
*
|
|
232
|
+
* A Proxy over the original function (rather than a new function) so `name`,
|
|
233
|
+
* `length`, own properties and prototype all survive; only the call itself is
|
|
234
|
+
* intercepted.
|
|
235
|
+
*
|
|
236
|
+
* Caveat, stated openly: whether a function is async is only knowable by
|
|
237
|
+
* calling it, so the invoke *starts* when the factory is called - the Effect
|
|
238
|
+
* resolves an already-in-flight Promise rather than deferring the call. Two
|
|
239
|
+
* consequences:
|
|
240
|
+
* - `Effect.retry` re-awaits the same call instead of re-invoking. To
|
|
241
|
+
* re-invoke per attempt, wrap the call site: `Effect.suspend(() =>
|
|
242
|
+
* eaws.getAmi(args))`.
|
|
243
|
+
* - A discarded Effect must not surface as an unhandled rejection, so the
|
|
244
|
+
* rejection is pre-observed on a side branch before the Effect awaits it.
|
|
245
|
+
*/
|
|
102
246
|
function wrapInvokeLike(fn) {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
try: () => result,
|
|
113
|
-
catch: (cause) => new PulumiError({ cause })
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
});
|
|
247
|
+
return new Proxy(fn, { apply(target, thisArg, args) {
|
|
248
|
+
const result = Reflect.apply(target, thisArg, args);
|
|
249
|
+
if (!isPromiseLike(result)) return result;
|
|
250
|
+
result.then(void 0, () => {});
|
|
251
|
+
return effect.Effect.tryPromise({
|
|
252
|
+
try: () => result,
|
|
253
|
+
catch: (cause) => new PulumiError({ cause })
|
|
254
|
+
});
|
|
255
|
+
} });
|
|
117
256
|
}
|
|
118
|
-
|
|
257
|
+
/** Wrapped constructors are cached so repeated reads of the same property
|
|
258
|
+
* hand back the same function - identity checks like
|
|
259
|
+
* `eaws.s3.Bucket === eaws.s3.Bucket` hold, and the `get` and
|
|
260
|
+
* `getOwnPropertyDescriptor` traps agree on a property's value. */
|
|
261
|
+
const wrapperCache = /* @__PURE__ */ new WeakMap();
|
|
119
262
|
function effectifyValue(value) {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
137
|
-
return value;
|
|
263
|
+
if (isResourceConstructor(value)) {
|
|
264
|
+
const cached = wrapperCache.get(value);
|
|
265
|
+
if (cached) return cached;
|
|
266
|
+
const wrapped = wrapResourceCtor(value);
|
|
267
|
+
wrapperCache.set(value, wrapped);
|
|
268
|
+
return wrapped;
|
|
269
|
+
}
|
|
270
|
+
if (typeof value === "function") {
|
|
271
|
+
const cached = wrapperCache.get(value);
|
|
272
|
+
if (cached) return cached;
|
|
273
|
+
const wrapped = wrapInvokeLike(value);
|
|
274
|
+
wrapperCache.set(value, wrapped);
|
|
275
|
+
return wrapped;
|
|
276
|
+
}
|
|
277
|
+
if (isNamespace(value)) return effectifyInner(value);
|
|
278
|
+
return value;
|
|
138
279
|
}
|
|
139
280
|
function effectifyInner(mod) {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
281
|
+
const cached = proxyCache.get(mod);
|
|
282
|
+
if (cached) return cached;
|
|
283
|
+
const proxy = new Proxy(Object.create(null), {
|
|
284
|
+
get(_target, prop) {
|
|
285
|
+
return effectifyValue(Reflect.get(mod, prop));
|
|
286
|
+
},
|
|
287
|
+
has(_target, prop) {
|
|
288
|
+
return Reflect.has(mod, prop);
|
|
289
|
+
},
|
|
290
|
+
ownKeys() {
|
|
291
|
+
return Reflect.ownKeys(mod);
|
|
292
|
+
},
|
|
293
|
+
getOwnPropertyDescriptor(_target, prop) {
|
|
294
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(mod, prop);
|
|
295
|
+
if (!descriptor) return void 0;
|
|
296
|
+
return {
|
|
297
|
+
value: effectifyValue(Reflect.get(mod, prop)),
|
|
298
|
+
enumerable: descriptor.enumerable,
|
|
299
|
+
configurable: true,
|
|
300
|
+
writable: true
|
|
301
|
+
};
|
|
302
|
+
},
|
|
303
|
+
set() {
|
|
304
|
+
return false;
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
proxyCache.set(mod, proxy);
|
|
308
|
+
return proxy;
|
|
168
309
|
}
|
|
310
|
+
/**
|
|
311
|
+
* Wrap a provider package (or any namespace) once, turning every resource
|
|
312
|
+
* constructor into an `Effect`-returning factory and every `Promise`-returning
|
|
313
|
+
* invoke into an `Effect`-returning function.
|
|
314
|
+
*
|
|
315
|
+
* Call this once per package at module scope and export the result - the
|
|
316
|
+
* wrapper is cached, so repeated calls and repeated property reads hand back
|
|
317
|
+
* the same objects, but there is no reason to re-wrap.
|
|
318
|
+
*
|
|
319
|
+
* @param mod - The provider package, or any namespace within one. Not mutated;
|
|
320
|
+
* the result is a read-only proxy that forwards to it.
|
|
321
|
+
* @returns A same-shaped view of `mod`, typed by {@link Effectify}.
|
|
322
|
+
*
|
|
323
|
+
* @example Constructing a resource
|
|
324
|
+
* ```ts
|
|
325
|
+
* import * as aws from "@pulumi/aws";
|
|
326
|
+
* const eaws = effectify(aws);
|
|
327
|
+
*
|
|
328
|
+
* const program = Effect.gen(function* () {
|
|
329
|
+
* const bucket = yield* eaws.s3.Bucket("assets", { forceDestroy: true });
|
|
330
|
+
* return bucket.id;
|
|
331
|
+
* });
|
|
332
|
+
* ```
|
|
333
|
+
*
|
|
334
|
+
* @example Passing an Effect as an argument
|
|
335
|
+
* A `CustomResource`'s args may hold Effects, which are resolved concurrently
|
|
336
|
+
* before the resource is constructed:
|
|
337
|
+
* ```ts
|
|
338
|
+
* yield* eaws.s3.BucketObject("readme", {
|
|
339
|
+
* bucket: bucketIdEffect,
|
|
340
|
+
* content: "hello",
|
|
341
|
+
* });
|
|
342
|
+
* ```
|
|
343
|
+
*
|
|
344
|
+
* @throws Nothing. Failures surface in the returned Effect's error channel as
|
|
345
|
+
* {@link PulumiError} - including synchronous throws from the constructor.
|
|
346
|
+
*
|
|
347
|
+
* @see {@link Effectify} for the type-level mapping.
|
|
348
|
+
*/
|
|
169
349
|
function effectify(mod) {
|
|
170
|
-
|
|
350
|
+
return effectifyInner(mod);
|
|
171
351
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
352
|
+
//#endregion
|
|
353
|
+
//#region src/automation.ts
|
|
354
|
+
const isLocal = (opts) => "workDir" in opts;
|
|
355
|
+
/** Select the stack, creating it if it does not exist, and return the handle
|
|
356
|
+
* every other operation here takes.
|
|
357
|
+
*
|
|
358
|
+
* Creating the workspace is itself work - it may write files and shell out to
|
|
359
|
+
* the CLI - so hold on to the returned `Stack` rather than re-selecting before
|
|
360
|
+
* each operation. */
|
|
361
|
+
const createOrSelectStack = (opts) => effect.Effect.tryPromise({
|
|
362
|
+
try: () => isLocal(opts) ? _pulumi_pulumi_automation_index_js.LocalWorkspace.createOrSelectStack({
|
|
363
|
+
stackName: opts.stackName,
|
|
364
|
+
workDir: opts.workDir
|
|
365
|
+
}, opts.workspaceOptions) : _pulumi_pulumi_automation_index_js.LocalWorkspace.createOrSelectStack({
|
|
366
|
+
stackName: opts.stackName,
|
|
367
|
+
projectName: opts.projectName,
|
|
368
|
+
program: opts.program
|
|
369
|
+
}, opts.workspaceOptions),
|
|
370
|
+
catch: (cause) => new AutomationError({
|
|
371
|
+
stage: "createOrSelectStack",
|
|
372
|
+
cause
|
|
373
|
+
})
|
|
186
374
|
});
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
375
|
+
/** Apply the whole config map in one `setAllConfig` call - a single CLI
|
|
376
|
+
* round-trip, where per-key `setConfig` costs one `pulumi config set`
|
|
377
|
+
* invocation each. */
|
|
378
|
+
const setStackConfig = (stack, config) => !config || Object.keys(config).length === 0 ? effect.Effect.void : effect.Effect.tryPromise({
|
|
379
|
+
try: () => stack.setAllConfig(config),
|
|
380
|
+
catch: (cause) => new AutomationError({
|
|
381
|
+
stage: "setConfig",
|
|
382
|
+
cause
|
|
383
|
+
})
|
|
190
384
|
});
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
385
|
+
/** Compute the plan without applying it.
|
|
386
|
+
*
|
|
387
|
+
* A preview is a full engine run against the provider, not a cheap check - see
|
|
388
|
+
* {@link DeployOptions.preview} before pairing one with an `up`. */
|
|
389
|
+
const previewStack = (stack, opts) => effect.Effect.tryPromise({
|
|
390
|
+
try: () => stack.preview(opts),
|
|
391
|
+
catch: (cause) => new AutomationError({
|
|
392
|
+
stage: "preview",
|
|
393
|
+
cause
|
|
394
|
+
})
|
|
194
395
|
});
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
396
|
+
/** Apply the program: create, update and delete resources to match it.
|
|
397
|
+
*
|
|
398
|
+
* The result carries the stack's outputs and a summary; pass `onOutput` to
|
|
399
|
+
* watch progress while it runs. */
|
|
400
|
+
const upStack = (stack, opts) => effect.Effect.tryPromise({
|
|
401
|
+
try: () => stack.up(opts),
|
|
402
|
+
catch: (cause) => new AutomationError({
|
|
403
|
+
stage: "up",
|
|
404
|
+
cause
|
|
405
|
+
})
|
|
198
406
|
});
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
407
|
+
/** Refresh the stack's state from the actual cloud resources, without
|
|
408
|
+
* changing them - what to run when state may have drifted (manual console
|
|
409
|
+
* edits, a crashed update) before deciding what to do about it. */
|
|
410
|
+
const refreshStack = (stack, opts) => effect.Effect.tryPromise({
|
|
411
|
+
try: () => stack.refresh(opts),
|
|
412
|
+
catch: (cause) => new AutomationError({
|
|
413
|
+
stage: "refresh",
|
|
414
|
+
cause
|
|
415
|
+
})
|
|
202
416
|
});
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
417
|
+
/** Read the stack's current outputs without running an update. */
|
|
418
|
+
const stackOutputs = (stack) => effect.Effect.tryPromise({
|
|
419
|
+
try: () => stack.outputs(),
|
|
420
|
+
catch: (cause) => new AutomationError({
|
|
421
|
+
stage: "outputs",
|
|
422
|
+
cause
|
|
423
|
+
})
|
|
206
424
|
});
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
425
|
+
/** Destroy the stack's resources. The stack itself remains registered with
|
|
426
|
+
* the backend - see `removeStack` / `teardownStack` to delete it too. */
|
|
427
|
+
const destroyStack = (stack, opts) => effect.Effect.tryPromise({
|
|
428
|
+
try: () => stack.destroy(opts),
|
|
429
|
+
catch: (cause) => new AutomationError({
|
|
430
|
+
stage: "destroy",
|
|
431
|
+
cause
|
|
432
|
+
})
|
|
210
433
|
});
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
434
|
+
/** Delete the stack and its configuration and history from the backend.
|
|
435
|
+
*
|
|
436
|
+
* This does not destroy resources - run `destroyStack` first, or use
|
|
437
|
+
* `teardownStack`. Pulumi refuses to remove a stack that still has resources
|
|
438
|
+
* unless `RemoveOptions.force` is set, and forcing it orphans them: they keep
|
|
439
|
+
* existing and billing with nothing tracking them. */
|
|
440
|
+
const removeStack = (stack, opts) => effect.Effect.tryPromise({
|
|
441
|
+
try: () => stack.workspace.removeStack(stack.name, opts),
|
|
442
|
+
catch: (cause) => new AutomationError({
|
|
443
|
+
stage: "removeStack",
|
|
444
|
+
cause
|
|
445
|
+
})
|
|
214
446
|
});
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
447
|
+
/** Full teardown: destroy the resources, then delete the stack.
|
|
448
|
+
*
|
|
449
|
+
* `destroyStack` alone leaves an empty stack behind, so anything creating
|
|
450
|
+
* stacks per-run (ephemeral environments, tests naming stacks by timestamp)
|
|
451
|
+
* accumulates them in the backend. */
|
|
452
|
+
const teardownStack = (stack, opts) => effect.Effect.gen(function* () {
|
|
453
|
+
const result = yield* destroyStack(stack, opts?.destroy);
|
|
454
|
+
yield* removeStack(stack, opts?.remove);
|
|
455
|
+
return result;
|
|
219
456
|
});
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
457
|
+
/**
|
|
458
|
+
* Select or create the stack, apply config, optionally preview, then up.
|
|
459
|
+
*
|
|
460
|
+
* The common path, assembled from the primitives above. Anything more
|
|
461
|
+
* involved - refreshing first, inspecting the plan before deciding, retrying a
|
|
462
|
+
* stage - should compose those directly rather than grow options here.
|
|
463
|
+
*
|
|
464
|
+
* @param opts - Which stack, and what to do with it.
|
|
465
|
+
* @returns The stack handle alongside the results, so callers can tear down
|
|
466
|
+
* afterwards without re-selecting.
|
|
467
|
+
*
|
|
468
|
+
* @example Deploy, use the outputs, then always tear down
|
|
469
|
+
* ```ts
|
|
470
|
+
* Effect.scoped(
|
|
471
|
+
* Effect.gen(function* () {
|
|
472
|
+
* const { result } = yield* Effect.acquireRelease(
|
|
473
|
+
* deploy({ stackName, projectName, program, up: { onOutput } }),
|
|
474
|
+
* ({ stack }) => teardownStack(stack).pipe(Effect.ignore)
|
|
475
|
+
* );
|
|
476
|
+
* return result.outputs.bucketId?.value;
|
|
477
|
+
* })
|
|
478
|
+
* );
|
|
479
|
+
* ```
|
|
480
|
+
*/
|
|
481
|
+
const deploy = (opts) => effect.Effect.gen(function* () {
|
|
482
|
+
const stack = yield* createOrSelectStack(opts);
|
|
483
|
+
yield* setStackConfig(stack, opts.config);
|
|
484
|
+
const preview = opts.preview ? yield* previewStack(stack, typeof opts.preview === "boolean" ? void 0 : opts.preview) : void 0;
|
|
485
|
+
return {
|
|
486
|
+
stack,
|
|
487
|
+
result: yield* upStack(stack, opts.up),
|
|
488
|
+
preview
|
|
489
|
+
};
|
|
229
490
|
});
|
|
230
|
-
|
|
491
|
+
//#endregion
|
|
231
492
|
exports.AutomationError = AutomationError;
|
|
232
493
|
exports.PulumiError = PulumiError;
|
|
233
494
|
exports.createOrSelectStack = createOrSelectStack;
|
|
@@ -243,5 +504,5 @@ exports.setStackConfig = setStackConfig;
|
|
|
243
504
|
exports.stackOutputs = stackOutputs;
|
|
244
505
|
exports.teardownStack = teardownStack;
|
|
245
506
|
exports.upStack = upStack;
|
|
246
|
-
|
|
507
|
+
|
|
247
508
|
//# sourceMappingURL=index.cjs.map
|