alchemy 2.0.0-beta.16 → 2.0.0-beta.18

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.
Files changed (99) hide show
  1. package/bin/alchemy.js +79 -6
  2. package/bin/alchemy.js.map +1 -1
  3. package/bin/cli.js +9 -7
  4. package/bin/commands/deploy.ts +11 -2
  5. package/bin/commands/profile.ts +21 -1
  6. package/bin/commands/state.ts +91 -1
  7. package/lib/AWS/CloudFront/CachePolicy.d.ts +106 -0
  8. package/lib/AWS/CloudFront/CachePolicy.d.ts.map +1 -0
  9. package/lib/AWS/CloudFront/CachePolicy.js +147 -0
  10. package/lib/AWS/CloudFront/CachePolicy.js.map +1 -0
  11. package/lib/AWS/CloudFront/KeyGroup.d.ts +72 -0
  12. package/lib/AWS/CloudFront/KeyGroup.d.ts.map +1 -0
  13. package/lib/AWS/CloudFront/KeyGroup.js +136 -0
  14. package/lib/AWS/CloudFront/KeyGroup.js.map +1 -0
  15. package/lib/AWS/CloudFront/OriginRequestPolicy.d.ts +84 -0
  16. package/lib/AWS/CloudFront/OriginRequestPolicy.d.ts.map +1 -0
  17. package/lib/AWS/CloudFront/OriginRequestPolicy.js +139 -0
  18. package/lib/AWS/CloudFront/OriginRequestPolicy.js.map +1 -0
  19. package/lib/AWS/CloudFront/PublicKey.d.ts +72 -0
  20. package/lib/AWS/CloudFront/PublicKey.d.ts.map +1 -0
  21. package/lib/AWS/CloudFront/PublicKey.js +137 -0
  22. package/lib/AWS/CloudFront/PublicKey.js.map +1 -0
  23. package/lib/AWS/CloudFront/ResponseHeadersPolicy.d.ts +114 -0
  24. package/lib/AWS/CloudFront/ResponseHeadersPolicy.d.ts.map +1 -0
  25. package/lib/AWS/CloudFront/ResponseHeadersPolicy.js +162 -0
  26. package/lib/AWS/CloudFront/ResponseHeadersPolicy.js.map +1 -0
  27. package/lib/AWS/CloudFront/index.d.ts +5 -0
  28. package/lib/AWS/CloudFront/index.d.ts.map +1 -1
  29. package/lib/AWS/CloudFront/index.js +5 -0
  30. package/lib/AWS/CloudFront/index.js.map +1 -1
  31. package/lib/AWS/ECS/CapacityProvider.d.ts +102 -0
  32. package/lib/AWS/ECS/CapacityProvider.d.ts.map +1 -0
  33. package/lib/AWS/ECS/CapacityProvider.js +217 -0
  34. package/lib/AWS/ECS/CapacityProvider.js.map +1 -0
  35. package/lib/AWS/ECS/index.d.ts +1 -0
  36. package/lib/AWS/ECS/index.d.ts.map +1 -1
  37. package/lib/AWS/ECS/index.js +1 -0
  38. package/lib/AWS/ECS/index.js.map +1 -1
  39. package/lib/AWS/Providers.d.ts.map +1 -1
  40. package/lib/AWS/Providers.js +7 -1
  41. package/lib/AWS/Providers.js.map +1 -1
  42. package/lib/Auth/Credentials.d.ts +6 -0
  43. package/lib/Auth/Credentials.d.ts.map +1 -1
  44. package/lib/Auth/Credentials.js +12 -1
  45. package/lib/Auth/Credentials.js.map +1 -1
  46. package/lib/Auth/Profile.d.ts +5 -0
  47. package/lib/Auth/Profile.d.ts.map +1 -1
  48. package/lib/Auth/Profile.js +11 -0
  49. package/lib/Auth/Profile.js.map +1 -1
  50. package/lib/Cloudflare/ApiToken/AccountApiToken.d.ts +2 -1
  51. package/lib/Cloudflare/ApiToken/AccountApiToken.d.ts.map +1 -1
  52. package/lib/Cloudflare/ApiToken/AccountApiToken.js +6 -5
  53. package/lib/Cloudflare/ApiToken/AccountApiToken.js.map +1 -1
  54. package/lib/Cloudflare/ApiToken/Common.d.ts +5 -11
  55. package/lib/Cloudflare/ApiToken/Common.d.ts.map +1 -1
  56. package/lib/Cloudflare/ApiToken/Common.js +4 -14
  57. package/lib/Cloudflare/ApiToken/Common.js.map +1 -1
  58. package/lib/Cloudflare/ApiToken/UserApiToken.js +4 -4
  59. package/lib/Cloudflare/ApiToken/UserApiToken.js.map +1 -1
  60. package/lib/GitHub/Comment.d.ts +2 -1
  61. package/lib/GitHub/Comment.d.ts.map +1 -1
  62. package/lib/GitHub/Comment.js +1 -0
  63. package/lib/GitHub/Comment.js.map +1 -1
  64. package/lib/GitHub/Providers.d.ts.map +1 -1
  65. package/lib/GitHub/Providers.js +2 -1
  66. package/lib/GitHub/Providers.js.map +1 -1
  67. package/lib/GitHub/Secret.d.ts +2 -2
  68. package/lib/GitHub/Secret.d.ts.map +1 -1
  69. package/lib/GitHub/Secrets.d.ts +45 -0
  70. package/lib/GitHub/Secrets.d.ts.map +1 -0
  71. package/lib/GitHub/Secrets.js +30 -0
  72. package/lib/GitHub/Secrets.js.map +1 -0
  73. package/lib/GitHub/Variable.d.ts +2 -2
  74. package/lib/GitHub/Variable.d.ts.map +1 -1
  75. package/lib/GitHub/index.d.ts +1 -0
  76. package/lib/GitHub/index.d.ts.map +1 -1
  77. package/lib/GitHub/index.js +1 -0
  78. package/lib/GitHub/index.js.map +1 -1
  79. package/package.json +1 -1
  80. package/src/AWS/CloudFront/CachePolicy.ts +271 -0
  81. package/src/AWS/CloudFront/KeyGroup.ts +233 -0
  82. package/src/AWS/CloudFront/OriginRequestPolicy.ts +256 -0
  83. package/src/AWS/CloudFront/PublicKey.ts +245 -0
  84. package/src/AWS/CloudFront/ResponseHeadersPolicy.ts +308 -0
  85. package/src/AWS/CloudFront/index.ts +11 -0
  86. package/src/AWS/ECS/CapacityProvider.ts +336 -0
  87. package/src/AWS/ECS/index.ts +4 -0
  88. package/src/AWS/Providers.ts +12 -0
  89. package/src/Auth/Credentials.ts +17 -1
  90. package/src/Auth/Profile.ts +15 -0
  91. package/src/Cloudflare/ApiToken/AccountApiToken.ts +6 -5
  92. package/src/Cloudflare/ApiToken/Common.ts +6 -18
  93. package/src/Cloudflare/ApiToken/UserApiToken.ts +4 -4
  94. package/src/GitHub/Comment.ts +4 -1
  95. package/src/GitHub/Providers.ts +8 -2
  96. package/src/GitHub/Secret.ts +2 -2
  97. package/src/GitHub/Secrets.ts +66 -0
  98. package/src/GitHub/Variable.ts +2 -2
  99. package/src/GitHub/index.ts +1 -0
package/bin/cli.js CHANGED
@@ -21,24 +21,26 @@
21
21
  //
22
22
  // foreground-child forwards stdio + signals and exits with the child's code,
23
23
  // so the launcher is transparent to the invoking shell / npm script.
24
- import { existsSync } from "node:fs";
25
- import { createRequire } from "node:module";
24
+ import { fileURLToPath } from "node:url";
26
25
  import path from "pathe";
27
26
  import { foregroundChild } from "foreground-child";
28
27
 
29
- const require = createRequire(import.meta.url);
30
-
31
28
  const execpath = (process.env.npm_execpath ?? "").toLowerCase();
32
29
  const userAgent = (process.env.npm_config_user_agent ?? "").toLowerCase();
33
30
  const invokedByBun = execpath.includes("bun") || userAgent.startsWith("bun/");
34
31
 
35
- const jsEntry = require.resolve("alchemy/bin/alchemy.js");
36
- const binDir = path.dirname(jsEntry);
32
+ // Derive the bin dir from this launcher's own location rather than
33
+ // require.resolve("alchemy/bin/alchemy.js"). The bundled alchemy.js is a
34
+ // build artifact (tsdown output) and may not exist in a fresh checkout
35
+ // (e.g. CI before `bun run build`); resolving it would throw
36
+ // MODULE_NOT_FOUND before we get a chance to fall back to the .ts source.
37
+ const binDir = path.dirname(fileURLToPath(import.meta.url));
38
+ const jsEntry = path.join(binDir, "alchemy.js");
37
39
  const tsEntry = path.join(binDir, "alchemy.ts");
38
40
 
39
41
  // Treat any install-tree path as published; only a raw checkout uses .ts.
40
42
  const useTs = !(
41
- jsEntry.includes("/node_modules/") || jsEntry.includes("\\node_modules\\")
43
+ binDir.includes("/node_modules/") || binDir.includes("\\node_modules\\")
42
44
  );
43
45
 
44
46
  // .ts only runs under bun. Force bun in dev even if node was the invoker.
@@ -84,7 +84,14 @@ const execStack = Effect.fn(function* ({
84
84
  if (dryRun) {
85
85
  yield* cli.displayPlan(updatePlan);
86
86
  } else {
87
- if (!yes) {
87
+ const hasChanges =
88
+ Object.keys(updatePlan.deletions).length > 0 ||
89
+ Object.values(updatePlan.resources).some(
90
+ (node) =>
91
+ node.action !== "noop" ||
92
+ node.bindings.some((b) => b.action !== "noop"),
93
+ );
94
+ if (!yes && hasChanges) {
88
95
  const approved = yield* cli.approvePlan(updatePlan);
89
96
  if (!approved) {
90
97
  return;
@@ -92,7 +99,9 @@ const execStack = Effect.fn(function* ({
92
99
  }
93
100
  const outputs = yield* apply(updatePlan);
94
101
 
95
- yield* Console.log(outputs);
102
+ if (outputs !== undefined) {
103
+ yield* Console.log(outputs);
104
+ }
96
105
  }
97
106
  }).pipe(Effect.provide(stack.services));
98
107
  }).pipe(Effect.provide(services));
@@ -6,7 +6,9 @@ import * as Logger from "effect/Logger";
6
6
  import { Command } from "effect/unstable/cli";
7
7
 
8
8
  import { AuthProviders } from "../../src/Auth/AuthProvider.ts";
9
+ import { deleteProfileCredentials } from "../../src/Auth/Credentials.ts";
9
10
  import {
11
+ deleteProfile,
10
12
  getProfile,
11
13
  readConfig,
12
14
  withProfileOverride,
@@ -81,6 +83,24 @@ const showCommand = Command.make(
81
83
  }),
82
84
  );
83
85
 
86
+ const clearCommand = Command.make(
87
+ "clear",
88
+ { profile },
89
+ Effect.fnUntraced(function* ({ profile }) {
90
+ const removed = yield* deleteProfile(profile);
91
+ yield* deleteProfileCredentials(profile);
92
+ if (removed) {
93
+ yield* Console.log(
94
+ `Cleared profile '${profile}' and all its credentials.`,
95
+ );
96
+ } else {
97
+ yield* Console.log(
98
+ `Profile '${profile}' not found in profiles.json; removed any stray credentials directory.`,
99
+ );
100
+ }
101
+ }),
102
+ );
103
+
84
104
  export const profileCommand = Command.make("profile", {}).pipe(
85
- Command.withSubcommands([showCommand]),
105
+ Command.withSubcommands([showCommand, clearCommand]),
86
106
  );
@@ -3,6 +3,7 @@ import * as Console from "effect/Console";
3
3
  import * as Effect from "effect/Effect";
4
4
  import * as Layer from "effect/Layer";
5
5
  import * as Logger from "effect/Logger";
6
+ import * as Option from "effect/Option";
6
7
  import { Argument, Command, Flag } from "effect/unstable/cli";
7
8
 
8
9
  import { AuthProviders } from "../../src/Auth/AuthProvider.ts";
@@ -10,10 +11,11 @@ import { withProfileOverride } from "../../src/Auth/Profile.ts";
10
11
  import { Stage } from "../../src/Stage.ts";
11
12
  import * as State from "../../src/State/index.ts";
12
13
  import { encodeState } from "../../src/State/StateEncoding.ts";
14
+ import * as Clank from "../../src/Util/Clank.ts";
13
15
  import { loadConfigProvider } from "../../src/Util/ConfigProvider.ts";
14
16
  import { fileLogger } from "../../src/Util/FileLogger.ts";
15
17
 
16
- import { envFile, importStack, main, profile, stage } from "./_shared.ts";
18
+ import { envFile, importStack, main, profile, stage, yes } from "./_shared.ts";
17
19
 
18
20
  /**
19
21
  * When set, the State service is replaced with the on-disk
@@ -222,6 +224,93 @@ const treeCommand = Command.make(
222
224
  }),
223
225
  );
224
226
 
227
+ const clearStackArg = Argument.string("stack").pipe(
228
+ Argument.withDescription(
229
+ "Stack name to clear. Omit to clear ALL stacks in the store.",
230
+ ),
231
+ Argument.optional,
232
+ );
233
+
234
+ const clearStageArg = Argument.string("stage").pipe(
235
+ Argument.withDescription(
236
+ "Stage to clear within the stack. Omit to clear all stages in the stack.",
237
+ ),
238
+ Argument.optional,
239
+ );
240
+
241
+ /**
242
+ * Destructive: removes resource state from the store. The actual cloud
243
+ * resources are NOT touched — this only affects what alchemy thinks
244
+ * exists. Always confirms before deleting unless `--yes` is passed.
245
+ */
246
+ const clearCommand = Command.make(
247
+ "clear",
248
+ {
249
+ stack: clearStackArg,
250
+ stageName: clearStageArg,
251
+ main,
252
+ envFile,
253
+ stage,
254
+ profile,
255
+ local: localFlag,
256
+ yes,
257
+ },
258
+ Effect.fnUntraced(function* ({ stack: stackOpt, stageName: stageOpt, yes: yesFlag, ...rest }) {
259
+ const stackName = Option.getOrUndefined(stackOpt);
260
+ const stageName = Option.getOrUndefined(stageOpt);
261
+
262
+ if (stageName !== undefined && stackName === undefined) {
263
+ yield* Console.log(
264
+ "Error: cannot specify <stage> without <stack>. Pass the stack name as the first argument.",
265
+ );
266
+ return yield* Effect.fail(new Error("missing stack"));
267
+ }
268
+
269
+ yield* withStateService(rest, (state) =>
270
+ Effect.gen(function* () {
271
+ const targets: ReadonlyArray<{ stack: string; stage?: string }> =
272
+ stackName === undefined
273
+ ? [...(yield* state.listStacks())]
274
+ .sort()
275
+ .map((s) => ({ stack: s }))
276
+ : stageName === undefined
277
+ ? [{ stack: stackName }]
278
+ : [{ stack: stackName, stage: stageName }];
279
+
280
+ if (targets.length === 0) {
281
+ yield* Console.log("(nothing to clear)");
282
+ return;
283
+ }
284
+
285
+ const scope =
286
+ stackName === undefined
287
+ ? `ALL stacks (${targets.length}): ${targets.map((t) => t.stack).join(", ")}`
288
+ : stageName === undefined
289
+ ? `stack '${stackName}' (all stages)`
290
+ : `stage '${stageName}' in stack '${stackName}'`;
291
+
292
+ if (!yesFlag) {
293
+ const ok = yield* Clank.confirm({
294
+ message: `About to delete ${scope} from the state store. This cannot be undone. Continue?`,
295
+ initialValue: false,
296
+ });
297
+ if (!ok) {
298
+ yield* Console.log("Cancelled.");
299
+ return;
300
+ }
301
+ }
302
+
303
+ for (const target of targets) {
304
+ yield* state.deleteStack(target);
305
+ yield* Console.log(
306
+ `cleared ${target.stack}${target.stage ? `/${target.stage}` : ""}`,
307
+ );
308
+ }
309
+ }),
310
+ );
311
+ }),
312
+ );
313
+
225
314
  export const stateCommand = Command.make("state", {}).pipe(
226
315
  Command.withSubcommands([
227
316
  stacksCommand,
@@ -229,5 +318,6 @@ export const stateCommand = Command.make("state", {}).pipe(
229
318
  resourcesCommand,
230
319
  getCommand,
231
320
  treeCommand,
321
+ clearCommand,
232
322
  ]),
233
323
  );
@@ -0,0 +1,106 @@
1
+ import * as cloudfront from "@distilled.cloud/aws/cloudfront";
2
+ import * as Provider from "../../Provider.ts";
3
+ import { Resource } from "../../Resource.ts";
4
+ import type { Providers } from "../Providers.ts";
5
+ export interface CachePolicyProps {
6
+ /**
7
+ * Name of the cache policy. If omitted, a deterministic name is generated.
8
+ *
9
+ * Names must be unique per AWS account. Changing the name triggers
10
+ * a replacement.
11
+ */
12
+ name?: string;
13
+ /**
14
+ * Optional comment describing the policy.
15
+ */
16
+ comment?: string;
17
+ /**
18
+ * Minimum amount of time, in seconds, that objects stay in the cache.
19
+ */
20
+ minTTL: number;
21
+ /**
22
+ * Default amount of time, in seconds, that objects stay in the cache when
23
+ * the origin does not send `Cache-Control` or `Expires` headers.
24
+ */
25
+ defaultTTL?: number;
26
+ /**
27
+ * Maximum amount of time, in seconds, that objects stay in the cache.
28
+ */
29
+ maxTTL?: number;
30
+ /**
31
+ * Controls which request values become part of the cache key and which
32
+ * additional headers/cookies/query strings CloudFront forwards to the origin.
33
+ */
34
+ parametersInCacheKeyAndForwardedToOrigin?: cloudfront.ParametersInCacheKeyAndForwardedToOrigin;
35
+ }
36
+ export interface CachePolicy extends Resource<"AWS.CloudFront.CachePolicy", CachePolicyProps, {
37
+ /**
38
+ * CloudFront-assigned cache policy identifier.
39
+ */
40
+ cachePolicyId: string;
41
+ /**
42
+ * Name of the cache policy.
43
+ */
44
+ name: string;
45
+ /**
46
+ * Most recent entity tag for update/delete operations.
47
+ */
48
+ etag: string | undefined;
49
+ /**
50
+ * Current comment on the policy.
51
+ */
52
+ comment: string | undefined;
53
+ /**
54
+ * Current minimum TTL.
55
+ */
56
+ minTTL: number;
57
+ /**
58
+ * Current default TTL.
59
+ */
60
+ defaultTTL: number | undefined;
61
+ /**
62
+ * Current maximum TTL.
63
+ */
64
+ maxTTL: number | undefined;
65
+ /**
66
+ * Current cache-key/forwarded-value configuration.
67
+ */
68
+ parametersInCacheKeyAndForwardedToOrigin: cloudfront.ParametersInCacheKeyAndForwardedToOrigin | undefined;
69
+ }, never, Providers> {
70
+ }
71
+ /**
72
+ * A CloudFront cache policy.
73
+ *
74
+ * Cache policies determine the values CloudFront includes in the cache key,
75
+ * the headers, cookies and query strings it forwards to the origin, and the
76
+ * TTL bounds for cached responses. Policies are referenced by ID on a
77
+ * Distribution's default behavior or per-path cache behaviors.
78
+ *
79
+ * For AWS-managed policies (CachingOptimized, CachingDisabled,
80
+ * AllViewerExceptHostHeader) reference them by ID via the constants in
81
+ * {@link ManagedPolicies} instead of creating a custom policy.
82
+ *
83
+ * @section Creating Cache Policies
84
+ * @example Cache by query string and Authorization header
85
+ * ```typescript
86
+ * const cachePolicy = yield* CachePolicy("ApiCachePolicy", {
87
+ * comment: "Cache GETs by query string + Authorization",
88
+ * minTTL: 0,
89
+ * defaultTTL: 60,
90
+ * maxTTL: 3600,
91
+ * parametersInCacheKeyAndForwardedToOrigin: {
92
+ * EnableAcceptEncodingGzip: true,
93
+ * EnableAcceptEncodingBrotli: true,
94
+ * HeadersConfig: {
95
+ * HeaderBehavior: "whitelist",
96
+ * Headers: { Quantity: 1, Items: ["Authorization"] },
97
+ * },
98
+ * CookiesConfig: { CookieBehavior: "none" },
99
+ * QueryStringsConfig: { QueryStringBehavior: "all" },
100
+ * },
101
+ * });
102
+ * ```
103
+ */
104
+ export declare const CachePolicy: import("../../Resource.ts").ResourceClass<CachePolicy>;
105
+ export declare const CachePolicyProvider: () => import("effect/Layer").Layer<Provider.Provider<CachePolicy>, never, import("effect/unstable/http/HttpClient").HttpClient | import("../../Stage.ts").Stage | import("../../Stack.ts").Stack | import("@distilled.cloud/aws/Region").Region | import("@distilled.cloud/aws/Credentials").Credentials>;
106
+ //# sourceMappingURL=CachePolicy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CachePolicy.d.ts","sourceRoot":"","sources":["../../../src/AWS/CloudFront/CachePolicy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,UAAU,MAAM,iCAAiC,CAAC;AAI9D,OAAO,KAAK,QAAQ,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,MAAM,WAAW,gBAAgB;IAC/B;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,wCAAwC,CAAC,EAAE,UAAU,CAAC,wCAAwC,CAAC;CAChG;AAED,MAAM,WAAW,WAAY,SAAQ,QAAQ,CAC3C,4BAA4B,EAC5B,gBAAgB,EAChB;IACE;;OAEG;IACH,aAAa,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB;;OAEG;IACH,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B;;OAEG;IACH,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B;;OAEG;IACH,wCAAwC,EACpC,UAAU,CAAC,wCAAwC,GACnD,SAAS,CAAC;CACf,EACD,KAAK,EACL,SAAS,CACV;CAAG;AAEJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,eAAO,MAAM,WAAW,wDAAsD,CAAC;AAE/E,eAAO,MAAM,mBAAmB,2SAmJ7B,CAAC"}
@@ -0,0 +1,147 @@
1
+ import * as cloudfront from "@distilled.cloud/aws/cloudfront";
2
+ import * as Effect from "effect/Effect";
3
+ import { isResolved } from "../../Diff.js";
4
+ import { createPhysicalName } from "../../PhysicalName.js";
5
+ import * as Provider from "../../Provider.js";
6
+ import { Resource } from "../../Resource.js";
7
+ /**
8
+ * A CloudFront cache policy.
9
+ *
10
+ * Cache policies determine the values CloudFront includes in the cache key,
11
+ * the headers, cookies and query strings it forwards to the origin, and the
12
+ * TTL bounds for cached responses. Policies are referenced by ID on a
13
+ * Distribution's default behavior or per-path cache behaviors.
14
+ *
15
+ * For AWS-managed policies (CachingOptimized, CachingDisabled,
16
+ * AllViewerExceptHostHeader) reference them by ID via the constants in
17
+ * {@link ManagedPolicies} instead of creating a custom policy.
18
+ *
19
+ * @section Creating Cache Policies
20
+ * @example Cache by query string and Authorization header
21
+ * ```typescript
22
+ * const cachePolicy = yield* CachePolicy("ApiCachePolicy", {
23
+ * comment: "Cache GETs by query string + Authorization",
24
+ * minTTL: 0,
25
+ * defaultTTL: 60,
26
+ * maxTTL: 3600,
27
+ * parametersInCacheKeyAndForwardedToOrigin: {
28
+ * EnableAcceptEncodingGzip: true,
29
+ * EnableAcceptEncodingBrotli: true,
30
+ * HeadersConfig: {
31
+ * HeaderBehavior: "whitelist",
32
+ * Headers: { Quantity: 1, Items: ["Authorization"] },
33
+ * },
34
+ * CookiesConfig: { CookieBehavior: "none" },
35
+ * QueryStringsConfig: { QueryStringBehavior: "all" },
36
+ * },
37
+ * });
38
+ * ```
39
+ */
40
+ export const CachePolicy = Resource("AWS.CloudFront.CachePolicy");
41
+ export const CachePolicyProvider = () => Provider.effect(CachePolicy, Effect.gen(function* () {
42
+ const getById = Effect.fn(function* (id) {
43
+ const config = yield* cloudfront
44
+ .getCachePolicyConfig({ Id: id })
45
+ .pipe(Effect.catchTag("NoSuchCachePolicy", () => Effect.succeed(undefined)));
46
+ if (!config?.CachePolicyConfig)
47
+ return undefined;
48
+ return { config: config.CachePolicyConfig, etag: config.ETag };
49
+ });
50
+ const getByName = Effect.fn(function* (name) {
51
+ const listed = yield* cloudfront.listCachePolicies({ Type: "custom" });
52
+ const summary = listed.CachePolicyList?.Items?.find((item) => item.CachePolicy?.CachePolicyConfig?.Name === name);
53
+ if (!summary?.CachePolicy?.Id)
54
+ return undefined;
55
+ return yield* getById(summary.CachePolicy.Id).pipe(Effect.map((found) => found ? { id: summary.CachePolicy.Id, ...found } : undefined));
56
+ });
57
+ const buildConfig = (name, props) => ({
58
+ Name: name,
59
+ Comment: props.comment,
60
+ MinTTL: props.minTTL,
61
+ DefaultTTL: props.defaultTTL,
62
+ MaxTTL: props.maxTTL,
63
+ ParametersInCacheKeyAndForwardedToOrigin: props.parametersInCacheKeyAndForwardedToOrigin,
64
+ });
65
+ const toAttrs = (id, config, etag) => ({
66
+ cachePolicyId: id,
67
+ name: config.Name,
68
+ etag,
69
+ comment: config.Comment,
70
+ minTTL: config.MinTTL,
71
+ defaultTTL: config.DefaultTTL,
72
+ maxTTL: config.MaxTTL,
73
+ parametersInCacheKeyAndForwardedToOrigin: config.ParametersInCacheKeyAndForwardedToOrigin,
74
+ });
75
+ return {
76
+ stables: ["cachePolicyId"],
77
+ diff: Effect.fn(function* ({ id, news, olds }) {
78
+ if (!isResolved(news))
79
+ return undefined;
80
+ if ((yield* createName(id, olds ?? {})) !==
81
+ (yield* createName(id, news))) {
82
+ return { action: "replace" };
83
+ }
84
+ }),
85
+ read: Effect.fn(function* ({ id, olds, output }) {
86
+ if (output?.cachePolicyId) {
87
+ const found = yield* getById(output.cachePolicyId);
88
+ if (found)
89
+ return toAttrs(output.cachePolicyId, found.config, found.etag);
90
+ }
91
+ const name = yield* createName(id, olds ?? {});
92
+ const found = yield* getByName(name);
93
+ if (!found)
94
+ return undefined;
95
+ return toAttrs(found.id, found.config, found.etag);
96
+ }),
97
+ create: Effect.fn(function* ({ id, news, session }) {
98
+ const name = yield* createName(id, news);
99
+ const created = yield* cloudfront
100
+ .createCachePolicy({ CachePolicyConfig: buildConfig(name, news) })
101
+ .pipe(Effect.catchTag("CachePolicyAlreadyExists", () => getByName(name).pipe(Effect.flatMap((existing) => existing
102
+ ? Effect.succeed({
103
+ CachePolicy: {
104
+ Id: existing.id,
105
+ LastModifiedTime: new Date(),
106
+ CachePolicyConfig: existing.config,
107
+ },
108
+ ETag: existing.etag,
109
+ Location: undefined,
110
+ })
111
+ : Effect.fail(new Error(`Cache policy '${name}' already exists but could not be recovered`))))));
112
+ if (!created.CachePolicy?.Id) {
113
+ return yield* Effect.fail(new Error("createCachePolicy returned no identifier"));
114
+ }
115
+ yield* session.note(created.CachePolicy.Id);
116
+ return toAttrs(created.CachePolicy.Id, created.CachePolicy.CachePolicyConfig, created.ETag);
117
+ }),
118
+ update: Effect.fn(function* ({ news, output, session }) {
119
+ const current = yield* getById(output.cachePolicyId);
120
+ const updated = yield* cloudfront.updateCachePolicy({
121
+ Id: output.cachePolicyId,
122
+ IfMatch: current?.etag ?? output.etag,
123
+ CachePolicyConfig: buildConfig(output.name, news),
124
+ });
125
+ if (!updated.CachePolicy?.Id) {
126
+ return yield* Effect.fail(new Error("updateCachePolicy returned no identifier"));
127
+ }
128
+ yield* session.note(output.cachePolicyId);
129
+ return toAttrs(updated.CachePolicy.Id, updated.CachePolicy.CachePolicyConfig, updated.ETag);
130
+ }),
131
+ delete: Effect.fn(function* ({ output }) {
132
+ const current = yield* getById(output.cachePolicyId);
133
+ if (!current)
134
+ return;
135
+ yield* cloudfront
136
+ .deleteCachePolicy({
137
+ Id: output.cachePolicyId,
138
+ IfMatch: current.etag,
139
+ })
140
+ .pipe(Effect.catchTag("NoSuchCachePolicy", () => Effect.void));
141
+ }),
142
+ };
143
+ }));
144
+ const createName = (id, props) => props.name
145
+ ? Effect.succeed(props.name)
146
+ : createPhysicalName({ id, maxLength: 128, lowercase: true });
147
+ //# sourceMappingURL=CachePolicy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CachePolicy.js","sourceRoot":"","sources":["../../../src/AWS/CloudFront/CachePolicy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ import type { Input } from "../../Input.ts";
2
+ import * as Provider from "../../Provider.ts";
3
+ import { Resource } from "../../Resource.ts";
4
+ import type { Providers } from "../Providers.ts";
5
+ export interface KeyGroupProps {
6
+ /**
7
+ * Name of the key group. If omitted, a deterministic name is generated.
8
+ *
9
+ * Names must be unique per AWS account. Changing the name triggers
10
+ * a replacement.
11
+ */
12
+ name?: string;
13
+ /**
14
+ * Public key IDs that belong to the group. Order is preserved.
15
+ */
16
+ items: Input<string>[];
17
+ /**
18
+ * Optional comment describing the group.
19
+ */
20
+ comment?: string;
21
+ }
22
+ export interface KeyGroup extends Resource<"AWS.CloudFront.KeyGroup", KeyGroupProps, {
23
+ /**
24
+ * CloudFront-assigned key group identifier. Used by Distributions in
25
+ * `TrustedKeyGroups` to authorize signed URLs/cookies.
26
+ */
27
+ keyGroupId: string;
28
+ /**
29
+ * Name of the key group.
30
+ */
31
+ name: string;
32
+ /**
33
+ * Public key IDs that belong to the group.
34
+ */
35
+ items: string[];
36
+ /**
37
+ * Most recent entity tag for update/delete operations.
38
+ */
39
+ etag: string | undefined;
40
+ /**
41
+ * Current comment on the group.
42
+ */
43
+ comment: string | undefined;
44
+ }, never, Providers> {
45
+ }
46
+ /**
47
+ * A CloudFront key group.
48
+ *
49
+ * Key groups bundle one or more {@link PublicKey} resources for use as
50
+ * `TrustedKeyGroups` on a Distribution's cache behavior. CloudFront uses
51
+ * the keys in the group to verify the signatures on signed URLs and
52
+ * signed cookies for that behavior.
53
+ *
54
+ * @section Creating Key Groups
55
+ * @example Group two public keys for signed URL verification
56
+ * ```typescript
57
+ * const primary = yield* PublicKey("PrimarySigningKey", {
58
+ * encodedKey: yield* fs.readFileString("./primary.pem"),
59
+ * });
60
+ * const secondary = yield* PublicKey("SecondarySigningKey", {
61
+ * encodedKey: yield* fs.readFileString("./secondary.pem"),
62
+ * });
63
+ *
64
+ * const keyGroup = yield* KeyGroup("SignedUrlKeys", {
65
+ * comment: "Trusted signers for /private",
66
+ * items: [primary.publicKeyId, secondary.publicKeyId],
67
+ * });
68
+ * ```
69
+ */
70
+ export declare const KeyGroup: import("../../Resource.ts").ResourceClass<KeyGroup>;
71
+ export declare const KeyGroupProvider: () => import("effect/Layer").Layer<Provider.Provider<KeyGroup>, never, import("effect/unstable/http/HttpClient").HttpClient | import("../../Stage.ts").Stage | import("../../Stack.ts").Stack | import("@distilled.cloud/aws/Region").Region | import("@distilled.cloud/aws/Credentials").Credentials>;
72
+ //# sourceMappingURL=KeyGroup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"KeyGroup.d.ts","sourceRoot":"","sources":["../../../src/AWS/CloudFront/KeyGroup.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAE5C,OAAO,KAAK,QAAQ,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,MAAM,WAAW,aAAa;IAC5B;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;IACvB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,QAAS,SAAQ,QAAQ,CACxC,yBAAyB,EACzB,aAAa,EACb;IACE;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB;;OAEG;IACH,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB;;OAEG;IACH,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B,EACD,KAAK,EACL,SAAS,CACV;CAAG;AAEJ;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,QAAQ,qDAAgD,CAAC;AAEtE,eAAO,MAAM,gBAAgB,wSAgJ1B,CAAC"}