1import type * as Effect from "effect/Effect";
2import type { RuntimeContext } from "../../RuntimeContext.ts";
3import * as Binding from "./Binding.ts";
4import type { VersionMetadataBinding } from "./VersionMetadataBinding.ts";
5
6const TypeId = "Cloudflare.Workers.VersionMetadata" as const;
7type TypeId = typeof TypeId;
8
9/**
10 * Runtime value Cloudflare exposes for a `version_metadata` binding — the
11 * deployed Worker version's `id`, `tag`, and `timestamp`.
12 */
13export interface WorkerVersionMetadata {
14 readonly id: string;
15 readonly tag: string;
16 readonly timestamp: string;
17}
18
19/**
20 * Effect-native accessor for the version metadata. The env binding only exists
21 * at the *exec* phase on the deployed Worker, so reading it is deferred behind
22 * an Effect that requires {@link RuntimeContext}. Yield it inside a handler to
23 * obtain the {@link WorkerVersionMetadata}.
24 */
25export type VersionMetadataAccessor = Effect.Effect<
26 WorkerVersionMetadata,
27 never,
28 RuntimeContext
29>;
30
31/**
32 * A Cloudflare Workers Version Metadata binding — a Worker-only binding with no
33 * backing cloud resource. Cloudflare provides the deployed Worker version at
34 * runtime (`id`, `tag`, `timestamp`).
35 *
36 * `VersionMetadata` is a single value that is at once the `Binding.Service` tag,
37 * the callable that produces a {@link VersionMetadataBinding}, and the type.
38 * Declare it on a Worker's `env` (it flows through `InferEnv` →
39 * {@link WorkerVersionMetadata}) or `yield*` it inside an Effect-native Worker
40 * to attach the binding and obtain a deferred {@link VersionMetadataAccessor}.
41 *
42 * ### Effect-style Worker (recommended)
43 * **Example:** Read the deployed version from inside a handler
44 * ```typescript
45 * import * as Effect from "effect/Effect";
46 *
47 * Cloudflare.Worker(
48 * "VersionWorker",
49 * { main: import.meta.url },
50 * Effect.gen(function* () {
51 * // Attaches the binding to this Worker AND returns a deferred accessor.
52 * const versionMetadata = yield* Cloudflare.Workers.VersionMetadata();
53 *
54 * return {
55 * fetch: Effect.gen(function* () {
56 * const { id, tag, timestamp } = yield* versionMetadata;
57 * return Response.json({ id, tag, timestamp });
58 * }),
59 * };
60 * }).pipe(Effect.provide(Cloudflare.Workers.VersionMetadataBinding)),
61 * );
62 * ```
63 *
64 * ### Worker binding metadata
65 * **Example:** Example
66 * ```typescript
67 * export const Worker = Cloudflare.Worker("Worker", {
68 * main: "./src/worker.ts",
69 * env: { CF_VERSION_METADATA: Cloudflare.Workers.VersionMetadata() },
70 * });
71 *
72 * export type WorkerEnv = Cloudflare.InferEnv<typeof Worker>;
73 * // { CF_VERSION_METADATA: WorkerVersionMetadata }
74 * ```
75 *
76 * @see https://developers.cloudflare.com/workers/runtime-apis/bindings/version-metadata/
77 *
78 * @binding
79 * @product Workers
80 * @category Workers & Compute
81 */
82export interface VersionMetadata extends Binding.Service<
83 VersionMetadata,
84 TypeId,
85 VersionMetadataAccessor
86> {
87 /**
88 * @param name Binding name (logical id) — the `env` key it resolves to.
89 * @default "CF_VERSION_METADATA"
90 */
91 (name?: string): VersionMetadataBinding;
92}
93
94export const VersionMetadata = Binding.Service<VersionMetadata>({
95 id: TypeId,
96 defaultName: "CF_VERSION_METADATA",
97 toWorkerBinding: (binding) => ({
98 type: "version_metadata",
99 name: binding.name,
100 }),
101});
102
103export const isVersionMetadata = (
104 value: unknown,
105): value is VersionMetadataBinding =>
106 Binding.isBinding(value) && value.kind === TypeId;