1import * as Effect from "effect/Effect";
2import type { Input } from "../../Input.ts";
3import * as Output from "../../Output.ts";
4import { sha256 } from "../../Util/sha256.ts";
5
6/**
7 * Lift a script-name input (a literal, an Output, or a Config/Effect-valued
8 * name) into an Output so it can feed plan-time derivations.
9 *
10 * @internal
11 */
12export const asScriptNameOutput = (
13 scriptName: Input<string>,
14): Output.Output<string> =>
15 Output.asOutput(
16 (Effect.isEffect(scriptName)
17 ? scriptName.pipe(Effect.orDie)
18 : scriptName) as string | Output.Output<string> | Effect.Effect<string>,
19 );
20
21/**
22 * Derive an account-global Workflow name from a resolved host Worker name and
23 * exported class.
24 *
25 * @internal
26 */
27export const generateWorkflowName = Effect.fn(function* (
28 scriptName: string,
29 className: string,
30) {
31 const base = `${scriptName}-${className}`
32 .toLowerCase()
33 .replaceAll(/[^a-z0-9-]/g, "-");
34 const hash = yield* sha256(base);
35 const suffix = `-${hash.slice(0, 8)}`;
36 // Trim trailing dashes left by mid-name truncation so the result never
37 // contains a `--` seam.
38 const head = base.slice(0, 64 - suffix.length).replace(/-+$/, "");
39 return `${head}${suffix}`;
40});
41
42/**
43 * Derive an account-global Workflow name when the host Worker name may still
44 * be an unresolved input.
45 *
46 * @internal
47 */
48export const makeWorkflowName = (
49 scriptName: Input<string>,
50 className: string,
51): Output.Output<string> =>
52 asScriptNameOutput(scriptName).pipe(
53 Output.mapEffect((scriptName) =>
54 generateWorkflowName(scriptName, className),
55 ),
56 );