alchemy 0.1.18 → 0.2.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 (310) hide show
  1. package/lib/alchemy.d.ts +67 -0
  2. package/lib/alchemy.js +40 -0
  3. package/lib/apply.d.ts +3 -20
  4. package/lib/apply.js +86 -82
  5. package/lib/aws/account-id.d.ts +0 -1
  6. package/lib/aws/account-id.js +0 -1
  7. package/lib/aws/bucket.d.ts +4 -7
  8. package/lib/aws/bucket.js +8 -12
  9. package/lib/aws/function.d.ts +4 -7
  10. package/lib/aws/function.js +9 -30
  11. package/lib/aws/index.d.ts +1 -1
  12. package/lib/aws/index.js +1 -1
  13. package/lib/aws/oidc/github-oidc-provider.d.ts +2 -6
  14. package/lib/aws/oidc/github-oidc-provider.js +10 -15
  15. package/lib/aws/oidc/index.d.ts +0 -1
  16. package/lib/aws/oidc/index.js +0 -1
  17. package/lib/aws/oidc/oidc-provider.d.ts +4 -6
  18. package/lib/aws/oidc/oidc-provider.js +9 -11
  19. package/lib/aws/policy-attachment.d.ts +9 -0
  20. package/lib/aws/policy-attachment.js +20 -0
  21. package/lib/aws/policy.d.ts +4 -16
  22. package/lib/aws/policy.js +9 -40
  23. package/lib/aws/queue.d.ts +4 -34
  24. package/lib/aws/queue.js +10 -20
  25. package/lib/aws/role.d.ts +4 -7
  26. package/lib/aws/role.js +17 -26
  27. package/lib/aws/ses.d.ts +4 -10
  28. package/lib/aws/ses.js +118 -131
  29. package/lib/aws/table.d.ts +4 -7
  30. package/lib/aws/table.js +8 -11
  31. package/lib/cloudflare/api.d.ts +0 -1
  32. package/lib/cloudflare/api.js +0 -1
  33. package/lib/cloudflare/asset-manifest.d.ts +0 -1
  34. package/lib/cloudflare/asset-manifest.js +0 -1
  35. package/lib/cloudflare/auth.d.ts +0 -1
  36. package/lib/cloudflare/auth.js +0 -1
  37. package/lib/cloudflare/bindings.d.ts +1 -1
  38. package/lib/cloudflare/bindings.js +3 -2
  39. package/lib/cloudflare/bound.d.ts +0 -1
  40. package/lib/cloudflare/bound.js +0 -1
  41. package/lib/cloudflare/bucket.d.ts +7 -15
  42. package/lib/cloudflare/bucket.js +12 -19
  43. package/lib/cloudflare/durable-object-namespace.d.ts +0 -3
  44. package/lib/cloudflare/durable-object-namespace.js +0 -4
  45. package/lib/cloudflare/generate-asset-manifest.d.ts +0 -1
  46. package/lib/cloudflare/generate-asset-manifest.js +1 -2
  47. package/lib/cloudflare/index.d.ts +0 -1
  48. package/lib/cloudflare/index.js +0 -1
  49. package/lib/cloudflare/kv-namespace.d.ts +6 -9
  50. package/lib/cloudflare/kv-namespace.js +16 -20
  51. package/lib/cloudflare/state.d.ts +0 -1
  52. package/lib/cloudflare/state.js +0 -1
  53. package/lib/cloudflare/static-site-router.d.ts +0 -1
  54. package/lib/cloudflare/static-site-router.js +0 -4
  55. package/lib/cloudflare/static-site.d.ts +5 -11
  56. package/lib/cloudflare/static-site.js +16 -28
  57. package/lib/cloudflare/types.d.ts +0 -1
  58. package/lib/cloudflare/types.js +0 -1
  59. package/lib/cloudflare/upload-asset-manifest.d.ts +0 -1
  60. package/lib/cloudflare/upload-asset-manifest.js +0 -1
  61. package/lib/cloudflare/worker-metadata.d.ts +0 -1
  62. package/lib/cloudflare/worker-metadata.js +0 -1
  63. package/lib/cloudflare/worker-migration.d.ts +0 -1
  64. package/lib/cloudflare/worker-migration.js +0 -1
  65. package/lib/cloudflare/worker.d.ts +8 -14
  66. package/lib/cloudflare/worker.js +37 -49
  67. package/lib/context.d.ts +58 -0
  68. package/lib/context.js +36 -0
  69. package/lib/destroy.d.ts +10 -13
  70. package/lib/destroy.js +104 -43
  71. package/lib/{esbuild.d.ts → esbuild/bundle.d.ts} +4 -18
  72. package/lib/{esbuild.js → esbuild/bundle.js} +10 -24
  73. package/lib/esbuild/index.d.ts +1 -0
  74. package/lib/esbuild/index.js +1 -0
  75. package/lib/fs/file.d.ts +13 -0
  76. package/lib/fs/file.js +20 -0
  77. package/lib/fs/folder.d.ts +10 -0
  78. package/lib/fs/folder.js +16 -0
  79. package/lib/fs/index.d.ts +2 -0
  80. package/lib/fs/index.js +2 -0
  81. package/lib/github/client.d.ts +0 -1
  82. package/lib/github/client.js +0 -1
  83. package/lib/github/index.d.ts +0 -1
  84. package/lib/github/index.js +0 -1
  85. package/lib/github/secret.d.ts +4 -6
  86. package/lib/github/secret.js +10 -12
  87. package/lib/index.d.ts +6 -10
  88. package/lib/index.js +5 -9
  89. package/lib/resource.d.ts +34 -62
  90. package/lib/resource.js +29 -270
  91. package/lib/scope.d.ts +35 -14
  92. package/lib/scope.js +78 -23
  93. package/lib/secret.d.ts +2 -7
  94. package/lib/secret.js +7 -20
  95. package/lib/state.d.ts +14 -10
  96. package/lib/state.js +15 -14
  97. package/lib/stripe/index.d.ts +0 -1
  98. package/lib/stripe/index.js +0 -1
  99. package/lib/stripe/price.d.ts +4 -5
  100. package/lib/stripe/price.js +10 -20
  101. package/lib/stripe/product.d.ts +4 -5
  102. package/lib/stripe/product.js +10 -20
  103. package/lib/stripe/webhook.d.ts +4 -6
  104. package/lib/stripe/webhook.js +12 -25
  105. package/lib/test/bun.d.ts +19 -0
  106. package/lib/test/bun.js +55 -0
  107. package/lib/{utils → util}/content-type.d.ts +0 -1
  108. package/lib/{utils → util}/content-type.js +0 -1
  109. package/lib/{encrypt.d.ts → util/encrypt.d.ts} +0 -1
  110. package/lib/{encrypt.js → util/encrypt.js} +0 -1
  111. package/lib/{error.d.ts → util/ignore.d.ts} +0 -1
  112. package/lib/{error.js → util/ignore.js} +0 -1
  113. package/lib/{utils → util}/retry.d.ts +0 -1
  114. package/lib/{utils → util}/retry.js +0 -1
  115. package/lib/util/serde.d.ts +3 -0
  116. package/lib/{serde.js → util/serde.js} +19 -10
  117. package/lib/util/slugify.d.ts +1 -0
  118. package/lib/util/slugify.js +3 -0
  119. package/package.json +5 -4
  120. package/src/alchemy.ts +120 -0
  121. package/src/apply.ts +109 -114
  122. package/src/aws/bucket.ts +11 -14
  123. package/src/aws/function.ts +13 -33
  124. package/src/aws/index.ts +1 -0
  125. package/src/aws/oidc/github-oidc-provider.ts +14 -19
  126. package/src/aws/oidc/oidc-provider.ts +19 -12
  127. package/src/aws/policy-attachment.ts +51 -0
  128. package/src/aws/policy.ts +15 -60
  129. package/src/aws/queue.ts +17 -21
  130. package/src/aws/role.ts +25 -28
  131. package/src/aws/ses.ts +150 -164
  132. package/src/aws/table.ts +15 -15
  133. package/src/cloudflare/bindings.ts +8 -0
  134. package/src/cloudflare/bucket.ts +39 -47
  135. package/src/cloudflare/durable-object-namespace.ts +0 -10
  136. package/src/cloudflare/generate-asset-manifest.ts +1 -1
  137. package/src/cloudflare/kv-namespace.ts +35 -33
  138. package/src/cloudflare/static-site-router.ts +0 -3
  139. package/src/cloudflare/static-site.ts +44 -60
  140. package/src/cloudflare/worker.ts +105 -111
  141. package/src/context.ts +122 -0
  142. package/src/destroy.ts +124 -73
  143. package/src/{esbuild.ts → esbuild/bundle.ts} +16 -28
  144. package/src/esbuild/index.ts +1 -0
  145. package/src/fs/file.ts +39 -0
  146. package/src/fs/folder.ts +32 -0
  147. package/src/fs/index.ts +2 -0
  148. package/src/github/secret.ts +19 -16
  149. package/src/index.ts +7 -15
  150. package/src/resource.ts +101 -462
  151. package/src/scope.ts +101 -36
  152. package/src/secret.ts +9 -32
  153. package/src/state.ts +30 -21
  154. package/src/stripe/price.ts +17 -22
  155. package/src/stripe/product.ts +17 -22
  156. package/src/stripe/webhook.ts +21 -27
  157. package/src/test/bun.ts +105 -0
  158. package/src/util/serde.ts +53 -0
  159. package/src/util/slugify.ts +3 -0
  160. package/lib/$.d.ts +0 -12
  161. package/lib/$.d.ts.map +0 -1
  162. package/lib/$.js +0 -42
  163. package/lib/$.js.map +0 -1
  164. package/lib/alchemize.d.ts +0 -47
  165. package/lib/alchemize.d.ts.map +0 -1
  166. package/lib/alchemize.js +0 -116
  167. package/lib/alchemize.js.map +0 -1
  168. package/lib/apply.d.ts.map +0 -1
  169. package/lib/apply.js.map +0 -1
  170. package/lib/aws/account-id.d.ts.map +0 -1
  171. package/lib/aws/account-id.js.map +0 -1
  172. package/lib/aws/bucket.d.ts.map +0 -1
  173. package/lib/aws/bucket.js.map +0 -1
  174. package/lib/aws/function.d.ts.map +0 -1
  175. package/lib/aws/function.js.map +0 -1
  176. package/lib/aws/index.d.ts.map +0 -1
  177. package/lib/aws/index.js.map +0 -1
  178. package/lib/aws/oidc/github-oidc-provider.d.ts.map +0 -1
  179. package/lib/aws/oidc/github-oidc-provider.js.map +0 -1
  180. package/lib/aws/oidc/index.d.ts.map +0 -1
  181. package/lib/aws/oidc/index.js.map +0 -1
  182. package/lib/aws/oidc/oidc-provider.d.ts.map +0 -1
  183. package/lib/aws/oidc/oidc-provider.js.map +0 -1
  184. package/lib/aws/policy.d.ts.map +0 -1
  185. package/lib/aws/policy.js.map +0 -1
  186. package/lib/aws/queue.d.ts.map +0 -1
  187. package/lib/aws/queue.js.map +0 -1
  188. package/lib/aws/role.d.ts.map +0 -1
  189. package/lib/aws/role.js.map +0 -1
  190. package/lib/aws/ses.d.ts.map +0 -1
  191. package/lib/aws/ses.js.map +0 -1
  192. package/lib/aws/table.d.ts.map +0 -1
  193. package/lib/aws/table.js.map +0 -1
  194. package/lib/cloudflare/api.d.ts.map +0 -1
  195. package/lib/cloudflare/api.js.map +0 -1
  196. package/lib/cloudflare/asset-manifest.d.ts.map +0 -1
  197. package/lib/cloudflare/asset-manifest.js.map +0 -1
  198. package/lib/cloudflare/auth.d.ts.map +0 -1
  199. package/lib/cloudflare/auth.js.map +0 -1
  200. package/lib/cloudflare/bindings.d.ts.map +0 -1
  201. package/lib/cloudflare/bindings.js.map +0 -1
  202. package/lib/cloudflare/bound.d.ts.map +0 -1
  203. package/lib/cloudflare/bound.js.map +0 -1
  204. package/lib/cloudflare/bucket.d.ts.map +0 -1
  205. package/lib/cloudflare/bucket.js.map +0 -1
  206. package/lib/cloudflare/durable-object-namespace.d.ts.map +0 -1
  207. package/lib/cloudflare/durable-object-namespace.js.map +0 -1
  208. package/lib/cloudflare/generate-asset-manifest.d.ts.map +0 -1
  209. package/lib/cloudflare/generate-asset-manifest.js.map +0 -1
  210. package/lib/cloudflare/index.d.ts.map +0 -1
  211. package/lib/cloudflare/index.js.map +0 -1
  212. package/lib/cloudflare/kv-namespace.d.ts.map +0 -1
  213. package/lib/cloudflare/kv-namespace.js.map +0 -1
  214. package/lib/cloudflare/state.d.ts.map +0 -1
  215. package/lib/cloudflare/state.js.map +0 -1
  216. package/lib/cloudflare/static-site-router.d.ts.map +0 -1
  217. package/lib/cloudflare/static-site-router.js.map +0 -1
  218. package/lib/cloudflare/static-site.d.ts.map +0 -1
  219. package/lib/cloudflare/static-site.js.map +0 -1
  220. package/lib/cloudflare/types.d.ts.map +0 -1
  221. package/lib/cloudflare/types.js.map +0 -1
  222. package/lib/cloudflare/upload-asset-manifest.d.ts.map +0 -1
  223. package/lib/cloudflare/upload-asset-manifest.js.map +0 -1
  224. package/lib/cloudflare/worker-metadata.d.ts.map +0 -1
  225. package/lib/cloudflare/worker-metadata.js.map +0 -1
  226. package/lib/cloudflare/worker-migration.d.ts.map +0 -1
  227. package/lib/cloudflare/worker-migration.js.map +0 -1
  228. package/lib/cloudflare/worker.d.ts.map +0 -1
  229. package/lib/cloudflare/worker.js.map +0 -1
  230. package/lib/destroy.d.ts.map +0 -1
  231. package/lib/destroy.js.map +0 -1
  232. package/lib/encrypt.d.ts.map +0 -1
  233. package/lib/encrypt.js.map +0 -1
  234. package/lib/error.d.ts.map +0 -1
  235. package/lib/error.js.map +0 -1
  236. package/lib/esbuild.d.ts.map +0 -1
  237. package/lib/esbuild.js.map +0 -1
  238. package/lib/fs.d.ts +0 -12
  239. package/lib/fs.d.ts.map +0 -1
  240. package/lib/fs.js +0 -41
  241. package/lib/fs.js.map +0 -1
  242. package/lib/github/client.d.ts.map +0 -1
  243. package/lib/github/client.js.map +0 -1
  244. package/lib/github/index.d.ts.map +0 -1
  245. package/lib/github/index.js.map +0 -1
  246. package/lib/github/secret.d.ts.map +0 -1
  247. package/lib/github/secret.js.map +0 -1
  248. package/lib/global.d.ts +0 -16
  249. package/lib/global.d.ts.map +0 -1
  250. package/lib/global.js +0 -42
  251. package/lib/global.js.map +0 -1
  252. package/lib/index.d.ts.map +0 -1
  253. package/lib/index.js.map +0 -1
  254. package/lib/input.d.ts +0 -7
  255. package/lib/input.d.ts.map +0 -1
  256. package/lib/input.js +0 -2
  257. package/lib/input.js.map +0 -1
  258. package/lib/main.d.ts +0 -3
  259. package/lib/main.d.ts.map +0 -1
  260. package/lib/main.js +0 -32
  261. package/lib/main.js.map +0 -1
  262. package/lib/output.d.ts +0 -25
  263. package/lib/output.d.ts.map +0 -1
  264. package/lib/output.js +0 -23
  265. package/lib/output.js.map +0 -1
  266. package/lib/print.d.ts +0 -7
  267. package/lib/print.d.ts.map +0 -1
  268. package/lib/print.js +0 -10
  269. package/lib/print.js.map +0 -1
  270. package/lib/resource.d.ts.map +0 -1
  271. package/lib/resource.js.map +0 -1
  272. package/lib/scope.d.ts.map +0 -1
  273. package/lib/scope.js.map +0 -1
  274. package/lib/secret.d.ts.map +0 -1
  275. package/lib/secret.js.map +0 -1
  276. package/lib/serde.d.ts +0 -3
  277. package/lib/serde.d.ts.map +0 -1
  278. package/lib/serde.js.map +0 -1
  279. package/lib/slug.d.ts +0 -2
  280. package/lib/slug.d.ts.map +0 -1
  281. package/lib/slug.js +0 -4
  282. package/lib/slug.js.map +0 -1
  283. package/lib/state.d.ts.map +0 -1
  284. package/lib/state.js.map +0 -1
  285. package/lib/stripe/index.d.ts.map +0 -1
  286. package/lib/stripe/index.js.map +0 -1
  287. package/lib/stripe/price.d.ts.map +0 -1
  288. package/lib/stripe/price.js.map +0 -1
  289. package/lib/stripe/product.d.ts.map +0 -1
  290. package/lib/stripe/product.js.map +0 -1
  291. package/lib/stripe/webhook.d.ts.map +0 -1
  292. package/lib/stripe/webhook.js.map +0 -1
  293. package/lib/utils/content-type.d.ts.map +0 -1
  294. package/lib/utils/content-type.js.map +0 -1
  295. package/lib/utils/retry.d.ts.map +0 -1
  296. package/lib/utils/retry.js.map +0 -1
  297. package/src/$.ts +0 -69
  298. package/src/alchemize.ts +0 -195
  299. package/src/fs.ts +0 -47
  300. package/src/global.ts +0 -63
  301. package/src/input.ts +0 -19
  302. package/src/main.ts +0 -34
  303. package/src/output.ts +0 -75
  304. package/src/print.ts +0 -18
  305. package/src/serde.ts +0 -46
  306. package/src/slug.ts +0 -3
  307. /package/src/{utils → util}/content-type.ts +0 -0
  308. /package/src/{encrypt.ts → util/encrypt.ts} +0 -0
  309. /package/src/{error.ts → util/ignore.ts} +0 -0
  310. /package/src/{utils → util}/retry.ts +0 -0
package/lib/resource.d.ts CHANGED
@@ -1,71 +1,43 @@
1
- import { type ApplyOptions } from "./apply";
2
- import type { DestroyOptions } from "./destroy";
3
- import type { Inputs, Input as input } from "./input";
4
- import { type Export, type Output, type Resolved } from "./output";
5
- import { type Scope as IScope } from "./scope";
6
- import type { State, StateStore } from "./state";
1
+ import type { Context } from "./context";
2
+ import { Scope as _Scope } from "./scope";
3
+ export declare const PROVIDERS: Map<string, any>;
7
4
  export type ResourceID = string;
8
5
  export type ResourceFQN = string;
9
- export type ResourceType = string;
10
- export declare const ResourceID: unique symbol;
11
- export declare const ResourceFQN: unique symbol;
12
- export declare const ResourceHandle: unique symbol;
13
- export declare const Provider: unique symbol;
14
- export declare const Input: unique symbol;
15
- export declare const Value: unique symbol;
16
- export declare const Apply: unique symbol;
17
- export declare const Provide: unique symbol;
18
- export declare const Scope: unique symbol;
19
- export declare const Options: unique symbol;
6
+ export type ResourceKind = string;
20
7
  export interface ProviderOptions {
21
8
  /**
22
9
  * If true, the resource will be updated even if the inputs have not changed.
23
10
  */
24
11
  alwaysUpdate: boolean;
25
12
  }
26
- export type Resource<In extends any[] = any[], Out = any> = {
27
- [ResourceID]: string;
28
- [Provider]: Provider<any, any[], any>;
29
- [Input]: Inputs<In>;
30
- [Value]?: Out;
31
- [Apply]: <O>(value: Out) => O;
32
- [Provide]: (value: Out) => void;
33
- [Options]: ProviderOptions;
34
- } & Output<Out> & Export<Resolved<Out>>;
35
- export interface BaseContext {
36
- quiet: boolean;
37
- stage: string;
38
- resourceID: ResourceID;
39
- resourceFQN: ResourceFQN;
40
- scope: IScope;
41
- get<T>(key: string): Promise<T | undefined>;
42
- set<T>(key: string, value: T): Promise<void>;
43
- delete<T>(key: string): Promise<T | undefined>;
44
- /**
45
- * Indicate that this resource is being replaced.
46
- * This will cause the resource to be deleted at the end of the stack's CREATE phase.
47
- */
48
- replace(): void;
49
- }
50
- export interface CreateContext extends BaseContext {
51
- event: "create";
52
- output?: undefined;
53
- }
54
- export interface UpdateContext<Outputs> extends BaseContext {
55
- event: "update";
56
- output: Outputs;
57
- }
58
- export interface DeleteContext<Outputs> extends BaseContext {
59
- event: "delete";
60
- output: Outputs;
61
- }
62
- export type Context<Outputs> = CreateContext | UpdateContext<Outputs> | DeleteContext<Outputs>;
63
- export type Provider<Type extends ResourceType = ResourceType, In extends any[] = any[], Out = any> = {
13
+ export type ResourceProps = {
14
+ [key: string]: any;
15
+ };
16
+ export type Provider<Type extends string = string, F extends ResourceLifecycleHandler = ResourceLifecycleHandler> = F & IsClass & {
64
17
  type: Type;
65
- update(stage: string, resource: Resource, deps: Set<ResourceID>, inputs: Inputs<In>, stateStore: StateStore, options: ApplyOptions): Promise<Awaited<Out>>;
66
- delete(stage: string, scope: IScope | undefined, resourceID: ResourceID, state: State, inputs: Inputs<In>, options: DestroyOptions): Promise<void>;
67
- } & (new (id: string, ...inputs: [...Inputs<In>, ...any[]]) => Resource<In, Out>);
68
- export declare function isResource(value: any): value is Resource;
69
- export declare function Resource<Args extends any[], Out>(type: string, options: Partial<ProviderOptions>, func: (ctx: Context<Out>, ...args: Args) => Promise<input<Out> | void> | input<Out> | void): Provider<string, Args, Awaited<Out>>;
70
- export declare function Resource<const Type extends ResourceType, Args extends any[], Out>(type: Type, func: (ctx: Context<Out>, ...args: Args) => Promise<input<Out> | void> | input<Out> | void): Provider<Type, Args, Awaited<Out>>;
71
- //# sourceMappingURL=resource.d.ts.map
18
+ options: Partial<ProviderOptions> | undefined;
19
+ handler: F;
20
+ };
21
+ export type PendingResource<Out = unknown, Kind extends ResourceKind = ResourceKind, ID extends ResourceID = ResourceID, FQN extends ResourceFQN = ResourceFQN, Scope extends _Scope = _Scope, Seq extends number = number> = Promise<Out> & {
22
+ Kind: Kind;
23
+ ID: ID;
24
+ FQN: FQN;
25
+ Seq: Seq;
26
+ Scope: Scope;
27
+ signal: () => void;
28
+ };
29
+ export interface Resource<Kind extends ResourceKind = ResourceKind, ID extends ResourceID = ResourceID, FQN extends ResourceFQN = ResourceFQN, Scope extends _Scope = _Scope, Seq extends number = number> {
30
+ Kind: Kind;
31
+ ID: ID;
32
+ FQN: FQN;
33
+ Scope: Scope;
34
+ Seq: Seq;
35
+ }
36
+ type IsClass = {
37
+ new (_: never): never;
38
+ };
39
+ type ResourceLifecycleHandler = (this: Context<any>, id: string, props: any) => Promise<Resource<string>>;
40
+ type Handler<F extends (...args: any[]) => any> = F | (((this: any, id: string, props: Parameters<F>[1]) => never) & IsClass);
41
+ export declare function Resource<const Type extends string, F extends ResourceLifecycleHandler>(type: Type, fn: F): Handler<F>;
42
+ export declare function Resource<const Type extends string, F extends ResourceLifecycleHandler>(type: Type, options: Partial<ProviderOptions>, fn: F): Handler<F>;
43
+ export {};
package/lib/resource.js CHANGED
@@ -1,276 +1,35 @@
1
- import { alchemize } from "./alchemize";
2
1
  import { apply } from "./apply";
3
- import { defaultStateStore, deletions, providers } from "./global";
4
- import { OutputChain } from "./output";
5
- import { getScope, pushScope } from "./scope";
6
- export const ResourceID = Symbol.for("ResourceID");
7
- export const ResourceFQN = Symbol.for("ResourceFQN");
8
- export const ResourceHandle = Symbol.for("ResourceHandle");
9
- export const Provider = Symbol.for("Provider");
10
- export const Input = Symbol.for("Input");
11
- export const Value = Symbol.for("Value");
12
- export const Apply = Symbol.for("Apply");
13
- export const Provide = Symbol.for("Provide");
14
- export const Scope = Symbol.for("Scope");
15
- export const Options = Symbol.for("Options");
16
- // properties that pierce through the Proxy
17
- const OrthogonalProperties = [
18
- ResourceID,
19
- ResourceFQN,
20
- ResourceHandle,
21
- Provider,
22
- Input,
23
- Value,
24
- Apply,
25
- Provide,
26
- Scope,
27
- Options,
28
- ];
29
- export function isResource(value) {
30
- return value?.[ResourceID] !== undefined;
31
- }
32
- const applied = new Map();
33
- const destroyed = new WeakMap();
34
- const i = 0;
2
+ import { Scope as _Scope } from "./scope";
3
+ export const PROVIDERS = new Map();
35
4
  export function Resource(type, ...args) {
36
- if (providers.has(type)) {
5
+ if (PROVIDERS.has(type)) {
37
6
  throw new Error(`Resource ${type} already exists`);
38
7
  }
39
- const [options, func] = args.length === 2 ? args : [undefined, args[0]];
40
- class Resource {
41
- static type = type;
42
- constructor(id, ...input) {
43
- const scope = getScope();
44
- const node = {
45
- provider: Resource,
46
- resource: this,
47
- };
48
- if (scope.nodes.has(id)) {
49
- // TODO(sam): do we want to throw?
50
- // it's kind of awesome that you can re-create a resource and call apply
51
- // console.warn(`Resource ${id} already exists in the stack: ${stack.id}`);
52
- }
53
- scope.nodes.set(id, node);
54
- // const resourceFQN = scope.getScopePath(id) + "/" + id;
55
- this[ResourceID] = id;
56
- this[ResourceHandle] = new Object();
57
- // this[ResourceFQN] = resourceFQN;
58
- // this[ResourceFQN] = i++;
59
- this[Provider] = Resource;
60
- this[Input] = input;
61
- this[Value] = undefined;
62
- this[Scope] = scope;
63
- this[Provide] = (value) => {
64
- this[Value] = value;
65
- };
66
- this[Options] = {
67
- alwaysUpdate: options?.alwaysUpdate ?? false,
68
- };
69
- return new Proxy(this, {
70
- // TODO(sam): this won't work for the sub-class (class Table extends Resource)
71
- getPrototypeOf() {
72
- return Resource.prototype;
73
- },
74
- get(target, prop) {
75
- if (OrthogonalProperties.includes(prop)) {
76
- return target[prop];
77
- }
78
- else if (prop === "apply") {
79
- return target[Apply].bind(target);
80
- }
81
- else {
82
- return target[Apply]((value) => value[prop]);
83
- }
84
- },
85
- });
86
- }
87
- apply(fn) {
88
- return this[Apply](fn);
89
- }
90
- [Apply](fn) {
91
- return new OutputChain(this, fn);
92
- }
93
- box = undefined;
94
- [Provide](value) {
95
- if (this.box) {
96
- throw new Error(`Output ${this[ResourceID]} already has a value`);
97
- }
98
- this.box = { value };
99
- const subscribers = this.subscribers;
100
- this.subscribers = [];
101
- subscribers.forEach((fn) => fn(value));
8
+ const [options, handler] = args.length === 2 ? args : [undefined, args[0]];
9
+ const provider = ((resourceID, props) => {
10
+ const scope = _Scope.current;
11
+ if (scope.resources.has(resourceID)) {
12
+ // TODO(sam): do we want to throw?
13
+ // it's kind of awesome that you can re-create a resource and call apply
14
+ // console.warn(`Resource ${id} already exists in the stack: ${stack.id}`);
102
15
  }
103
- subscribers = [];
104
- /**
105
- * @internal
106
- */
107
- subscribe(fn) {
108
- if (this.box) {
109
- fn(this.box.value);
110
- }
111
- else {
112
- this.subscribers.push(fn);
113
- }
114
- }
115
- static async update(stage, resource, deps, inputs, stateStore, options) {
116
- // const stack = resource[ResourceStack];
117
- const resourceID = resource[ResourceID];
118
- const resourceFQN = `${resource[Scope].getScopePath(stage)}/${resourceID}`;
119
- const cacheKey = resource[ResourceHandle];
120
- if (!applied.has(cacheKey)) {
121
- let res, rej;
122
- const promise = new Promise((resolve, reject) => {
123
- res = resolve;
124
- rej = reject;
125
- });
126
- applied.set(cacheKey, promise);
127
- update().then(res).catch(rej);
128
- return promise;
129
- }
130
- return await applied.get(cacheKey);
131
- async function update() {
132
- let resourceState = await stateStore.get(resourceID);
133
- if (resourceState === undefined) {
134
- resourceState = {
135
- provider: type,
136
- status: "creating",
137
- data: {},
138
- output: undefined,
139
- deps: [...deps],
140
- inputs,
141
- };
142
- await stateStore.set(resourceID, resourceState);
143
- }
144
- // Skip update if inputs haven't changed and resource is in a stable state
145
- if (resourceState.status === "created" ||
146
- resourceState.status === "updated") {
147
- if (JSON.stringify(resourceState.inputs) === JSON.stringify(inputs) &&
148
- !resource[Options].alwaysUpdate) {
149
- if (!options?.quiet) {
150
- console.log(`Skip: ${resourceFQN} (no changes)`);
151
- }
152
- if (resourceState.output !== undefined) {
153
- resource[Provide](resourceState.output);
154
- }
155
- return resourceState.output;
156
- }
157
- }
158
- const event = resourceState.status === "creating" ? "create" : "update";
159
- resourceState.status = event === "create" ? "creating" : "updating";
160
- resourceState.oldInputs = resourceState.inputs;
161
- resourceState.inputs = inputs;
162
- if (!options?.quiet) {
163
- console.log(`${event === "create" ? "Create" : "Update"}: ${resourceFQN}`);
164
- }
165
- await stateStore.set(resourceID, resourceState);
166
- let isReplaced = false;
167
- const quiet = options.quiet ?? false;
168
- const evaluated = await pushScope(resource[Scope], resourceID, async () => {
169
- const result = await func({
170
- stage,
171
- resourceID,
172
- resourceFQN,
173
- event,
174
- scope: getScope(),
175
- output: resourceState.output,
176
- replace: () => {
177
- if (isReplaced) {
178
- console.warn(`Resource ${type} ${resourceFQN} is already marked as REPLACE`);
179
- return;
180
- }
181
- isReplaced = true;
182
- deletions.push({
183
- id: resourceID,
184
- data: {
185
- ...resourceState.data,
186
- },
187
- inputs: inputs,
188
- });
189
- },
190
- get: (key) => resourceState.data[key],
191
- set: async (key, value) => {
192
- resourceState.data[key] = value;
193
- await stateStore.set(resourceID, resourceState);
194
- },
195
- delete: async (key) => {
196
- const value = resourceState.data[key];
197
- delete resourceState.data[key];
198
- await stateStore.set(resourceID, resourceState);
199
- return value;
200
- },
201
- quiet,
202
- }, ...inputs);
203
- if (result === undefined) {
204
- return undefined;
205
- }
206
- const evaluated = await apply(result, {
207
- stage,
208
- scope: getScope(),
209
- quiet,
210
- });
211
- return evaluated;
212
- });
213
- if (!options?.quiet) {
214
- console.log(`${event === "create" ? "Created" : "Updated"}: ${resourceFQN}`);
215
- }
216
- await stateStore.set(resourceID, {
217
- provider: type,
218
- data: resourceState.data,
219
- status: event === "create" ? "created" : "updated",
220
- output: evaluated,
221
- inputs,
222
- deps: [...deps],
223
- });
224
- if (evaluated !== undefined) {
225
- resource[Provide](evaluated);
226
- }
227
- return evaluated;
228
- }
229
- }
230
- static async delete(stage, scope, resourceID, state, inputs, options) {
231
- const { Scope } = await import("./scope");
232
- const resourceFQN = `${scope.getScopePath(stage)}/${resourceID}`;
233
- const nestedScope = new Scope(resourceID, scope);
234
- await alchemize({
235
- mode: "destroy",
236
- stage,
237
- scope: nestedScope,
238
- // TODO(sam): should use the appropriate state store
239
- stateStore: defaultStateStore,
240
- quiet: options.quiet,
241
- });
242
- if (!options?.quiet) {
243
- console.log(`Delete: ${resourceFQN}`);
244
- }
245
- await func({
246
- stage,
247
- scope,
248
- resourceID: resourceID,
249
- resourceFQN: resourceFQN,
250
- event: "delete",
251
- output: state.output,
252
- replace() {
253
- throw new Error("Cannot replace a resource that is being deleted");
254
- },
255
- get: (key) => {
256
- return state.data[key];
257
- },
258
- set: async (key, value) => {
259
- state.data[key] = value;
260
- },
261
- delete: async (key) => {
262
- const value = state.data[key];
263
- delete state.data[key];
264
- return value;
265
- },
266
- quiet: options.quiet ?? false,
267
- }, ...inputs);
268
- if (!options?.quiet) {
269
- console.log(`Deleted: ${resourceFQN}`);
270
- }
271
- }
272
- }
273
- providers.set(type, Resource);
274
- return Resource;
16
+ // get a sequence number (unique within the scope) for the resource
17
+ const seq = scope.seq();
18
+ const meta = {
19
+ Kind: type,
20
+ ID: resourceID,
21
+ FQN: scope.fqn(resourceID),
22
+ Seq: seq,
23
+ Scope: scope,
24
+ };
25
+ const promise = apply(meta, props, options);
26
+ const resource = Object.assign(promise, meta);
27
+ scope.resources.set(resourceID, resource);
28
+ return resource;
29
+ });
30
+ provider.type = type;
31
+ provider.handler = handler;
32
+ provider.options = options;
33
+ PROVIDERS.set(type, provider);
34
+ return provider;
275
35
  }
276
- //# sourceMappingURL=resource.js.map
package/lib/scope.d.ts CHANGED
@@ -1,18 +1,39 @@
1
- import type { Output } from "./output";
2
- import type { Provider, Resource } from "./resource";
1
+ import type { PendingResource, ResourceID } from "./resource";
2
+ import { type StateStore, type StateStoreType } from "./state";
3
+ export type ScopeOptions = {
4
+ appName?: string;
5
+ stage: string;
6
+ parent?: Scope;
7
+ scopeName?: string;
8
+ password?: string;
9
+ stateStore?: StateStoreType;
10
+ quiet?: boolean;
11
+ };
3
12
  export declare class Scope {
13
+ static get(): Scope | undefined;
14
+ static get current(): Scope;
15
+ readonly resources: Map<string, PendingResource>;
16
+ readonly appName: string | undefined;
17
+ readonly stage: string;
4
18
  readonly scopeName: string | null;
5
19
  readonly parent: Scope | undefined;
6
- readonly nodes: Map<string, {
7
- provider: Provider<any, any[], any>;
8
- resource: Resource<any, any>;
9
- }>;
10
- readonly callbacks: Output<void>[];
11
- constructor(scopeName?: string | null, parent?: Scope | undefined);
12
- getScopePath(root: string): string;
20
+ readonly password: string | undefined;
21
+ readonly state: StateStore;
22
+ readonly quiet: boolean;
23
+ constructor(options: ScopeOptions);
24
+ delete(resourceID: ResourceID): Promise<void>;
25
+ private _seq;
26
+ seq(): number;
27
+ get chain(): string[];
28
+ enter(): void;
29
+ init(): Promise<void>;
30
+ deinit(): Promise<void>;
31
+ fqn(resourceID: ResourceID): string;
32
+ finalize(): Promise<void>;
33
+ run<T>(fn: (scope: Scope) => Promise<T>): Promise<T>;
34
+ [Symbol.asyncDispose](): Promise<void>;
35
+ /**
36
+ * Returns a string representation of the scope.
37
+ */
38
+ toString(): string;
13
39
  }
14
- export declare const rootScope: Scope;
15
- export declare function getScope(): Scope;
16
- export declare function withScope<T>(scope: Scope, fn: () => T | Promise<T>): Promise<T>;
17
- export declare function pushScope<T>(parent: Scope, scopeName: string, fn: () => T | Promise<T>): Promise<T>;
18
- //# sourceMappingURL=scope.d.ts.map
package/lib/scope.js CHANGED
@@ -1,30 +1,85 @@
1
1
  import { AsyncLocalStorage } from "node:async_hooks";
2
- import path from "node:path";
2
+ import { FileSystemStateStore, } from "./state";
3
3
  const scopeStorage = new AsyncLocalStorage();
4
4
  export class Scope {
5
+ static get() {
6
+ return scopeStorage.getStore();
7
+ }
8
+ static get current() {
9
+ const scope = Scope.get();
10
+ if (!scope) {
11
+ throw new Error("Not running within an Alchemy Scope");
12
+ }
13
+ return scope;
14
+ }
15
+ resources = new Map();
16
+ appName;
17
+ stage;
5
18
  scopeName;
6
19
  parent;
7
- nodes = new Map();
8
- callbacks = [];
9
- constructor(scopeName = null, parent = undefined) {
10
- this.scopeName = scopeName;
11
- this.parent = parent;
12
- }
13
- getScopePath(root) {
14
- // First, compute the parent's scope path
15
- const parentPath = this.parent ? this.parent.getScopePath(root) : root;
16
- // Then join the current scope name (if any) onto the parent's path
17
- return this.scopeName ? path.join(parentPath, this.scopeName) : parentPath;
20
+ password;
21
+ state;
22
+ quiet;
23
+ constructor(options) {
24
+ this.appName = options.appName;
25
+ this.stage = options.stage;
26
+ this.scopeName = options.scopeName ?? null;
27
+ this.parent = options.parent ?? Scope.get();
28
+ this.quiet = options.quiet ?? this.parent?.quiet ?? false;
29
+ if (this.parent && !this.scopeName) {
30
+ throw new Error("Scope name is required when creating a child scope");
31
+ }
32
+ this.password = options.password;
33
+ this.state = new (options.stateStore ?? FileSystemStateStore)(this);
34
+ }
35
+ async delete(resourceID) {
36
+ await this.state.delete(resourceID);
37
+ this.resources.delete(resourceID);
38
+ }
39
+ _seq = 0;
40
+ seq() {
41
+ return this._seq++;
42
+ }
43
+ get chain() {
44
+ const thisScope = this.scopeName ? [this.scopeName] : [];
45
+ const app = this.appName ? [this.appName] : [];
46
+ if (this.parent) {
47
+ return [...this.parent.chain, ...thisScope];
48
+ }
49
+ else {
50
+ return [...app, this.stage, ...thisScope];
51
+ }
52
+ }
53
+ enter() {
54
+ scopeStorage.enterWith(this);
55
+ }
56
+ async init() {
57
+ await this.state.init?.();
58
+ }
59
+ async deinit() {
60
+ await this.state.deinit?.();
61
+ }
62
+ fqn(resourceID) {
63
+ return [...this.chain, resourceID].join("/");
64
+ }
65
+ async finalize() {
66
+ // TODO
67
+ }
68
+ async run(fn) {
69
+ return scopeStorage.run(this, () => fn(this));
70
+ }
71
+ [Symbol.asyncDispose]() {
72
+ return this.finalize();
73
+ }
74
+ /**
75
+ * Returns a string representation of the scope.
76
+ */
77
+ toString() {
78
+ return `Scope(
79
+ chain=${this.chain.join("/")},
80
+ resources=[${Array.from(this.resources.values())
81
+ .map((r) => r.ID)
82
+ .join(",\n ")}]
83
+ )`;
18
84
  }
19
85
  }
20
- export const rootScope = new Scope(null);
21
- export function getScope() {
22
- return scopeStorage.getStore() ?? rootScope;
23
- }
24
- export function withScope(scope, fn) {
25
- return scopeStorage.run(scope, async () => fn());
26
- }
27
- export function pushScope(parent, scopeName, fn) {
28
- return scopeStorage.run(new Scope(scopeName, parent), async () => fn());
29
- }
30
- //# sourceMappingURL=scope.js.map
package/lib/secret.d.ts CHANGED
@@ -1,12 +1,7 @@
1
- import type { Input } from "./input";
2
- import { type Output } from "./output";
3
- export declare function secret<S extends Input<string> | undefined>(unencrypted: S): S extends undefined ? never : S extends Output<string> ? Output<Secret> : Secret;
4
1
  export declare class Secret {
5
2
  readonly unencrypted: string;
6
3
  readonly type = "secret";
7
4
  constructor(unencrypted: string);
8
5
  }
9
- export declare function setSecretPassphrase(passphrase: string): void;
10
- export declare function getSecretPassphrase(): string;
11
- export declare function tryGetSecretPassphrase(): string | undefined;
12
- //# sourceMappingURL=secret.d.ts.map
6
+ export declare function isSecret(binding: any): binding is Secret;
7
+ export declare function secret<S extends string | undefined>(unencrypted: S): Secret;
package/lib/secret.js CHANGED
@@ -1,12 +1,3 @@
1
- import { AsyncLocalStorage } from "node:async_hooks";
2
- import { isOutput } from "./output";
3
- const passphraseContext = new AsyncLocalStorage();
4
- export function secret(unencrypted) {
5
- if (unencrypted === undefined) {
6
- throw new Error("Secret cannot be undefined");
7
- }
8
- return (isOutput(unencrypted) ? unencrypted.apply(secret) : new Secret(unencrypted));
9
- }
10
1
  export class Secret {
11
2
  unencrypted;
12
3
  type = "secret";
@@ -14,17 +5,13 @@ export class Secret {
14
5
  this.unencrypted = unencrypted;
15
6
  }
16
7
  }
17
- export function setSecretPassphrase(passphrase) {
18
- passphraseContext.enterWith(passphrase);
8
+ export function isSecret(binding) {
9
+ return (binding instanceof Secret ||
10
+ (typeof binding === "object" && binding.type === "secret_text"));
19
11
  }
20
- export function getSecretPassphrase() {
21
- const passphrase = passphraseContext.getStore();
22
- if (!passphrase) {
23
- throw new Error("No passphrase set");
12
+ export function secret(unencrypted) {
13
+ if (unencrypted === undefined) {
14
+ throw new Error("Secret cannot be undefined");
24
15
  }
25
- return passphrase;
26
- }
27
- export function tryGetSecretPassphrase() {
28
- return passphraseContext.getStore();
16
+ return new Secret(unencrypted);
29
17
  }
30
- //# sourceMappingURL=secret.js.map
package/lib/state.d.ts CHANGED
@@ -1,12 +1,17 @@
1
- export interface State {
1
+ import type { Resource, ResourceProps } from "./resource";
2
+ import type { Scope } from "./scope";
3
+ export interface State<Kind extends string = string, Props extends ResourceProps = ResourceProps, Out extends Resource = Resource> {
2
4
  status: `creating` | `created` | `updating` | `updated` | `deleting` | `deleted`;
3
- provider: string;
5
+ kind: Kind;
6
+ id: string;
7
+ fqn: string;
8
+ seq: number;
4
9
  data: Record<string, any>;
5
- deps: string[];
6
- inputs: any[];
7
- oldInputs?: any[];
8
- output: any;
10
+ props: Props;
11
+ oldProps?: Props;
12
+ output: Out;
9
13
  }
14
+ export type StateStoreType = new (scope: Scope) => StateStore;
10
15
  export interface StateStore {
11
16
  /** Initialize the state container if one is required */
12
17
  init?(): Promise<void>;
@@ -22,10 +27,10 @@ export interface StateStore {
22
27
  set(key: string, value: State): Promise<void>;
23
28
  delete(key: string): Promise<void>;
24
29
  }
25
- export type StateStoreType = new (dir: string) => StateStore;
26
30
  export declare class FileSystemStateStore implements StateStore {
27
- readonly path: string;
28
- constructor(dir: string);
31
+ readonly scope: Scope;
32
+ readonly dir: string;
33
+ constructor(scope: Scope);
29
34
  init(): Promise<void>;
30
35
  deinit(): Promise<void>;
31
36
  count(): Promise<number>;
@@ -37,4 +42,3 @@ export declare class FileSystemStateStore implements StateStore {
37
42
  getBatch(ids: string[]): Promise<Record<string, State>>;
38
43
  private getPath;
39
44
  }
40
- //# sourceMappingURL=state.d.ts.map