| 1 | import * as Output from "./Output.ts"; |
| 2 | import type { ResourceLike } from "./Resource.ts"; |
| 3 | |
| 4 | // special runtime-only symbol for probing the Ref proxy for its metadata |
| 5 | const RefMetadata = Symbol.for("alchemy/RefMetadata"); |
| 6 | |
| 7 | export const isRef = (s: any): s is Ref<any> => |
| 8 | s && s[RefMetadata] !== undefined; |
| 9 | |
| 10 | export const getRefMetadata = <R extends ResourceLike>( |
| 11 | ref: Ref<R>, |
| 12 | ): RefMetadata<R> => (ref as any)[RefMetadata]; |
| 13 | |
| 14 | export interface Ref<R extends ResourceLike = ResourceLike> { |
| 15 | /** @internal phantom */ |
| 16 | Ref: R; |
| 17 | } |
| 18 | |
| 19 | export interface RefMetadata<R extends ResourceLike> { |
| 20 | id: R["LogicalId"]; |
| 21 | stack?: string; |
| 22 | stage?: string; |
| 23 | /** |
| 24 | * The resource type of the ref's target (e.g. |
| 25 | * `"Cloudflare.KV.Namespace"`). Known statically — `MyResource.ref` |
| 26 | * carries its own type — so duck-typing classifiers (Worker env |
| 27 | * bindings, capability helpers) that read `.Type` can identify a ref |
| 28 | * exactly like a locally-declared resource. |
| 29 | */ |
| 30 | type?: string; |
| 31 | } |
| 32 | |
| 33 | export const ref = <R extends ResourceLike>( |
| 34 | id: string, |
| 35 | { |
| 36 | stack, |
| 37 | stage, |
| 38 | }: { |
| 39 | stack?: string; |
| 40 | stage?: string; |
| 41 | } = {}, |
| 42 | type?: string, |
| 43 | ): Ref<R> => { |
| 44 | const ref = new Proxy( |
| 45 | {}, |
| 46 | { |
| 47 | get: (_, prop) => { |
| 48 | if (prop === RefMetadata) { |
| 49 | return { |
| 50 | stack, |
| 51 | stage, |
| 52 | id, |
| 53 | type, |
| 54 | } satisfies RefMetadata<R>; |
| 55 | } |
| 56 | return (Output.of(ref) as any)[prop]; |
| 57 | }, |
| 58 | }, |
| 59 | ) as Ref<R>; |
| 60 | return ref; |
| 61 | }; |