alchemy-effect 0.3.0 → 0.4.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/bin/alchemy-effect.js +539 -223
- package/bin/alchemy-effect.js.map +1 -1
- package/lib/apply.d.ts +4 -4
- package/lib/apply.d.ts.map +1 -1
- package/lib/apply.js +411 -131
- package/lib/apply.js.map +1 -1
- package/lib/aws/dynamodb/table.provider.d.ts.map +1 -1
- package/lib/aws/dynamodb/table.provider.js +1 -0
- package/lib/aws/dynamodb/table.provider.js.map +1 -1
- package/lib/aws/ec2/index.d.ts +8 -0
- package/lib/aws/ec2/index.d.ts.map +1 -1
- package/lib/aws/ec2/index.js +8 -0
- package/lib/aws/ec2/index.js.map +1 -1
- package/lib/aws/ec2/internet-gateway.d.ts +65 -0
- package/lib/aws/ec2/internet-gateway.d.ts.map +1 -0
- package/lib/aws/ec2/internet-gateway.js +4 -0
- package/lib/aws/ec2/internet-gateway.js.map +1 -0
- package/lib/aws/ec2/internet-gateway.provider.d.ts +6 -0
- package/lib/aws/ec2/internet-gateway.provider.d.ts.map +1 -0
- package/lib/aws/ec2/internet-gateway.provider.js +193 -0
- package/lib/aws/ec2/internet-gateway.provider.js.map +1 -0
- package/lib/aws/ec2/route-table-association.d.ts +63 -0
- package/lib/aws/ec2/route-table-association.d.ts.map +1 -0
- package/lib/aws/ec2/route-table-association.js +4 -0
- package/lib/aws/ec2/route-table-association.js.map +1 -0
- package/lib/aws/ec2/route-table-association.provider.d.ts +4 -0
- package/lib/aws/ec2/route-table-association.provider.d.ts.map +1 -0
- package/lib/aws/ec2/route-table-association.provider.js +121 -0
- package/lib/aws/ec2/route-table-association.provider.js.map +1 -0
- package/lib/aws/ec2/route-table.d.ts +159 -0
- package/lib/aws/ec2/route-table.d.ts.map +1 -0
- package/lib/aws/ec2/route-table.js +4 -0
- package/lib/aws/ec2/route-table.js.map +1 -0
- package/lib/aws/ec2/route-table.provider.d.ts +6 -0
- package/lib/aws/ec2/route-table.provider.d.ts.map +1 -0
- package/lib/aws/ec2/route-table.provider.js +213 -0
- package/lib/aws/ec2/route-table.provider.js.map +1 -0
- package/lib/aws/ec2/route.d.ts +155 -0
- package/lib/aws/ec2/route.d.ts.map +1 -0
- package/lib/aws/ec2/route.js +3 -0
- package/lib/aws/ec2/route.js.map +1 -0
- package/lib/aws/ec2/route.provider.d.ts +4 -0
- package/lib/aws/ec2/route.provider.d.ts.map +1 -0
- package/lib/aws/ec2/route.provider.js +166 -0
- package/lib/aws/ec2/route.provider.js.map +1 -0
- package/lib/aws/ec2/subnet.provider.d.ts.map +1 -1
- package/lib/aws/ec2/subnet.provider.js +1 -1
- package/lib/aws/ec2/subnet.provider.js.map +1 -1
- package/lib/aws/ec2/vpc.d.ts +1 -0
- package/lib/aws/ec2/vpc.d.ts.map +1 -1
- package/lib/aws/ec2/vpc.provider.d.ts +2 -2
- package/lib/aws/ec2/vpc.provider.d.ts.map +1 -1
- package/lib/aws/ec2/vpc.provider.js +38 -15
- package/lib/aws/ec2/vpc.provider.js.map +1 -1
- package/lib/aws/index.d.ts +2 -3
- package/lib/aws/index.d.ts.map +1 -1
- package/lib/aws/index.js +2 -1
- package/lib/aws/index.js.map +1 -1
- package/lib/aws/lambda/function.provider.d.ts +2 -2
- package/lib/aws/lambda/function.provider.d.ts.map +1 -1
- package/lib/aws/lambda/function.provider.js +21 -20
- package/lib/aws/lambda/function.provider.js.map +1 -1
- package/lib/aws/sqs/queue.provider.d.ts +2 -2
- package/lib/aws/sqs/queue.provider.d.ts.map +1 -1
- package/lib/aws/sqs/queue.provider.js +3 -2
- package/lib/aws/sqs/queue.provider.js.map +1 -1
- package/lib/cli/index.d.ts +178 -99
- package/lib/cli/index.d.ts.map +1 -1
- package/lib/cloudflare/kv/namespace.client.d.ts +1 -1
- package/lib/cloudflare/kv/namespace.provider.d.ts.map +1 -1
- package/lib/cloudflare/kv/namespace.provider.js +1 -0
- package/lib/cloudflare/kv/namespace.provider.js.map +1 -1
- package/lib/cloudflare/r2/bucket.provider.d.ts.map +1 -1
- package/lib/cloudflare/r2/bucket.provider.js +6 -1
- package/lib/cloudflare/r2/bucket.provider.js.map +1 -1
- package/lib/cloudflare/worker/worker.provider.d.ts +1 -1
- package/lib/cloudflare/worker/worker.provider.d.ts.map +1 -1
- package/lib/cloudflare/worker/worker.provider.js +6 -2
- package/lib/cloudflare/worker/worker.provider.js.map +1 -1
- package/lib/diff.d.ts +8 -6
- package/lib/diff.d.ts.map +1 -1
- package/lib/diff.js +13 -0
- package/lib/diff.js.map +1 -1
- package/lib/event.d.ts +1 -1
- package/lib/event.d.ts.map +1 -1
- package/lib/instance-id.d.ts +8 -0
- package/lib/instance-id.d.ts.map +1 -0
- package/lib/instance-id.js +12 -0
- package/lib/instance-id.js.map +1 -0
- package/lib/output.d.ts +4 -2
- package/lib/output.d.ts.map +1 -1
- package/lib/output.js +18 -4
- package/lib/output.js.map +1 -1
- package/lib/physical-name.d.ts +14 -1
- package/lib/physical-name.d.ts.map +1 -1
- package/lib/physical-name.js +41 -2
- package/lib/physical-name.js.map +1 -1
- package/lib/plan.d.ts +49 -42
- package/lib/plan.d.ts.map +1 -1
- package/lib/plan.js +359 -127
- package/lib/plan.js.map +1 -1
- package/lib/provider.d.ts +26 -9
- package/lib/provider.d.ts.map +1 -1
- package/lib/provider.js +9 -0
- package/lib/provider.js.map +1 -1
- package/lib/resource.d.ts +2 -2
- package/lib/resource.d.ts.map +1 -1
- package/lib/resource.js.map +1 -1
- package/lib/state.d.ts +86 -9
- package/lib/state.d.ts.map +1 -1
- package/lib/state.js +21 -18
- package/lib/state.js.map +1 -1
- package/lib/tags.d.ts +15 -0
- package/lib/tags.d.ts.map +1 -1
- package/lib/tags.js +27 -0
- package/lib/tags.js.map +1 -1
- package/lib/test.d.ts +2 -2
- package/lib/test.d.ts.map +1 -1
- package/lib/test.js +4 -4
- package/lib/test.js.map +1 -1
- package/lib/todo.d.ts +3 -0
- package/lib/todo.d.ts.map +1 -0
- package/lib/todo.js +3 -0
- package/lib/todo.js.map +1 -0
- package/lib/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/apply.ts +758 -374
- package/src/aws/dynamodb/table.provider.ts +1 -0
- package/src/aws/ec2/index.ts +8 -0
- package/src/aws/ec2/internet-gateway.provider.ts +316 -0
- package/src/aws/ec2/internet-gateway.ts +79 -0
- package/src/aws/ec2/route-table-association.provider.ts +214 -0
- package/src/aws/ec2/route-table-association.ts +82 -0
- package/src/aws/ec2/route-table.provider.ts +306 -0
- package/src/aws/ec2/route-table.ts +175 -0
- package/src/aws/ec2/route.provider.ts +213 -0
- package/src/aws/ec2/route.ts +192 -0
- package/src/aws/ec2/subnet.provider.ts +2 -2
- package/src/aws/ec2/vpc.provider.ts +43 -19
- package/src/aws/ec2/vpc.ts +2 -0
- package/src/aws/index.ts +4 -1
- package/src/aws/lambda/function.provider.ts +25 -23
- package/src/aws/sqs/queue.provider.ts +3 -2
- package/src/cloudflare/kv/namespace.provider.ts +1 -0
- package/src/cloudflare/r2/bucket.provider.ts +7 -1
- package/src/cloudflare/worker/worker.provider.ts +6 -2
- package/src/diff.ts +35 -17
- package/src/event.ts +6 -0
- package/src/instance-id.ts +16 -0
- package/src/output.ts +29 -5
- package/src/physical-name.ts +57 -2
- package/src/plan.ts +488 -197
- package/src/provider.ts +46 -9
- package/src/resource.ts +50 -4
- package/src/state.ts +150 -35
- package/src/tags.ts +31 -0
- package/src/test.ts +5 -5
- package/src/todo.ts +4 -0
|
@@ -38,7 +38,13 @@ export const bucketProvider = () =>
|
|
|
38
38
|
return { action: "replace" };
|
|
39
39
|
}
|
|
40
40
|
if (output.storageClass !== (news.storageClass ?? "Standard")) {
|
|
41
|
-
return {
|
|
41
|
+
return {
|
|
42
|
+
action: "update",
|
|
43
|
+
stables:
|
|
44
|
+
output.name === createName(id, news.name)
|
|
45
|
+
? ["name"]
|
|
46
|
+
: undefined,
|
|
47
|
+
};
|
|
42
48
|
}
|
|
43
49
|
return { action: "noop" };
|
|
44
50
|
}),
|
|
@@ -7,8 +7,8 @@ import type { ScopedPlanStatusSession } from "../../cli/service.ts";
|
|
|
7
7
|
import { DotAlchemy } from "../../dot-alchemy.ts";
|
|
8
8
|
import { ESBuild } from "../../esbuild.ts";
|
|
9
9
|
import { sha256 } from "../../sha256.ts";
|
|
10
|
-
import { CloudflareApi } from "../api.ts";
|
|
11
10
|
import { Account } from "../account.ts";
|
|
11
|
+
import { CloudflareApi } from "../api.ts";
|
|
12
12
|
import { Assets } from "./assets.provider.ts";
|
|
13
13
|
import { Worker, type WorkerAttr, type WorkerProps } from "./worker.ts";
|
|
14
14
|
|
|
@@ -187,6 +187,7 @@ export const workerProvider = () =>
|
|
|
187
187
|
});
|
|
188
188
|
|
|
189
189
|
return {
|
|
190
|
+
stables: ["id"],
|
|
190
191
|
diff: Effect.fnUntraced(function* ({ id, olds, news, output }) {
|
|
191
192
|
if (output.accountId !== accountId) {
|
|
192
193
|
return { action: "replace" };
|
|
@@ -203,7 +204,10 @@ export const workerProvider = () =>
|
|
|
203
204
|
assets?.hash !== output.hash.assets ||
|
|
204
205
|
bundle.hash !== output.hash.bundle
|
|
205
206
|
) {
|
|
206
|
-
return {
|
|
207
|
+
return {
|
|
208
|
+
action: "update",
|
|
209
|
+
stables: output.name === workerName ? ["name"] : undefined,
|
|
210
|
+
};
|
|
207
211
|
}
|
|
208
212
|
}),
|
|
209
213
|
create: Effect.fnUntraced(function* ({ id, news, bindings, session }) {
|
package/src/diff.ts
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
|
-
export type Diff =
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
1
|
+
export type Diff = NoopDiff | UpdateDiff | ReplaceDiff;
|
|
2
|
+
|
|
3
|
+
export interface NoopDiff {
|
|
4
|
+
action: "noop";
|
|
5
|
+
stables?: undefined;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface UpdateDiff {
|
|
9
|
+
action: "update";
|
|
10
|
+
/** properties that won't change as part of this update */
|
|
11
|
+
stables?: string[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface ReplaceDiff {
|
|
15
|
+
action: "replace";
|
|
16
|
+
deleteFirst?: boolean;
|
|
17
|
+
stables?: undefined;
|
|
18
|
+
}
|
|
18
19
|
|
|
19
20
|
export const somePropsAreDifferent = <Props extends Record<string, any>>(
|
|
20
21
|
olds: Props,
|
|
@@ -28,3 +29,20 @@ export const somePropsAreDifferent = <Props extends Record<string, any>>(
|
|
|
28
29
|
}
|
|
29
30
|
return false;
|
|
30
31
|
};
|
|
32
|
+
|
|
33
|
+
export const anyPropsAreDifferent = <Props extends Record<string, any>>(
|
|
34
|
+
olds: Props,
|
|
35
|
+
news: Props,
|
|
36
|
+
) => {
|
|
37
|
+
for (const prop in olds) {
|
|
38
|
+
if (olds[prop] !== news[prop]) {
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
for (const prop in news) {
|
|
43
|
+
if (!(prop in olds)) {
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return false;
|
|
48
|
+
};
|
package/src/event.ts
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
export type ApplyStatus =
|
|
2
|
+
| "attaching"
|
|
3
|
+
| "post-attach"
|
|
2
4
|
| "pending"
|
|
5
|
+
| "pre-creating"
|
|
3
6
|
| "creating"
|
|
7
|
+
| "creating replacement"
|
|
4
8
|
| "created"
|
|
5
9
|
| "updating"
|
|
6
10
|
| "updated"
|
|
7
11
|
| "deleting"
|
|
8
12
|
| "deleted"
|
|
13
|
+
| "replacing"
|
|
14
|
+
| "replaced"
|
|
9
15
|
| "success"
|
|
10
16
|
| "fail";
|
|
11
17
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
|
|
3
|
+
/** A 16-byte (128-bit) random hex-encoded string representing an physical instance of a logical resource */
|
|
4
|
+
export type InstanceId = string;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @returns Hex-encoded instance ID (16 random bytes)
|
|
8
|
+
*/
|
|
9
|
+
export const generateInstanceId = () =>
|
|
10
|
+
Effect.sync(() => {
|
|
11
|
+
const bytes = new Uint8Array(16);
|
|
12
|
+
crypto.getRandomValues(bytes);
|
|
13
|
+
return Array.from(bytes)
|
|
14
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
15
|
+
.join("");
|
|
16
|
+
});
|
package/src/output.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { pipe } from "effect";
|
|
2
|
-
import * as App from "./app.ts";
|
|
3
2
|
import * as Data from "effect/Data";
|
|
4
3
|
import * as Effect from "effect/Effect";
|
|
4
|
+
import * as App from "./app.ts";
|
|
5
5
|
import { isPrimitive } from "./data.ts";
|
|
6
6
|
import type { From } from "./policy.ts";
|
|
7
|
+
import { getRefMetadata, isRef, ref as stageRef, type Ref } from "./ref.ts";
|
|
7
8
|
import type { AnyResource, Resource } from "./resource.ts";
|
|
8
|
-
import type { IsAny, UnionToIntersection } from "./util.ts";
|
|
9
9
|
import * as State from "./state.ts";
|
|
10
|
-
import
|
|
10
|
+
import type { IsAny, UnionToIntersection } from "./util.ts";
|
|
11
11
|
|
|
12
12
|
// a special symbol only used at runtime to probe the Output proxy
|
|
13
13
|
const ExprSymbol = Symbol.for("alchemy/Expr");
|
|
@@ -125,7 +125,7 @@ const proxy = (self: any): any => {
|
|
|
125
125
|
return new EffectExpr(self.expr, args[0]);
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
|
-
|
|
128
|
+
return undefined;
|
|
129
129
|
},
|
|
130
130
|
},
|
|
131
131
|
);
|
|
@@ -367,7 +367,7 @@ export const evaluate: <A, Upstream extends AnyResource, Req>(
|
|
|
367
367
|
}),
|
|
368
368
|
);
|
|
369
369
|
}
|
|
370
|
-
return resource.
|
|
370
|
+
return resource.attr;
|
|
371
371
|
} else if (Array.isArray(expr)) {
|
|
372
372
|
return yield* Effect.all(expr.map((item) => evaluate(item, upstream)));
|
|
373
373
|
} else if (typeof expr === "object" && expr !== null) {
|
|
@@ -389,6 +389,30 @@ export type Upstream<O extends Output<any, any, any>> =
|
|
|
389
389
|
}
|
|
390
390
|
: never;
|
|
391
391
|
|
|
392
|
+
export const hasOutputs = (value: any): value is Output<any, any, any> =>
|
|
393
|
+
Object.keys(upstreamAny(value)).length > 0;
|
|
394
|
+
|
|
395
|
+
export const upstreamAny = (
|
|
396
|
+
value: any,
|
|
397
|
+
): {
|
|
398
|
+
[ID in string]: Resource;
|
|
399
|
+
} => {
|
|
400
|
+
if (isExpr(value)) {
|
|
401
|
+
return upstream(value);
|
|
402
|
+
} else if (Array.isArray(value)) {
|
|
403
|
+
return Object.assign({}, ...value.map(resolveUpstream));
|
|
404
|
+
} else if (
|
|
405
|
+
value &&
|
|
406
|
+
(typeof value === "object" || typeof value === "function")
|
|
407
|
+
) {
|
|
408
|
+
return Object.assign(
|
|
409
|
+
{},
|
|
410
|
+
...Object.values(value).map((value) => resolveUpstream(value)),
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
return {};
|
|
414
|
+
};
|
|
415
|
+
|
|
392
416
|
export const upstream = <E extends Output<any, AnyResource, any>>(
|
|
393
417
|
expr: E,
|
|
394
418
|
): {
|
package/src/physical-name.ts
CHANGED
|
@@ -1,7 +1,62 @@
|
|
|
1
1
|
import * as Effect from "effect/Effect";
|
|
2
2
|
import { App } from "./app.ts";
|
|
3
3
|
|
|
4
|
-
export const physicalName = Effect.fn(function* (
|
|
4
|
+
export const physicalName = Effect.fn(function* ({
|
|
5
|
+
id,
|
|
6
|
+
instanceId,
|
|
7
|
+
// 16 base32 characters = 80 bits of entropy = 4 × 10⁻⁷
|
|
8
|
+
suffixLength = 16,
|
|
9
|
+
}: {
|
|
10
|
+
id: string;
|
|
11
|
+
/** Hex-encoded instance ID (16 random bytes) */
|
|
12
|
+
instanceId: string;
|
|
13
|
+
suffixLength?: number;
|
|
14
|
+
}) {
|
|
5
15
|
const app = yield* App;
|
|
6
|
-
return `${app.name}-${id}-${app.stage}`;
|
|
16
|
+
return `${app.name}-${id}-${app.stage}-${base32(Buffer.from(instanceId, "hex")).slice(0, suffixLength)}`;
|
|
7
17
|
});
|
|
18
|
+
|
|
19
|
+
// Base32 is ideal for physical names because it's denser than hex (5 bits per char vs 4)
|
|
20
|
+
// and compatible with DNS/S3 (as opposed to base64 which contains uppercase letters and symbols).
|
|
21
|
+
|
|
22
|
+
// base32.ts
|
|
23
|
+
// Concise, fast RFC 4648 Base32 encoder (no padding), lowercase output.
|
|
24
|
+
// Charset: a-z2-7 (DNS/S3 friendly)
|
|
25
|
+
const ALPH = "abcdefghijklmnopqrstuvwxyz234567";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Encode bytes into RFC4648 Base32 (no padding), lowercase.
|
|
29
|
+
*
|
|
30
|
+
* Performance notes:
|
|
31
|
+
* - O(n) single pass, no big-int
|
|
32
|
+
* - Avoids per-byte string concatenation by using a char array
|
|
33
|
+
*/
|
|
34
|
+
export function base32(bytes: Uint8Array): string {
|
|
35
|
+
const n = bytes.length;
|
|
36
|
+
if (n === 0) return "";
|
|
37
|
+
|
|
38
|
+
// Base32 length without padding: ceil(n*8/5)
|
|
39
|
+
const outLen = ((n * 8 + 4) / 5) | 0;
|
|
40
|
+
const out = Array.from<string>({ length: outLen });
|
|
41
|
+
|
|
42
|
+
let buffer = 0;
|
|
43
|
+
let bits = 0;
|
|
44
|
+
let o = 0;
|
|
45
|
+
|
|
46
|
+
for (let i = 0; i < n; i++) {
|
|
47
|
+
buffer = (buffer << 8) | bytes[i];
|
|
48
|
+
bits += 8;
|
|
49
|
+
|
|
50
|
+
while (bits >= 5) {
|
|
51
|
+
out[o++] = ALPH[(buffer >>> (bits - 5)) & 31];
|
|
52
|
+
bits -= 5;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (bits > 0) {
|
|
57
|
+
out[o++] = ALPH[(buffer << (5 - bits)) & 31];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// outLen computed as exact ceiling; o should match, but slice defensively.
|
|
61
|
+
return o === outLen ? out.join("") : out.slice(0, o).join("");
|
|
62
|
+
}
|