1import * as Output from "./Output.ts";
2import type { ResourceLike } from "./Resource.ts";
3
4// special runtime-only symbol for probing the Ref proxy for its metadata
5const RefMetadata = Symbol.for("alchemy/RefMetadata");
6
7export const isRef = (s: any): s is Ref<any> =>
8 s && s[RefMetadata] !== undefined;
9
10export const getRefMetadata = <R extends ResourceLike>(
11 ref: Ref<R>,
12): RefMetadata<R> => (ref as any)[RefMetadata];
13
14export interface Ref<R extends ResourceLike = ResourceLike> {
15 /** @internal phantom */
16 Ref: R;
17}
18
19export 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
33export 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};