import * as Effect from "effect/Effect";
import * as Schedule from "effect/Schedule";
/**
* Derives a deterministic Service Catalog idempotency token from the
* resource's instance ID so retried creates never double-provision.
* Tokens must match `[a-zA-Z0-9][a-zA-Z0-9_-]*` and be ≤ 128 chars.
*/
export const idempotencyToken = (instanceId: string): string =>
instanceId.replaceAll(/[^a-zA-Z0-9]/g, "").slice(0, 64) || "alchemy";
/**
* Retries an effect while Service Catalog reports the resource as in use
* (`ResourceInUseException`) on a bounded schedule (~30s). Deleting a
* portfolio or product right after its associations were removed can
* transiently hit this due to eventual consistency.
*
* NOTE: extracted to module scope with an explicit return annotation —
* inlining `Effect.retry` in a provider lifecycle op leaves `Retry.Return`'s
* conditional type unresolved in declaration emit, which widens the
* provider's layer to an `unknown` R and poisons `AWS.providers()` for
* every downstream consumer.
*/
export const retryWhileResourceInUse = (
self: Effect.Effect,
): Effect.Effect =>
Effect.retry(self, {
while: (e) => e._tag === "ResourceInUseException",
schedule: Schedule.max([Schedule.fixed("2 seconds"), Schedule.recurs(10)]),
});
/**
* Polls a boolean check on a bounded fixed schedule until it observes
* `true` (or gives up silently after `attempts`). Service Catalog
* association lists are eventually consistent, so a freshly-created
* association may take a few seconds to appear.
*/
export const awaitVisible = (
check: Effect.Effect,
attempts = 10,
): Effect.Effect =>
Effect.gen(function* () {
for (let i = 0; i < attempts; i++) {
if (yield* check) return;
yield* Effect.sleep("2 seconds");
}
});