1/**
2 * `@alchemy.run/frontend-frameworks/octane` — OctaneJS integration implementing
3 * framework-core's `Framework` service, with the deploy target passed as a
4 * value (Cloudflare Workers by default, via the
5 * `@alchemy.run/frontend-frameworks/octane/cloudflare` subpath module).
6 *
7 * Octane wraps Vite: the project's own `vite build` (with
8 * `@octanejs/vite-plugin` in its `vite.config.ts` and `adapter:
9 * cloudflare()` in its `octane.config.ts`) already produces the deployable
10 * output — client assets in `dist/client` and a self-contained Worker entry
11 * at `dist/server/worker.js`. This package just drives that build
12 * programmatically and maps the on-disk output onto the `BuildOutput`
13 * contract; there are no adapter forks and no bundler-plugin injection.
14 *
15 * The default export is the e2e-harness factory contract: a
16 * `(options) => Layer<Framework>` function that reads the harness's
17 * cloudflare-target configuration (compatibility date/flags). Use
18 * {@link layer} directly for the fully-typed path with Octane-specific
19 * options.
20 *
21 * This module (and `Octane.ts`) is target-agnostic by contract: it must not
22 * import anything Cloudflare-specific. The Cloudflare half — the adapter
23 * validation constants — lives behind `@alchemy.run/frontend-frameworks/octane/cloudflare`.
24 */
25import type { Framework } from "../core/index.ts";
26import type * as FileSystem from "effect/FileSystem";
27import type * as Layer from "effect/Layer";
28import type * as Path from "effect/Path";
29import { layer, type OctaneOptions } from "./Octane.ts";
30
31export {
32 DEFAULT_TARGET_SPECIFIER,
33 layer,
34 make,
35 OCTANE_VITE_PLUGIN_SPECIFIER,
36 readOctaneOutput,
37 type OctaneOptions,
38 type OctaneOutputDirs,
39 type OctaneTarget,
40 type OctaneTargetConfig,
41 type OctaneTargetInput,
42 type OctaneViteDevServer,
43 type OctaneViteModule,
44 type OctaneVitePluginModule,
45 type ResolvedOctaneConfigSlice,
46} from "./Octane.ts";
47
48/**
49 * The structural subset of the e2e harness's cloudflare worker options this
50 * package reads (`CloudflareVitePluginOptions`). Typed structurally so the
51 * package does not depend on the harness.
52 */
53export interface HarnessWorkerOptions {
54 readonly compatibilityDate?: string | undefined;
55 readonly compatibilityFlags?: Array<string> | undefined;
56}
57
58/**
59 * The structural subset of the e2e harness's `Options` this package reads.
60 * The harness carries cloudflare configuration target-scoped
61 * (`target.cloudflare.worker`); the top-level `vite` field is the harness's
62 * deprecated alias for the same shape (target-scoped wins).
63 */
64export interface HarnessOptions {
65 readonly target?:
66 | {
67 readonly cloudflare?:
68 | {
69 readonly worker?: HarnessWorkerOptions | undefined;
70 }
71 | undefined;
72 }
73 | undefined;
74 /** @deprecated Harness alias for `target.cloudflare.worker`. */
75 readonly vite?: HarnessWorkerOptions | undefined;
76}
77
78/**
79 * Map the harness's options onto {@link OctaneOptions}, preferring the
80 * target-scoped carriage (`target.cloudflare.worker`) over the deprecated
81 * top-level `vite` alias.
82 */
83export const fromHarnessOptions = (options: HarnessOptions): OctaneOptions => {
84 const worker = options.target?.cloudflare?.worker ?? options.vite;
85 return {
86 compatibilityDate: worker?.compatibilityDate,
87 compatibilityFlags: worker?.compatibilityFlags,
88 };
89};
90
91/**
92 * The e2e-harness factory contract (`framework: "@alchemy.run/frontend-frameworks/octane"`
93 * in `e2e.config.ts`).
94 */
95const factory = (
96 options: HarnessOptions,
97): Layer.Layer<Framework, never, FileSystem.FileSystem | Path.Path> =>
98 layer(fromHarnessOptions(options));
99
100export default factory;