1import * as Effect from "effect/Effect";
2import * as Layer from "effect/Layer";
3import type * as HttpClient from "effect/http/HttpClient";
4import { CloudflareEnvironment } from "../CloudflareEnvironment.ts";
5import type { Credentials } from "../Credentials.ts";
6import type { Queue } from "./Queue.ts";
7import { makeWriteQueueHttpClient } from "./WriteQueueHttp.ts";
8import { WriteQueue } from "./WriteQueue.ts";
9
10/**
11 * Local implementation of the {@link WriteQueue} binding — pushes messages
12 * to a Cloudflare Queue over the bulk-push HTTP API using the **current
13 * credentials** instead of a native Worker binding
14 * ({@link WriteQueueBinding}) or a scoped API token ({@link WriteQueueHttp}).
15 *
16 * Provide it on an {@link Action} (or any deploy-time Effect) so you can send
17 * messages to a queue with the same `send`/`sendBatch` client you'd use inside
18 * a Worker — no Worker host and no minted token.
19 *
20 * @example Seeding a queue from an Action
21 * ```typescript
22 * const Seed = Alchemy.Action(
23 * "Seed",
24 * Effect.gen(function* () {
25 * const q = yield* Cloudflare.Queues.WriteQueue(queue);
26 * return Effect.fn(function* () {
27 * yield* q.send({ text: "hi", sentAt: Date.now() });
28 * yield* q.sendBatch([{ body: { event: "click", id: 1 } }]);
29 * });
30 * }).pipe(Effect.provide(Cloudflare.Queues.WriteQueueLocal)),
31 * );
32 * ```
33 *
34 * The queue id is resolved at apply time through the ambient
35 * {@link RuntimeContext} (in an Action, that's the resolve context the engine
36 * provides around the body), so `WriteQueue(queue)` works even though the
37 * queue is created in the same deploy.
38 */
39export const WriteQueueLocal = Layer.effect(
40 WriteQueue,
41 Effect.gen(function* () {
42 // Account + credentials are ambient during stack-eval (the stack's
43 // providers layer). Capture the full context so the bulk-push effect can
44 // run with the current credentials instead of a scoped token.
45 const { accountId } = yield* yield* CloudflareEnvironment;
46 const context = yield* Effect.context<
47 Credentials | HttpClient.HttpClient
48 >();
49
50 return Effect.fn(function* (queue: Queue) {
51 // Deferred accessor — resolves the queueId against the tracker at apply
52 // time. No `host.bind`: the local variant registers no binding.
53 const queueId = yield* queue.queueId;
54
55 return makeWriteQueueHttpClient(
56 {
57 authorize: (eff) => eff.pipe(Effect.provideContext(context)),
58 accountId: Effect.succeed(accountId),
59 },
60 queueId,
61 );
62 });
63 }),
64);