import * as Bundle from "../Bundle/Bundle.ts"; import { Platform, type Main, type PlatformProps } from "../Platform.ts"; import * as Provider from "../Provider.ts"; import type { Resource } from "../Resource.ts"; import type { ServerHost } from "../Server/Process.ts"; import { Stack } from "../Stack.ts"; import type { Providers } from "./Providers.ts"; import { type FlyHostRuntimeContext } from "./hosted.ts"; export type UrlAuth = "public" | "sprite"; export type SpriteStatus = "cold" | "warm" | "running"; export interface SpriteProps extends PlatformProps { /** * Module entrypoint bundled with rolldown and written onto the Sprite. * Typically `import.meta.url`. A content-hash change updates in place. */ main: string; /** * Sprite name. Unique per organization. If omitted, a unique name is * generated from the stack, stage and logical ID. Changing it replaces * the Sprite. */ name?: string; /** * Who can hit {@link Sprite} `url`. `public` is open. `sprite` requires * Sprites auth. * * @default "public" */ urlAuth?: UrlAuth; /** * Port the hosted HTTP server listens on. Written to `PORT` and used * as the Sprite service `http_port`. * * @default 3000 */ port?: number; /** * Named export to load from `main`. * * @default "default" */ handler?: string; /** * Additional environment variables for the hosted process. Merged * after binding-injected `env`. */ env?: Record; /** * Bundler configuration for `main`: rolldown `input`/`output` * overrides plus pure-annotation options (`pure`). */ build?: Bundle.BundleConfig; } /** * Binding contract accepted by {@link Sprite} for injected env. */ export interface SpriteBinding { env?: Record; } export type Sprite = Resource<"Fly.Sprite", SpriteProps, { /** Fly Sprite id. */ spriteId: string; /** Physical Sprite name (unique per org). */ name: string; /** * Public Sprite URL (`https://{name}-….sprites.app`). */ url: string; /** Observed runtime status. Hibernates to `cold` when idle. */ status: SpriteStatus; /** Observed URL auth setting. */ urlAuth: UrlAuth; /** Organization slug, if the API returned one. */ orgSlug: string | undefined; /** Content hash of the bundled program. */ code: { hash: string; }; }, SpriteBinding, Providers>; export declare const isSprite: (value: unknown) => value is Sprite; export type SpriteServices = ServerHost; export type SpriteShape = Main; export type SpriteRuntimeContext = FlyHostRuntimeContext; /** * A Sprite is an Effect program running in a Fly.io Sprite. Sprites * are org-scoped Linux sandboxes. They hibernate when idle and wake * on demand. There is no parent {@link App}. Unlike a {@link Service}, * Alchemy does not build a Docker image. * * @see https://sprites.dev/api/sprites * * ### Declare a Sprite * A Sprite is a class. Props describe the sandbox. The Effect is the * program that runs on it. There is no parent App. * * `main: import.meta.url` is the bundle entrypoint. Alchemy bundles * this file with Rolldown, writes it onto the Sprite, and runs it as * a Sprite service on {@link port}. Auth is `FLY_API_TOKEN`. * * **Example:** Class + main * ```typescript * export default class Box extends Fly.Sprite()( * "Box", * { main: import.meta.url }, * Effect.gen(function* () { * return {}; * }), * ) {} * ``` * * ### Serve HTTP with fetch * Return `fetch` from the init Effect to boot an HTTP server. The * Sprite URL proxies to {@link port}. * * **Example:** Hello * ```typescript * export default class Box extends Fly.Sprite()( * "Box", * { main: import.meta.url }, * Effect.gen(function* () { * return { * fetch: Effect.succeed(HttpServerResponse.text("hello")), * }; * }), * ) {} * ``` * * ### The public URL * Yield the Sprite in the Stack. `box.url` is * `https://{name}-….sprites.app`. * * **Example:** Stack output * ```typescript * export default Alchemy.Stack( * "MyApp", * { providers: Fly.providers(), state: Alchemy.localState() }, * Effect.gen(function* () { * const box = yield* Box; * return { url: box.url }; * }), * ); * ``` * * ### URL auth * `urlAuth` is `public` or `sprite`. Default is `public` so `url` * answers without a Sprites token. * * **Example:** Sprite-auth URL * ```typescript * export default class Box extends Fly.Sprite()( * "Box", * { main: import.meta.url, urlAuth: "sprite" }, * Effect.gen(function* () { * return { * fetch: Effect.succeed(HttpServerResponse.text("hello")), * }; * }), * ) {} * ``` * * :::note[Default is public] * Fly's API default is `sprite`. Alchemy defaults to `public` so a * `fetch` handler is reachable. * ::: * * ### Config * Yield `Config` in init. Alchemy reads the value from the env of * whoever deploys and writes it onto the Sprite. Do not pass * `env: { ... }` on a Sprite. * * Yield `FileSystem.FileSystem` in init, never inside `fetch`. * * **Example:** Config.redacted * ```typescript * import * as Config from "effect/Config"; * import * as FileSystem from "effect/FileSystem"; * import * as Redacted from "effect/Redacted"; * * export default class Box extends Fly.Sprite()( * "Box", * { main: import.meta.url }, * Effect.gen(function* () { * const apiKey = yield* Config.redacted("API_KEY"); * const fs = yield* FileSystem.FileSystem; * yield* fs.makeDirectory("/tmp", { recursive: true }); * * return { * fetch: Effect.gen(function* () { * const token = Redacted.value(apiKey); * return HttpServerResponse.text("ok"); * }), * }; * }), * ) {} * ``` * * ### Exec * {@link Exec} runs a command on the Sprite. Provide {@link ExecHttp}. * * **Example:** ls * ```typescript * const exec = yield* Fly.Exec(Box); * const result = yield* exec({ cmd: ["ls", "-la"] }); * ``` * * ### Checkpoint * {@link Checkpoint} snapshots and restores the Sprite disk. Provide * {@link CheckpointHttp}. * * **Example:** Create and restore * ```typescript * const checkpoint = yield* Fly.Checkpoint(Box); * yield* checkpoint.create({ comment: "before" }); * yield* checkpoint.restore("v1"); * ``` * * :::caution[Restore is destructive] * The disk rewinds. Later writes are gone. * ::: * * ### A stable name * Omit `name` and Alchemy generates one from the stack, stage, and * logical ID. * * **Example:** Explicit name * ```typescript * export default class Box extends Fly.Sprite()( * "Box", * { main: import.meta.url, name: "box" }, * Effect.gen(function* () { * return { * fetch: Effect.succeed(HttpServerResponse.text("hello")), * }; * }), * ) {} * ``` * * :::caution[Changing `name` replaces the Sprite] * Fly cannot rename a Sprite. Alchemy creates the new name, then * deletes the old one. * ::: * * ### Named export * `handler` is the named export to load from `main`. Default is * `"default"`. * * **Example:** Custom handler * ```typescript * export default class Box extends Fly.Sprite()( * "Box", * { main: import.meta.url, handler: "box" }, * Effect.gen(function* () { * return { * fetch: Effect.succeed(HttpServerResponse.text("hello")), * }; * }), * ) {} * ``` * * @resource */ export declare const Sprite: Platform; declare const SpriteNotCreated_base: new = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & { readonly _tag: "Fly.SpriteNotCreated"; } & Readonly; export declare class SpriteNotCreated extends SpriteNotCreated_base<{ name: string; }> { } declare const SpriteExecFailed_base: new = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & { readonly _tag: "Fly.SpriteExecFailed"; } & Readonly; export declare class SpriteExecFailed extends SpriteExecFailed_base<{ name: string; cmd: string; exitCode: number | undefined; stderr: string | undefined; }> { } export declare const SpriteProvider: () => import("effect/Layer").Layer, never, import("effect/FileSystem").FileSystem | import("effect/Path").Path | Stack | import("../Stage.ts").Stage | import("@distilled.cloud/fly-io").FlyIoOpContext>; export {}; //# sourceMappingURL=Sprite.d.ts.map