1import * as Effect from "effect/Effect";
2import * as Binding from "../../Binding.ts";
3import type { Output as OutputType } from "../../Output.ts";
4import type { ResourceLike } from "../../Resource.ts";
5import { isBindingHost } from "../Lambda/Function.ts";
6import { inFleetWiseRegion } from "./internal.ts";
7
8/**
9 * Shared scaffolding for AWS IoT FleetWise HTTP bindings.
10 *
11 * NOT exported from `index.ts` — every thin `{Op}Http.ts` in this service is
12 * a `Layer.effect(Cap, makeFleetWise…HttpBinding({ … }))` over one of the two
13 * builders below. Everything except the operation, the injected identifier,
14 * and the IAM action list + granted ARNs is boilerplate. Every runtime call
15 * is piped through {@link inFleetWiseRegion} because AWS IoT FleetWise is
16 * offered only in `us-east-1`/`eu-central-1` — a Lambda running anywhere else
17 * transparently calls the service's home region.
18 */
19
20/**
21 * Build the impl Effect for a FleetWise operation scoped to one resource
22 * (vehicle, fleet, campaign, or one of the manifests/catalogs): the
23 * deploy-time half grants `actions` on `resources`, and the runtime callable
24 * injects the bound resource's identifier as the `requestKey` field of every
25 * request.
26 */
27export const makeFleetWiseResourceHttpBinding = <
28 Res extends ResourceLike,
29 K extends string,
30 I extends { [P in K]: string },
31 A,
32 E,
33 R,
34>(options: {
35 /** Fully-qualified binding tag, e.g. `AWS.IoTFleetWise.GetVehicleStatus`. */
36 tag: string;
37 /** The distilled operation; the identifier is injected. */
38 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
39 /** IAM actions granted on `resources`. */
40 actions: readonly string[];
41 /** The request field the bound resource's identifier is injected as. */
42 requestKey: K;
43 /**
44 * Resolve the injected identifier from the bound resource. `Req` is pinned
45 * to `never` (resource attributes are `Output<string, never>`): the default
46 * `Output<string>` widens `Req` to `any`, which leaks into the per-resource
47 * Effect's requirements and breaks the binding contract's `never`.
48 */
49 identifier: (resource: Res) => OutputType<string, never>;
50 /** ARNs the actions are granted on. */
51 resources: (resource: Res) => (OutputType<string> | string)[];
52}) =>
53 Effect.gen(function* () {
54 // Capture the client in the FleetWise home region: yielding an
55 // operation captures its services (region, credentials, HTTP client)
56 // once, so the pin must wrap the capture, not the later calls.
57 const op = yield* inFleetWiseRegion(options.operation);
58
59 return Effect.fn(function* (resource: Res) {
60 const Identifier = yield* options.identifier(resource);
61 if (!globalThis.__ALCHEMY_RUNTIME__) {
62 const host = yield* Binding.Host;
63 if (isBindingHost(host)) {
64 yield* host.bind`Allow(${host}, ${options.tag}(${resource}))`({
65 policyStatements: [
66 {
67 Effect: "Allow",
68 Action: [...options.actions],
69 Resource: options.resources(resource),
70 },
71 ],
72 });
73 }
74 }
75 return Effect.fn(`${options.tag}(${resource.LogicalId})`)(function* (
76 request?: Omit<I, K>,
77 ) {
78 return yield* op({
79 ...request,
80 [options.requestKey]: yield* Identifier,
81 } as unknown as I);
82 });
83 });
84 });
85
86/**
87 * Build the impl Effect for an account-level FleetWise operation (no bound
88 * resource): the deploy-time half grants `actions` on `resources`
89 * (default `*` — batch vehicle operations authorize on vehicle ARNs that
90 * only exist at runtime), and the runtime callable passes the caller's
91 * request through unchanged.
92 */
93export const makeFleetWiseAccountHttpBinding = <I, A, E, R>(options: {
94 /** Fully-qualified binding tag, e.g. `AWS.IoTFleetWise.ListVehicles`. */
95 tag: string;
96 /** The distilled operation, invoked with the caller's request as-is. */
97 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
98 /** IAM actions granted on `resources`. */
99 actions: readonly string[];
100 /**
101 * ARN patterns the actions are granted on.
102 * @default ["*"]
103 */
104 resources?: readonly string[];
105}) =>
106 Effect.gen(function* () {
107 // See makeFleetWiseResourceHttpBinding: the region pin wraps the
108 // service capture, not the later calls.
109 const op = yield* inFleetWiseRegion(options.operation);
110
111 return Effect.fn(function* () {
112 if (!globalThis.__ALCHEMY_RUNTIME__) {
113 const host = yield* Binding.Host;
114 if (isBindingHost(host)) {
115 yield* host.bind`Allow(${host}, ${options.tag}())`({
116 policyStatements: [
117 {
118 Effect: "Allow",
119 Action: [...options.actions],
120 Resource: [...(options.resources ?? ["*"])],
121 },
122 ],
123 });
124 }
125 }
126 return Effect.fn(options.tag)(function* (request?: I) {
127 return yield* op((request ?? {}) as I);
128 });
129 });
130 });