honertia 0.1.37 → 0.1.40
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 +421 -9
- package/dist/cache.d.ts +76 -7
- package/dist/cache.d.ts.map +1 -1
- package/dist/cache.js +139 -21
- package/dist/effect/auth.d.ts +2 -2
- package/dist/effect/auth.d.ts.map +1 -1
- package/dist/effect/bridge.d.ts +2 -2
- package/dist/effect/bridge.d.ts.map +1 -1
- package/dist/effect/bridge.js +43 -2
- package/dist/effect/index.d.ts +2 -2
- package/dist/effect/index.d.ts.map +1 -1
- package/dist/effect/index.js +1 -1
- package/dist/effect/services.d.ts +64 -0
- package/dist/effect/services.d.ts.map +1 -1
- package/dist/effect/services.js +20 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -824,7 +824,7 @@ export const listProjects = action(
|
|
|
824
824
|
})
|
|
825
825
|
),
|
|
826
826
|
S.Array(ProjectSchema),
|
|
827
|
-
Duration.minutes(5)
|
|
827
|
+
{ ttl: Duration.minutes(5) }
|
|
828
828
|
)
|
|
829
829
|
|
|
830
830
|
return yield* render('Projects/Index', { projects: userProjects })
|
|
@@ -1676,7 +1676,7 @@ export const listProjects = action(
|
|
|
1676
1676
|
catch: (error) => new Error(String(error)),
|
|
1677
1677
|
}),
|
|
1678
1678
|
S.Array(ProjectSchema),
|
|
1679
|
-
Duration.minutes(5)
|
|
1679
|
+
{ ttl: Duration.minutes(5) }
|
|
1680
1680
|
)
|
|
1681
1681
|
|
|
1682
1682
|
return yield* render('Projects/Index', { projects: userProjects })
|
|
@@ -1688,12 +1688,19 @@ export const listProjects = action(
|
|
|
1688
1688
|
|
|
1689
1689
|
| Function | Description |
|
|
1690
1690
|
|----------|-------------|
|
|
1691
|
-
| `cache(key, compute, schema,
|
|
1691
|
+
| `cache(key, compute, schema, options)` | Get from cache or compute and store |
|
|
1692
1692
|
| `cacheGet(key, schema)` | Get value from cache (returns `Option`) |
|
|
1693
|
-
| `cacheSet(key, value, schema,
|
|
1693
|
+
| `cacheSet(key, value, schema, options)` | Store value in cache |
|
|
1694
1694
|
| `cacheInvalidate(key)` | Delete a single cache key |
|
|
1695
1695
|
| `cacheInvalidatePrefix(prefix)` | Delete all keys with prefix |
|
|
1696
1696
|
|
|
1697
|
+
The `options` parameter is an object with the following properties:
|
|
1698
|
+
|
|
1699
|
+
| Property | Type | Description |
|
|
1700
|
+
|----------|------|-------------|
|
|
1701
|
+
| `ttl` | `Duration.DurationInput` | Time-to-live for cached values (required) |
|
|
1702
|
+
| `swr` | `Duration.DurationInput` | Stale-while-revalidate window (optional) |
|
|
1703
|
+
|
|
1697
1704
|
### Cache Invalidation
|
|
1698
1705
|
|
|
1699
1706
|
Invalidate cache when data changes:
|
|
@@ -1783,7 +1790,7 @@ if (Option.isSome(cached)) {
|
|
|
1783
1790
|
const user = yield* fetchUser(id)
|
|
1784
1791
|
|
|
1785
1792
|
// Store in cache
|
|
1786
|
-
yield* cacheSet(`user:${id}`, user, UserSchema, Duration.hours(1))
|
|
1793
|
+
yield* cacheSet(`user:${id}`, user, UserSchema, { ttl: Duration.hours(1) })
|
|
1787
1794
|
|
|
1788
1795
|
return user
|
|
1789
1796
|
```
|
|
@@ -1921,8 +1928,8 @@ describe('cache', () => {
|
|
|
1921
1928
|
return { id: '1', name: 'Test' }
|
|
1922
1929
|
})
|
|
1923
1930
|
|
|
1924
|
-
const first = yield* cache('test:1', compute, TestSchema, Duration.hours(1))
|
|
1925
|
-
const second = yield* cache('test:1', compute, TestSchema, Duration.hours(1))
|
|
1931
|
+
const first = yield* cache('test:1', compute, TestSchema, { ttl: Duration.hours(1) })
|
|
1932
|
+
const second = yield* cache('test:1', compute, TestSchema, { ttl: Duration.hours(1) })
|
|
1926
1933
|
|
|
1927
1934
|
expect(first).toEqual(second)
|
|
1928
1935
|
expect(callCount).toBe(1) // Only computed once
|
|
@@ -1937,9 +1944,9 @@ describe('cache', () => {
|
|
|
1937
1944
|
return { id: '1', name: `Call ${callCount}` }
|
|
1938
1945
|
})
|
|
1939
1946
|
|
|
1940
|
-
yield* cache('test:1', compute, TestSchema, Duration.hours(1))
|
|
1947
|
+
yield* cache('test:1', compute, TestSchema, { ttl: Duration.hours(1) })
|
|
1941
1948
|
yield* cacheInvalidate('test:1')
|
|
1942
|
-
yield* cache('test:1', compute, TestSchema, Duration.hours(1))
|
|
1949
|
+
yield* cache('test:1', compute, TestSchema, { ttl: Duration.hours(1) })
|
|
1943
1950
|
|
|
1944
1951
|
expect(callCount).toBe(2) // Computed twice
|
|
1945
1952
|
}).pipe(Effect.provide(makeTestCache()), Effect.runPromise))
|
|
@@ -1979,6 +1986,410 @@ Recommended cache key patterns:
|
|
|
1979
1986
|
`api:exchange:${currency}`
|
|
1980
1987
|
```
|
|
1981
1988
|
|
|
1989
|
+
### Stale-While-Revalidate (SWR)
|
|
1990
|
+
|
|
1991
|
+
The cache supports the stale-while-revalidate pattern for improved latency and resilience. When enabled, stale values are returned immediately while a background refresh is triggered.
|
|
1992
|
+
|
|
1993
|
+
```typescript
|
|
1994
|
+
import { Effect, Duration } from 'effect'
|
|
1995
|
+
import { cache, DatabaseService } from 'honertia/effect'
|
|
1996
|
+
|
|
1997
|
+
const UserSchema = S.Struct({
|
|
1998
|
+
id: S.String,
|
|
1999
|
+
name: S.String,
|
|
2000
|
+
email: S.String,
|
|
2001
|
+
})
|
|
2002
|
+
|
|
2003
|
+
// Basic usage with TTL only
|
|
2004
|
+
const user = yield* cache(
|
|
2005
|
+
`user:${id}`,
|
|
2006
|
+
fetchUser(id),
|
|
2007
|
+
UserSchema,
|
|
2008
|
+
{ ttl: Duration.hours(1) }
|
|
2009
|
+
)
|
|
2010
|
+
|
|
2011
|
+
// With stale-while-revalidate
|
|
2012
|
+
const user = yield* cache(
|
|
2013
|
+
`user:${id}`,
|
|
2014
|
+
fetchUser(id),
|
|
2015
|
+
UserSchema,
|
|
2016
|
+
{
|
|
2017
|
+
ttl: Duration.hours(1), // Fresh for 1 hour
|
|
2018
|
+
swr: Duration.minutes(5), // Serve stale for 5 more minutes while refreshing
|
|
2019
|
+
}
|
|
2020
|
+
)
|
|
2021
|
+
```
|
|
2022
|
+
|
|
2023
|
+
**How SWR works:**
|
|
2024
|
+
|
|
2025
|
+
| Cache State | Behavior |
|
|
2026
|
+
|-------------|----------|
|
|
2027
|
+
| Fresh (age < TTL) | Return cached value immediately |
|
|
2028
|
+
| Stale (TTL < age < TTL + SWR) | Return stale value immediately, trigger background refresh |
|
|
2029
|
+
| Expired (age > TTL + SWR) | Compute new value synchronously |
|
|
2030
|
+
| Cold (no cache) | Compute new value synchronously |
|
|
2031
|
+
|
|
2032
|
+
**Benefits:**
|
|
2033
|
+
- **Faster responses**: Users always get an immediate response (stale or fresh)
|
|
2034
|
+
- **Reduced latency spikes**: No waiting for slow database queries during cache refresh
|
|
2035
|
+
- **Graceful degradation**: If the background refresh fails, stale data is still served
|
|
2036
|
+
|
|
2037
|
+
**Real-world example:**
|
|
2038
|
+
|
|
2039
|
+
```typescript
|
|
2040
|
+
import { Effect, Duration, Schema as S } from 'effect'
|
|
2041
|
+
import { action, authorize, cache, render, DatabaseService } from 'honertia/effect'
|
|
2042
|
+
import { eq } from 'drizzle-orm'
|
|
2043
|
+
import { projects } from '~/db/schema'
|
|
2044
|
+
|
|
2045
|
+
const ProjectListSchema = S.Array(
|
|
2046
|
+
S.Struct({
|
|
2047
|
+
id: S.String,
|
|
2048
|
+
name: S.String,
|
|
2049
|
+
createdAt: S.Date,
|
|
2050
|
+
})
|
|
2051
|
+
)
|
|
2052
|
+
|
|
2053
|
+
export const indexProjects = action(
|
|
2054
|
+
Effect.gen(function* () {
|
|
2055
|
+
const auth = yield* authorize()
|
|
2056
|
+
const db = yield* DatabaseService
|
|
2057
|
+
|
|
2058
|
+
// Cache project list with SWR
|
|
2059
|
+
// - Fresh for 5 minutes
|
|
2060
|
+
// - Serve stale for 1 additional minute while refreshing in background
|
|
2061
|
+
const userProjects = yield* cache(
|
|
2062
|
+
`projects:user:${auth.user.id}`,
|
|
2063
|
+
Effect.tryPromise(() =>
|
|
2064
|
+
db.query.projects.findMany({
|
|
2065
|
+
where: eq(projects.userId, auth.user.id),
|
|
2066
|
+
orderBy: (p, { desc }) => [desc(p.createdAt)],
|
|
2067
|
+
})
|
|
2068
|
+
),
|
|
2069
|
+
ProjectListSchema,
|
|
2070
|
+
{
|
|
2071
|
+
ttl: Duration.minutes(5),
|
|
2072
|
+
swr: Duration.minutes(1),
|
|
2073
|
+
}
|
|
2074
|
+
)
|
|
2075
|
+
|
|
2076
|
+
return yield* render('Projects/Index', { projects: userProjects })
|
|
2077
|
+
})
|
|
2078
|
+
)
|
|
2079
|
+
```
|
|
2080
|
+
|
|
2081
|
+
### Background Tasks with ExecutionContextService
|
|
2082
|
+
|
|
2083
|
+
The `ExecutionContextService` provides access to Cloudflare Workers' `waitUntil` API, allowing you to run tasks after the response is sent. This is automatically used by the cache's SWR feature for background refresh.
|
|
2084
|
+
|
|
2085
|
+
```typescript
|
|
2086
|
+
import { Effect } from 'effect'
|
|
2087
|
+
import { action, ExecutionContextService, authorize, render } from 'honertia/effect'
|
|
2088
|
+
|
|
2089
|
+
export const dashboard = action(
|
|
2090
|
+
Effect.gen(function* () {
|
|
2091
|
+
const auth = yield* authorize()
|
|
2092
|
+
const ctx = yield* ExecutionContextService
|
|
2093
|
+
|
|
2094
|
+
// Send analytics in background - doesn't block response
|
|
2095
|
+
yield* ctx.runInBackground(
|
|
2096
|
+
Effect.tryPromise(() =>
|
|
2097
|
+
fetch('https://analytics.example.com/events', {
|
|
2098
|
+
method: 'POST',
|
|
2099
|
+
body: JSON.stringify({
|
|
2100
|
+
event: 'page_view',
|
|
2101
|
+
userId: auth.user.id,
|
|
2102
|
+
page: 'dashboard',
|
|
2103
|
+
timestamp: Date.now(),
|
|
2104
|
+
}),
|
|
2105
|
+
})
|
|
2106
|
+
)
|
|
2107
|
+
)
|
|
2108
|
+
|
|
2109
|
+
return yield* render('Dashboard', { user: auth.user })
|
|
2110
|
+
})
|
|
2111
|
+
)
|
|
2112
|
+
```
|
|
2113
|
+
|
|
2114
|
+
**ExecutionContextService API:**
|
|
2115
|
+
|
|
2116
|
+
| Method | Description |
|
|
2117
|
+
|--------|-------------|
|
|
2118
|
+
| `isAvailable` | `boolean` - Whether background execution is available |
|
|
2119
|
+
| `runInBackground(effect)` | Run an Effect after the response is sent |
|
|
2120
|
+
| `waitUntil(promise)` | Raw `waitUntil` for external promises |
|
|
2121
|
+
|
|
2122
|
+
**Common use cases:**
|
|
2123
|
+
|
|
2124
|
+
```typescript
|
|
2125
|
+
// Audit logging
|
|
2126
|
+
const auditLog = (action: string, details: Record<string, unknown>) =>
|
|
2127
|
+
Effect.gen(function* () {
|
|
2128
|
+
const ctx = yield* ExecutionContextService
|
|
2129
|
+
const user = yield* authorize()
|
|
2130
|
+
const db = yield* DatabaseService
|
|
2131
|
+
|
|
2132
|
+
yield* ctx.runInBackground(
|
|
2133
|
+
Effect.tryPromise(() =>
|
|
2134
|
+
db.insert(auditLogs).values({
|
|
2135
|
+
userId: user.id,
|
|
2136
|
+
action,
|
|
2137
|
+
details,
|
|
2138
|
+
timestamp: new Date(),
|
|
2139
|
+
})
|
|
2140
|
+
)
|
|
2141
|
+
)
|
|
2142
|
+
})
|
|
2143
|
+
|
|
2144
|
+
// Webhook delivery with retries
|
|
2145
|
+
const deliverWebhook = (url: string, payload: unknown) =>
|
|
2146
|
+
Effect.gen(function* () {
|
|
2147
|
+
const ctx = yield* ExecutionContextService
|
|
2148
|
+
|
|
2149
|
+
yield* ctx.runInBackground(
|
|
2150
|
+
Effect.tryPromise(() =>
|
|
2151
|
+
fetch(url, {
|
|
2152
|
+
method: 'POST',
|
|
2153
|
+
headers: { 'Content-Type': 'application/json' },
|
|
2154
|
+
body: JSON.stringify(payload),
|
|
2155
|
+
})
|
|
2156
|
+
).pipe(
|
|
2157
|
+
Effect.retry({ times: 3 }),
|
|
2158
|
+
Effect.catchAll((error) =>
|
|
2159
|
+
Effect.logError('Webhook delivery failed', { url, error })
|
|
2160
|
+
)
|
|
2161
|
+
)
|
|
2162
|
+
)
|
|
2163
|
+
})
|
|
2164
|
+
|
|
2165
|
+
// Conditional background work
|
|
2166
|
+
const maybeNotifySlack = (message: string) =>
|
|
2167
|
+
Effect.gen(function* () {
|
|
2168
|
+
const ctx = yield* ExecutionContextService
|
|
2169
|
+
const bindings = yield* BindingsService
|
|
2170
|
+
|
|
2171
|
+
// Only run if Slack is configured and background is available
|
|
2172
|
+
if (bindings.SLACK_WEBHOOK_URL && ctx.isAvailable) {
|
|
2173
|
+
yield* ctx.runInBackground(
|
|
2174
|
+
Effect.tryPromise(() =>
|
|
2175
|
+
fetch(bindings.SLACK_WEBHOOK_URL, {
|
|
2176
|
+
method: 'POST',
|
|
2177
|
+
body: JSON.stringify({ text: message }),
|
|
2178
|
+
})
|
|
2179
|
+
)
|
|
2180
|
+
)
|
|
2181
|
+
}
|
|
2182
|
+
})
|
|
2183
|
+
```
|
|
2184
|
+
|
|
2185
|
+
**Important notes:**
|
|
2186
|
+
- Background tasks run after the response is sent to the user
|
|
2187
|
+
- Errors in background tasks are logged but don't crash the worker
|
|
2188
|
+
- In non-Worker environments (tests, local dev), `isAvailable` is `false` and tasks are skipped
|
|
2189
|
+
- Use `catchAll` to handle errors gracefully in background tasks
|
|
2190
|
+
|
|
2191
|
+
### Cache Key Versioning
|
|
2192
|
+
|
|
2193
|
+
Cache versioning ensures cache correctness when your data schema changes. Without versioning, schema changes can cause decode errors or serve stale data with the wrong shape.
|
|
2194
|
+
|
|
2195
|
+
**The Problem:**
|
|
2196
|
+
|
|
2197
|
+
```typescript
|
|
2198
|
+
// Version 1 of your schema
|
|
2199
|
+
const UserSchemaV1 = S.Struct({
|
|
2200
|
+
id: S.String,
|
|
2201
|
+
name: S.String,
|
|
2202
|
+
email: S.String,
|
|
2203
|
+
})
|
|
2204
|
+
|
|
2205
|
+
// You deploy with cached data...
|
|
2206
|
+
|
|
2207
|
+
// Version 2 adds a required field
|
|
2208
|
+
const UserSchemaV2 = S.Struct({
|
|
2209
|
+
id: S.String,
|
|
2210
|
+
name: S.String,
|
|
2211
|
+
email: S.String,
|
|
2212
|
+
avatar: S.String, // New required field!
|
|
2213
|
+
})
|
|
2214
|
+
|
|
2215
|
+
// Cached V1 data fails to decode with V2 schema → Runtime error
|
|
2216
|
+
```
|
|
2217
|
+
|
|
2218
|
+
**Solution 1: Auto Schema Versioning (Recommended)**
|
|
2219
|
+
|
|
2220
|
+
Pass `version: true` to automatically version cache keys based on a hash of the schema structure. When you change the schema, the hash changes, and old cached data is automatically bypassed.
|
|
2221
|
+
|
|
2222
|
+
```typescript
|
|
2223
|
+
import { Effect, Duration, Schema as S } from 'effect'
|
|
2224
|
+
import { cache, DatabaseService } from 'honertia/effect'
|
|
2225
|
+
|
|
2226
|
+
const UserSchema = S.Struct({
|
|
2227
|
+
id: S.String,
|
|
2228
|
+
name: S.String,
|
|
2229
|
+
email: S.String,
|
|
2230
|
+
})
|
|
2231
|
+
|
|
2232
|
+
// Cache key becomes "a1b2c3:user:123" (hash:key)
|
|
2233
|
+
const user = yield* cache(
|
|
2234
|
+
`user:${id}`,
|
|
2235
|
+
fetchUser(id),
|
|
2236
|
+
UserSchema,
|
|
2237
|
+
{ ttl: Duration.hours(1), version: true }
|
|
2238
|
+
)
|
|
2239
|
+
```
|
|
2240
|
+
|
|
2241
|
+
**When to use `version: true`:**
|
|
2242
|
+
- You want automatic cache invalidation when schemas evolve
|
|
2243
|
+
- You're iterating quickly on data structures during development
|
|
2244
|
+
- You want zero-downtime deployments without manual cache clearing
|
|
2245
|
+
|
|
2246
|
+
**When NOT to use `version: true`:**
|
|
2247
|
+
- You need cache hits to survive deployments (use explicit versions instead)
|
|
2248
|
+
- Schema changes are intentionally backward-compatible
|
|
2249
|
+
- You're caching data that doesn't depend on schema structure
|
|
2250
|
+
|
|
2251
|
+
**Solution 2: Explicit Version Strings**
|
|
2252
|
+
|
|
2253
|
+
For more control, pass an explicit version string. Bump it manually when your schema changes.
|
|
2254
|
+
|
|
2255
|
+
```typescript
|
|
2256
|
+
// Cache key becomes "v2:user:123"
|
|
2257
|
+
const user = yield* cache(
|
|
2258
|
+
`user:${id}`,
|
|
2259
|
+
fetchUser(id),
|
|
2260
|
+
UserSchema,
|
|
2261
|
+
{ ttl: Duration.hours(1), version: 'v2' }
|
|
2262
|
+
)
|
|
2263
|
+
```
|
|
2264
|
+
|
|
2265
|
+
**When to use explicit versions:**
|
|
2266
|
+
- You want cache hits to survive deployments
|
|
2267
|
+
- You need predictable cache keys for debugging
|
|
2268
|
+
- You're coordinating schema changes across services
|
|
2269
|
+
|
|
2270
|
+
**Full Example: User Profile with Versioned Cache**
|
|
2271
|
+
|
|
2272
|
+
```typescript
|
|
2273
|
+
import { Effect, Duration, Schema as S } from 'effect'
|
|
2274
|
+
import {
|
|
2275
|
+
action,
|
|
2276
|
+
authorize,
|
|
2277
|
+
cache,
|
|
2278
|
+
cacheInvalidate,
|
|
2279
|
+
render,
|
|
2280
|
+
redirect,
|
|
2281
|
+
DatabaseService,
|
|
2282
|
+
validateRequest,
|
|
2283
|
+
dbMutation,
|
|
2284
|
+
} from 'honertia/effect'
|
|
2285
|
+
import { eq } from 'drizzle-orm'
|
|
2286
|
+
import { users } from '~/db/schema'
|
|
2287
|
+
|
|
2288
|
+
// Schema definition - changing this auto-invalidates cache when version: true
|
|
2289
|
+
const UserProfileSchema = S.Struct({
|
|
2290
|
+
id: S.String,
|
|
2291
|
+
name: S.String,
|
|
2292
|
+
email: S.String,
|
|
2293
|
+
bio: S.NullOr(S.String),
|
|
2294
|
+
avatarUrl: S.NullOr(S.String),
|
|
2295
|
+
})
|
|
2296
|
+
|
|
2297
|
+
// GET /profile - cached with auto-versioning
|
|
2298
|
+
export const showProfile = action(
|
|
2299
|
+
Effect.gen(function* () {
|
|
2300
|
+
const auth = yield* authorize()
|
|
2301
|
+
const db = yield* DatabaseService
|
|
2302
|
+
|
|
2303
|
+
const profile = yield* cache(
|
|
2304
|
+
`user:profile:${auth.user.id}`,
|
|
2305
|
+
Effect.tryPromise(() =>
|
|
2306
|
+
db.query.users.findFirst({
|
|
2307
|
+
where: eq(users.id, auth.user.id),
|
|
2308
|
+
columns: { id: true, name: true, email: true, bio: true, avatarUrl: true },
|
|
2309
|
+
})
|
|
2310
|
+
),
|
|
2311
|
+
UserProfileSchema,
|
|
2312
|
+
{
|
|
2313
|
+
ttl: Duration.hours(1),
|
|
2314
|
+
swr: Duration.minutes(5),
|
|
2315
|
+
version: true, // Auto-invalidates when UserProfileSchema changes
|
|
2316
|
+
}
|
|
2317
|
+
)
|
|
2318
|
+
|
|
2319
|
+
return yield* render('Profile/Show', { profile })
|
|
2320
|
+
})
|
|
2321
|
+
)
|
|
2322
|
+
|
|
2323
|
+
// PUT /profile - invalidate cache after update
|
|
2324
|
+
export const updateProfile = action(
|
|
2325
|
+
Effect.gen(function* () {
|
|
2326
|
+
const auth = yield* authorize()
|
|
2327
|
+
const input = yield* validateRequest(UpdateProfileSchema, {
|
|
2328
|
+
errorComponent: 'Profile/Edit',
|
|
2329
|
+
})
|
|
2330
|
+
const db = yield* DatabaseService
|
|
2331
|
+
|
|
2332
|
+
yield* dbMutation(db, async (db) => {
|
|
2333
|
+
await db.update(users)
|
|
2334
|
+
.set({ name: input.name, bio: input.bio })
|
|
2335
|
+
.where(eq(users.id, auth.user.id))
|
|
2336
|
+
})
|
|
2337
|
+
|
|
2338
|
+
// Invalidate with same versioning strategy
|
|
2339
|
+
yield* cacheInvalidate(`user:profile:${auth.user.id}`, {
|
|
2340
|
+
schema: UserProfileSchema,
|
|
2341
|
+
version: true,
|
|
2342
|
+
})
|
|
2343
|
+
|
|
2344
|
+
return yield* redirect('/profile')
|
|
2345
|
+
})
|
|
2346
|
+
)
|
|
2347
|
+
```
|
|
2348
|
+
|
|
2349
|
+
**Versioning with `cacheGet` and `cacheInvalidate`:**
|
|
2350
|
+
|
|
2351
|
+
When using manual cache operations, pass the same version option:
|
|
2352
|
+
|
|
2353
|
+
```typescript
|
|
2354
|
+
import { cacheGet, cacheSet, cacheInvalidate } from 'honertia/effect'
|
|
2355
|
+
|
|
2356
|
+
// Get with auto-versioning
|
|
2357
|
+
const cached = yield* cacheGet(`user:${id}`, UserSchema, { version: true })
|
|
2358
|
+
|
|
2359
|
+
// Set with explicit version
|
|
2360
|
+
yield* cacheSet(`user:${id}`, user, UserSchema, {
|
|
2361
|
+
ttl: Duration.hours(1),
|
|
2362
|
+
version: 'v2',
|
|
2363
|
+
})
|
|
2364
|
+
|
|
2365
|
+
// Invalidate with versioning - requires schema when using version
|
|
2366
|
+
yield* cacheInvalidate(`user:${id}`, { schema: UserSchema, version: true })
|
|
2367
|
+
|
|
2368
|
+
// Simple invalidation (no versioning)
|
|
2369
|
+
yield* cacheInvalidate(`user:${id}`)
|
|
2370
|
+
```
|
|
2371
|
+
|
|
2372
|
+
**How Schema Hashing Works:**
|
|
2373
|
+
|
|
2374
|
+
The auto-versioning feature uses the djb2 hash algorithm on the serialized schema AST:
|
|
2375
|
+
|
|
2376
|
+
1. Schema structure is serialized to a string representation
|
|
2377
|
+
2. A fast, deterministic hash is computed (djb2)
|
|
2378
|
+
3. The hash is prepended to the cache key
|
|
2379
|
+
|
|
2380
|
+
This means:
|
|
2381
|
+
- Same schema definition → same hash → cache hits work
|
|
2382
|
+
- Changed schema structure → different hash → cache miss, fresh data computed
|
|
2383
|
+
- Adding/removing fields, changing types, or modifying constraints all change the hash
|
|
2384
|
+
|
|
2385
|
+
**Cache Options Reference:**
|
|
2386
|
+
|
|
2387
|
+
| Option | Type | Description |
|
|
2388
|
+
|--------|------|-------------|
|
|
2389
|
+
| `ttl` | `Duration.DurationInput` | Time-to-live for cached values (required) |
|
|
2390
|
+
| `swr` | `Duration.DurationInput` | Stale-while-revalidate window (optional) |
|
|
2391
|
+
| `version` | `string \| boolean` | `true` = auto schema hash, `string` = explicit prefix (optional) |
|
|
2392
|
+
|
|
1982
2393
|
---
|
|
1983
2394
|
|
|
1984
2395
|
## Services Reference
|
|
@@ -1990,6 +2401,7 @@ Recommended cache key patterns:
|
|
|
1990
2401
|
| `AuthUserService` | Current user session | `const user = yield* AuthUserService` |
|
|
1991
2402
|
| `BindingsService` | Cloudflare bindings | `const { KV } = yield* BindingsService` |
|
|
1992
2403
|
| `CacheService` | KV-backed cache client | `const cache = yield* CacheService` |
|
|
2404
|
+
| `ExecutionContextService` | Background task execution | `const ctx = yield* ExecutionContextService` |
|
|
1993
2405
|
| `RequestService` | Request context | `const req = yield* RequestService` |
|
|
1994
2406
|
|
|
1995
2407
|
### Using BindingsService
|
package/dist/cache.d.ts
CHANGED
|
@@ -4,9 +4,36 @@
|
|
|
4
4
|
* Simple cache abstraction for storing expensive DB operations.
|
|
5
5
|
* Uses CacheService which is automatically provided and backed by Cloudflare KV by default.
|
|
6
6
|
* Can be swapped for Redis, Memcached, or any other implementation.
|
|
7
|
+
*
|
|
8
|
+
* Supports stale-while-revalidate (SWR) pattern for improved latency and resilience.
|
|
7
9
|
*/
|
|
8
10
|
import { Effect, Option, Schema, ParseResult, Duration } from 'effect';
|
|
9
|
-
import { CacheService, CacheClientError } from './effect/services.js';
|
|
11
|
+
import { CacheService, CacheClientError, ExecutionContextService } from './effect/services.js';
|
|
12
|
+
export type CacheOptions = {
|
|
13
|
+
/** Time-to-live for cached values */
|
|
14
|
+
ttl: Duration.DurationInput;
|
|
15
|
+
/**
|
|
16
|
+
* Stale-while-revalidate window. When set, stale values within this window
|
|
17
|
+
* are returned immediately while a background refresh is triggered.
|
|
18
|
+
* Without ExecutionContext, the refresh happens on the next request.
|
|
19
|
+
*/
|
|
20
|
+
swr?: Duration.DurationInput;
|
|
21
|
+
/**
|
|
22
|
+
* Cache key versioning for safe schema migrations.
|
|
23
|
+
* - `string`: Explicit version prefix (e.g., 'v2')
|
|
24
|
+
* - `true`: Auto-generate version from schema hash
|
|
25
|
+
* - `false` or omitted: No versioning
|
|
26
|
+
*/
|
|
27
|
+
version?: string | boolean;
|
|
28
|
+
};
|
|
29
|
+
export type CacheGetOptions = {
|
|
30
|
+
/**
|
|
31
|
+
* Cache key versioning (must match what was used when caching).
|
|
32
|
+
* - `string`: Explicit version prefix
|
|
33
|
+
* - `true`: Auto-generate version from schema hash
|
|
34
|
+
*/
|
|
35
|
+
version?: string | boolean;
|
|
36
|
+
};
|
|
10
37
|
declare const CacheError_base: Schema.TaggedErrorClass<CacheError, "CacheError", {
|
|
11
38
|
readonly _tag: Schema.tag<"CacheError">;
|
|
12
39
|
} & {
|
|
@@ -17,18 +44,44 @@ export declare class CacheError extends CacheError_base {
|
|
|
17
44
|
}
|
|
18
45
|
/**
|
|
19
46
|
* Cache a computed value with automatic serialization and TTL.
|
|
47
|
+
* Supports stale-while-revalidate (SWR) for improved latency.
|
|
20
48
|
*
|
|
21
49
|
* @example
|
|
22
50
|
* ```typescript
|
|
51
|
+
* // Basic usage
|
|
23
52
|
* const user = yield* cache(
|
|
24
53
|
* `user:${id}`,
|
|
25
54
|
* Effect.tryPromise(() => db.query.users.findFirst({ where: eq(users.id, id) })),
|
|
26
55
|
* UserSchema,
|
|
27
|
-
* Duration.hours(1)
|
|
56
|
+
* { ttl: Duration.hours(1) }
|
|
57
|
+
* )
|
|
58
|
+
*
|
|
59
|
+
* // With stale-while-revalidate
|
|
60
|
+
* const user = yield* cache(
|
|
61
|
+
* `user:${id}`,
|
|
62
|
+
* fetchUser(id),
|
|
63
|
+
* UserSchema,
|
|
64
|
+
* { ttl: Duration.hours(1), swr: Duration.minutes(5) }
|
|
65
|
+
* )
|
|
66
|
+
*
|
|
67
|
+
* // With auto schema versioning (cache auto-invalidates when schema changes)
|
|
68
|
+
* const user = yield* cache(
|
|
69
|
+
* `user:${id}`,
|
|
70
|
+
* fetchUser(id),
|
|
71
|
+
* UserSchema,
|
|
72
|
+
* { ttl: Duration.hours(1), version: true }
|
|
73
|
+
* )
|
|
74
|
+
*
|
|
75
|
+
* // With explicit version
|
|
76
|
+
* const user = yield* cache(
|
|
77
|
+
* `user:${id}`,
|
|
78
|
+
* fetchUser(id),
|
|
79
|
+
* UserSchema,
|
|
80
|
+
* { ttl: Duration.hours(1), version: 'v2' }
|
|
28
81
|
* )
|
|
29
82
|
* ```
|
|
30
83
|
*/
|
|
31
|
-
export declare const cache: <V, E, R>(key: string, compute: Effect.Effect<V, E, R>, schema: Schema.Schema<V>,
|
|
84
|
+
export declare const cache: <V, E, R>(key: string, compute: Effect.Effect<V, E, R>, schema: Schema.Schema<V>, options: CacheOptions) => Effect.Effect<V, E | CacheError | CacheClientError | ParseResult.ParseError, R | CacheService | ExecutionContextService>;
|
|
32
85
|
/**
|
|
33
86
|
* Get a value from cache without computing.
|
|
34
87
|
*
|
|
@@ -38,27 +91,43 @@ export declare const cache: <V, E, R>(key: string, compute: Effect.Effect<V, E,
|
|
|
38
91
|
* if (Option.isSome(cached)) {
|
|
39
92
|
* return cached.value
|
|
40
93
|
* }
|
|
94
|
+
*
|
|
95
|
+
* // With versioning (must match what was used when caching)
|
|
96
|
+
* const cached = yield* cacheGet(`user:${id}`, UserSchema, { version: true })
|
|
41
97
|
* ```
|
|
42
98
|
*/
|
|
43
|
-
export declare const cacheGet: <V>(key: string, schema: Schema.Schema<V
|
|
99
|
+
export declare const cacheGet: <V>(key: string, schema: Schema.Schema<V>, options?: CacheGetOptions) => Effect.Effect<Option.Option<V>, CacheError | CacheClientError | ParseResult.ParseError, CacheService>;
|
|
44
100
|
/**
|
|
45
101
|
* Set a value in cache.
|
|
46
102
|
*
|
|
47
103
|
* @example
|
|
48
104
|
* ```typescript
|
|
49
|
-
* yield* cacheSet(`user:${id}`, user, UserSchema, Duration.hours(1))
|
|
105
|
+
* yield* cacheSet(`user:${id}`, user, UserSchema, { ttl: Duration.hours(1) })
|
|
106
|
+
*
|
|
107
|
+
* // With auto schema versioning
|
|
108
|
+
* yield* cacheSet(`user:${id}`, user, UserSchema, { ttl: Duration.hours(1), version: true })
|
|
50
109
|
* ```
|
|
51
110
|
*/
|
|
52
|
-
export declare const cacheSet: <V>(key: string, value: V, schema: Schema.Schema<V>,
|
|
111
|
+
export declare const cacheSet: <V>(key: string, value: V, schema: Schema.Schema<V>, options: CacheOptions) => Effect.Effect<void, CacheError | CacheClientError | ParseResult.ParseError, CacheService>;
|
|
112
|
+
export type CacheInvalidateOptions<V> = {
|
|
113
|
+
/** Schema used when caching (required if version is set) */
|
|
114
|
+
schema: Schema.Schema<V>;
|
|
115
|
+
/** Version option (must match what was used when caching) */
|
|
116
|
+
version: string | boolean;
|
|
117
|
+
};
|
|
53
118
|
/**
|
|
54
119
|
* Invalidate a cache key.
|
|
55
120
|
*
|
|
56
121
|
* @example
|
|
57
122
|
* ```typescript
|
|
123
|
+
* // Simple invalidation
|
|
58
124
|
* yield* cacheInvalidate(`user:${id}`)
|
|
125
|
+
*
|
|
126
|
+
* // Invalidate versioned key
|
|
127
|
+
* yield* cacheInvalidate(`user:${id}`, { schema: UserSchema, version: true })
|
|
59
128
|
* ```
|
|
60
129
|
*/
|
|
61
|
-
export declare const cacheInvalidate: (key: string) => Effect.Effect<void, CacheClientError, CacheService>;
|
|
130
|
+
export declare const cacheInvalidate: <V = unknown>(key: string, options?: CacheInvalidateOptions<V>) => Effect.Effect<void, CacheClientError, CacheService>;
|
|
62
131
|
/**
|
|
63
132
|
* Invalidate all cache keys with a given prefix.
|
|
64
133
|
*
|
package/dist/cache.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AACtE,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAA;AAM9F,MAAM,MAAM,YAAY,GAAG;IACzB,qCAAqC;IACrC,GAAG,EAAE,QAAQ,CAAC,aAAa,CAAA;IAC3B;;;;OAIG;IACH,GAAG,CAAC,EAAE,QAAQ,CAAC,aAAa,CAAA;IAC5B;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;CAC3B,CAAA;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;CAC3B,CAAA;;;;;;;AAMD,qBAAa,UAAW,SAAQ,eAG9B;CAAG;AA2DL;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,eAAO,MAAM,KAAK,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAC3B,KAAK,MAAM,EACX,SAAS,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAC/B,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EACxB,SAAS,YAAY,KACpB,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,GAAG,UAAU,GAAG,gBAAgB,GAAG,WAAW,CAAC,UAAU,EAAE,CAAC,GAAG,YAAY,GAAG,uBAAuB,CAuDtH,CAAA;AAEJ;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,QAAQ,GAAI,CAAC,EACxB,KAAK,MAAM,EACX,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EACxB,UAAU,eAAe,KACxB,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,UAAU,GAAG,gBAAgB,GAAG,WAAW,CAAC,UAAU,EAAE,YAAY,CAenG,CAAA;AAEJ;;;;;;;;;;GAUG;AACH,eAAO,MAAM,QAAQ,GAAI,CAAC,EACxB,KAAK,MAAM,EACX,OAAO,CAAC,EACR,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EACxB,SAAS,YAAY,KACpB,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,gBAAgB,GAAG,WAAW,CAAC,UAAU,EAAE,YAAY,CAevF,CAAA;AAEJ,MAAM,MAAM,sBAAsB,CAAC,CAAC,IAAI;IACtC,4DAA4D;IAC5D,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;IACxB,6DAA6D;IAC7D,OAAO,EAAE,MAAM,GAAG,OAAO,CAAA;CAC1B,CAAA;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,eAAe,GAAI,CAAC,GAAG,OAAO,EACzC,KAAK,MAAM,EACX,UAAU,sBAAsB,CAAC,CAAC,CAAC,KAClC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,gBAAgB,EAAE,YAAY,CAOjD,CAAA;AAEJ;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,GAChC,QAAQ,MAAM,KACb,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,gBAAgB,EAAE,YAAY,CAWjD,CAAA"}
|
package/dist/cache.js
CHANGED
|
@@ -4,9 +4,11 @@
|
|
|
4
4
|
* Simple cache abstraction for storing expensive DB operations.
|
|
5
5
|
* Uses CacheService which is automatically provided and backed by Cloudflare KV by default.
|
|
6
6
|
* Can be swapped for Redis, Memcached, or any other implementation.
|
|
7
|
+
*
|
|
8
|
+
* Supports stale-while-revalidate (SWR) pattern for improved latency and resilience.
|
|
7
9
|
*/
|
|
8
10
|
import { Effect, Option, Schema, Duration } from 'effect';
|
|
9
|
-
import { CacheService } from './effect/services.js';
|
|
11
|
+
import { CacheService, ExecutionContextService } from './effect/services.js';
|
|
10
12
|
// ============================================================================
|
|
11
13
|
// Errors
|
|
12
14
|
// ============================================================================
|
|
@@ -16,34 +18,128 @@ export class CacheError extends Schema.TaggedError()('CacheError', {
|
|
|
16
18
|
}) {
|
|
17
19
|
}
|
|
18
20
|
// ============================================================================
|
|
21
|
+
// Internal
|
|
22
|
+
// ============================================================================
|
|
23
|
+
/** Internal schema for storing value with metadata */
|
|
24
|
+
const CacheEntrySchema = (valueSchema) => Schema.Struct({
|
|
25
|
+
v: valueSchema,
|
|
26
|
+
t: Schema.Number, // cachedAt timestamp
|
|
27
|
+
});
|
|
28
|
+
/**
|
|
29
|
+
* Simple string hash function (djb2 algorithm).
|
|
30
|
+
* Produces a short, deterministic hash for cache key versioning.
|
|
31
|
+
*/
|
|
32
|
+
const hashString = (str) => {
|
|
33
|
+
let hash = 5381;
|
|
34
|
+
for (let i = 0; i < str.length; i++) {
|
|
35
|
+
hash = ((hash << 5) + hash) ^ str.charCodeAt(i);
|
|
36
|
+
}
|
|
37
|
+
// Convert to unsigned 32-bit and then to base36 for short strings
|
|
38
|
+
return (hash >>> 0).toString(36);
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Generate a version string from a schema's AST.
|
|
42
|
+
* The hash changes when the schema structure changes.
|
|
43
|
+
*/
|
|
44
|
+
const hashSchema = (schema) => {
|
|
45
|
+
// Stringify the AST - this captures the schema structure
|
|
46
|
+
const astString = JSON.stringify(schema.ast);
|
|
47
|
+
return hashString(astString);
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Resolve the effective cache key with optional versioning.
|
|
51
|
+
*/
|
|
52
|
+
const resolveKey = (key, schema, version) => {
|
|
53
|
+
if (version === true) {
|
|
54
|
+
return `${hashSchema(schema)}:${key}`;
|
|
55
|
+
}
|
|
56
|
+
if (typeof version === 'string') {
|
|
57
|
+
return `${version}:${key}`;
|
|
58
|
+
}
|
|
59
|
+
return key;
|
|
60
|
+
};
|
|
61
|
+
// ============================================================================
|
|
19
62
|
// Composable API
|
|
20
63
|
// ============================================================================
|
|
21
64
|
/**
|
|
22
65
|
* Cache a computed value with automatic serialization and TTL.
|
|
66
|
+
* Supports stale-while-revalidate (SWR) for improved latency.
|
|
23
67
|
*
|
|
24
68
|
* @example
|
|
25
69
|
* ```typescript
|
|
70
|
+
* // Basic usage
|
|
26
71
|
* const user = yield* cache(
|
|
27
72
|
* `user:${id}`,
|
|
28
73
|
* Effect.tryPromise(() => db.query.users.findFirst({ where: eq(users.id, id) })),
|
|
29
74
|
* UserSchema,
|
|
30
|
-
* Duration.hours(1)
|
|
75
|
+
* { ttl: Duration.hours(1) }
|
|
76
|
+
* )
|
|
77
|
+
*
|
|
78
|
+
* // With stale-while-revalidate
|
|
79
|
+
* const user = yield* cache(
|
|
80
|
+
* `user:${id}`,
|
|
81
|
+
* fetchUser(id),
|
|
82
|
+
* UserSchema,
|
|
83
|
+
* { ttl: Duration.hours(1), swr: Duration.minutes(5) }
|
|
84
|
+
* )
|
|
85
|
+
*
|
|
86
|
+
* // With auto schema versioning (cache auto-invalidates when schema changes)
|
|
87
|
+
* const user = yield* cache(
|
|
88
|
+
* `user:${id}`,
|
|
89
|
+
* fetchUser(id),
|
|
90
|
+
* UserSchema,
|
|
91
|
+
* { ttl: Duration.hours(1), version: true }
|
|
92
|
+
* )
|
|
93
|
+
*
|
|
94
|
+
* // With explicit version
|
|
95
|
+
* const user = yield* cache(
|
|
96
|
+
* `user:${id}`,
|
|
97
|
+
* fetchUser(id),
|
|
98
|
+
* UserSchema,
|
|
99
|
+
* { ttl: Duration.hours(1), version: 'v2' }
|
|
31
100
|
* )
|
|
32
101
|
* ```
|
|
33
102
|
*/
|
|
34
|
-
export const cache = (key, compute, schema,
|
|
103
|
+
export const cache = (key, compute, schema, options) => Effect.gen(function* () {
|
|
35
104
|
const cacheService = yield* CacheService;
|
|
105
|
+
const executionContext = yield* ExecutionContextService;
|
|
106
|
+
const entrySchema = CacheEntrySchema(schema);
|
|
107
|
+
const jsonSchema = Schema.parseJson(entrySchema);
|
|
108
|
+
const effectiveKey = resolveKey(key, schema, options.version);
|
|
109
|
+
const ttlMs = Duration.toMillis(Duration.decode(options.ttl));
|
|
110
|
+
const swrMs = options.swr ? Duration.toMillis(Duration.decode(options.swr)) : 0;
|
|
111
|
+
const totalTtlSeconds = Math.ceil((ttlMs + swrMs) / 1000);
|
|
112
|
+
// Helper to store a value in cache
|
|
113
|
+
const storeInCache = (value) => Effect.gen(function* () {
|
|
114
|
+
const newEntry = { v: value, t: Date.now() };
|
|
115
|
+
const serialized = yield* Schema.encode(jsonSchema)(newEntry);
|
|
116
|
+
yield* cacheService.put(effectiveKey, serialized, { expirationTtl: totalTtlSeconds });
|
|
117
|
+
});
|
|
36
118
|
// Check cache first
|
|
37
|
-
const cached = yield* cacheService.get(
|
|
119
|
+
const cached = yield* cacheService.get(effectiveKey);
|
|
38
120
|
if (cached !== null) {
|
|
39
|
-
|
|
121
|
+
const entry = yield* Schema.decodeUnknown(jsonSchema)(cached);
|
|
122
|
+
const age = Date.now() - entry.t;
|
|
123
|
+
if (age < ttlMs) {
|
|
124
|
+
// Fresh - return immediately
|
|
125
|
+
return entry.v;
|
|
126
|
+
}
|
|
127
|
+
if (swrMs > 0 && age < ttlMs + swrMs) {
|
|
128
|
+
// Stale but within SWR window - return stale, trigger background refresh
|
|
129
|
+
if (executionContext.isAvailable) {
|
|
130
|
+
yield* executionContext.runInBackground(Effect.gen(function* () {
|
|
131
|
+
const freshValue = yield* compute;
|
|
132
|
+
yield* storeInCache(freshValue);
|
|
133
|
+
}));
|
|
134
|
+
}
|
|
135
|
+
return entry.v;
|
|
136
|
+
}
|
|
137
|
+
// Beyond SWR window - fall through to recompute
|
|
40
138
|
}
|
|
41
|
-
// Compute value
|
|
139
|
+
// Compute value (cold cache or expired)
|
|
42
140
|
const value = yield* compute;
|
|
43
|
-
// Store
|
|
44
|
-
|
|
45
|
-
const ttlSeconds = Duration.toSeconds(Duration.decode(ttl));
|
|
46
|
-
yield* cacheService.put(key, serialized, { expirationTtl: ttlSeconds });
|
|
141
|
+
// Store with timestamp
|
|
142
|
+
yield* storeInCache(value);
|
|
47
143
|
return value;
|
|
48
144
|
});
|
|
49
145
|
/**
|
|
@@ -55,42 +151,64 @@ export const cache = (key, compute, schema, ttl) => Effect.gen(function* () {
|
|
|
55
151
|
* if (Option.isSome(cached)) {
|
|
56
152
|
* return cached.value
|
|
57
153
|
* }
|
|
154
|
+
*
|
|
155
|
+
* // With versioning (must match what was used when caching)
|
|
156
|
+
* const cached = yield* cacheGet(`user:${id}`, UserSchema, { version: true })
|
|
58
157
|
* ```
|
|
59
158
|
*/
|
|
60
|
-
export const cacheGet = (key, schema) => Effect.gen(function* () {
|
|
159
|
+
export const cacheGet = (key, schema, options) => Effect.gen(function* () {
|
|
61
160
|
const cacheService = yield* CacheService;
|
|
62
|
-
const
|
|
161
|
+
const entrySchema = CacheEntrySchema(schema);
|
|
162
|
+
const jsonSchema = Schema.parseJson(entrySchema);
|
|
163
|
+
const effectiveKey = resolveKey(key, schema, options?.version);
|
|
164
|
+
const cached = yield* cacheService.get(effectiveKey);
|
|
63
165
|
if (cached === null) {
|
|
64
166
|
return Option.none();
|
|
65
167
|
}
|
|
66
|
-
const
|
|
67
|
-
return Option.some(
|
|
168
|
+
const entry = yield* Schema.decodeUnknown(jsonSchema)(cached);
|
|
169
|
+
return Option.some(entry.v);
|
|
68
170
|
});
|
|
69
171
|
/**
|
|
70
172
|
* Set a value in cache.
|
|
71
173
|
*
|
|
72
174
|
* @example
|
|
73
175
|
* ```typescript
|
|
74
|
-
* yield* cacheSet(`user:${id}`, user, UserSchema, Duration.hours(1))
|
|
176
|
+
* yield* cacheSet(`user:${id}`, user, UserSchema, { ttl: Duration.hours(1) })
|
|
177
|
+
*
|
|
178
|
+
* // With auto schema versioning
|
|
179
|
+
* yield* cacheSet(`user:${id}`, user, UserSchema, { ttl: Duration.hours(1), version: true })
|
|
75
180
|
* ```
|
|
76
181
|
*/
|
|
77
|
-
export const cacheSet = (key, value, schema,
|
|
182
|
+
export const cacheSet = (key, value, schema, options) => Effect.gen(function* () {
|
|
78
183
|
const cacheService = yield* CacheService;
|
|
79
|
-
const
|
|
80
|
-
const
|
|
81
|
-
|
|
184
|
+
const entrySchema = CacheEntrySchema(schema);
|
|
185
|
+
const jsonSchema = Schema.parseJson(entrySchema);
|
|
186
|
+
const effectiveKey = resolveKey(key, schema, options.version);
|
|
187
|
+
const ttlMs = Duration.toMillis(Duration.decode(options.ttl));
|
|
188
|
+
const swrMs = options.swr ? Duration.toMillis(Duration.decode(options.swr)) : 0;
|
|
189
|
+
const totalTtlSeconds = Math.ceil((ttlMs + swrMs) / 1000);
|
|
190
|
+
const entry = { v: value, t: Date.now() };
|
|
191
|
+
const serialized = yield* Schema.encode(jsonSchema)(entry);
|
|
192
|
+
yield* cacheService.put(effectiveKey, serialized, { expirationTtl: totalTtlSeconds });
|
|
82
193
|
});
|
|
83
194
|
/**
|
|
84
195
|
* Invalidate a cache key.
|
|
85
196
|
*
|
|
86
197
|
* @example
|
|
87
198
|
* ```typescript
|
|
199
|
+
* // Simple invalidation
|
|
88
200
|
* yield* cacheInvalidate(`user:${id}`)
|
|
201
|
+
*
|
|
202
|
+
* // Invalidate versioned key
|
|
203
|
+
* yield* cacheInvalidate(`user:${id}`, { schema: UserSchema, version: true })
|
|
89
204
|
* ```
|
|
90
205
|
*/
|
|
91
|
-
export const cacheInvalidate = (key) => Effect.gen(function* () {
|
|
206
|
+
export const cacheInvalidate = (key, options) => Effect.gen(function* () {
|
|
92
207
|
const cacheService = yield* CacheService;
|
|
93
|
-
|
|
208
|
+
const effectiveKey = options
|
|
209
|
+
? resolveKey(key, options.schema, options.version)
|
|
210
|
+
: key;
|
|
211
|
+
yield* cacheService.delete(effectiveKey);
|
|
94
212
|
});
|
|
95
213
|
/**
|
|
96
214
|
* Invalidate all cache keys with a given prefix.
|
package/dist/effect/auth.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { Effect, Layer, Schema as S } from 'effect';
|
|
7
7
|
import type { Hono, MiddlewareHandler, Env } from 'hono';
|
|
8
|
-
import { AuthUserService, AuthService, HonertiaService, RequestService, type AuthUser } from './services.js';
|
|
8
|
+
import { AuthUserService, AuthService, DatabaseService, HonertiaService, RequestService, type AuthUser } from './services.js';
|
|
9
9
|
import { UnauthorizedError, ValidationError } from './errors.js';
|
|
10
10
|
import { type EffectHandler } from './routing.js';
|
|
11
11
|
/**
|
|
@@ -102,7 +102,7 @@ export declare function shareAuthMiddleware<E extends Env>(): MiddlewareHandler<
|
|
|
102
102
|
* that's what the factory functions (betterAuthFormAction, betterAuthLogoutAction)
|
|
103
103
|
* return, and effectAuthRoutes provides these services automatically.
|
|
104
104
|
*/
|
|
105
|
-
export type AuthActionEffect<R = RequestService | AuthService, E extends Error = Error> = EffectHandler<R, E>;
|
|
105
|
+
export type AuthActionEffect<R = RequestService | AuthService | DatabaseService, E extends Error = Error> = EffectHandler<R, E>;
|
|
106
106
|
/**
|
|
107
107
|
* Configuration for auth routes.
|
|
108
108
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/effect/auth.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,MAAM,EAAE,KAAK,EAAU,MAAM,IAAI,CAAC,EAAE,MAAM,QAAQ,CAAA;AAC3D,OAAO,KAAK,EAAE,IAAI,EAAE,iBAAiB,EAAE,GAAG,EAAE,MAAM,MAAM,CAAA;AACxD,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,eAAe,EAAE,cAAc,EAAE,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/effect/auth.ts"],"names":[],"mappings":"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"}
|
package/dist/effect/bridge.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { Layer, ManagedRuntime } from 'effect';
|
|
7
7
|
import type { Context as HonoContext, MiddlewareHandler, Env } from 'hono';
|
|
8
|
-
import { DatabaseService, AuthService, AuthUserService, HonertiaService, RequestService, ResponseFactoryService, BindingsService, CacheService } from './services.js';
|
|
8
|
+
import { DatabaseService, AuthService, AuthUserService, HonertiaService, RequestService, ResponseFactoryService, BindingsService, CacheService, ExecutionContextService } from './services.js';
|
|
9
9
|
/**
|
|
10
10
|
* Configuration for the Effect bridge.
|
|
11
11
|
*
|
|
@@ -61,7 +61,7 @@ declare module 'hono' {
|
|
|
61
61
|
/**
|
|
62
62
|
* Build the Effect layer from Hono context.
|
|
63
63
|
*/
|
|
64
|
-
export declare function buildContextLayer<E extends Env, CustomServices = never>(c: HonoContext<E>, config?: EffectBridgeConfig<E, CustomServices>): Layer.Layer<RequestService | ResponseFactoryService | HonertiaService | DatabaseService | AuthService | AuthUserService | BindingsService | CacheService | CustomServices, never, never>;
|
|
64
|
+
export declare function buildContextLayer<E extends Env, CustomServices = never>(c: HonoContext<E>, config?: EffectBridgeConfig<E, CustomServices>): Layer.Layer<RequestService | ResponseFactoryService | HonertiaService | DatabaseService | AuthService | AuthUserService | BindingsService | CacheService | ExecutionContextService | CustomServices, never, never>;
|
|
65
65
|
/**
|
|
66
66
|
* Get the Effect runtime from Hono context.
|
|
67
67
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../../src/effect/bridge.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAU,KAAK,EAAE,cAAc,EAAU,MAAM,QAAQ,CAAA;AAG9D,OAAO,KAAK,EAAE,OAAO,IAAI,WAAW,EAAE,iBAAiB,EAAE,GAAG,EAAE,MAAM,MAAM,CAAA;AAC1E,OAAO,EACL,eAAe,EACf,WAAW,EACX,eAAe,EACf,eAAe,EACf,cAAc,EACd,sBAAsB,EACtB,eAAe,EACf,YAAY,
|
|
1
|
+
{"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../../src/effect/bridge.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAU,KAAK,EAAE,cAAc,EAAU,MAAM,QAAQ,CAAA;AAG9D,OAAO,KAAK,EAAE,OAAO,IAAI,WAAW,EAAE,iBAAiB,EAAE,GAAG,EAAE,MAAM,MAAM,CAAA;AAC1E,OAAO,EACL,eAAe,EACf,WAAW,EACX,eAAe,EACf,eAAe,EACf,cAAc,EACd,sBAAsB,EACtB,eAAe,EACf,YAAY,EAEZ,uBAAuB,EAUxB,MAAM,eAAe,CAAA;AAGtB;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,WAAW,kBAAkB,CAAC,CAAC,SAAS,GAAG,EAAE,cAAc,GAAG,KAAK;IACvE;;;OAGG;IACH,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,EAAE,KAAK,CAAC,CAAA;IAC3E;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACjC;AAED;;GAEG;AACH,QAAA,MAAM,cAAc,eAA0B,CAAA;AAE9C;;GAEG;AACH,QAAA,MAAM,aAAa,eAAyB,CAAA;AAa5C,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAM7D;AAqCD;;GAEG;AACH,OAAO,QAAQ,MAAM,CAAC;IACpB,UAAU,kBAAkB;QAC1B,CAAC,cAAc,CAAC,CAAC,EAAE,cAAc,CAAC,cAAc,CAC5C,eAAe,GACf,WAAW,GACX,eAAe,GACf,eAAe,GACf,cAAc,GACd,sBAAsB,EACxB,KAAK,CACN,CAAA;QACD,CAAC,aAAa,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAC1C;CACF;AA0JD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,GAAG,EAAE,cAAc,GAAG,KAAK,EACrE,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,EACjB,MAAM,CAAC,EAAE,kBAAkB,CAAC,CAAC,EAAE,cAAc,CAAC,GAC7C,KAAK,CAAC,KAAK,CACV,cAAc,GACd,sBAAsB,GACtB,eAAe,GACf,eAAe,GACf,WAAW,GACX,eAAe,GACf,eAAe,GACf,YAAY,GACZ,uBAAuB,GACvB,cAAc,EAChB,KAAK,EACL,KAAK,CACN,CA8FA;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,GAAG,EAC5C,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,GAChB,cAAc,CAAC,cAAc,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,SAAS,CAEvD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,GAAG,EAAE,cAAc,GAAG,KAAK,EAChE,MAAM,CAAC,EAAE,kBAAkB,CAAC,CAAC,EAAE,cAAc,CAAC,GAC7C,iBAAiB,CAAC,CAAC,CAAC,CA6CtB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,GAAG,EAC3C,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,GAChB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAErC"}
|
package/dist/effect/bridge.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { Effect, Layer, ManagedRuntime, Option } from 'effect';
|
|
7
7
|
import { HonertiaConfigurationError } from './errors.js';
|
|
8
8
|
import { ErrorCodes } from './error-catalog.js';
|
|
9
|
-
import { DatabaseService, AuthService, AuthUserService, HonertiaService, RequestService, ResponseFactoryService, BindingsService, CacheService, CacheClientError, } from './services.js';
|
|
9
|
+
import { DatabaseService, AuthService, AuthUserService, HonertiaService, RequestService, ResponseFactoryService, BindingsService, CacheService, CacheClientError, ExecutionContextService, } from './services.js';
|
|
10
10
|
import { TestCaptureService } from './test-layers.js';
|
|
11
11
|
/**
|
|
12
12
|
* Symbol for storing Effect runtime in Hono context.
|
|
@@ -123,6 +123,35 @@ function createUnconfiguredCacheClient() {
|
|
|
123
123
|
list: () => Effect.fail(error),
|
|
124
124
|
};
|
|
125
125
|
}
|
|
126
|
+
/**
|
|
127
|
+
* Create an ExecutionContextClient from Cloudflare's ExecutionContext.
|
|
128
|
+
*/
|
|
129
|
+
function createExecutionContextClient(ctx) {
|
|
130
|
+
return {
|
|
131
|
+
isAvailable: true,
|
|
132
|
+
waitUntil: (promise) => ctx.waitUntil(promise),
|
|
133
|
+
runInBackground: (effect) => Effect.flatMap(Effect.context(), (context) => Effect.sync(() => {
|
|
134
|
+
const promise = Effect.runPromise(effect.pipe(Effect.provide(context), Effect.catchAllCause((cause) => {
|
|
135
|
+
// Log errors but don't crash - this is background work
|
|
136
|
+
console.error('[Background Task Error]', cause);
|
|
137
|
+
return Effect.void;
|
|
138
|
+
})));
|
|
139
|
+
ctx.waitUntil(promise);
|
|
140
|
+
})),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Create a no-op ExecutionContextClient for environments without ExecutionContext.
|
|
145
|
+
*/
|
|
146
|
+
function createNoopExecutionContextClient() {
|
|
147
|
+
return {
|
|
148
|
+
isAvailable: false,
|
|
149
|
+
waitUntil: () => {
|
|
150
|
+
// No-op - silently ignore in non-Worker environments
|
|
151
|
+
},
|
|
152
|
+
runInBackground: () => Effect.void,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
126
155
|
/**
|
|
127
156
|
* Create a HonertiaRenderer from Hono context.
|
|
128
157
|
*/
|
|
@@ -161,7 +190,19 @@ export function buildContextLayer(c, config) {
|
|
|
161
190
|
const auth = c.var?.auth;
|
|
162
191
|
const authLayer = Layer.succeed(AuthService, (auth ??
|
|
163
192
|
createUnconfiguredServiceProxy('AuthService', 'auth: (c) => createAuth(...)', 'auth: (c) => betterAuth({ database: c.var.db, ... })', ErrorCodes.CFG_301_AUTH_NOT_CONFIGURED)));
|
|
164
|
-
|
|
193
|
+
// ExecutionContext layer - for background task execution
|
|
194
|
+
// Note: Hono's executionCtx getter throws in non-Worker environments, so we wrap in try/catch
|
|
195
|
+
let executionCtx;
|
|
196
|
+
try {
|
|
197
|
+
executionCtx = c.executionCtx;
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
executionCtx = undefined;
|
|
201
|
+
}
|
|
202
|
+
const executionContextLayer = Layer.succeed(ExecutionContextService, executionCtx
|
|
203
|
+
? createExecutionContextClient(executionCtx)
|
|
204
|
+
: createNoopExecutionContextClient());
|
|
205
|
+
let baseLayer = Layer.mergeAll(requestLayer, responseLayer, honertiaLayer, bindingsLayer, cacheLayer, databaseLayer, authLayer, executionContextLayer);
|
|
165
206
|
if (c.var?.authUser) {
|
|
166
207
|
baseLayer = Layer.merge(baseLayer, Layer.succeed(AuthUserService, c.var.authUser));
|
|
167
208
|
}
|
package/dist/effect/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Re-exports all Effect-related functionality.
|
|
5
5
|
*/
|
|
6
|
-
export { DatabaseService, AuthService, AuthUserService, EmailService, HonertiaService, RequestService, ResponseFactoryService, BindingsService, CacheService, CacheClientError, authorize, type AuthUser, type EmailClient, type HonertiaRenderer, type RequestContext, type ResponseFactory, type CacheClient, type HonertiaDatabaseType, type HonertiaAuthType, type HonertiaBindingsType, type HonertiaAuthUserType, type DefaultAuthUser, type DatabaseType, type SchemaType, type AuthType, type BindingsType, } from './services.js';
|
|
6
|
+
export { DatabaseService, AuthService, AuthUserService, EmailService, HonertiaService, RequestService, ResponseFactoryService, BindingsService, CacheService, CacheClientError, ExecutionContextService, authorize, type AuthUser, type EmailClient, type HonertiaRenderer, type RequestContext, type ResponseFactory, type CacheClient, type ExecutionContextClient, type HonertiaDatabaseType, type HonertiaAuthType, type HonertiaBindingsType, type HonertiaAuthUserType, type DefaultAuthUser, type DatabaseType, type SchemaType, type AuthType, type BindingsType, } from './services.js';
|
|
7
7
|
export { ValidationError, UnauthorizedError, NotFoundError, ForbiddenError, HttpError, RouteConfigurationError, HonertiaConfigurationError, Redirect, isStructuredError, toStructuredError, type AppError, type StructuredErrorCapable, } from './errors.js';
|
|
8
8
|
export type { ErrorCategory, ErrorContext, SourceLocation, CodeSnippet, RouteContext, HandlerContext, RequestContext as ErrorRequestContext, ServiceContext, FixType, FixPosition, FixOperation, PostAction, FixSuggestion, ErrorDocs, HonertiaStructuredError, FieldError, ValidationErrorData, ConfigurationErrorData, BindingErrorData, ErrorDefinition, FixGenerator, } from './error-types.js';
|
|
9
9
|
export { ErrorCodes, ErrorCatalog, createStructuredError, getConfigErrorCode, getErrorDefinition, getErrorsByCategory, type ErrorCode, } from './error-catalog.js';
|
|
@@ -21,6 +21,6 @@ export { RouteRegistry, getGlobalRegistry, resetGlobalRegistry, type HttpMethod,
|
|
|
21
21
|
export { describeRoute, createRouteTester, generateTestCases, type TestUserType, type TestUser, type TestRequestOptions, type TestExpectation, type TestContext, type TestCaseOptions, type TestFn, type TestAppConfig, } from './testing.js';
|
|
22
22
|
export { TestLayer, TestCaptureService, type TestCaptures, } from './test-layers.js';
|
|
23
23
|
export { BoundModels, BoundModelNotFound, bound, pluralize, parseBindings, toHonoPath, type ParsedBinding, type BoundModel, } from './binding.js';
|
|
24
|
-
export { CacheError, cache, cacheGet, cacheSet, cacheInvalidate, cacheInvalidatePrefix, } from '../cache.js';
|
|
24
|
+
export { CacheError, cache, cacheGet, cacheSet, cacheInvalidate, cacheInvalidatePrefix, type CacheOptions, type CacheGetOptions, type CacheInvalidateOptions, } from '../cache.js';
|
|
25
25
|
export { RequireAuthLayer, RequireGuestLayer, createGuestLayer, isAuthenticated, currentUser, requireAuth, requireGuest, shareAuth, shareAuthMiddleware, effectAuthRoutes, betterAuthFormAction, betterAuthLogoutAction, loadUser, type AuthRoutesConfig, type BetterAuthFormActionConfig, type BetterAuthLogoutConfig, type BetterAuthActionResult, } from './auth.js';
|
|
26
26
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/effect/index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/effect/index.ts"],"names":[],"mappings":"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"}
|
package/dist/effect/index.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Re-exports all Effect-related functionality.
|
|
5
5
|
*/
|
|
6
6
|
// Services
|
|
7
|
-
export { DatabaseService, AuthService, AuthUserService, EmailService, HonertiaService, RequestService, ResponseFactoryService, BindingsService, CacheService, CacheClientError, authorize, } from './services.js';
|
|
7
|
+
export { DatabaseService, AuthService, AuthUserService, EmailService, HonertiaService, RequestService, ResponseFactoryService, BindingsService, CacheService, CacheClientError, ExecutionContextService, authorize, } from './services.js';
|
|
8
8
|
// Errors
|
|
9
9
|
export { ValidationError, UnauthorizedError, NotFoundError, ForbiddenError, HttpError, RouteConfigurationError, HonertiaConfigurationError, Redirect, isStructuredError, toStructuredError, } from './errors.js';
|
|
10
10
|
// Error Catalog
|
|
@@ -288,6 +288,70 @@ export declare class CacheClientError {
|
|
|
288
288
|
declare const CacheService_base: Context.TagClass<CacheService, "honertia/Cache", CacheClient>;
|
|
289
289
|
export declare class CacheService extends CacheService_base {
|
|
290
290
|
}
|
|
291
|
+
/**
|
|
292
|
+
* Client interface for background execution.
|
|
293
|
+
* Wraps Cloudflare's ExecutionContext with Effect-native methods.
|
|
294
|
+
*
|
|
295
|
+
* @example
|
|
296
|
+
* ```typescript
|
|
297
|
+
* const ctx = yield* ExecutionContextService
|
|
298
|
+
*
|
|
299
|
+
* // Effect-native: run an Effect in the background
|
|
300
|
+
* yield* ctx.runInBackground(
|
|
301
|
+
* sendAnalyticsEvent({ userId, page: 'dashboard' })
|
|
302
|
+
* )
|
|
303
|
+
*
|
|
304
|
+
* // Raw API: for promises from external libraries
|
|
305
|
+
* ctx.waitUntil(externalLib.doSomething())
|
|
306
|
+
*
|
|
307
|
+
* // Check availability before optional background work
|
|
308
|
+
* if (ctx.isAvailable) {
|
|
309
|
+
* yield* ctx.runInBackground(touchSession())
|
|
310
|
+
* }
|
|
311
|
+
* ```
|
|
312
|
+
*/
|
|
313
|
+
export interface ExecutionContextClient {
|
|
314
|
+
/**
|
|
315
|
+
* Whether background execution is available.
|
|
316
|
+
* False in tests or non-Worker environments.
|
|
317
|
+
*/
|
|
318
|
+
readonly isAvailable: boolean;
|
|
319
|
+
/**
|
|
320
|
+
* Run an Effect in the background after the response is sent.
|
|
321
|
+
* Errors are caught and logged - they won't crash the worker.
|
|
322
|
+
*
|
|
323
|
+
* The Effect's requirements (R) must be satisfied by the current context.
|
|
324
|
+
* Returns immediately - the actual work happens asynchronously.
|
|
325
|
+
*/
|
|
326
|
+
runInBackground: <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<void, never, R>;
|
|
327
|
+
/**
|
|
328
|
+
* Raw waitUntil - extends worker lifetime for a Promise.
|
|
329
|
+
* Use this for promises from external libraries.
|
|
330
|
+
* No-op if background execution is unavailable.
|
|
331
|
+
*/
|
|
332
|
+
waitUntil: (promise: Promise<unknown>) => void;
|
|
333
|
+
}
|
|
334
|
+
declare const ExecutionContextService_base: Context.TagClass<ExecutionContextService, "honertia/ExecutionContext", ExecutionContextClient>;
|
|
335
|
+
/**
|
|
336
|
+
* Execution Context Service - Background task execution for Cloudflare Workers.
|
|
337
|
+
*
|
|
338
|
+
* Automatically provided by the Effect bridge. Uses Cloudflare's `waitUntil`
|
|
339
|
+
* to run work after the response is sent.
|
|
340
|
+
*
|
|
341
|
+
* @example
|
|
342
|
+
* ```typescript
|
|
343
|
+
* // In an action - send analytics without blocking response
|
|
344
|
+
* const ctx = yield* ExecutionContextService
|
|
345
|
+
* yield* ctx.runInBackground(
|
|
346
|
+
* Effect.tryPromise(() => fetch('https://analytics.example.com/events', {
|
|
347
|
+
* method: 'POST',
|
|
348
|
+
* body: JSON.stringify({ event: 'page_view', userId })
|
|
349
|
+
* }))
|
|
350
|
+
* )
|
|
351
|
+
* ```
|
|
352
|
+
*/
|
|
353
|
+
export declare class ExecutionContextService extends ExecutionContextService_base {
|
|
354
|
+
}
|
|
291
355
|
/**
|
|
292
356
|
* Authorization helper - opt-in to auth check.
|
|
293
357
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"services.d.ts","sourceRoot":"","sources":["../../src/effect/services.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"services.d.ts","sourceRoot":"","sources":["../../src/effect/services.ts"],"names":[],"mappings":"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"}
|
package/dist/effect/services.js
CHANGED
|
@@ -66,6 +66,26 @@ export class CacheClientError {
|
|
|
66
66
|
}
|
|
67
67
|
export class CacheService extends Context.Tag('honertia/Cache')() {
|
|
68
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Execution Context Service - Background task execution for Cloudflare Workers.
|
|
71
|
+
*
|
|
72
|
+
* Automatically provided by the Effect bridge. Uses Cloudflare's `waitUntil`
|
|
73
|
+
* to run work after the response is sent.
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```typescript
|
|
77
|
+
* // In an action - send analytics without blocking response
|
|
78
|
+
* const ctx = yield* ExecutionContextService
|
|
79
|
+
* yield* ctx.runInBackground(
|
|
80
|
+
* Effect.tryPromise(() => fetch('https://analytics.example.com/events', {
|
|
81
|
+
* method: 'POST',
|
|
82
|
+
* body: JSON.stringify({ event: 'page_view', userId })
|
|
83
|
+
* }))
|
|
84
|
+
* )
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
export class ExecutionContextService extends Context.Tag('honertia/ExecutionContext')() {
|
|
88
|
+
}
|
|
69
89
|
/**
|
|
70
90
|
* Authorization helper - opt-in to auth check.
|
|
71
91
|
*
|