alchemy 0.1.11 → 0.1.13

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 (312) hide show
  1. package/README.md +324 -0
  2. package/lib/$.d.ts +8 -3
  3. package/lib/$.d.ts.map +1 -1
  4. package/lib/$.js +38 -27
  5. package/lib/$.js.map +1 -1
  6. package/lib/alchemize.d.ts.map +1 -1
  7. package/lib/alchemize.js +7 -0
  8. package/lib/alchemize.js.map +1 -1
  9. package/lib/apply.d.ts +2 -2
  10. package/lib/apply.d.ts.map +1 -1
  11. package/lib/apply.js +3 -3
  12. package/lib/apply.js.map +1 -1
  13. package/lib/aws/index.d.ts +1 -0
  14. package/lib/aws/index.d.ts.map +1 -1
  15. package/lib/aws/index.js +1 -0
  16. package/lib/aws/index.js.map +1 -1
  17. package/lib/aws/ses.d.ts +75 -0
  18. package/lib/aws/ses.d.ts.map +1 -0
  19. package/lib/aws/ses.js +197 -0
  20. package/lib/aws/ses.js.map +1 -0
  21. package/lib/cloudflare/api.d.ts +88 -0
  22. package/lib/cloudflare/api.d.ts.map +1 -0
  23. package/lib/cloudflare/api.js +184 -0
  24. package/lib/cloudflare/api.js.map +1 -0
  25. package/lib/cloudflare/asset-manifest.d.ts +15 -0
  26. package/lib/cloudflare/asset-manifest.d.ts.map +1 -0
  27. package/lib/cloudflare/asset-manifest.js +2 -0
  28. package/lib/cloudflare/asset-manifest.js.map +1 -0
  29. package/lib/cloudflare/auth.d.ts +52 -0
  30. package/lib/cloudflare/auth.d.ts.map +1 -0
  31. package/lib/cloudflare/auth.js +114 -0
  32. package/lib/cloudflare/auth.js.map +1 -0
  33. package/lib/cloudflare/bindings.d.ts +254 -0
  34. package/lib/cloudflare/bindings.d.ts.map +1 -0
  35. package/lib/cloudflare/bindings.js +2 -0
  36. package/lib/cloudflare/bindings.js.map +1 -0
  37. package/lib/cloudflare/durable-object-namespace.d.ts +23 -0
  38. package/lib/cloudflare/durable-object-namespace.d.ts.map +1 -0
  39. package/lib/cloudflare/durable-object-namespace.js +26 -0
  40. package/lib/cloudflare/durable-object-namespace.js.map +1 -0
  41. package/lib/cloudflare/generate-asset-manifest.d.ts +3 -0
  42. package/lib/cloudflare/generate-asset-manifest.d.ts.map +1 -0
  43. package/lib/cloudflare/generate-asset-manifest.js +69 -0
  44. package/lib/cloudflare/generate-asset-manifest.js.map +1 -0
  45. package/lib/cloudflare/index.d.ts +7 -0
  46. package/lib/cloudflare/index.d.ts.map +1 -0
  47. package/lib/cloudflare/index.js +7 -0
  48. package/lib/cloudflare/index.js.map +1 -0
  49. package/lib/cloudflare/kv-namespace.d.ts +61 -0
  50. package/lib/cloudflare/kv-namespace.d.ts.map +1 -0
  51. package/lib/cloudflare/kv-namespace.js +93 -0
  52. package/lib/cloudflare/kv-namespace.js.map +1 -0
  53. package/lib/cloudflare/state.d.ts +83 -0
  54. package/lib/cloudflare/state.d.ts.map +1 -0
  55. package/lib/cloudflare/state.js +109 -0
  56. package/lib/cloudflare/state.js.map +1 -0
  57. package/lib/cloudflare/static-site-router.d.ts +19 -0
  58. package/lib/cloudflare/static-site-router.d.ts.map +1 -0
  59. package/lib/cloudflare/static-site-router.js +110 -0
  60. package/lib/cloudflare/static-site-router.js.map +1 -0
  61. package/lib/cloudflare/static-site.d.ts +166 -0
  62. package/lib/cloudflare/static-site.d.ts.map +1 -0
  63. package/lib/cloudflare/static-site.js +168 -0
  64. package/lib/cloudflare/static-site.js.map +1 -0
  65. package/lib/cloudflare/types.d.ts +52 -0
  66. package/lib/cloudflare/types.d.ts.map +1 -0
  67. package/lib/cloudflare/types.js +21 -0
  68. package/lib/cloudflare/types.js.map +1 -0
  69. package/lib/cloudflare/upload-asset-manifest.d.ts +12 -0
  70. package/lib/cloudflare/upload-asset-manifest.d.ts.map +1 -0
  71. package/lib/cloudflare/upload-asset-manifest.js +55 -0
  72. package/lib/cloudflare/upload-asset-manifest.js.map +1 -0
  73. package/lib/cloudflare/worker-metadata.d.ts +130 -0
  74. package/lib/cloudflare/worker-metadata.d.ts.map +1 -0
  75. package/lib/cloudflare/worker-metadata.js +2 -0
  76. package/lib/cloudflare/worker-metadata.js.map +1 -0
  77. package/lib/cloudflare/worker-migration.d.ts +110 -0
  78. package/lib/cloudflare/worker-migration.d.ts.map +1 -0
  79. package/lib/cloudflare/worker-migration.js +11 -0
  80. package/lib/cloudflare/worker-migration.js.map +1 -0
  81. package/lib/cloudflare/worker.d.ts +105 -0
  82. package/lib/cloudflare/worker.d.ts.map +1 -0
  83. package/lib/cloudflare/worker.js +358 -0
  84. package/lib/cloudflare/worker.js.map +1 -0
  85. package/lib/destroy.d.ts +1 -1
  86. package/lib/destroy.d.ts.map +1 -1
  87. package/lib/destroy.js +2 -1
  88. package/lib/destroy.js.map +1 -1
  89. package/lib/esbuild.d.ts +83 -1
  90. package/lib/esbuild.d.ts.map +1 -1
  91. package/lib/esbuild.js +27 -18
  92. package/lib/esbuild.js.map +1 -1
  93. package/lib/fs.d.ts +1 -1
  94. package/lib/global.d.ts +0 -2
  95. package/lib/global.d.ts.map +1 -1
  96. package/lib/global.js +3 -3
  97. package/lib/global.js.map +1 -1
  98. package/lib/index.d.ts +1 -0
  99. package/lib/index.d.ts.map +1 -1
  100. package/lib/index.js +1 -0
  101. package/lib/index.js.map +1 -1
  102. package/lib/main.js +1 -1
  103. package/lib/main.js.map +1 -1
  104. package/lib/output.d.ts +18 -13
  105. package/lib/output.d.ts.map +1 -1
  106. package/lib/output.js +12 -5
  107. package/lib/output.js.map +1 -1
  108. package/lib/print.d.ts +7 -0
  109. package/lib/print.d.ts.map +1 -0
  110. package/lib/print.js +10 -0
  111. package/lib/print.js.map +1 -0
  112. package/lib/resource.d.ts +9 -4
  113. package/lib/resource.d.ts.map +1 -1
  114. package/lib/resource.js +127 -93
  115. package/lib/resource.js.map +1 -1
  116. package/lib/scope.d.ts +3 -0
  117. package/lib/scope.d.ts.map +1 -1
  118. package/lib/scope.js +4 -12
  119. package/lib/scope.js.map +1 -1
  120. package/lib/utils/content-type.d.ts +8 -0
  121. package/lib/utils/content-type.d.ts.map +1 -0
  122. package/lib/utils/content-type.js +41 -0
  123. package/lib/utils/content-type.js.map +1 -0
  124. package/package.json +27 -8
  125. package/src/$.ts +63 -30
  126. package/src/alchemize.ts +12 -0
  127. package/src/apply.ts +6 -6
  128. package/src/aws/index.ts +1 -0
  129. package/src/aws/ses.ts +346 -0
  130. package/src/cloudflare/api.ts +282 -0
  131. package/src/cloudflare/asset-manifest.ts +16 -0
  132. package/src/cloudflare/auth.ts +187 -0
  133. package/src/cloudflare/bindings.ts +297 -0
  134. package/src/cloudflare/durable-object-namespace.ts +43 -0
  135. package/src/cloudflare/generate-asset-manifest.ts +93 -0
  136. package/src/cloudflare/index.ts +6 -0
  137. package/src/cloudflare/kv-namespace.ts +195 -0
  138. package/src/cloudflare/state.ts +138 -0
  139. package/src/cloudflare/static-site-router.ts +150 -0
  140. package/src/cloudflare/static-site.ts +395 -0
  141. package/src/cloudflare/types.ts +71 -0
  142. package/src/cloudflare/upload-asset-manifest.ts +83 -0
  143. package/src/cloudflare/worker-metadata.ts +154 -0
  144. package/src/cloudflare/worker-migration.ts +145 -0
  145. package/src/cloudflare/worker.ts +666 -0
  146. package/src/destroy.ts +2 -2
  147. package/src/esbuild.ts +34 -22
  148. package/src/global.ts +1 -3
  149. package/src/index.ts +1 -1
  150. package/src/main.ts +1 -1
  151. package/src/output.ts +53 -37
  152. package/src/print.ts +18 -0
  153. package/src/resource.ts +163 -116
  154. package/src/scope.ts +7 -12
  155. package/src/utils/content-type.ts +42 -0
  156. package/lib/agent/ai.d.ts +0 -40
  157. package/lib/agent/ai.d.ts.map +0 -1
  158. package/lib/agent/ai.js +0 -132
  159. package/lib/agent/ai.js.map +0 -1
  160. package/lib/agent/anthropic.d.ts +0 -9
  161. package/lib/agent/anthropic.d.ts.map +0 -1
  162. package/lib/agent/anthropic.js +0 -25
  163. package/lib/agent/anthropic.js.map +0 -1
  164. package/lib/agent/dependencies.d.ts +0 -4
  165. package/lib/agent/dependencies.d.ts.map +0 -1
  166. package/lib/agent/dependencies.js +0 -48
  167. package/lib/agent/dependencies.js.map +0 -1
  168. package/lib/agent/file-context.d.ts +0 -13
  169. package/lib/agent/file-context.d.ts.map +0 -1
  170. package/lib/agent/file-context.js +0 -6
  171. package/lib/agent/file-context.js.map +0 -1
  172. package/lib/agent/index.d.ts +0 -10
  173. package/lib/agent/index.d.ts.map +0 -1
  174. package/lib/agent/index.js +0 -10
  175. package/lib/agent/index.js.map +0 -1
  176. package/lib/agent/model.d.ts +0 -8
  177. package/lib/agent/model.d.ts.map +0 -1
  178. package/lib/agent/model.js +0 -29
  179. package/lib/agent/model.js.map +0 -1
  180. package/lib/agent/module.d.ts +0 -81
  181. package/lib/agent/module.d.ts.map +0 -1
  182. package/lib/agent/module.js +0 -309
  183. package/lib/agent/module.js.map +0 -1
  184. package/lib/agent/openai.d.ts +0 -6
  185. package/lib/agent/openai.d.ts.map +0 -1
  186. package/lib/agent/openai.js +0 -41
  187. package/lib/agent/openai.js.map +0 -1
  188. package/lib/agent/program.d.ts +0 -12
  189. package/lib/agent/program.d.ts.map +0 -1
  190. package/lib/agent/program.js +0 -21
  191. package/lib/agent/program.js.map +0 -1
  192. package/lib/agent/prompts.d.ts +0 -4
  193. package/lib/agent/prompts.d.ts.map +0 -1
  194. package/lib/agent/prompts.js +0 -11
  195. package/lib/agent/prompts.js.map +0 -1
  196. package/lib/agent/scrape.d.ts +0 -71
  197. package/lib/agent/scrape.d.ts.map +0 -1
  198. package/lib/agent/scrape.js +0 -45
  199. package/lib/agent/scrape.js.map +0 -1
  200. package/lib/aws/auto/docs.d.ts +0 -11
  201. package/lib/aws/auto/docs.d.ts.map +0 -1
  202. package/lib/aws/auto/docs.js +0 -56
  203. package/lib/aws/auto/docs.js.map +0 -1
  204. package/lib/aws/auto/index.d.ts +0 -5
  205. package/lib/aws/auto/index.d.ts.map +0 -1
  206. package/lib/aws/auto/index.js +0 -5
  207. package/lib/aws/auto/index.js.map +0 -1
  208. package/lib/aws/auto/resource.d.ts +0 -24
  209. package/lib/aws/auto/resource.d.ts.map +0 -1
  210. package/lib/aws/auto/resource.js +0 -194
  211. package/lib/aws/auto/resource.js.map +0 -1
  212. package/lib/aws/auto/service.d.ts +0 -10
  213. package/lib/aws/auto/service.d.ts.map +0 -1
  214. package/lib/aws/auto/service.js +0 -34
  215. package/lib/aws/auto/service.js.map +0 -1
  216. package/lib/aws/auto/spec.d.ts +0 -83
  217. package/lib/aws/auto/spec.d.ts.map +0 -1
  218. package/lib/aws/auto/spec.js +0 -58
  219. package/lib/aws/auto/spec.js.map +0 -1
  220. package/lib/markdown/design.d.ts +0 -17
  221. package/lib/markdown/design.d.ts.map +0 -1
  222. package/lib/markdown/design.js +0 -92
  223. package/lib/markdown/design.js.map +0 -1
  224. package/lib/markdown/extract.d.ts +0 -7
  225. package/lib/markdown/extract.d.ts.map +0 -1
  226. package/lib/markdown/extract.js +0 -10
  227. package/lib/markdown/extract.js.map +0 -1
  228. package/lib/markdown/index.d.ts +0 -4
  229. package/lib/markdown/index.d.ts.map +0 -1
  230. package/lib/markdown/index.js +0 -4
  231. package/lib/markdown/index.js.map +0 -1
  232. package/lib/markdown/requirements.d.ts +0 -79
  233. package/lib/markdown/requirements.d.ts.map +0 -1
  234. package/lib/markdown/requirements.js +0 -105
  235. package/lib/markdown/requirements.js.map +0 -1
  236. package/lib/typescript/check-omission.d.ts +0 -2
  237. package/lib/typescript/check-omission.d.ts.map +0 -1
  238. package/lib/typescript/check-omission.js +0 -43
  239. package/lib/typescript/check-omission.js.map +0 -1
  240. package/lib/typescript/debug-type-errors.d.ts +0 -3
  241. package/lib/typescript/debug-type-errors.d.ts.map +0 -1
  242. package/lib/typescript/debug-type-errors.js +0 -35
  243. package/lib/typescript/debug-type-errors.js.map +0 -1
  244. package/lib/typescript/extract.d.ts +0 -7
  245. package/lib/typescript/extract.d.ts.map +0 -1
  246. package/lib/typescript/extract.js +0 -10
  247. package/lib/typescript/extract.js.map +0 -1
  248. package/lib/typescript/index.d.ts +0 -7
  249. package/lib/typescript/index.d.ts.map +0 -1
  250. package/lib/typescript/index.js +0 -7
  251. package/lib/typescript/index.js.map +0 -1
  252. package/lib/typescript/install-dependencies.d.ts +0 -29
  253. package/lib/typescript/install-dependencies.d.ts.map +0 -1
  254. package/lib/typescript/install-dependencies.js +0 -52
  255. package/lib/typescript/install-dependencies.js.map +0 -1
  256. package/lib/typescript/package.d.ts +0 -157
  257. package/lib/typescript/package.d.ts.map +0 -1
  258. package/lib/typescript/package.js +0 -115
  259. package/lib/typescript/package.js.map +0 -1
  260. package/lib/typescript/repair-omissions.d.ts +0 -3
  261. package/lib/typescript/repair-omissions.d.ts.map +0 -1
  262. package/lib/typescript/repair-omissions.js +0 -23
  263. package/lib/typescript/repair-omissions.js.map +0 -1
  264. package/lib/typescript/repair.d.ts +0 -3
  265. package/lib/typescript/repair.d.ts.map +0 -1
  266. package/lib/typescript/repair.js +0 -34
  267. package/lib/typescript/repair.js.map +0 -1
  268. package/lib/typescript/tsconfig.d.ts +0 -469
  269. package/lib/typescript/tsconfig.d.ts.map +0 -1
  270. package/lib/typescript/tsconfig.js +0 -143
  271. package/lib/typescript/tsconfig.js.map +0 -1
  272. package/lib/typescript/typescript.d.ts +0 -107
  273. package/lib/typescript/typescript.d.ts.map +0 -1
  274. package/lib/typescript/typescript.js +0 -180
  275. package/lib/typescript/typescript.js.map +0 -1
  276. package/lib/typescript/validate.d.ts +0 -10
  277. package/lib/typescript/validate.d.ts.map +0 -1
  278. package/lib/typescript/validate.js +0 -49
  279. package/lib/typescript/validate.js.map +0 -1
  280. package/src/agent/ai.ts +0 -215
  281. package/src/agent/anthropic.ts +0 -31
  282. package/src/agent/dependencies.ts +0 -62
  283. package/src/agent/file-context.ts +0 -8
  284. package/src/agent/index.ts +0 -9
  285. package/src/agent/model.ts +0 -39
  286. package/src/agent/module.ts +0 -418
  287. package/src/agent/openai.ts +0 -50
  288. package/src/agent/program.md +0 -38
  289. package/src/agent/program.ts +0 -37
  290. package/src/agent/prompts.ts +0 -20
  291. package/src/agent/scrape.ts +0 -47
  292. package/src/aws/auto/cfn.json +0 -225310
  293. package/src/aws/auto/docs.ts +0 -73
  294. package/src/aws/auto/index.ts +0 -4
  295. package/src/aws/auto/resource.ts +0 -269
  296. package/src/aws/auto/service.ts +0 -51
  297. package/src/aws/auto/spec.ts +0 -160
  298. package/src/markdown/design.ts +0 -125
  299. package/src/markdown/extract.ts +0 -9
  300. package/src/markdown/index.ts +0 -3
  301. package/src/markdown/requirements.ts +0 -135
  302. package/src/typescript/check-omission.ts +0 -47
  303. package/src/typescript/debug-type-errors.ts +0 -42
  304. package/src/typescript/extract.ts +0 -9
  305. package/src/typescript/index.ts +0 -6
  306. package/src/typescript/install-dependencies.ts +0 -61
  307. package/src/typescript/package.ts +0 -143
  308. package/src/typescript/repair-omissions.ts +0 -31
  309. package/src/typescript/repair.ts +0 -45
  310. package/src/typescript/tsconfig.ts +0 -170
  311. package/src/typescript/typescript.ts +0 -217
  312. package/src/typescript/validate.ts +0 -73
package/src/esbuild.ts CHANGED
@@ -60,14 +60,12 @@ export interface BundleOutput {
60
60
 
61
61
  export class Bundle extends Resource(
62
62
  "esbuild::Bundle",
63
+ {
64
+ alwaysUpdate: true,
65
+ },
63
66
  async (ctx, props: BundleProps): Promise<BundleOutput> => {
64
67
  // Determine output path
65
- const outputPath =
66
- props.outfile ||
67
- path.join(
68
- props.outdir || "dist",
69
- path.basename(props.entryPoint, path.extname(props.entryPoint)) + ".js",
70
- );
68
+ const outputPath = getOutputPath(props);
71
69
 
72
70
  // Ensure output directory exists
73
71
  await fs.promises.mkdir(path.dirname(outputPath), { recursive: true });
@@ -79,22 +77,7 @@ export class Bundle extends Resource(
79
77
  return { path: outputPath, hash: "" };
80
78
  }
81
79
 
82
- // Build the bundle
83
- const result = await esbuild.build({
84
- entryPoints: [props.entryPoint],
85
- outfile: outputPath,
86
- bundle: true,
87
- format: props.format,
88
- target: props.target,
89
- minify: props.minify,
90
- sourcemap: props.sourcemap,
91
- external: props.external,
92
- platform: props.platform,
93
- metafile: true,
94
- write: true,
95
- ...props.options,
96
- });
97
-
80
+ const result = await bundle(props);
98
81
  // Calculate hash of the output
99
82
  const contents = await fs.promises.readFile(outputPath);
100
83
  const hash = crypto.createHash("sha256").update(contents).digest("hex");
@@ -126,3 +109,32 @@ export class Bundle extends Resource(
126
109
  return ctx.get<string>("hash");
127
110
  }
128
111
  }
112
+
113
+ export async function bundle(props: BundleProps) {
114
+ const outputPath = getOutputPath(props);
115
+ // Build the bundle
116
+ return await esbuild.build({
117
+ entryPoints: [props.entryPoint],
118
+ outfile: outputPath,
119
+ bundle: true,
120
+ format: props.format,
121
+ target: props.target,
122
+ minify: props.minify,
123
+ sourcemap: props.sourcemap,
124
+ external: props.external,
125
+ platform: props.platform,
126
+ metafile: true,
127
+ write: true,
128
+ ...props.options,
129
+ });
130
+ }
131
+
132
+ function getOutputPath(props: BundleProps) {
133
+ return (
134
+ props.outfile ||
135
+ path.join(
136
+ props.outdir || "dist",
137
+ path.basename(props.entryPoint, path.extname(props.entryPoint)) + ".js",
138
+ )
139
+ );
140
+ }
package/src/global.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import type { Provider, ResourceType } from "./resource";
4
- import { Scope } from "./scope";
5
4
  import { FileSystemStateStore } from "./state";
6
5
 
7
6
  export interface Config {
@@ -14,6 +13,7 @@ const alchemy = path.resolve(process.cwd(), "alchemy.ts");
14
13
  let _config: Config | undefined;
15
14
 
16
15
  const exists =
16
+ // @ts-ignore
17
17
  fs.promises.exists ??
18
18
  async function exists(path: string): Promise<boolean> {
19
19
  try {
@@ -51,8 +51,6 @@ export const defaultStage = config.defaultStage ?? _stage;
51
51
 
52
52
  export const defaultStateStore = config.stateStore ?? FileSystemStateStore;
53
53
 
54
- export const rootScope = new Scope(null);
55
-
56
54
  export const providers = new Map<
57
55
  ResourceType,
58
56
  Provider<ResourceType, any[], any>
package/src/index.ts CHANGED
@@ -4,6 +4,7 @@ export * from "./error";
4
4
  export * from "./global";
5
5
  export * from "./input";
6
6
  export * from "./output";
7
+ export * from "./print";
7
8
  export {
8
9
  Resource,
9
10
  type Context,
@@ -11,5 +12,4 @@ export {
11
12
  type DeleteContext,
12
13
  type UpdateContext,
13
14
  } from "./resource";
14
-
15
15
  export type { FileSystemStateStore as StateStore } from "./state";
package/src/main.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  import { parseArgs } from "util";
3
3
 
4
4
  const { values, positionals } = parseArgs({
5
- args: Bun.argv.slice(2),
5
+ args: process.argv.slice(2),
6
6
  options: {
7
7
  help: {
8
8
  type: "boolean",
package/src/output.ts CHANGED
@@ -1,5 +1,3 @@
1
- export type Eval<U> = U extends Output<infer V> ? Eval<V> : U;
2
-
3
1
  export function isOutput<T>(value: any): value is Output<T> {
4
2
  return (
5
3
  value && typeof value === "object" && typeof value.apply === "function"
@@ -13,43 +11,61 @@ export type Outputs<P extends readonly any[]> = P extends [
13
11
  ? [Output<First>, ...Outputs<Rest>]
14
12
  : [];
15
13
 
16
- export type Output<T> = {
17
- apply<U>(fn: (value: T) => U): Output<Eval<U>>;
18
- } & (T extends string
19
- ? {
20
- [k in keyof typeof String.prototype]: Output<
21
- (typeof String.prototype)[k]
22
- >;
23
- }
24
- : T extends number
25
- ? {
26
- [K in keyof typeof Number.prototype]: Output<
27
- (typeof Number.prototype)[K]
28
- >;
29
- }
30
- : T extends boolean
31
- ? {
32
- [K in keyof typeof Boolean.prototype]: Output<
33
- (typeof Boolean.prototype)[K]
34
- >;
35
- }
36
- : T extends any[]
37
- ? Outputs<T>
38
- : {
39
- [k in keyof T]: Output<T[k]>;
40
- });
14
+ export interface Output<T> {
15
+ apply<U>(fn: (value: T) => U): Output<U>;
16
+ }
41
17
 
42
- export const Output = class<T, U> {
18
+ export class OutputChain<T, U> {
19
+ public readonly fn: (value: T) => U;
43
20
  constructor(
44
21
  public readonly parent: Output<T>,
45
- public readonly fn: (value: T) => U,
46
- ) {}
22
+ fn: (value: T) => U,
23
+ ) {
24
+ let result:
25
+ | {
26
+ value: U;
27
+ }
28
+ | undefined;
29
+ this.fn = (value: T) => {
30
+ if (result === undefined) {
31
+ result = {
32
+ value: fn(value),
33
+ };
34
+ }
35
+ return result.value as U;
36
+ };
37
+ }
47
38
 
48
- public apply<V>(fn: (value: U) => V): Output<V> {
49
- // @ts-expect-error - we know we are an "Output"
50
- return new Output(this, fn);
39
+ public apply<V>(fn: (value: U) => Output<V>): Output<V>;
40
+ public apply<V>(fn: (value: U) => V): Output<V>;
41
+ public apply<V>(fn: (value: U) => any): Output<V> {
42
+ return new OutputChain(this, fn);
51
43
  }
52
- } as new <T, U>(
53
- parent: Output<T>,
54
- fn: (value: T) => U,
55
- ) => Output<U>;
44
+ }
45
+
46
+ export type Resolved<O> = O extends Output<infer U>
47
+ ? U
48
+ : O extends null
49
+ ? O
50
+ : O extends any[]
51
+ ? ResolveN<O>
52
+ : O extends object
53
+ ? {
54
+ [k in keyof O]: Resolved<O[k]>;
55
+ }
56
+ : O;
57
+
58
+ type ResolveN<O extends any[]> = O extends [infer First, ...infer Rest]
59
+ ? [Resolved<First>, ...ResolveN<Rest>]
60
+ : [];
61
+
62
+ // export data from a resource
63
+ export type Export<Out> = Out extends null
64
+ ? Output<Out>
65
+ : Out extends any[]
66
+ ? Outputs<Out>
67
+ : Out extends object
68
+ ? {
69
+ [k in keyof Out]: Output<Out[k]>;
70
+ }
71
+ : Output<Out>;
package/src/print.ts ADDED
@@ -0,0 +1,18 @@
1
+ import { Resource } from "./resource";
2
+
3
+ export class Print extends Resource(
4
+ "Print",
5
+ {
6
+ alwaysUpdate: true,
7
+ },
8
+ async (
9
+ ctx,
10
+ values: {
11
+ [key: string]: any;
12
+ },
13
+ ) => {
14
+ if (ctx.event !== "delete") {
15
+ console.log(JSON.stringify(values, null, 2));
16
+ }
17
+ },
18
+ ) {}
package/src/resource.ts CHANGED
@@ -3,14 +3,17 @@ import { type ApplyOptions, apply } from "./apply";
3
3
  import type { DestroyOptions } from "./destroy";
4
4
  import { defaultStateStore, deletions, providers } from "./global";
5
5
  import type { Inputs, Input as input } from "./input";
6
- import { Output } from "./output";
7
- import { Scope as IScope, getScope, pushScope } from "./scope";
6
+ import { type Export, type Output, OutputChain, type Resolved } from "./output";
7
+ import { type Scope as IScope, getScope, pushScope } from "./scope";
8
8
  import type { State, StateStore } from "./state";
9
9
 
10
10
  export type ResourceID = string;
11
+ export type ResourceFQN = string;
11
12
  export type ResourceType = string;
12
13
 
13
14
  export const ResourceID = Symbol.for("ResourceID");
15
+ export const ResourceFQN = Symbol.for("ResourceFQN");
16
+ export const ResourceHandle = Symbol.for("ResourceHandle");
14
17
  export const Provider = Symbol.for("Provider");
15
18
  export const Input = Symbol.for("Input");
16
19
  export const Value = Symbol.for("Value");
@@ -22,6 +25,8 @@ export const Options = Symbol.for("Options");
22
25
  // properties that pierce through the Proxy
23
26
  const OrthogonalProperties = [
24
27
  ResourceID,
28
+ ResourceFQN,
29
+ ResourceHandle,
25
30
  Provider,
26
31
  Input,
27
32
  Value,
@@ -46,12 +51,18 @@ export type Resource<In extends any[] = any[], Out = any> = {
46
51
  [Apply]: <O>(value: Out) => O;
47
52
  [Provide]: (value: Out) => void;
48
53
  [Options]: ProviderOptions;
49
- } & Output<Out>;
54
+ } & Output<Out> &
55
+ Export<
56
+ // the resource handler may export another Output - we need to resolve it first
57
+ // then, export that resolved value
58
+ Resolved<Out>
59
+ >;
50
60
 
51
61
  export interface BaseContext {
52
62
  quiet: boolean;
53
63
  stage: string;
54
64
  resourceID: ResourceID;
65
+ resourceFQN: ResourceFQN;
55
66
  scope: IScope;
56
67
  get<T>(key: string): Promise<T | undefined>;
57
68
  set<T>(key: string, value: T): Promise<void>;
@@ -65,6 +76,7 @@ export interface BaseContext {
65
76
 
66
77
  export interface CreateContext extends BaseContext {
67
78
  event: "create";
79
+ output?: undefined;
68
80
  }
69
81
 
70
82
  export interface UpdateContext<Outputs> extends BaseContext {
@@ -113,18 +125,24 @@ export function isResource(value: any): value is Resource {
113
125
  return value?.[ResourceID] !== undefined;
114
126
  }
115
127
 
128
+ const applied = new Map<Object, any>();
129
+
130
+ const destroyed = new WeakMap<Resource<any, any>, Promise<void>>();
131
+
132
+ const i = 0;
133
+
116
134
  export function Resource<
117
- const Type extends ResourceType,
135
+ // const Type extends ResourceType,
118
136
  Args extends any[],
119
137
  Out,
120
138
  >(
121
- type: Type,
139
+ type: string,
122
140
  options: Partial<ProviderOptions>,
123
141
  func: (
124
142
  ctx: Context<Out>,
125
143
  ...args: Args
126
144
  ) => Promise<input<Out> | void> | input<Out> | void,
127
- ): Provider<Type, Args, Awaited<Out>>;
145
+ ): Provider<string, Args, Awaited<Out>>;
128
146
 
129
147
  export function Resource<
130
148
  const Type extends ResourceType,
@@ -166,6 +184,8 @@ export function Resource<
166
184
 
167
185
  interface Resource {
168
186
  [ResourceID]: ResourceID;
187
+ [ResourceFQN]: string;
188
+ [ResourceHandle]: Object;
169
189
  [Provider]: Provider<Type, Args, Out>;
170
190
  [Input]: Inputs<Args>;
171
191
  [Value]?: Out;
@@ -190,7 +210,12 @@ export function Resource<
190
210
  }
191
211
  scope.nodes.set(id, node as any);
192
212
 
213
+ // const resourceFQN = scope.getScopePath(id) + "/" + id;
214
+
193
215
  this[ResourceID] = id;
216
+ this[ResourceHandle] = new Object();
217
+ // this[ResourceFQN] = resourceFQN;
218
+ // this[ResourceFQN] = i++;
194
219
  this[Provider] = Resource as any;
195
220
  this[Input] = input;
196
221
  this[Value] = undefined;
@@ -210,6 +235,8 @@ export function Resource<
210
235
  get(target: any, prop) {
211
236
  if (OrthogonalProperties.includes(prop as any)) {
212
237
  return target[prop];
238
+ } else if (prop === "apply") {
239
+ return target[Apply].bind(target);
213
240
  } else {
214
241
  return target[Apply]((value: Out) => value[prop as keyof Out]);
215
242
  }
@@ -217,8 +244,12 @@ export function Resource<
217
244
  });
218
245
  }
219
246
 
247
+ public apply<U>(fn: (value: Out) => U): Output<U> {
248
+ return this[Apply](fn);
249
+ }
250
+
220
251
  public [Apply]<U>(fn: (value: Out) => U): Output<U> {
221
- return new Output<Out, U>(this as any, fn);
252
+ return new OutputChain<Out, U>(this as any, fn);
222
253
  }
223
254
 
224
255
  private box?: {
@@ -258,132 +289,146 @@ export function Resource<
258
289
  ): Promise<Awaited<Out | void>> {
259
290
  // const stack = resource[ResourceStack];
260
291
  const resourceID = resource[ResourceID];
261
-
262
- let resourceState: State | undefined = await stateStore.get(resourceID);
263
- if (resourceState === undefined) {
264
- resourceState = {
265
- provider: type,
266
- status: "creating",
267
- data: {},
268
- output: undefined,
269
- deps: [...deps],
270
- inputs,
271
- };
272
- await stateStore.set(resourceID, resourceState);
273
- }
274
-
275
292
  const resourceFQN = `${resource[Scope].getScopePath(stage)}/${resourceID}`;
293
+ const cacheKey = resource[ResourceHandle];
294
+ if (!applied.has(cacheKey)) {
295
+ let res, rej;
296
+ const promise = new Promise<Awaited<Out | void>>((resolve, reject) => {
297
+ res = resolve;
298
+ rej = reject;
299
+ });
300
+ applied.set(cacheKey, promise);
301
+ update().then(res).catch(rej);
302
+ return promise;
303
+ }
304
+ return await applied.get(cacheKey);
305
+
306
+ async function update(): Promise<Awaited<Out | void>> {
307
+ let resourceState: State | undefined = await stateStore.get(resourceID);
308
+ if (resourceState === undefined) {
309
+ resourceState = {
310
+ provider: type,
311
+ status: "creating",
312
+ data: {},
313
+ output: undefined,
314
+ deps: [...deps],
315
+ inputs,
316
+ };
317
+ await stateStore.set(resourceID, resourceState);
318
+ }
276
319
 
277
- // Skip update if inputs haven't changed and resource is in a stable state
278
- if (
279
- resourceState.status === "created" ||
280
- resourceState.status === "updated"
281
- ) {
320
+ // Skip update if inputs haven't changed and resource is in a stable state
282
321
  if (
283
- JSON.stringify(resourceState.inputs) === JSON.stringify(inputs) &&
284
- !resource[Options].alwaysUpdate
322
+ resourceState.status === "created" ||
323
+ resourceState.status === "updated"
285
324
  ) {
286
- if (!options?.quiet) {
287
- console.log(`Skip: ${resourceFQN} (no changes)`);
325
+ if (
326
+ JSON.stringify(resourceState.inputs) === JSON.stringify(inputs) &&
327
+ !resource[Options].alwaysUpdate
328
+ ) {
329
+ if (!options?.quiet) {
330
+ console.log(`Skip: ${resourceFQN} (no changes)`);
331
+ }
332
+ if (resourceState.output !== undefined) {
333
+ resource[Provide](resourceState.output);
334
+ }
335
+ return resourceState.output;
288
336
  }
289
- if (resourceState.output !== undefined) {
290
- resource[Provide](resourceState.output);
291
- }
292
- return resourceState.output;
293
337
  }
294
- }
295
338
 
296
- const event = resourceState.status === "creating" ? "create" : "update";
297
- resourceState.status = event === "create" ? "creating" : "updating";
298
- resourceState.oldInputs = resourceState.inputs;
299
- resourceState.inputs = inputs;
339
+ const event = resourceState.status === "creating" ? "create" : "update";
340
+ resourceState.status = event === "create" ? "creating" : "updating";
341
+ resourceState.oldInputs = resourceState.inputs;
342
+ resourceState.inputs = inputs;
300
343
 
301
- if (!options?.quiet) {
302
- console.log(
303
- `${event === "create" ? "Create" : "Update"}: ${resourceFQN}`,
304
- );
305
- }
344
+ if (!options?.quiet) {
345
+ console.log(
346
+ `${event === "create" ? "Create" : "Update"}: ${resourceFQN}`,
347
+ );
348
+ }
306
349
 
307
- await stateStore.set(resourceID, resourceState);
350
+ await stateStore.set(resourceID, resourceState);
308
351
 
309
- let isReplaced = false;
352
+ let isReplaced = false;
353
+
354
+ const quiet = options.quiet ?? false;
355
+
356
+ const evaluated = await pushScope(
357
+ resource[Scope],
358
+ resourceID,
359
+ async () => {
360
+ const result = await func(
361
+ {
362
+ stage,
363
+ resourceID,
364
+ resourceFQN,
365
+ event,
366
+ scope: getScope(),
367
+ output: resourceState.output,
368
+ replace: () => {
369
+ if (isReplaced) {
370
+ console.warn(
371
+ `Resource ${type} ${resourceFQN} is already marked as REPLACE`,
372
+ );
373
+ return;
374
+ }
375
+ isReplaced = true;
376
+ deletions.push({
377
+ id: resourceID,
378
+ data: {
379
+ ...resourceState!.data,
380
+ },
381
+ inputs: inputs,
382
+ });
383
+ },
384
+ get: (key) => resourceState!.data[key],
385
+ set: async (key, value) => {
386
+ resourceState!.data[key] = value;
387
+ await stateStore.set(resourceID, resourceState!);
388
+ },
389
+ delete: async (key) => {
390
+ const value = resourceState!.data[key];
391
+ delete resourceState!.data[key];
392
+ await stateStore.set(resourceID, resourceState!);
393
+ return value;
394
+ },
395
+ quiet,
396
+ },
397
+ ...inputs,
398
+ );
310
399
 
311
- const quiet = options.quiet ?? false;
400
+ if (result === undefined) {
401
+ return undefined;
402
+ }
312
403
 
313
- const evaluated = await pushScope(
314
- resource[Scope],
315
- resourceID,
316
- async () => {
317
- const result = await func(
318
- {
404
+ const evaluated = await apply(result as Out, {
319
405
  stage,
320
- resourceID,
321
- event,
322
406
  scope: getScope(),
323
- output: resourceState.output,
324
- replace: () => {
325
- if (isReplaced) {
326
- console.warn(
327
- `Resource ${type} ${resourceFQN} is already marked as REPLACE`,
328
- );
329
- return;
330
- }
331
- isReplaced = true;
332
- deletions.push({
333
- id: resourceID,
334
- data: {
335
- ...resourceState!.data,
336
- },
337
- inputs: inputs,
338
- });
339
- },
340
- get: (key) => resourceState!.data[key],
341
- set: async (key, value) => {
342
- resourceState!.data[key] = value;
343
- await stateStore.set(resourceID, resourceState!);
344
- },
345
- delete: async (key) => {
346
- const value = resourceState!.data[key];
347
- delete resourceState!.data[key];
348
- await stateStore.set(resourceID, resourceState!);
349
- return value;
350
- },
351
407
  quiet,
352
- },
353
- ...inputs,
354
- );
355
-
356
- if (result === undefined) {
357
- return undefined;
358
- }
359
-
360
- const evaluated = await apply(result as Out, {
361
- stage,
362
- scope: getScope(),
363
- quiet,
364
- });
365
-
366
- return evaluated;
367
- },
368
- );
408
+ });
369
409
 
370
- if (!options?.quiet) {
371
- console.log(
372
- `${event === "create" ? "Created" : "Updated"}: ${resourceFQN}`,
410
+ return evaluated;
411
+ },
373
412
  );
413
+
414
+ if (!options?.quiet) {
415
+ console.log(
416
+ `${event === "create" ? "Created" : "Updated"}: ${resourceFQN}`,
417
+ );
418
+ }
419
+ await stateStore.set(resourceID, {
420
+ provider: type,
421
+ data: resourceState.data,
422
+ status: event === "create" ? "created" : "updated",
423
+ output: evaluated,
424
+ inputs,
425
+ deps: [...deps],
426
+ });
427
+ if (evaluated !== undefined) {
428
+ resource[Provide](evaluated as Out);
429
+ }
430
+ return evaluated as Awaited<Out>;
374
431
  }
375
- await stateStore.set(resourceID, {
376
- provider: type,
377
- data: resourceState.data,
378
- status: event === "create" ? "created" : "updated",
379
- output: evaluated,
380
- inputs,
381
- deps: [...deps],
382
- });
383
- if (evaluated !== undefined) {
384
- resource[Provide](evaluated);
385
- }
386
- return evaluated;
387
432
  }
388
433
 
389
434
  static async delete(
@@ -394,8 +439,9 @@ export function Resource<
394
439
  inputs: Args,
395
440
  options: DestroyOptions,
396
441
  ) {
442
+ const { Scope } = await import("./scope");
397
443
  const resourceFQN = `${scope.getScopePath(stage)}/${resourceID}`;
398
- const nestedScope = new IScope(resourceID, scope);
444
+ const nestedScope = new Scope(resourceID, scope);
399
445
 
400
446
  await alchemize({
401
447
  mode: "destroy",
@@ -415,6 +461,7 @@ export function Resource<
415
461
  stage,
416
462
  scope,
417
463
  resourceID: resourceID,
464
+ resourceFQN: resourceFQN,
418
465
  event: "delete",
419
466
  output: state.output,
420
467
  replace() {
package/src/scope.ts CHANGED
@@ -1,10 +1,8 @@
1
1
  import { AsyncLocalStorage } from "node:async_hooks";
2
2
  import path from "node:path";
3
- import { rootScope } from "./global";
3
+ import type { Output } from "./output";
4
4
  import type { Provider, Resource, ResourceID } from "./resource";
5
5
 
6
- const scopeGlobal = new Map<string | null, Scope>();
7
-
8
6
  const scopeStorage = new AsyncLocalStorage<Scope>();
9
7
 
10
8
  export class Scope {
@@ -16,6 +14,8 @@ export class Scope {
16
14
  }
17
15
  >();
18
16
 
17
+ public readonly callbacks: Output<void>[] = [];
18
+
19
19
  constructor(
20
20
  public readonly scopeName: string | null = null,
21
21
  public readonly parent: Scope | undefined = undefined,
@@ -29,6 +29,8 @@ export class Scope {
29
29
  }
30
30
  }
31
31
 
32
+ export const rootScope = new Scope(null);
33
+
32
34
  export function getScope(): Scope {
33
35
  return scopeStorage.getStore() ?? rootScope;
34
36
  }
@@ -37,13 +39,7 @@ export function withScope<T>(
37
39
  scope: Scope,
38
40
  fn: () => T | Promise<T>,
39
41
  ): Promise<T> {
40
- return scopeStorage.run(scope, async () => {
41
- try {
42
- return await fn();
43
- } catch (error) {
44
- throw error;
45
- }
46
- });
42
+ return scopeStorage.run(scope, async () => fn());
47
43
  }
48
44
 
49
45
  export function pushScope<T>(
@@ -51,6 +47,5 @@ export function pushScope<T>(
51
47
  scopeName: string,
52
48
  fn: () => T | Promise<T>,
53
49
  ): Promise<T> {
54
- const newScope = new Scope(scopeName, parent);
55
- return withScope(newScope, fn);
50
+ return scopeStorage.run(new Scope(scopeName, parent), async () => fn());
56
51
  }