1import type * as Redacted from "effect/Redacted";
2import type { BundleConfig } from "../Bundle/Bundle.ts";
3import { Platform, type Main, type PlatformProps } from "../Platform.ts";
4import type { Resource } from "../Resource.ts";
5import type { BranchScope } from "./BranchScope.ts";
6import type {
7 FunctionEnvironment,
8 FunctionRequest,
9} from "./FunctionEnvironment.ts";
10import {
11 makeFunctionRuntimeContext,
12 type FunctionRuntimeContext,
13} from "./FunctionRuntimeContext.ts";
14import type { Providers } from "./Providers.ts";
15
16/**
17 * Prebuilt Node 24 Fetch application. The directory or ZIP must contain
18 * `index.mjs` at its root, exporting a Fetch handler or an object with `fetch`.
19 * Include all runtime dependencies; Alchemy does not bundle prebuilt artifacts.
20 * Archives must not contain symlinks, traversal paths, or deployment secrets.
21 */
22export type FunctionArtifact =
23 | {
24 /** Directory packaged into a deterministic ZIP. */ directory: string;
25 zip?: never;
26 }
27 | { /** Path to an existing ZIP archive. */ zip: string; directory?: never };
28
29export interface FunctionCommonProps extends PlatformProps {
30 /** Immutable lowercase alphanumeric identifier, at most 20 characters. Generated when omitted. */
31 slug?: string;
32 /** Mutable display name. Removing it restores the slug. */
33 name?: string;
34 /** Write-only values. Removing a managed key sends an empty deletion value. Platform-injected keys cannot be overridden. */
35 env?: Record<string, string | Redacted.Redacted<string> | undefined>;
36 /** Node-targeted bundler options; not used with prebuilt artifacts. */
37 bundle?: BundleConfig;
38 /** Local Neon CLI executable (version 2.45.0 or newer) and optional listening port. */
39 dev?: { command?: string; port?: number };
40}
41
42export type FunctionProps = BranchScope &
43 FunctionCommonProps &
44 (
45 | {
46 /** Native Fetch or Effect entry module. */ main: string;
47 artifact?: never;
48 }
49 | {
50 /** Prebuilt Fetch application, mutually exclusive with main. */ artifact: FunctionArtifact;
51 main?: never;
52 }
53 );
54
55export interface FunctionAttributes {
56 /** Owning project. */ projectId: string;
57 /** Owning branch. */ branchId: string;
58 /** Stable function ID. */ functionId: string;
59 /** Immutable invocation slug. */ slug: string;
60 /** Observed display name. */ name: string;
61 /** Public invocation URL. Authenticate callers in the handler. */ url: string;
62 /** Latest deployment identifier. */ currentDeploymentId: number | undefined;
63 /** Deployment currently serving traffic. */ activeDeploymentId:
64 | number
65 | undefined;
66 /** Latest deployment build status. */ status: string | undefined;
67 /** Digest of the last successfully applied artifact, not a remote code attestation. */ codeHash:
68 | string
69 | undefined;
70 /** Last applied environment digest, not proof of remote write-only value equality. */ environmentHash?: string;
71 /** Previously managed environment names. Values cannot be read back. */ environment: string[];
72}
73
74export interface FunctionBinding {
75 /** Namespaced application configuration; conflicts with user values are rejected. */
76 env?: Record<string, string | Redacted.Redacted<string> | undefined>;
77}
78
79export interface Function extends Resource<
80 "Neon.Function",
81 FunctionProps,
82 FunctionAttributes,
83 FunctionBinding,
84 Providers
85> {}
86
87/**
88 * Node.js 24 Fetch function, with native and Effect entrypoints.
89 *
90 * Functions have public URLs. The `functions:invoke` credential scope does not
91 * establish a private invocation policy. Authenticate requests in your handler.
92 * Native same-branch database/storage credentials are injected by Neon; a typed
93 * read-only binding does not restrict the credentials available to the process.
94 * Explicit adoption takes ownership of user-defined environment names: existing
95 * keys omitted from `env` and bindings are removed. Values are write-only, so
96 * cached digests cannot detect out-of-band value drift.
97 *
98 * ### Streaming disconnects <!-- api-prose -->
99 *
100 * Neon’s production host currently does not reliably propagate client disconnects
101 * to uncompressed response streams. An abandoned stream can retain its producer
102 * and request-scoped resources until it finishes. Prefer finite responses and
103 * application-bounded streams; do not rely on disconnect cleanup for long-lived
104 * streaming workloads until [the upstream issue](https://github.com/neondatabase/neon-pkgs/issues/636)
105 * is resolved. Alchemy propagates abort and body-cancel signals when the host supplies them.
106 *
107 * ### Native Fetch
108 * **Example:** Bundle a native object, bare handler, or Hono application
109 * ```typescript
110 * const api = yield* Neon.Function("Api", { branch, main: "./src/api.ts" });
111 * ```
112 *
113 * ### Effect Fetch
114 * **Example:** Class entrypoint
115 * ```typescript
116 * export default class Api extends Neon.Function<Api>()(
117 * "Api", { branch, main: import.meta.url },
118 * Effect.succeed({ fetch: Effect.succeed(HttpServerResponse.text("ok")) }),
119 * ) {}
120 * ```
121 *
122 * ### Prebuilt Applications
123 * **Example:** Deploy Website build output without rebundling
124 * ```typescript
125 * const site = yield* Neon.Function("Site", { branch, artifact: { directory: "./dist" } });
126 * ```
127 *
128 * @resource
129 * @product Function
130 */
131export const Function: Platform<
132 Function,
133 FunctionEnvironment,
134 Main<FunctionEnvironment | FunctionRequest>,
135 FunctionRuntimeContext
136> = Platform<Function>("Neon.Function", {
137 createRuntimeContext: makeFunctionRuntimeContext,
138});