alchemy 0.27.0 → 0.29.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.
Files changed (63) hide show
  1. package/README.md +1 -1
  2. package/lib/alchemy.d.ts +21 -1
  3. package/lib/alchemy.d.ts.map +1 -1
  4. package/lib/alchemy.js +42 -8
  5. package/lib/alchemy.js.map +1 -1
  6. package/lib/apply.js +3 -12
  7. package/lib/apply.js.map +1 -1
  8. package/lib/cloudflare/do-state-store/internal.d.ts +1 -1
  9. package/lib/cloudflare/do-state-store/internal.d.ts.map +1 -1
  10. package/lib/cloudflare/do-state-store/internal.js +2 -2
  11. package/lib/cloudflare/do-state-store/internal.js.map +1 -1
  12. package/lib/cloudflare/do-state-store/store.d.ts.map +1 -1
  13. package/lib/cloudflare/do-state-store/store.js +19 -4
  14. package/lib/cloudflare/do-state-store/store.js.map +1 -1
  15. package/lib/cloudflare/secrets-store.d.ts.map +1 -1
  16. package/lib/cloudflare/secrets-store.js +21 -17
  17. package/lib/cloudflare/secrets-store.js.map +1 -1
  18. package/lib/cloudflare/worker-assets.d.ts.map +1 -1
  19. package/lib/cloudflare/worker-assets.js +9 -0
  20. package/lib/cloudflare/worker-assets.js.map +1 -1
  21. package/lib/destroy.d.ts.map +1 -1
  22. package/lib/destroy.js +3 -6
  23. package/lib/destroy.js.map +1 -1
  24. package/lib/github/comment.d.ts +156 -0
  25. package/lib/github/comment.d.ts.map +1 -0
  26. package/lib/github/comment.js +192 -0
  27. package/lib/github/comment.js.map +1 -0
  28. package/lib/github/index.d.ts +1 -0
  29. package/lib/github/index.d.ts.map +1 -1
  30. package/lib/github/index.js +1 -0
  31. package/lib/github/index.js.map +1 -1
  32. package/lib/index.d.ts +1 -1
  33. package/lib/index.d.ts.map +1 -1
  34. package/lib/index.js +1 -0
  35. package/lib/index.js.map +1 -1
  36. package/lib/scope.d.ts +2 -1
  37. package/lib/scope.d.ts.map +1 -1
  38. package/lib/scope.js +1 -1
  39. package/lib/scope.js.map +1 -1
  40. package/lib/util/cli.d.ts +2 -13
  41. package/lib/util/cli.d.ts.map +1 -1
  42. package/lib/util/cli.js +46 -60
  43. package/lib/util/cli.js.map +1 -1
  44. package/lib/util/logger.d.ts +1 -1
  45. package/lib/util/logger.d.ts.map +1 -1
  46. package/lib/util/telemetry/client.js +1 -1
  47. package/lib/util/telemetry/client.js.map +1 -1
  48. package/package.json +2 -5
  49. package/src/alchemy.ts +71 -9
  50. package/src/apply.ts +4 -17
  51. package/src/cloudflare/do-state-store/internal.ts +2 -1
  52. package/src/cloudflare/do-state-store/store.ts +26 -4
  53. package/src/cloudflare/secrets-store.ts +22 -21
  54. package/src/cloudflare/worker-assets.ts +16 -0
  55. package/src/destroy.ts +4 -7
  56. package/src/github/comment.ts +278 -0
  57. package/src/github/index.ts +1 -0
  58. package/src/index.ts +1 -1
  59. package/src/scope.ts +10 -6
  60. package/src/util/cli.ts +117 -0
  61. package/src/util/logger.ts +1 -1
  62. package/src/util/telemetry/client.ts +1 -1
  63. package/src/util/cli.tsx +0 -221
package/lib/util/cli.js CHANGED
@@ -1,64 +1,32 @@
1
- import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { Box, render, Static, Text, useApp } from "ink";
3
- import Spinner from "ink-spinner";
4
- import { format } from "node:util";
5
- import { useCallback, useEffect, useRef, useState } from "react";
6
1
  import packageJson from "../../package.json" with { type: "json" };
7
2
  import { dedent } from "./dedent.js";
8
- export function LoggerCLI({ alchemyInfo, setLogger }) {
9
- const [logs, setLogs] = useState([{ id: 0, text: "#heading" }]);
10
- const [tasks, setTasks] = useState(new Map());
11
- const { exit } = useApp();
12
- let nextId = useRef(1);
13
- const log = useCallback((...args) => setLogs((prev) => [
14
- ...prev,
15
- { id: nextId.current++, text: format(...args) },
16
- ]), []);
17
- const task = useCallback((id, data) => {
18
- setTasks((prev) => {
19
- prev.set(id, data);
20
- return prev;
21
- });
22
- }, []);
23
- useEffect(() => {
24
- setLogger({
25
- log: log,
26
- warn: log,
27
- error: log,
28
- task: task,
29
- exit: () => exit(),
30
- });
31
- return () => {
32
- setTasks(new Map());
33
- setLogs([]);
34
- };
35
- }, [exit, log, setLogger, task]);
36
- return (_jsxs(_Fragment, { children: [_jsx(Static, { items: logs, children: (log) => {
37
- if (log.id === 0) {
38
- return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { flexDirection: "column", borderStyle: "round", padding: 1, children: [_jsxs(Text, { color: "green", bold: true, children: ["Alchemy (v", packageJson.version, ")"] }), _jsxs(Text, { color: "gray", bold: true, children: ["App:", " ", _jsx(Text, { color: "magenta", bold: false, children: alchemyInfo.appName })] }), _jsxs(Text, { color: "gray", bold: true, children: ["Phase:", " ", _jsx(Text, { color: "magenta", bold: false, children: alchemyInfo.phase })] }), _jsxs(Text, { color: "gray", bold: true, children: ["Stage:", " ", _jsx(Text, { color: "magenta", bold: false, children: alchemyInfo.stage })] })] }), _jsx(Text, { color: "green", bold: true, children: "Logs" })] }, log.text));
39
- }
40
- return (_jsx(Text, { color: "gray", wrap: "wrap", children: log.text }, log.id));
41
- } }), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { color: "green", bold: true, children: "Tasks" }), _jsx(Box, { flexDirection: "column", paddingLeft: 1, children: Array.from(tasks.entries()).map(([id, task]) => (_jsxs(Box, { flexDirection: "row", gap: 1, children: [!task.status || task.status === "pending" ? (_jsx(Spinner, {})) : task.status === "success" ? (_jsx(Text, { color: "green", children: "\u2713" })) : (_jsx(Text, { color: "red", children: "\u2717" })), task.prefix && (_jsx(Text, { color: task.prefixColor ?? "white", bold: true, children: task.prefix.padStart(9) })), task.resource && (_jsxs(Text, { color: "gray", bold: true, children: ["[", task.resource, "]"] })), _jsx(Text, { bold: true, children: task.message })] }, id))) })] })] }));
42
- }
3
+ // ANSI color codes
4
+ const colors = {
5
+ reset: "\x1b[0m",
6
+ cyanBright: "\x1b[96m",
7
+ yellowBright: "\x1b[93m",
8
+ magenta: "\x1b[35m",
9
+ greenBright: "\x1b[92m",
10
+ redBright: "\x1b[91m",
11
+ gray: "\x1b[90m",
12
+ };
13
+ // Check if colors should be disabled
14
+ const shouldDisableColors = () => {
15
+ return Boolean(process.env.CI || process.env.NO_COLOR || !process.stdout.isTTY);
16
+ };
17
+ // Apply color if colors are enabled
18
+ const colorize = (text, color) => {
19
+ if (shouldDisableColors()) {
20
+ return text;
21
+ }
22
+ return `${colors[color]}${text}${colors.reset}`;
23
+ };
43
24
  let loggerApi = null;
44
- export const createLoggerInstance = (alchemyInfo) => {
25
+ export const createLoggerInstance = (alchemyInfo, customLogger) => {
45
26
  if (loggerApi)
46
27
  return loggerApi;
47
- if (process.env.CI ||
48
- process.env.USE_FALLBACK_LOGGER ||
49
- !process.stdin.isTTY) {
50
- loggerApi = createFallbackLogger(alchemyInfo);
51
- return loggerApi;
52
- }
53
- render(_jsx(LoggerCLI, { alchemyInfo: alchemyInfo, setLogger: (logger) => {
54
- loggerApi = logger;
55
- } }), {
56
- exitOnCtrlC: true,
57
- });
58
- process.on("SIGINT", () => {
59
- loggerApi?.exit();
60
- process.exit(0);
61
- });
28
+ // Use custom logger if provided, otherwise use the basic fallback logger
29
+ loggerApi = customLogger ?? createFallbackLogger(alchemyInfo);
62
30
  return loggerApi;
63
31
  };
64
32
  export const createDummyLogger = () => {
@@ -72,7 +40,7 @@ export const createDummyLogger = () => {
72
40
  };
73
41
  export const createFallbackLogger = (alchemyInfo) => {
74
42
  console.log(dedent `
75
- Alchemy (v${packageJson.version})
43
+ ${colorize("Alchemy", "cyanBright")} (v${packageJson.version})
76
44
  App: ${alchemyInfo.appName}
77
45
  Phase: ${alchemyInfo.phase}
78
46
  Stage: ${alchemyInfo.stage}
@@ -80,9 +48,27 @@ export const createFallbackLogger = (alchemyInfo) => {
80
48
  `);
81
49
  return {
82
50
  log: console.log,
83
- error: console.error,
84
- warn: console.warn,
85
- task: () => { },
51
+ error: (...args) => console.error(colorize("ERROR", "redBright"), ...args),
52
+ warn: (...args) => console.warn(colorize("WARN", "yellowBright"), ...args),
53
+ task: (_id, data) => {
54
+ const prefix = data.prefix ? `[${data.prefix}]` : "";
55
+ // Pad the prefix to ensure consistent alignment (12 characters total including brackets)
56
+ const paddedPrefix = prefix.padEnd(12);
57
+ const prefixWithColor = data.prefixColor && prefix
58
+ ? colorize(paddedPrefix, data.prefixColor)
59
+ : paddedPrefix;
60
+ const resource = data.resource ? colorize(data.resource, "gray") : "";
61
+ const message = data.message;
62
+ if (prefixWithColor && resource) {
63
+ console.log(`${prefixWithColor}${resource} ${message}`);
64
+ }
65
+ else if (prefixWithColor) {
66
+ console.log(`${prefixWithColor}${message}`);
67
+ }
68
+ else {
69
+ console.log(`${message}`);
70
+ }
71
+ },
86
72
  exit: () => { },
87
73
  };
88
74
  };
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/util/cli.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/util/cli.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,3 @@
1
- import type { LoggerApi } from "./cli.tsx";
1
+ import type { LoggerApi } from "./cli.ts";
2
2
  export declare const logger: LoggerApi;
3
3
  //# sourceMappingURL=logger.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/util/logger.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAE3C,eAAO,MAAM,MAAM,WAajB,CAAC"}
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/util/logger.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE1C,eAAO,MAAM,MAAM,WAajB,CAAC"}
@@ -29,7 +29,7 @@ export class TelemetryClient {
29
29
  }
30
30
  record(event, timestamp = Date.now()) {
31
31
  if (!this.context) {
32
- throw new Error("Context not initialized");
32
+ return;
33
33
  }
34
34
  const payload = {
35
35
  ...event,
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/util/telemetry/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/util/telemetry/client.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "alchemy",
3
- "version": "0.27.0",
3
+ "version": "0.29.0",
4
4
  "type": "module",
5
5
  "module": "./lib/index.js",
6
6
  "license": "Apache-2.0",
@@ -162,7 +162,6 @@
162
162
  "@types/diff": "^5.0.0",
163
163
  "@types/libsodium-wrappers": "^0.7.14",
164
164
  "@types/node": "latest",
165
- "@types/react": "^19.1.6",
166
165
  "@types/turndown": "^5.0.5",
167
166
  "ai": "^4.1.16",
168
167
  "arktype": "^2.1.16",
@@ -170,12 +169,10 @@
170
169
  "change-case": "^5.4.4",
171
170
  "cloudflare": "^4.2.0",
172
171
  "cpx": "^1.5.0",
173
- "ink": "^6.0.0",
174
- "ink-spinner": "^5.0.0",
175
172
  "libsodium-wrappers": "^0.7.15",
173
+ "miniflare": "^4.0.0",
176
174
  "openpgp": "^6.1.0",
177
175
  "prettier": "^3.5.3",
178
- "react": "^19.1.0",
179
176
  "turndown": "^7.2.0",
180
177
  "typedoc": "^0.28.1",
181
178
  "typedoc-plugin-markdown": "^4.6.0",
package/src/alchemy.ts CHANGED
@@ -17,6 +17,40 @@ import { secret } from "./secret.ts";
17
17
  import type { StateStoreType } from "./state.ts";
18
18
  import { logger } from "./util/logger.ts";
19
19
  import { TelemetryClient } from "./util/telemetry/client.ts";
20
+ import type { LoggerApi } from "./util/cli.ts";
21
+
22
+ /**
23
+ * Parses CLI arguments to extract alchemy options
24
+ */
25
+ function parseCliArgs(): Partial<AlchemyOptions> {
26
+ const args = process.argv.slice(2);
27
+ const options: Partial<AlchemyOptions> = {};
28
+
29
+ // Parse phase from CLI arguments
30
+ if (args.includes("--destroy")) {
31
+ options.phase = "destroy";
32
+ } else if (args.includes("--read")) {
33
+ options.phase = "read";
34
+ }
35
+
36
+ // Parse quiet flag
37
+ if (args.includes("--quiet")) {
38
+ options.quiet = true;
39
+ }
40
+
41
+ // Parse stage argument (--stage my-stage)
42
+ const stageIndex = args.indexOf("--stage");
43
+ if (stageIndex !== -1 && stageIndex + 1 < args.length) {
44
+ options.stage = args[stageIndex + 1];
45
+ }
46
+
47
+ // Get password from environment variables
48
+ if (process.env.ALCHEMY_PASSWORD) {
49
+ options.password = process.env.ALCHEMY_PASSWORD;
50
+ }
51
+
52
+ return options;
53
+ }
20
54
 
21
55
  /**
22
56
  * Type alias for semantic highlighting of `alchemy` as a type keyword
@@ -28,9 +62,16 @@ export const alchemy: Alchemy = _alchemy as any;
28
62
  /**
29
63
  * The Alchemy interface provides core functionality and is augmented by providers.
30
64
  * Supports both application scoping with secrets and template string interpolation.
65
+ * Automatically parses CLI arguments for common options.
66
+ *
67
+ * @example
68
+ * // Simple usage with automatic CLI argument parsing
69
+ * const app = await alchemy("my-app");
70
+ * // Now supports: --destroy, --read, --quiet, --stage my-stage
71
+ * // Environment variables: PASSWORD, ALCHEMY_PASSWORD, ALCHEMY_STAGE, USER
31
72
  *
32
73
  * @example
33
- * // Create an application scope with stage and secret handling
74
+ * // Create an application scope with explicit options (overrides CLI args)
34
75
  * const app = await alchemy("github:alchemy", {
35
76
  * stage: "prod",
36
77
  * phase: "up",
@@ -68,8 +109,15 @@ export interface Alchemy {
68
109
  /**
69
110
  * Creates a new application scope with the given name and options.
70
111
  * Used to create and manage resources with proper secret handling.
112
+ * Automatically parses CLI arguments: --destroy, --read, --quiet, --stage <name>
113
+ * Environment variables: PASSWORD, ALCHEMY_PASSWORD, ALCHEMY_STAGE, USER
71
114
  *
72
115
  * @example
116
+ * // Simple usage with CLI argument parsing
117
+ * const app = await alchemy("my-app");
118
+ *
119
+ * @example
120
+ * // With explicit options (overrides CLI args)
73
121
  * const app = await alchemy("my-app", {
74
122
  * stage: "prod",
75
123
  * // Required for encrypting/decrypting secrets
@@ -114,20 +162,29 @@ async function _alchemy(
114
162
  ): Promise<Scope | string | never> {
115
163
  if (typeof args[0] === "string") {
116
164
  const [appName, options] = args as [string, AlchemyOptions?];
117
- const phase = isRuntime ? "read" : (options?.phase ?? "up");
165
+
166
+ // Parse CLI arguments and merge with provided options (explicit options take precedence)
167
+ const cliOptions = parseCliArgs();
168
+ const mergedOptions = {
169
+ ...cliOptions,
170
+ ...options,
171
+ };
172
+
173
+ const phase = isRuntime ? "read" : (mergedOptions?.phase ?? "up");
118
174
  const telemetryClient =
119
- options?.parent?.telemetryClient ??
175
+ mergedOptions?.parent?.telemetryClient ??
120
176
  TelemetryClient.create({
121
177
  phase,
122
- enabled: options?.telemetry ?? true,
123
- quiet: options?.quiet ?? false,
178
+ enabled: mergedOptions?.telemetry ?? true,
179
+ quiet: mergedOptions?.quiet ?? false,
124
180
  });
125
181
  const root = new Scope({
126
- ...options,
182
+ ...mergedOptions,
127
183
  appName,
128
- stage: options?.stage ?? process.env.ALCHEMY_STAGE,
184
+ stage:
185
+ mergedOptions?.stage ?? process.env.ALCHEMY_STAGE ?? process.env.USER,
129
186
  phase,
130
- password: options?.password ?? process.env.ALCHEMY_PASSWORD,
187
+ password: mergedOptions?.password ?? process.env.ALCHEMY_PASSWORD,
131
188
  telemetryClient,
132
189
  });
133
190
  try {
@@ -137,7 +194,7 @@ async function _alchemy(
137
194
  // see Scope.finalize for where we pop the global scope
138
195
  Scope.globals.push(root);
139
196
  }
140
- if (options?.phase === "destroy") {
197
+ if (mergedOptions?.phase === "destroy") {
141
198
  await destroy(root);
142
199
  return process.exit(0);
143
200
  }
@@ -308,6 +365,11 @@ export interface AlchemyOptions {
308
365
  * @default true
309
366
  */
310
367
  telemetry?: boolean;
368
+ /**
369
+ * A custom logger instance to use for this scope.
370
+ * If not provided, the default fallback logger will be used.
371
+ */
372
+ logger?: LoggerApi;
311
373
  }
312
374
 
313
375
  export interface ScopeOptions extends AlchemyOptions {
package/src/apply.ts CHANGED
@@ -15,7 +15,7 @@ import {
15
15
  import { Scope } from "./scope.ts";
16
16
  import { serialize } from "./serde.ts";
17
17
  import type { State } from "./state.ts";
18
- import { formatFQN } from "./util/cli.tsx";
18
+ import { formatFQN } from "./util/cli.ts";
19
19
  import { logger } from "./util/logger.ts";
20
20
  import type { Telemetry } from "./util/telemetry/index.ts";
21
21
 
@@ -41,12 +41,6 @@ async function _apply<Out extends Resource>(
41
41
  const scope = resource[ResourceScope];
42
42
  const start = performance.now();
43
43
  try {
44
- logger.task(resource[ResourceFQN], {
45
- prefix: "SETUP",
46
- prefixColor: "cyanBright",
47
- resource: formatFQN(resource[ResourceFQN]),
48
- message: "Setting up Resource...",
49
- });
50
44
  const quiet = props?.quiet ?? scope.quiet;
51
45
  await scope.init();
52
46
  let state: State | undefined = (await scope.state.get(
@@ -112,13 +106,12 @@ async function _apply<Out extends Resource>(
112
106
  ) {
113
107
  if (!quiet) {
114
108
  logger.task(resource[ResourceFQN], {
115
- prefix: "SKIPPED",
109
+ prefix: "skipped",
116
110
  prefixColor: "yellowBright",
117
111
  resource: formatFQN(resource[ResourceFQN]),
118
112
  message: "Skipped Resource (no changes)",
119
113
  status: "success",
120
114
  });
121
- logger.log(`Skipping ${resource[ResourceFQN]} (no changes)`);
122
115
  }
123
116
  options?.resolveInnerScope?.(
124
117
  new Scope({
@@ -142,14 +135,11 @@ async function _apply<Out extends Resource>(
142
135
 
143
136
  if (!quiet) {
144
137
  logger.task(resource[ResourceFQN], {
145
- prefix: phase === "create" ? "CREATING" : "UPDATING",
138
+ prefix: phase === "create" ? "creating" : "updating",
146
139
  prefixColor: "magenta",
147
140
  resource: formatFQN(resource[ResourceFQN]),
148
141
  message: `${phase === "create" ? "Creating" : "Updating"} Resource...`,
149
142
  });
150
- logger.log(
151
- `${phase === "create" ? "Create" : "Update"}: "${resource[ResourceFQN]}"`,
152
- );
153
143
  }
154
144
 
155
145
  scope.telemetryClient.record({
@@ -195,15 +185,12 @@ async function _apply<Out extends Resource>(
195
185
  );
196
186
  if (!quiet) {
197
187
  logger.task(resource[ResourceFQN], {
198
- prefix: phase === "create" ? "CREATED" : "UPDATED",
188
+ prefix: phase === "create" ? "created" : "updated",
199
189
  prefixColor: "greenBright",
200
190
  resource: formatFQN(resource[ResourceFQN]),
201
191
  message: `${phase === "create" ? "Created" : "Updated"} Resource`,
202
192
  status: "success",
203
193
  });
204
- logger.log(
205
- `${phase === "create" ? "Created" : "Updated"}: "${resource[ResourceFQN]}"`,
206
- );
207
194
  }
208
195
 
209
196
  const status = phase === "create" ? "created" : "updated";
@@ -104,6 +104,7 @@ export async function upsertStateStoreWorker(
104
104
  api: CloudflareApi,
105
105
  workerName: string,
106
106
  token: string,
107
+ force: boolean,
107
108
  ) {
108
109
  const key = `worker:${workerName}`;
109
110
  const cached = cache.get(key);
@@ -111,7 +112,7 @@ export async function upsertStateStoreWorker(
111
112
  return;
112
113
  }
113
114
  const { found, tag } = await getWorkerStatus(api, workerName);
114
- if (found && tag === TAG) {
115
+ if (found && tag === TAG && !force) {
115
116
  cache.set(key, TAG);
116
117
  return;
117
118
  }
@@ -88,7 +88,12 @@ export class DOStateStore implements StateStore {
88
88
  const api = await createCloudflareApi(this.options);
89
89
  const [subdomain, _] = await Promise.all([
90
90
  getAccountSubdomain(api),
91
- upsertStateStoreWorker(api, workerName, token),
91
+ upsertStateStoreWorker(
92
+ api,
93
+ workerName,
94
+ token,
95
+ this.options.worker?.force ?? false,
96
+ ),
92
97
  ]);
93
98
  const client = new DOStateStoreClient({
94
99
  app: this.scope.appName ?? "alchemy",
@@ -97,9 +102,26 @@ export class DOStateStore implements StateStore {
97
102
  token,
98
103
  });
99
104
  // This ensures the token is correct and the worker is ready to use.
100
- // RPC methods are retried, which is what we want here in case the worker is not ready yet.
101
- await client.rpc("validate", null);
102
- return client;
105
+ let last: Response | undefined;
106
+ let delay = 1000;
107
+ for (let i = 0; i < 20; i++) {
108
+ const res = await client.validate();
109
+ if (res.ok) {
110
+ return client;
111
+ }
112
+ if (!last) {
113
+ console.log("Waiting for state store deployment...");
114
+ }
115
+ last = res;
116
+ // Exponential backoff with jitter
117
+ const jitter = Math.random() * 0.1 * delay;
118
+ await new Promise((resolve) => setTimeout(resolve, delay + jitter));
119
+ delay *= 1.5; // Increase the delay for next attempt
120
+ delay = Math.min(delay, 10000); // Cap at 10 seconds
121
+ }
122
+ throw new Error(
123
+ `Failed to access state store: ${last?.status} ${last?.statusText}`,
124
+ );
103
125
  }
104
126
 
105
127
  private async getClient() {
@@ -220,33 +220,34 @@ const _SecretsStore = Resource("cloudflare::SecretsStore", async function <
220
220
 
221
221
  await insertSecrets(api, storeId, props);
222
222
  } else {
223
- try {
223
+ // If adopt is true, first check if a store with this name already exists
224
+ if (props.adopt) {
225
+ console.log(`Checking for existing secrets store '${name}' to adopt`);
226
+ const existingStore = await findSecretsStoreByName(api, name);
227
+
228
+ if (existingStore) {
229
+ console.log(`Found existing secrets store '${name}', adopting it`);
230
+ storeId = existingStore.id;
231
+ createdAt = existingStore.createdAt || Date.now();
232
+ } else {
233
+ console.log(
234
+ `No existing secrets store '${name}' found, creating new one`,
235
+ );
236
+ const { id } = await createSecretsStore(api, {
237
+ ...props,
238
+ name,
239
+ });
240
+ createdAt = Date.now();
241
+ storeId = id;
242
+ }
243
+ } else {
244
+ // Default behavior: create a new store
224
245
  const { id } = await createSecretsStore(api, {
225
246
  ...props,
226
247
  name,
227
248
  });
228
249
  createdAt = Date.now();
229
250
  storeId = id;
230
- } catch (error) {
231
- if (
232
- props.adopt &&
233
- error instanceof Error &&
234
- error.message.includes("already exists")
235
- ) {
236
- console.log(`Secrets store '${name}' already exists, adopting it`);
237
- const existingStore = await findSecretsStoreByName(api, name);
238
-
239
- if (!existingStore) {
240
- throw new Error(
241
- `Failed to find existing secrets store '${name}' for adoption`,
242
- );
243
- }
244
-
245
- storeId = existingStore.id;
246
- createdAt = existingStore.createdAt || Date.now();
247
- } else {
248
- throw error;
249
- }
250
251
  }
251
252
 
252
253
  await insertSecrets(api, storeId, props);
@@ -3,6 +3,7 @@ import fs from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { AsyncQueue } from "../util/async-queue.ts";
5
5
  import { getContentType } from "../util/content-type.ts";
6
+ import { CloudflareApiError } from "./api-error.ts";
6
7
  import type { CloudflareApi } from "./api.ts";
7
8
  import type { Assets } from "./assets.ts";
8
9
  import type { AssetsConfig, WorkerProps } from "./worker.ts";
@@ -84,6 +85,21 @@ export async function uploadAssets(
84
85
  const sessionData =
85
86
  (await uploadSessionResponse.json()) as UploadSessionResponse;
86
87
 
88
+ if (!sessionData?.success) {
89
+ if (sessionData?.errors) {
90
+ throw new CloudflareApiError(
91
+ `Failed to start assets upload session:\n${sessionData.errors
92
+ .map((error) => `- ${error.code}: ${error.message}`)
93
+ .join("\n")}`,
94
+ uploadSessionResponse,
95
+ );
96
+ }
97
+ throw new CloudflareApiError(
98
+ `Failed to start assets upload session: ${uploadSessionResponse.statusText}`,
99
+ uploadSessionResponse,
100
+ );
101
+ }
102
+
87
103
  // If there are no buckets, assets are already uploaded or empty
88
104
  if (!sessionData.result.buckets || sessionData.result.buckets.length === 0) {
89
105
  return {
package/src/destroy.ts CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  ResourceSeq,
11
11
  } from "./resource.ts";
12
12
  import { Scope } from "./scope.ts";
13
- import { formatFQN } from "./util/cli.tsx";
13
+ import { formatFQN } from "./util/cli.ts";
14
14
  import { logger } from "./util/logger.ts";
15
15
 
16
16
  export class DestroyedSignal extends Error {}
@@ -70,7 +70,6 @@ export async function destroy<Type extends string>(
70
70
  parent: instance[ResourceScope],
71
71
  scopeName: instance[ResourceID],
72
72
  });
73
- logger.log("Destroying scope", scope.chain.join("/"));
74
73
  return await destroy(scope, options);
75
74
  }
76
75
 
@@ -90,12 +89,11 @@ export async function destroy<Type extends string>(
90
89
  try {
91
90
  if (!quiet) {
92
91
  logger.task(instance[ResourceFQN], {
93
- prefix: "DELETING",
94
- prefixColor: "magenta",
92
+ prefix: "deleting",
93
+ prefixColor: "redBright",
95
94
  resource: formatFQN(instance[ResourceFQN]),
96
95
  message: "Deleting Resource...",
97
96
  });
98
- logger.log(`Delete: "${instance[ResourceFQN]}"`);
99
97
  }
100
98
 
101
99
  const state = await scope.state.get(instance[ResourceID]);
@@ -152,13 +150,12 @@ export async function destroy<Type extends string>(
152
150
 
153
151
  if (!quiet) {
154
152
  logger.task(instance[ResourceFQN], {
155
- prefix: "DELETED",
153
+ prefix: "deleted",
156
154
  prefixColor: "greenBright",
157
155
  resource: formatFQN(instance[ResourceFQN]),
158
156
  message: "Deleted Resource",
159
157
  status: "success",
160
158
  });
161
- logger.log(`Deleted: "${instance[ResourceFQN]}"`);
162
159
  }
163
160
  } catch (error) {
164
161
  logger.error(error);