alchemy 2.0.0-beta.17 → 2.0.0-beta.19

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 (182) hide show
  1. package/bin/alchemy.js +428 -80
  2. package/bin/alchemy.js.map +1 -1
  3. package/bin/alchemy.ts +2 -0
  4. package/bin/commands/_shared.ts +34 -0
  5. package/bin/commands/bootstrap.ts +10 -3
  6. package/bin/commands/deploy.ts +23 -3
  7. package/bin/commands/login.ts +18 -3
  8. package/bin/commands/logs.ts +11 -2
  9. package/bin/commands/profile.ts +11 -5
  10. package/bin/commands/state.ts +21 -13
  11. package/bin/commands/tail.ts +10 -2
  12. package/lib/AWS/CloudFront/CachePolicy.d.ts +106 -0
  13. package/lib/AWS/CloudFront/CachePolicy.d.ts.map +1 -0
  14. package/lib/AWS/CloudFront/CachePolicy.js +147 -0
  15. package/lib/AWS/CloudFront/CachePolicy.js.map +1 -0
  16. package/lib/AWS/CloudFront/KeyGroup.d.ts +72 -0
  17. package/lib/AWS/CloudFront/KeyGroup.d.ts.map +1 -0
  18. package/lib/AWS/CloudFront/KeyGroup.js +136 -0
  19. package/lib/AWS/CloudFront/KeyGroup.js.map +1 -0
  20. package/lib/AWS/CloudFront/OriginRequestPolicy.d.ts +84 -0
  21. package/lib/AWS/CloudFront/OriginRequestPolicy.d.ts.map +1 -0
  22. package/lib/AWS/CloudFront/OriginRequestPolicy.js +139 -0
  23. package/lib/AWS/CloudFront/OriginRequestPolicy.js.map +1 -0
  24. package/lib/AWS/CloudFront/PublicKey.d.ts +72 -0
  25. package/lib/AWS/CloudFront/PublicKey.d.ts.map +1 -0
  26. package/lib/AWS/CloudFront/PublicKey.js +137 -0
  27. package/lib/AWS/CloudFront/PublicKey.js.map +1 -0
  28. package/lib/AWS/CloudFront/ResponseHeadersPolicy.d.ts +114 -0
  29. package/lib/AWS/CloudFront/ResponseHeadersPolicy.d.ts.map +1 -0
  30. package/lib/AWS/CloudFront/ResponseHeadersPolicy.js +162 -0
  31. package/lib/AWS/CloudFront/ResponseHeadersPolicy.js.map +1 -0
  32. package/lib/AWS/CloudFront/index.d.ts +5 -0
  33. package/lib/AWS/CloudFront/index.d.ts.map +1 -1
  34. package/lib/AWS/CloudFront/index.js +5 -0
  35. package/lib/AWS/CloudFront/index.js.map +1 -1
  36. package/lib/AWS/ECS/CapacityProvider.d.ts +102 -0
  37. package/lib/AWS/ECS/CapacityProvider.d.ts.map +1 -0
  38. package/lib/AWS/ECS/CapacityProvider.js +217 -0
  39. package/lib/AWS/ECS/CapacityProvider.js.map +1 -0
  40. package/lib/AWS/ECS/index.d.ts +1 -0
  41. package/lib/AWS/ECS/index.d.ts.map +1 -1
  42. package/lib/AWS/ECS/index.js +1 -0
  43. package/lib/AWS/ECS/index.js.map +1 -1
  44. package/lib/AWS/Providers.d.ts.map +1 -1
  45. package/lib/AWS/Providers.js +7 -1
  46. package/lib/AWS/Providers.js.map +1 -1
  47. package/lib/Agent/lsp/client.d.ts +2 -2
  48. package/lib/Apply.d.ts.map +1 -1
  49. package/lib/Apply.js +41 -9
  50. package/lib/Apply.js.map +1 -1
  51. package/lib/Axiom/Annotation.d.ts +49 -0
  52. package/lib/Axiom/Annotation.d.ts.map +1 -0
  53. package/lib/Axiom/Annotation.js +74 -0
  54. package/lib/Axiom/Annotation.js.map +1 -0
  55. package/lib/Axiom/ApiToken.d.ts +62 -0
  56. package/lib/Axiom/ApiToken.d.ts.map +1 -0
  57. package/lib/Axiom/ApiToken.js +98 -0
  58. package/lib/Axiom/ApiToken.js.map +1 -0
  59. package/lib/Axiom/AuthProvider.d.ts +48 -0
  60. package/lib/Axiom/AuthProvider.d.ts.map +1 -0
  61. package/lib/Axiom/AuthProvider.js +177 -0
  62. package/lib/Axiom/AuthProvider.js.map +1 -0
  63. package/lib/Axiom/Chart.d.ts +72 -0
  64. package/lib/Axiom/Chart.d.ts.map +1 -0
  65. package/lib/Axiom/Chart.js +14 -0
  66. package/lib/Axiom/Chart.js.map +1 -0
  67. package/lib/Axiom/Credentials.d.ts +12 -0
  68. package/lib/Axiom/Credentials.d.ts.map +1 -0
  69. package/lib/Axiom/Credentials.js +34 -0
  70. package/lib/Axiom/Credentials.js.map +1 -0
  71. package/lib/Axiom/Dashboard.d.ts +124 -0
  72. package/lib/Axiom/Dashboard.d.ts.map +1 -0
  73. package/lib/Axiom/Dashboard.js +140 -0
  74. package/lib/Axiom/Dashboard.js.map +1 -0
  75. package/lib/Axiom/Dataset.d.ts +101 -0
  76. package/lib/Axiom/Dataset.d.ts.map +1 -0
  77. package/lib/Axiom/Dataset.js +183 -0
  78. package/lib/Axiom/Dataset.js.map +1 -0
  79. package/lib/Axiom/Monitor.d.ts +74 -0
  80. package/lib/Axiom/Monitor.d.ts.map +1 -0
  81. package/lib/Axiom/Monitor.js +103 -0
  82. package/lib/Axiom/Monitor.js.map +1 -0
  83. package/lib/Axiom/Notifier.d.ts +63 -0
  84. package/lib/Axiom/Notifier.d.ts.map +1 -0
  85. package/lib/Axiom/Notifier.js +83 -0
  86. package/lib/Axiom/Notifier.js.map +1 -0
  87. package/lib/Axiom/Providers.d.ts +15 -0
  88. package/lib/Axiom/Providers.d.ts.map +1 -0
  89. package/lib/Axiom/Providers.js +32 -0
  90. package/lib/Axiom/Providers.js.map +1 -0
  91. package/lib/Axiom/View.d.ts +55 -0
  92. package/lib/Axiom/View.d.ts.map +1 -0
  93. package/lib/Axiom/View.js +79 -0
  94. package/lib/Axiom/View.js.map +1 -0
  95. package/lib/Axiom/VirtualField.d.ts +42 -0
  96. package/lib/Axiom/VirtualField.d.ts.map +1 -0
  97. package/lib/Axiom/VirtualField.js +71 -0
  98. package/lib/Axiom/VirtualField.js.map +1 -0
  99. package/lib/Axiom/index.d.ts +13 -0
  100. package/lib/Axiom/index.d.ts.map +1 -0
  101. package/lib/Axiom/index.js +13 -0
  102. package/lib/Axiom/index.js.map +1 -0
  103. package/lib/Bundle/Bundle.d.ts +20 -2
  104. package/lib/Bundle/Bundle.d.ts.map +1 -1
  105. package/lib/Bundle/Bundle.js +18 -3
  106. package/lib/Bundle/Bundle.js.map +1 -1
  107. package/lib/Bundle/PurePlugin.d.ts +109 -0
  108. package/lib/Bundle/PurePlugin.d.ts.map +1 -0
  109. package/lib/Bundle/PurePlugin.js +469 -0
  110. package/lib/Bundle/PurePlugin.js.map +1 -0
  111. package/lib/Bundle/index.d.ts +1 -0
  112. package/lib/Bundle/index.d.ts.map +1 -1
  113. package/lib/Bundle/index.js +1 -0
  114. package/lib/Bundle/index.js.map +1 -1
  115. package/lib/Cloudflare/Workers/Worker.d.ts +12 -0
  116. package/lib/Cloudflare/Workers/Worker.d.ts.map +1 -1
  117. package/lib/Cloudflare/Workers/Worker.js +19 -4
  118. package/lib/Cloudflare/Workers/Worker.js.map +1 -1
  119. package/lib/GitHub/Secrets.d.ts +45 -0
  120. package/lib/GitHub/Secrets.d.ts.map +1 -0
  121. package/lib/GitHub/Secrets.js +30 -0
  122. package/lib/GitHub/Secrets.js.map +1 -0
  123. package/lib/GitHub/index.d.ts +1 -0
  124. package/lib/GitHub/index.d.ts.map +1 -1
  125. package/lib/GitHub/index.js +1 -0
  126. package/lib/GitHub/index.js.map +1 -1
  127. package/lib/Output.d.ts.map +1 -1
  128. package/lib/Output.js +1 -1
  129. package/lib/Output.js.map +1 -1
  130. package/lib/Plan.d.ts.map +1 -1
  131. package/lib/Plan.js +10 -4
  132. package/lib/Plan.js.map +1 -1
  133. package/lib/Resource.d.ts +2 -2
  134. package/lib/Resource.d.ts.map +1 -1
  135. package/lib/Telemetry/Attributes.d.ts +43 -0
  136. package/lib/Telemetry/Attributes.d.ts.map +1 -0
  137. package/lib/Telemetry/Attributes.js +144 -0
  138. package/lib/Telemetry/Attributes.js.map +1 -0
  139. package/lib/Telemetry/Layer.d.ts +14 -0
  140. package/lib/Telemetry/Layer.d.ts.map +1 -0
  141. package/lib/Telemetry/Layer.js +50 -0
  142. package/lib/Telemetry/Layer.js.map +1 -0
  143. package/lib/Telemetry/Metrics.d.ts +34 -0
  144. package/lib/Telemetry/Metrics.d.ts.map +1 -0
  145. package/lib/Telemetry/Metrics.js +57 -0
  146. package/lib/Telemetry/Metrics.js.map +1 -0
  147. package/package.json +12 -1
  148. package/src/AWS/CloudFront/CachePolicy.ts +271 -0
  149. package/src/AWS/CloudFront/KeyGroup.ts +233 -0
  150. package/src/AWS/CloudFront/OriginRequestPolicy.ts +256 -0
  151. package/src/AWS/CloudFront/PublicKey.ts +245 -0
  152. package/src/AWS/CloudFront/ResponseHeadersPolicy.ts +308 -0
  153. package/src/AWS/CloudFront/index.ts +11 -0
  154. package/src/AWS/ECS/CapacityProvider.ts +336 -0
  155. package/src/AWS/ECS/index.ts +4 -0
  156. package/src/AWS/Providers.ts +12 -0
  157. package/src/Apply.ts +118 -9
  158. package/src/Axiom/Annotation.ts +95 -0
  159. package/src/Axiom/ApiToken.ts +125 -0
  160. package/src/Axiom/AuthProvider.ts +341 -0
  161. package/src/Axiom/Chart.ts +94 -0
  162. package/src/Axiom/Credentials.ts +70 -0
  163. package/src/Axiom/Dashboard.ts +192 -0
  164. package/src/Axiom/Dataset.ts +291 -0
  165. package/src/Axiom/Monitor.ts +122 -0
  166. package/src/Axiom/Notifier.ts +104 -0
  167. package/src/Axiom/Providers.ts +58 -0
  168. package/src/Axiom/View.ts +106 -0
  169. package/src/Axiom/VirtualField.ts +90 -0
  170. package/src/Axiom/index.ts +12 -0
  171. package/src/Bundle/Bundle.ts +39 -1
  172. package/src/Bundle/PurePlugin.ts +556 -0
  173. package/src/Bundle/index.ts +1 -0
  174. package/src/Cloudflare/Workers/Worker.ts +37 -6
  175. package/src/GitHub/Secrets.ts +66 -0
  176. package/src/GitHub/index.ts +1 -0
  177. package/src/Output.ts +2 -1
  178. package/src/Plan.ts +13 -4
  179. package/src/Resource.ts +2 -2
  180. package/src/Telemetry/Attributes.ts +216 -0
  181. package/src/Telemetry/Layer.ts +65 -0
  182. package/src/Telemetry/Metrics.ts +101 -0
package/bin/alchemy.ts CHANGED
@@ -7,6 +7,7 @@ import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient";
7
7
  import packageJson from "../package.json" with { type: "json" };
8
8
  import { AlchemyContextLive } from "../src/AlchemyContext.ts";
9
9
  import { inkCLI } from "../src/Cli/InkCLI.tsx";
10
+ import { TelemetryLive } from "../src/Telemetry/Layer.ts";
10
11
  import { PlatformServices, runMain } from "../src/Util/PlatformServices.ts";
11
12
 
12
13
  import { handleCancellation } from "./commands/_shared.ts";
@@ -45,6 +46,7 @@ const services = Layer.mergeAll(
45
46
  Layer.provideMerge(AlchemyContextLive, PlatformServices),
46
47
  FetchHttpClient.layer,
47
48
  ConfigProvider.layer(ConfigProvider.fromEnv()),
49
+ TelemetryLive,
48
50
  inkCLI(),
49
51
  );
50
52
 
@@ -11,6 +11,7 @@ import * as Flag from "effect/unstable/cli/Flag";
11
11
 
12
12
  import { AuthError } from "../../src/Auth/AuthProvider.ts";
13
13
  import type * as Stack from "../../src/Stack.ts";
14
+ import { recordCli } from "../../src/Telemetry/Metrics.ts";
14
15
  import { PromptCancelled } from "../../src/Util/Clank.ts";
15
16
 
16
17
  export const USER = Config.string("USER").pipe(
@@ -218,6 +219,39 @@ export const parseSince = (value: string): Date => {
218
219
  return parsed;
219
220
  };
220
221
 
222
+ /**
223
+ * Wraps a CLI command handler with a top-level OpenTelemetry span
224
+ * (`cli.<command>`) and bumps the `alchemy.cli.invocations` counter.
225
+ *
226
+ * `attrs` runs against the parsed command args and contributes
227
+ * additional attributes to the span (e.g. stage, profile, dry-run flag).
228
+ *
229
+ * Usage:
230
+ * ```ts
231
+ * Command.make(
232
+ * "deploy",
233
+ * { ...flags },
234
+ * instrumentCommand("deploy", (a) => ({
235
+ * "alchemy.stage": a.stage,
236
+ * "alchemy.profile": a.profile,
237
+ * }))(execStack),
238
+ * );
239
+ * ```
240
+ */
241
+ export const instrumentCommand =
242
+ <Args>(
243
+ command: string,
244
+ attrs: (args: Args) => Record<string, unknown> = () => ({}),
245
+ ) =>
246
+ <A, E, R>(
247
+ handler: (args: Args) => Effect.Effect<A, E, R>,
248
+ ): ((args: Args) => Effect.Effect<A, E, R>) =>
249
+ (args) =>
250
+ handler(args).pipe(
251
+ Effect.withSpan(`cli.${command}`, { attributes: attrs(args) }),
252
+ recordCli(command),
253
+ );
254
+
221
255
  export const importStack = Effect.fn(function* (main: string) {
222
256
  const path = yield* Path.Path;
223
257
  const module = yield* Effect.promise(
@@ -21,7 +21,7 @@ import * as AWSRegion from "../../src/AWS/Region.ts";
21
21
  import { loadConfigProvider } from "../../src/Util/ConfigProvider.ts";
22
22
  import { fileLogger } from "../../src/Util/FileLogger.ts";
23
23
 
24
- import { envFile } from "./_shared.ts";
24
+ import { envFile, instrumentCommand } from "./_shared.ts";
25
25
 
26
26
  const awsProfile = Flag.string("profile").pipe(
27
27
  Flag.withDescription("AWS profile to use for credentials"),
@@ -50,7 +50,14 @@ export const bootstrapCommand = Command.make(
50
50
  region: awsRegion,
51
51
  destroy: bootstrapDestroy,
52
52
  },
53
- Effect.fnUntraced(function* ({ envFile, profile, region, destroy }) {
53
+ instrumentCommand(
54
+ "bootstrap",
55
+ (a: { profile: string; region: string | undefined; destroy: boolean }) => ({
56
+ "alchemy.profile": a.profile,
57
+ "alchemy.region": a.region ?? "",
58
+ "alchemy.destroy": a.destroy,
59
+ }),
60
+ )(Effect.fnUntraced(function* ({ envFile, profile, region, destroy }) {
54
61
  const logger = Logger.layer([fileLogger("bootstrap.txt")]);
55
62
 
56
63
  return yield* Effect.gen(function* () {
@@ -113,5 +120,5 @@ export const bootstrapCommand = Command.make(
113
120
  );
114
121
  });
115
122
  }).pipe(Effect.provide(logger));
116
- }),
123
+ })),
117
124
  );
@@ -21,12 +21,30 @@ import {
21
21
  envFile,
22
22
  force,
23
23
  importStack,
24
+ instrumentCommand,
24
25
  main,
25
26
  profile,
26
27
  stage,
27
28
  yes,
28
29
  } from "./_shared.ts";
29
30
 
31
+ const stackSpanAttrs = (args: {
32
+ main: string;
33
+ stage: string;
34
+ profile: string;
35
+ dryRun?: boolean;
36
+ force?: boolean;
37
+ yes?: boolean;
38
+ destroy?: boolean;
39
+ }) => ({
40
+ "alchemy.stage": args.stage,
41
+ "alchemy.profile": args.profile,
42
+ "alchemy.main": args.main,
43
+ "alchemy.dry_run": !!args.dryRun,
44
+ "alchemy.force": !!args.force,
45
+ "alchemy.destroy": !!args.destroy,
46
+ });
47
+
30
48
  const execStack = Effect.fn(function* ({
31
49
  main,
32
50
  stage,
@@ -118,7 +136,7 @@ export const deployCommand = Command.make(
118
136
  yes,
119
137
  profile,
120
138
  },
121
- execStack,
139
+ instrumentCommand("deploy", stackSpanAttrs)(execStack),
122
140
  );
123
141
 
124
142
  export const destroyCommand = Command.make(
@@ -131,11 +149,12 @@ export const destroyCommand = Command.make(
131
149
  yes,
132
150
  profile,
133
151
  },
134
- (args) =>
152
+ instrumentCommand("destroy", stackSpanAttrs)((args) =>
135
153
  execStack({
136
154
  ...args,
137
155
  destroy: true,
138
156
  }),
157
+ ),
139
158
  );
140
159
 
141
160
  export const planCommand = Command.make(
@@ -146,10 +165,11 @@ export const planCommand = Command.make(
146
165
  stage,
147
166
  profile,
148
167
  },
149
- (args) =>
168
+ instrumentCommand("plan", stackSpanAttrs)((args) =>
150
169
  execStack({
151
170
  ...args,
152
171
  // plan is the same as deploy with dryRun always set to true
153
172
  dryRun: true,
154
173
  }),
174
+ ),
155
175
  );
@@ -18,7 +18,14 @@ import * as State from "../../src/State/index.ts";
18
18
  import { loadConfigProvider } from "../../src/Util/ConfigProvider.ts";
19
19
  import { fileLogger } from "../../src/Util/FileLogger.ts";
20
20
 
21
- import { envFile, importStack, main, profile, stage } from "./_shared.ts";
21
+ import {
22
+ envFile,
23
+ importStack,
24
+ instrumentCommand,
25
+ main,
26
+ profile,
27
+ stage,
28
+ } from "./_shared.ts";
22
29
 
23
30
  const loginConfigure = Flag.boolean("configure").pipe(
24
31
  Flag.withDescription(
@@ -36,7 +43,15 @@ export const loginCommand = Command.make(
36
43
  profile,
37
44
  configure: loginConfigure,
38
45
  },
39
- Effect.fnUntraced(function* ({ main, stage, envFile, profile, configure }) {
46
+ instrumentCommand(
47
+ "login",
48
+ (a: { main: string; stage: string; profile: string; configure: boolean }) => ({
49
+ "alchemy.stage": a.stage,
50
+ "alchemy.profile": a.profile,
51
+ "alchemy.main": a.main,
52
+ "alchemy.configure": a.configure,
53
+ }),
54
+ )(Effect.fnUntraced(function* ({ main, stage, envFile, profile, configure }) {
40
55
  const stackEffect = yield* importStack(main);
41
56
 
42
57
  const authProviders: AuthProviders["Service"] = {};
@@ -92,5 +107,5 @@ export const loginCommand = Command.make(
92
107
  { discard: true },
93
108
  );
94
109
  }).pipe(Effect.provide(services));
95
- }),
110
+ })),
96
111
  );
@@ -18,6 +18,7 @@ import {
18
18
  envFile,
19
19
  formatLocalTimestamp,
20
20
  importStack,
21
+ instrumentCommand,
21
22
  main,
22
23
  parseResourceFilter,
23
24
  parseSince,
@@ -52,7 +53,15 @@ export const logsCommand = Command.make(
52
53
  limit: logsLimit,
53
54
  since: logsSince,
54
55
  },
55
- Effect.fnUntraced(function* ({
56
+ instrumentCommand(
57
+ "logs",
58
+ (a: { main: string; stage: string; profile: string; limit: number }) => ({
59
+ "alchemy.stage": a.stage,
60
+ "alchemy.profile": a.profile,
61
+ "alchemy.main": a.main,
62
+ "alchemy.limit": a.limit,
63
+ }),
64
+ )(Effect.fnUntraced(function* ({
56
65
  main,
57
66
  stage,
58
67
  envFile,
@@ -153,5 +162,5 @@ export const logsCommand = Command.make(
153
162
  }
154
163
  }).pipe(Effect.provide(stack.services));
155
164
  }).pipe(Effect.provide(services));
156
- }),
165
+ })),
157
166
  );
@@ -18,12 +18,15 @@ import { CloudflareAuth } from "../../src/Cloudflare/Auth/AuthProvider.ts";
18
18
  import { loadConfigProvider } from "../../src/Util/ConfigProvider.ts";
19
19
  import { fileLogger } from "../../src/Util/FileLogger.ts";
20
20
 
21
- import { envFile, profile } from "./_shared.ts";
21
+ import { envFile, instrumentCommand, profile } from "./_shared.ts";
22
22
 
23
23
  const showCommand = Command.make(
24
24
  "show",
25
25
  { profile, envFile },
26
- Effect.fnUntraced(function* ({ profile, envFile }) {
26
+ instrumentCommand(
27
+ "profile.show",
28
+ (a: { profile: string }) => ({ "alchemy.profile": a.profile }),
29
+ )(Effect.fnUntraced(function* ({ profile, envFile }) {
27
30
  const stored = yield* getProfile(profile);
28
31
  if (stored == null) {
29
32
  const config = yield* readConfig;
@@ -80,13 +83,16 @@ const showCommand = Command.make(
80
83
  yield* provider.prettyPrint(profile, cfg);
81
84
  }
82
85
  }).pipe(Effect.provide(services));
83
- }),
86
+ })),
84
87
  );
85
88
 
86
89
  const clearCommand = Command.make(
87
90
  "clear",
88
91
  { profile },
89
- Effect.fnUntraced(function* ({ profile }) {
92
+ instrumentCommand(
93
+ "profile.clear",
94
+ (a: { profile: string }) => ({ "alchemy.profile": a.profile }),
95
+ )(Effect.fnUntraced(function* ({ profile }) {
90
96
  const removed = yield* deleteProfile(profile);
91
97
  yield* deleteProfileCredentials(profile);
92
98
  if (removed) {
@@ -98,7 +104,7 @@ const clearCommand = Command.make(
98
104
  `Profile '${profile}' not found in profiles.json; removed any stray credentials directory.`,
99
105
  );
100
106
  }
101
- }),
107
+ })),
102
108
  );
103
109
 
104
110
  export const profileCommand = Command.make("profile", {}).pipe(
@@ -15,7 +15,15 @@ import * as Clank from "../../src/Util/Clank.ts";
15
15
  import { loadConfigProvider } from "../../src/Util/ConfigProvider.ts";
16
16
  import { fileLogger } from "../../src/Util/FileLogger.ts";
17
17
 
18
- import { envFile, importStack, main, profile, stage, yes } from "./_shared.ts";
18
+ import {
19
+ envFile,
20
+ importStack,
21
+ instrumentCommand,
22
+ main,
23
+ profile,
24
+ stage,
25
+ yes,
26
+ } from "./_shared.ts";
19
27
 
20
28
  /**
21
29
  * When set, the State service is replaced with the on-disk
@@ -91,7 +99,7 @@ const withStateService = <A, E>(
91
99
  const stacksCommand = Command.make(
92
100
  "stacks",
93
101
  { main, envFile, stage, profile, local: localFlag },
94
- Effect.fnUntraced(function* (args) {
102
+ instrumentCommand("state.stacks")(Effect.fnUntraced(function* (args) {
95
103
  yield* withStateService(args, (state) =>
96
104
  Effect.gen(function* () {
97
105
  const stacks = yield* state.listStacks();
@@ -104,13 +112,13 @@ const stacksCommand = Command.make(
104
112
  }
105
113
  }),
106
114
  );
107
- }),
115
+ })),
108
116
  );
109
117
 
110
118
  const stagesCommand = Command.make(
111
119
  "stages",
112
120
  { stack: stackArg, main, envFile, stage, profile, local: localFlag },
113
- Effect.fnUntraced(function* ({ stack: stackName, ...rest }) {
121
+ instrumentCommand("state.stages")(Effect.fnUntraced(function* ({ stack: stackName, ...rest }) {
114
122
  yield* withStateService(rest, (state) =>
115
123
  Effect.gen(function* () {
116
124
  const stages = yield* state.listStages(stackName);
@@ -123,7 +131,7 @@ const stagesCommand = Command.make(
123
131
  }
124
132
  }),
125
133
  );
126
- }),
134
+ })),
127
135
  );
128
136
 
129
137
  const resourcesCommand = Command.make(
@@ -139,7 +147,7 @@ const resourcesCommand = Command.make(
139
147
  profile,
140
148
  local: localFlag,
141
149
  },
142
- Effect.fnUntraced(function* ({ stack: stackName, stageName, ...rest }) {
150
+ instrumentCommand("state.resources")(Effect.fnUntraced(function* ({ stack: stackName, stageName, ...rest }) {
143
151
  yield* withStateService(rest, (state) =>
144
152
  Effect.gen(function* () {
145
153
  const fqns = yield* state.list({ stack: stackName, stage: stageName });
@@ -152,7 +160,7 @@ const resourcesCommand = Command.make(
152
160
  }
153
161
  }),
154
162
  );
155
- }),
163
+ })),
156
164
  );
157
165
 
158
166
  const getCommand = Command.make(
@@ -169,7 +177,7 @@ const getCommand = Command.make(
169
177
  profile,
170
178
  local: localFlag,
171
179
  },
172
- Effect.fnUntraced(function* ({ stack: stackName, stageName, fqn, ...rest }) {
180
+ instrumentCommand("state.get")(Effect.fnUntraced(function* ({ stack: stackName, stageName, fqn, ...rest }) {
173
181
  yield* withStateService(rest, (state) =>
174
182
  Effect.gen(function* () {
175
183
  const value = yield* state.get({
@@ -187,13 +195,13 @@ const getCommand = Command.make(
187
195
  yield* Console.log(JSON.stringify(encodeState(value), null, 2));
188
196
  }),
189
197
  );
190
- }),
198
+ })),
191
199
  );
192
200
 
193
201
  const treeCommand = Command.make(
194
202
  "tree",
195
203
  { main, envFile, stage, profile, local: localFlag },
196
- Effect.fnUntraced(function* (args) {
204
+ instrumentCommand("state.tree")(Effect.fnUntraced(function* (args) {
197
205
  yield* withStateService(args, (state) =>
198
206
  Effect.gen(function* () {
199
207
  const stacks = [...(yield* state.listStacks())].sort();
@@ -221,7 +229,7 @@ const treeCommand = Command.make(
221
229
  }
222
230
  }),
223
231
  );
224
- }),
232
+ })),
225
233
  );
226
234
 
227
235
  const clearStackArg = Argument.string("stack").pipe(
@@ -255,7 +263,7 @@ const clearCommand = Command.make(
255
263
  local: localFlag,
256
264
  yes,
257
265
  },
258
- Effect.fnUntraced(function* ({ stack: stackOpt, stageName: stageOpt, yes: yesFlag, ...rest }) {
266
+ instrumentCommand("state.clear")(Effect.fnUntraced(function* ({ stack: stackOpt, stageName: stageOpt, yes: yesFlag, ...rest }) {
259
267
  const stackName = Option.getOrUndefined(stackOpt);
260
268
  const stageName = Option.getOrUndefined(stageOpt);
261
269
 
@@ -308,7 +316,7 @@ const clearCommand = Command.make(
308
316
  }
309
317
  }),
310
318
  );
311
- }),
319
+ })),
312
320
  );
313
321
 
314
322
  export const stateCommand = Command.make("state", {}).pipe(
@@ -18,6 +18,7 @@ import {
18
18
  envFile,
19
19
  formatLocalTimestamp,
20
20
  importStack,
21
+ instrumentCommand,
21
22
  main,
22
23
  parseResourceFilter,
23
24
  profile,
@@ -36,7 +37,14 @@ export const tailCommand = Command.make(
36
37
  profile,
37
38
  filter: resourceFilter,
38
39
  },
39
- Effect.fnUntraced(function* ({ main, stage, envFile, profile, filter }) {
40
+ instrumentCommand(
41
+ "tail",
42
+ (a: { main: string; stage: string; profile: string }) => ({
43
+ "alchemy.stage": a.stage,
44
+ "alchemy.profile": a.profile,
45
+ "alchemy.main": a.main,
46
+ }),
47
+ )(Effect.fnUntraced(function* ({ main, stage, envFile, profile, filter }) {
40
48
  const stackEffect = yield* importStack(main);
41
49
 
42
50
  const services = Layer.mergeAll(
@@ -134,5 +142,5 @@ export const tailCommand = Command.make(
134
142
  }).pipe(Stream.runForEach((line) => Console.log(line)));
135
143
  }).pipe(Effect.provide(stack.services));
136
144
  }).pipe(Effect.provide(services));
137
- }),
145
+ })),
138
146
  );
@@ -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