effect-vercel 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -11,6 +11,7 @@ import * as AiGateway from "effect-vercel/AiGateway"
11
11
  | ------------------------------------ | ------------------------------------------------------------------------------- |
12
12
  | `effect-vercel/AiGateway` | [Vercel AI Gateway](https://vercel.com/docs/ai-gateway) for Effect AI |
13
13
  | `effect-vercel/AiGatewayCredentials` | How AI Gateway requests authenticate |
14
+ | `effect-vercel/Functions` | Bindings for `@vercel/functions`: `waitUntil` |
14
15
  | `effect-vercel/Oidc` | The OIDC token Vercel issues to a deployment |
15
16
  | `effect-vercel/RuntimeCache` | [Runtime Cache](https://vercel.com/docs/runtime-cache) for Effect `Persistence` |
16
17
 
@@ -19,7 +20,7 @@ import * as AiGateway from "effect-vercel/AiGateway"
19
20
  ```sh
20
21
  pnpm add effect-vercel effect
21
22
  pnpm add @effect/ai-anthropic # only for effect-vercel/AiGateway
22
- pnpm add @vercel/functions # only for effect-vercel/RuntimeCache
23
+ pnpm add @vercel/functions # only for effect-vercel/Functions and RuntimeCache
23
24
  ```
24
25
 
25
26
  ## AI Gateway
@@ -120,6 +121,29 @@ unset. It fails with an `OidcError` when neither is set.
120
121
  It reads the token on every run, since it rotates per request. It does not refresh an
121
122
  expired local token; re-run `vercel env pull`.
122
123
 
124
+ ## Functions
125
+
126
+ `Functions.waitUntil(fiber)` keeps the Function running until `fiber` ends, as `waitUntil`
127
+ from `@vercel/functions` does for a promise. Use it to finish work after the response is sent.
128
+
129
+ ```ts
130
+ import * as Functions from "effect-vercel/Functions"
131
+ import { Effect } from "effect"
132
+
133
+ const handler = Effect.gen(function* () {
134
+ yield* Effect.forkDetach(sendReceipt).pipe(Effect.tap(Functions.waitUntil))
135
+ return new Response("OK")
136
+ })
137
+ ```
138
+
139
+ - Fork with `Effect.forkDetach`, or into a scope that outlives the request. A child fiber is
140
+ interrupted when the request fiber ends, whatever `waitUntil` says.
141
+ - The fiber keeps the request's services. Acquire resources it needs inside its own
142
+ `Effect.scoped`, since the request's resources may be released first.
143
+ - Failures are not reported. Handle or log them in the forked effect.
144
+ - Vercel stops the work when the Function reaches its maximum duration.
145
+ - Off Vercel, `waitUntil` does nothing and the fiber runs in the background.
146
+
123
147
  ## Runtime Cache
124
148
 
125
149
  `RuntimeCache.layer` provides Effect's `Persistence`, stored in the Vercel Runtime Cache.
@@ -0,0 +1,14 @@
1
+ import { Effect, Fiber } from "effect";
2
+ //#region src/Functions.d.ts
3
+ /**
4
+ * Keeps the Function running until `fiber` ends, as `waitUntil` from
5
+ * `@vercel/functions` does for a promise. Off Vercel it does nothing.
6
+ *
7
+ * The fiber must outlive the request fiber, so fork it with
8
+ * `Effect.forkDetach` or into a scope that outlives the request. A child
9
+ * fiber is interrupted when the request fiber ends. Failures are not
10
+ * reported, so handle them in the forked effect.
11
+ */
12
+ export declare const waitUntil: <A, E>(fiber: Fiber.Fiber<A, E>) => Effect.Effect<void>;
13
+ //#endregion
14
+ //# sourceMappingURL=Functions.d.ts.map
@@ -0,0 +1,34 @@
1
+ import { Effect } from "effect";
2
+ import { waitUntil as waitUntil$1 } from "@vercel/functions";
3
+ //#region src/Functions.ts
4
+ /**
5
+ * Effect bindings for `@vercel/functions`.
6
+ *
7
+ * `waitUntil` keeps the Function running until a fiber ends, so work forked
8
+ * during a request can finish after the response is sent.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * import * as Functions from "effect-vercel/Functions"
13
+ * import { Effect } from "effect"
14
+ *
15
+ * const handler = Effect.gen(function* () {
16
+ * yield* Effect.forkDetach(sendReceipt).pipe(Effect.tap(Functions.waitUntil))
17
+ * return new Response("OK")
18
+ * })
19
+ * ```
20
+ */
21
+ /**
22
+ * Keeps the Function running until `fiber` ends, as `waitUntil` from
23
+ * `@vercel/functions` does for a promise. Off Vercel it does nothing.
24
+ *
25
+ * The fiber must outlive the request fiber, so fork it with
26
+ * `Effect.forkDetach` or into a scope that outlives the request. A child
27
+ * fiber is interrupted when the request fiber ends. Failures are not
28
+ * reported, so handle them in the forked effect.
29
+ */
30
+ const waitUntil = (fiber) => Effect.sync(() => waitUntil$1(new Promise((resolve) => fiber.addObserver(() => resolve()))));
31
+ //#endregion
32
+ export { waitUntil };
33
+
34
+ //# sourceMappingURL=Functions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Functions.js","names":["vercelWaitUntil"],"sources":["../src/Functions.ts"],"sourcesContent":["/**\n * Effect bindings for `@vercel/functions`.\n *\n * `waitUntil` keeps the Function running until a fiber ends, so work forked\n * during a request can finish after the response is sent.\n *\n * @example\n * ```ts\n * import * as Functions from \"effect-vercel/Functions\"\n * import { Effect } from \"effect\"\n *\n * const handler = Effect.gen(function* () {\n * yield* Effect.forkDetach(sendReceipt).pipe(Effect.tap(Functions.waitUntil))\n * return new Response(\"OK\")\n * })\n * ```\n */\n\nimport { waitUntil as vercelWaitUntil } from \"@vercel/functions\"\nimport { Effect, type Fiber } from \"effect\"\n\n/**\n * Keeps the Function running until `fiber` ends, as `waitUntil` from\n * `@vercel/functions` does for a promise. Off Vercel it does nothing.\n *\n * The fiber must outlive the request fiber, so fork it with\n * `Effect.forkDetach` or into a scope that outlives the request. A child\n * fiber is interrupted when the request fiber ends. Failures are not\n * reported, so handle them in the forked effect.\n */\nexport const waitUntil = <A, E>(fiber: Fiber.Fiber<A, E>): Effect.Effect<void> =>\n Effect.sync(() =>\n vercelWaitUntil(new Promise<void>((resolve) => fiber.addObserver(() => resolve()))),\n )\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BA,MAAa,aAAmB,UAC9B,OAAO,WACLA,YAAgB,IAAI,SAAe,YAAY,MAAM,kBAAkB,QAAQ,CAAC,CAAC,CAAC,CACpF"}
@@ -1,6 +1,6 @@
1
1
  import { Duration, Effect, Layer } from "effect";
2
- import { createHash } from "node:crypto";
3
2
  import { getCache } from "@vercel/functions";
3
+ import { createHash } from "node:crypto";
4
4
  import { Persistence } from "effect/persistence";
5
5
  //#region src/RuntimeCache.ts
6
6
  /**
package/package.json CHANGED
@@ -1,14 +1,15 @@
1
1
  {
2
2
  "name": "effect-vercel",
3
- "version": "0.4.0",
4
- "description": "Effect integrations for Vercel: AI Gateway, OIDC, Runtime Cache",
3
+ "version": "0.5.0",
4
+ "description": "Effect integrations for Vercel: AI Gateway, OIDC, Runtime Cache, waitUntil",
5
5
  "keywords": [
6
6
  "ai-gateway",
7
7
  "effect",
8
8
  "effect-ai",
9
9
  "oidc",
10
10
  "runtime-cache",
11
- "vercel"
11
+ "vercel",
12
+ "wait-until"
12
13
  ],
13
14
  "homepage": "https://github.com/duailibe/effect-vercel#readme",
14
15
  "bugs": {
@@ -29,6 +30,7 @@
29
30
  "exports": {
30
31
  "./AiGateway": "./dist/AiGateway.js",
31
32
  "./AiGatewayCredentials": "./dist/AiGatewayCredentials.js",
33
+ "./Functions": "./dist/Functions.js",
32
34
  "./Oidc": "./dist/Oidc.js",
33
35
  "./RuntimeCache": "./dist/RuntimeCache.js",
34
36
  "./package.json": "./package.json"
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Effect bindings for `@vercel/functions`.
3
+ *
4
+ * `waitUntil` keeps the Function running until a fiber ends, so work forked
5
+ * during a request can finish after the response is sent.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * import * as Functions from "effect-vercel/Functions"
10
+ * import { Effect } from "effect"
11
+ *
12
+ * const handler = Effect.gen(function* () {
13
+ * yield* Effect.forkDetach(sendReceipt).pipe(Effect.tap(Functions.waitUntil))
14
+ * return new Response("OK")
15
+ * })
16
+ * ```
17
+ */
18
+
19
+ import { waitUntil as vercelWaitUntil } from "@vercel/functions"
20
+ import { Effect, type Fiber } from "effect"
21
+
22
+ /**
23
+ * Keeps the Function running until `fiber` ends, as `waitUntil` from
24
+ * `@vercel/functions` does for a promise. Off Vercel it does nothing.
25
+ *
26
+ * The fiber must outlive the request fiber, so fork it with
27
+ * `Effect.forkDetach` or into a scope that outlives the request. A child
28
+ * fiber is interrupted when the request fiber ends. Failures are not
29
+ * reported, so handle them in the forked effect.
30
+ */
31
+ export const waitUntil = <A, E>(fiber: Fiber.Fiber<A, E>): Effect.Effect<void> =>
32
+ Effect.sync(() =>
33
+ vercelWaitUntil(new Promise<void>((resolve) => fiber.addObserver(() => resolve()))),
34
+ )