1import type * as NFW from "@distilled.cloud/aws/network-firewall";
2import * as Effect from "effect/Effect";
3import * as Schedule from "effect/Schedule";
4
5/** Convert a Network Firewall `Tag[]` list into a plain record. */
6export const nfwTagsToRecord = (
7 tags: readonly NFW.Tag[] | undefined,
8): Record<string, string> =>
9 Object.fromEntries((tags ?? []).map((t) => [t.Key, t.Value]));
10
11/** Convert a plain record into a Network Firewall `Tag[]` list. */
12export const recordToNfwTags = (record: Record<string, string>): NFW.Tag[] =>
13 Object.entries(record).map(([Key, Value]) => ({ Key, Value }));
14
15// Explicitly-typed pipeable retry helpers. Inlining `Effect.retry` in a
16// provider lifecycle op leaks `Retry.Return`'s conditional into declaration
17// emit and widens the provider layer to `unknown` R for every consumer of
18// `AWS.providers()`.
19
20/**
21 * Network Firewall rejects deletion of a rule group / firewall policy that is
22 * still referenced (or was referenced moments ago) with
23 * `InvalidOperationException`. Dependents are deleted first by the engine,
24 * but the service releases the reference asynchronously — retry through a
25 * short window.
26 */
27export const retryWhileNfwInUse = <A, E extends { readonly _tag: string }, R>(
28 self: Effect.Effect<A, E, R>,
29): Effect.Effect<A, E, R> =>
30 Effect.retry(self, {
31 while: (e) => e._tag === "InvalidOperationException",
32 schedule: Schedule.max([Schedule.fixed("5 seconds"), Schedule.recurs(12)]),
33 });
34
35/**
36 * A freshly-created rule group / policy can transiently return
37 * `ResourceNotFoundException` from a follow-up describe. Bounded retry
38 * through the eventual-consistency window.
39 */
40export const retryWhileNfwNotFound = <
41 A,
42 E extends { readonly _tag: string },
43 R,
44>(
45 self: Effect.Effect<A, E, R>,
46): Effect.Effect<A, E, R> =>
47 Effect.retry(self, {
48 while: (e) => e._tag === "ResourceNotFoundException",
49 schedule: Schedule.max([Schedule.fixed("2 seconds"), Schedule.recurs(10)]),
50 });