honertia 0.1.31 → 0.1.32

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
@@ -792,24 +792,39 @@ export const showAbout = action(
792
792
  )
793
793
  ```
794
794
 
795
- ### GET with Authentication
795
+ ### GET with Authentication and Caching
796
796
 
797
797
  ```typescript
798
- import { Effect } from 'effect'
799
- import { action, authorize, render, DatabaseService } from 'honertia/effect'
798
+ import { Effect, Schema as S, Duration } from 'effect'
799
+ import { action, authorize, render, DatabaseService, cache } from 'honertia/effect'
800
800
  import { eq } from 'drizzle-orm'
801
801
  import { projects } from '~/db/schema'
802
802
 
803
+ const ProjectSchema = S.Struct({
804
+ id: S.String,
805
+ userId: S.String,
806
+ name: S.String,
807
+ description: S.NullOr(S.String),
808
+ createdAt: S.Date,
809
+ updatedAt: S.Date,
810
+ })
811
+
803
812
  export const listProjects = action(
804
813
  Effect.gen(function* () {
805
814
  const auth = yield* authorize()
806
815
  const db = yield* DatabaseService
807
816
 
808
- const userProjects = yield* Effect.tryPromise(() =>
809
- db.query.projects.findMany({
810
- where: eq(projects.userId, auth.user.id),
811
- orderBy: (p, { desc }) => [desc(p.createdAt)],
812
- })
817
+ // Cache expensive database query for 5 minutes
818
+ const userProjects = yield* cache(
819
+ `projects:user:${auth.user.id}`,
820
+ Effect.tryPromise(() =>
821
+ db.query.projects.findMany({
822
+ where: eq(projects.userId, auth.user.id),
823
+ orderBy: (p, { desc }) => [desc(p.createdAt)],
824
+ })
825
+ ),
826
+ S.Array(ProjectSchema),
827
+ Duration.minutes(5)
813
828
  )
814
829
 
815
830
  return yield* render('Projects/Index', { projects: userProjects })
@@ -1532,6 +1547,371 @@ return yield* httpError(429, 'Rate limited')
1532
1547
 
1533
1548
  ---
1534
1549
 
1550
+ ## Caching
1551
+
1552
+ Honertia provides a `CacheService` for caching expensive database operations. It's automatically provided and backed by Cloudflare KV by default, but can be swapped for Redis, Memcached, or any other implementation.
1553
+
1554
+ ### Setup
1555
+
1556
+ Add KV to your `wrangler.toml`:
1557
+
1558
+ ```toml
1559
+ [[kv_namespaces]]
1560
+ binding = "KV"
1561
+ id = "your-kv-namespace-id"
1562
+ ```
1563
+
1564
+ Update your bindings type in `src/types.ts`:
1565
+
1566
+ ```typescript
1567
+ export type Bindings = {
1568
+ DATABASE_URL: string
1569
+ BETTER_AUTH_SECRET: string
1570
+ KV: KVNamespace // Add this
1571
+ }
1572
+ ```
1573
+
1574
+ No additional registration needed - `CacheService` is automatically available in all actions.
1575
+
1576
+ ### Basic Usage
1577
+
1578
+ ```typescript
1579
+ import { Effect, Schema as S, Duration } from 'effect'
1580
+ import { action, authorize, render, DatabaseService, cache } from 'honertia/effect'
1581
+ import { eq } from 'drizzle-orm'
1582
+ import { projects } from '~/db/schema'
1583
+
1584
+ const ProjectSchema = S.Struct({
1585
+ id: S.String,
1586
+ userId: S.String,
1587
+ name: S.String,
1588
+ description: S.NullOr(S.String),
1589
+ createdAt: S.Date,
1590
+ updatedAt: S.Date,
1591
+ })
1592
+
1593
+ export const listProjects = action(
1594
+ Effect.gen(function* () {
1595
+ const auth = yield* authorize()
1596
+ const db = yield* DatabaseService
1597
+
1598
+ // Cache the database query for 5 minutes
1599
+ const userProjects = yield* cache(
1600
+ `projects:user:${auth.user.id}`,
1601
+ Effect.tryPromise({
1602
+ try: () =>
1603
+ db.query.projects.findMany({
1604
+ where: eq(projects.userId, auth.user.id),
1605
+ orderBy: (p, { desc }) => [desc(p.createdAt)],
1606
+ }),
1607
+ catch: (error) => new Error(String(error)),
1608
+ }),
1609
+ S.Array(ProjectSchema),
1610
+ Duration.minutes(5)
1611
+ )
1612
+
1613
+ return yield* render('Projects/Index', { projects: userProjects })
1614
+ })
1615
+ )
1616
+ ```
1617
+
1618
+ ### Cache Functions
1619
+
1620
+ | Function | Description |
1621
+ |----------|-------------|
1622
+ | `cache(key, compute, schema, ttl)` | Get from cache or compute and store |
1623
+ | `cacheGet(key, schema)` | Get value from cache (returns `Option`) |
1624
+ | `cacheSet(key, value, schema, ttl)` | Store value in cache |
1625
+ | `cacheInvalidate(key)` | Delete a single cache key |
1626
+ | `cacheInvalidatePrefix(prefix)` | Delete all keys with prefix |
1627
+
1628
+ ### Cache Invalidation
1629
+
1630
+ Invalidate cache when data changes:
1631
+
1632
+ ```typescript
1633
+ import { Effect, Schema as S } from 'effect'
1634
+ import {
1635
+ action,
1636
+ authorize,
1637
+ validateRequest,
1638
+ DatabaseService,
1639
+ redirect,
1640
+ asTrusted,
1641
+ dbMutation,
1642
+ requiredString,
1643
+ cacheInvalidate,
1644
+ } from 'honertia/effect'
1645
+ import { projects } from '~/db/schema'
1646
+
1647
+ const CreateProjectSchema = S.Struct({
1648
+ name: requiredString,
1649
+ description: S.optional(S.String),
1650
+ })
1651
+
1652
+ export const createProject = action(
1653
+ Effect.gen(function* () {
1654
+ const auth = yield* authorize()
1655
+ const input = yield* validateRequest(CreateProjectSchema, {
1656
+ errorComponent: 'Projects/Create',
1657
+ })
1658
+ const db = yield* DatabaseService
1659
+
1660
+ yield* dbMutation(db, async (db) => {
1661
+ await db.insert(projects).values(
1662
+ asTrusted({
1663
+ name: input.name,
1664
+ description: input.description ?? null,
1665
+ userId: auth.user.id,
1666
+ })
1667
+ )
1668
+ })
1669
+
1670
+ // Invalidate the user's project list cache
1671
+ yield* cacheInvalidate(`projects:user:${auth.user.id}`)
1672
+
1673
+ return yield* redirect('/projects')
1674
+ })
1675
+ )
1676
+ ```
1677
+
1678
+ ### Invalidate by Prefix
1679
+
1680
+ Delete all cache keys matching a prefix:
1681
+
1682
+ ```typescript
1683
+ import { cacheInvalidatePrefix } from 'honertia/effect'
1684
+
1685
+ // Invalidate all caches for a user
1686
+ yield* cacheInvalidatePrefix(`user:${userId}:`)
1687
+
1688
+ // Invalidate all project-related caches
1689
+ yield* cacheInvalidatePrefix('projects:')
1690
+ ```
1691
+
1692
+ ### Manual Get/Set
1693
+
1694
+ For more control over cache operations:
1695
+
1696
+ ```typescript
1697
+ import { Effect, Option, Schema as S, Duration } from 'effect'
1698
+ import { cacheGet, cacheSet } from 'honertia/effect'
1699
+
1700
+ const UserSchema = S.Struct({
1701
+ id: S.String,
1702
+ name: S.String,
1703
+ email: S.String,
1704
+ })
1705
+
1706
+ // Check cache first
1707
+ const cached = yield* cacheGet(`user:${id}`, UserSchema)
1708
+
1709
+ if (Option.isSome(cached)) {
1710
+ return cached.value
1711
+ }
1712
+
1713
+ // Compute value
1714
+ const user = yield* fetchUser(id)
1715
+
1716
+ // Store in cache
1717
+ yield* cacheSet(`user:${id}`, user, UserSchema, Duration.hours(1))
1718
+
1719
+ return user
1720
+ ```
1721
+
1722
+ ### Using CacheService Directly
1723
+
1724
+ For advanced use cases, access the underlying service:
1725
+
1726
+ ```typescript
1727
+ import { Effect } from 'effect'
1728
+ import { CacheService } from 'honertia/effect'
1729
+
1730
+ const handler = action(
1731
+ Effect.gen(function* () {
1732
+ const cache = yield* CacheService
1733
+
1734
+ // Raw get (returns string | null)
1735
+ const raw = yield* cache.get('my-key')
1736
+
1737
+ // Raw put
1738
+ yield* cache.put('my-key', JSON.stringify({ data: 'value' }), {
1739
+ expirationTtl: 3600, // seconds
1740
+ })
1741
+
1742
+ // Delete
1743
+ yield* cache.delete('my-key')
1744
+
1745
+ // List keys by prefix
1746
+ const keys = yield* cache.list({ prefix: 'user:' })
1747
+ })
1748
+ )
1749
+ ```
1750
+
1751
+ ### Custom Cache Implementation
1752
+
1753
+ Swap out Cloudflare KV for Redis or any other backend by providing a custom `CacheService` layer:
1754
+
1755
+ ```typescript
1756
+ import { Effect, Layer } from 'effect'
1757
+ import { CacheService, CacheClientError, type CacheClient } from 'honertia/effect'
1758
+ import { createClient } from 'redis'
1759
+
1760
+ const createRedisCacheClient = (redisUrl: string): CacheClient => {
1761
+ const client = createClient({ url: redisUrl })
1762
+
1763
+ return {
1764
+ get: (key) =>
1765
+ Effect.tryPromise({
1766
+ try: () => client.get(key),
1767
+ catch: (e) => new CacheClientError('Redis get failed', e),
1768
+ }),
1769
+ put: (key, value, options) =>
1770
+ Effect.tryPromise({
1771
+ try: () =>
1772
+ client.set(key, value, options?.expirationTtl ? { EX: options.expirationTtl } : undefined),
1773
+ catch: (e) => new CacheClientError('Redis set failed', e),
1774
+ }).pipe(Effect.asVoid),
1775
+ delete: (key) =>
1776
+ Effect.tryPromise({
1777
+ try: () => client.del(key),
1778
+ catch: (e) => new CacheClientError('Redis delete failed', e),
1779
+ }).pipe(Effect.asVoid),
1780
+ list: (options) =>
1781
+ Effect.tryPromise({
1782
+ try: async () => {
1783
+ const keys = await client.keys(options?.prefix ? `${options.prefix}*` : '*')
1784
+ return { keys: keys.map((name) => ({ name })) }
1785
+ },
1786
+ catch: (e) => new CacheClientError('Redis keys failed', e),
1787
+ }),
1788
+ }
1789
+ }
1790
+
1791
+ // Provide in your app setup
1792
+ const RedisCacheLayer = Layer.succeed(
1793
+ CacheService,
1794
+ createRedisCacheClient(process.env.REDIS_URL!)
1795
+ )
1796
+ ```
1797
+
1798
+ ### Testing with Cache
1799
+
1800
+ Create a test layer that uses an in-memory store:
1801
+
1802
+ ```typescript
1803
+ import { Effect, Layer, Option, Schema as S, Duration } from 'effect'
1804
+ import { CacheService, cache, cacheGet, cacheInvalidate, type CacheClient } from 'honertia/effect'
1805
+ import { describe, it, expect } from 'bun:test'
1806
+
1807
+ const makeTestCache = (): Layer.Layer<CacheService> => {
1808
+ const store = new Map<string, { value: string; expiresAt: number }>()
1809
+
1810
+ const client: CacheClient = {
1811
+ get: (key) =>
1812
+ Effect.sync(() => {
1813
+ const entry = store.get(key)
1814
+ if (!entry || entry.expiresAt < Date.now()) {
1815
+ store.delete(key)
1816
+ return null
1817
+ }
1818
+ return entry.value
1819
+ }),
1820
+ put: (key, value, options) =>
1821
+ Effect.sync(() => {
1822
+ const ttlMs = (options?.expirationTtl ?? 3600) * 1000
1823
+ store.set(key, { value, expiresAt: Date.now() + ttlMs })
1824
+ }),
1825
+ delete: (key) =>
1826
+ Effect.sync(() => {
1827
+ store.delete(key)
1828
+ }),
1829
+ list: (options) =>
1830
+ Effect.sync(() => ({
1831
+ keys: [...store.keys()]
1832
+ .filter((k) => !options?.prefix || k.startsWith(options.prefix))
1833
+ .map((name) => ({ name })),
1834
+ })),
1835
+ }
1836
+
1837
+ return Layer.succeed(CacheService, client)
1838
+ }
1839
+
1840
+ const TestSchema = S.Struct({
1841
+ id: S.String,
1842
+ name: S.String,
1843
+ })
1844
+
1845
+ describe('cache', () => {
1846
+ it('returns cached value on second call', () =>
1847
+ Effect.gen(function* () {
1848
+ let callCount = 0
1849
+
1850
+ const compute = Effect.sync(() => {
1851
+ callCount++
1852
+ return { id: '1', name: 'Test' }
1853
+ })
1854
+
1855
+ const first = yield* cache('test:1', compute, TestSchema, Duration.hours(1))
1856
+ const second = yield* cache('test:1', compute, TestSchema, Duration.hours(1))
1857
+
1858
+ expect(first).toEqual(second)
1859
+ expect(callCount).toBe(1) // Only computed once
1860
+ }).pipe(Effect.provide(makeTestCache()), Effect.runPromise))
1861
+
1862
+ it('recomputes after invalidation', () =>
1863
+ Effect.gen(function* () {
1864
+ let callCount = 0
1865
+
1866
+ const compute = Effect.sync(() => {
1867
+ callCount++
1868
+ return { id: '1', name: `Call ${callCount}` }
1869
+ })
1870
+
1871
+ yield* cache('test:1', compute, TestSchema, Duration.hours(1))
1872
+ yield* cacheInvalidate('test:1')
1873
+ yield* cache('test:1', compute, TestSchema, Duration.hours(1))
1874
+
1875
+ expect(callCount).toBe(2) // Computed twice
1876
+ }).pipe(Effect.provide(makeTestCache()), Effect.runPromise))
1877
+
1878
+ it('returns Option.none for missing keys', () =>
1879
+ Effect.gen(function* () {
1880
+ const result = yield* cacheGet('nonexistent', TestSchema)
1881
+ expect(Option.isNone(result)).toBe(true)
1882
+ }).pipe(Effect.provide(makeTestCache()), Effect.runPromise))
1883
+ })
1884
+ ```
1885
+
1886
+ ### Cache Key Patterns
1887
+
1888
+ Recommended cache key patterns:
1889
+
1890
+ ```typescript
1891
+ // User-scoped data
1892
+ `user:${userId}:profile`
1893
+ `user:${userId}:settings`
1894
+ `user:${userId}:notifications`
1895
+
1896
+ // Resource lists
1897
+ `projects:user:${userId}`
1898
+ `posts:category:${categoryId}`
1899
+
1900
+ // Individual resources
1901
+ `project:${projectId}`
1902
+ `user:${userId}`
1903
+
1904
+ // Computed data
1905
+ `stats:daily:${date}`
1906
+ `leaderboard:weekly`
1907
+
1908
+ // API responses
1909
+ `api:weather:${city}`
1910
+ `api:exchange:${currency}`
1911
+ ```
1912
+
1913
+ ---
1914
+
1535
1915
  ## Services Reference
1536
1916
 
1537
1917
  | Service | Description | Usage |
@@ -1540,6 +1920,7 @@ return yield* httpError(429, 'Rate limited')
1540
1920
  | `AuthService` | Better-auth instance | `const auth = yield* AuthService` |
1541
1921
  | `AuthUserService` | Current user session | `const user = yield* AuthUserService` |
1542
1922
  | `BindingsService` | Cloudflare bindings | `const { KV } = yield* BindingsService` |
1923
+ | `CacheService` | KV-backed cache client | `const cache = yield* CacheService` |
1543
1924
  | `RequestService` | Request context | `const req = yield* RequestService` |
1544
1925
 
1545
1926
  ### Using BindingsService
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Cache Module
3
+ *
4
+ * Simple cache abstraction for storing expensive DB operations.
5
+ * Uses CacheService which is automatically provided and backed by Cloudflare KV by default.
6
+ * Can be swapped for Redis, Memcached, or any other implementation.
7
+ */
8
+ import { Effect, Option, Schema, ParseResult, Duration } from 'effect';
9
+ import { CacheService, CacheClientError } from './effect/services.js';
10
+ declare const CacheError_base: Schema.TaggedErrorClass<CacheError, "CacheError", {
11
+ readonly _tag: Schema.tag<"CacheError">;
12
+ } & {
13
+ reason: typeof Schema.String;
14
+ cause: Schema.optional<typeof Schema.Unknown>;
15
+ }>;
16
+ export declare class CacheError extends CacheError_base {
17
+ }
18
+ /**
19
+ * Cache a computed value with automatic serialization and TTL.
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * const user = yield* cache(
24
+ * `user:${id}`,
25
+ * Effect.tryPromise(() => db.query.users.findFirst({ where: eq(users.id, id) })),
26
+ * UserSchema,
27
+ * Duration.hours(1)
28
+ * )
29
+ * ```
30
+ */
31
+ export declare const cache: <V, E, R>(key: string, compute: Effect.Effect<V, E, R>, schema: Schema.Schema<V>, ttl: Duration.DurationInput) => Effect.Effect<V, E | CacheError | CacheClientError | ParseResult.ParseError, R | CacheService>;
32
+ /**
33
+ * Get a value from cache without computing.
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * const cached = yield* cacheGet(`user:${id}`, UserSchema)
38
+ * if (Option.isSome(cached)) {
39
+ * return cached.value
40
+ * }
41
+ * ```
42
+ */
43
+ export declare const cacheGet: <V>(key: string, schema: Schema.Schema<V>) => Effect.Effect<Option.Option<V>, CacheError | CacheClientError | ParseResult.ParseError, CacheService>;
44
+ /**
45
+ * Set a value in cache.
46
+ *
47
+ * @example
48
+ * ```typescript
49
+ * yield* cacheSet(`user:${id}`, user, UserSchema, Duration.hours(1))
50
+ * ```
51
+ */
52
+ export declare const cacheSet: <V>(key: string, value: V, schema: Schema.Schema<V>, ttl: Duration.DurationInput) => Effect.Effect<void, CacheError | CacheClientError | ParseResult.ParseError, CacheService>;
53
+ /**
54
+ * Invalidate a cache key.
55
+ *
56
+ * @example
57
+ * ```typescript
58
+ * yield* cacheInvalidate(`user:${id}`)
59
+ * ```
60
+ */
61
+ export declare const cacheInvalidate: (key: string) => Effect.Effect<void, CacheClientError, CacheService>;
62
+ /**
63
+ * Invalidate all cache keys with a given prefix.
64
+ *
65
+ * @example
66
+ * ```typescript
67
+ * yield* cacheInvalidatePrefix(`user:${userId}:`)
68
+ * ```
69
+ */
70
+ export declare const cacheInvalidatePrefix: (prefix: string) => Effect.Effect<void, CacheClientError, CacheService>;
71
+ export {};
72
+ //# sourceMappingURL=cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AACtE,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;;;;;;;AAMrE,qBAAa,UAAW,SAAQ,eAG9B;CAAG;AAML;;;;;;;;;;;;GAYG;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,KAAK,QAAQ,CAAC,aAAa,KAC1B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,GAAG,UAAU,GAAG,gBAAgB,GAAG,WAAW,CAAC,UAAU,EAAE,CAAC,GAAG,YAAY,CAqB5F,CAAA;AAEJ;;;;;;;;;;GAUG;AACH,eAAO,MAAM,QAAQ,GAAI,CAAC,EACxB,KAAK,MAAM,EACX,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KACvB,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,UAAU,GAAG,gBAAgB,GAAG,WAAW,CAAC,UAAU,EAAE,YAAY,CAYnG,CAAA;AAEJ;;;;;;;GAOG;AACH,eAAO,MAAM,QAAQ,GAAI,CAAC,EACxB,KAAK,MAAM,EACX,OAAO,CAAC,EACR,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EACxB,KAAK,QAAQ,CAAC,aAAa,KAC1B,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,gBAAgB,GAAG,WAAW,CAAC,UAAU,EAAE,YAAY,CAQvF,CAAA;AAEJ;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,GAC1B,KAAK,MAAM,KACV,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,gBAAgB,EAAE,YAAY,CAIjD,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 ADDED
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Cache Module
3
+ *
4
+ * Simple cache abstraction for storing expensive DB operations.
5
+ * Uses CacheService which is automatically provided and backed by Cloudflare KV by default.
6
+ * Can be swapped for Redis, Memcached, or any other implementation.
7
+ */
8
+ import { Effect, Option, Schema, Duration } from 'effect';
9
+ import { CacheService } from './effect/services.js';
10
+ // ============================================================================
11
+ // Errors
12
+ // ============================================================================
13
+ export class CacheError extends Schema.TaggedError()('CacheError', {
14
+ reason: Schema.String,
15
+ cause: Schema.optional(Schema.Unknown),
16
+ }) {
17
+ }
18
+ // ============================================================================
19
+ // Composable API
20
+ // ============================================================================
21
+ /**
22
+ * Cache a computed value with automatic serialization and TTL.
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * const user = yield* cache(
27
+ * `user:${id}`,
28
+ * Effect.tryPromise(() => db.query.users.findFirst({ where: eq(users.id, id) })),
29
+ * UserSchema,
30
+ * Duration.hours(1)
31
+ * )
32
+ * ```
33
+ */
34
+ export const cache = (key, compute, schema, ttl) => Effect.gen(function* () {
35
+ const cacheService = yield* CacheService;
36
+ // Check cache first
37
+ const cached = yield* cacheService.get(key);
38
+ if (cached !== null) {
39
+ return yield* Schema.decodeUnknown(Schema.parseJson(schema))(cached);
40
+ }
41
+ // Compute value
42
+ const value = yield* compute;
43
+ // Store in cache
44
+ const serialized = yield* Schema.encode(Schema.parseJson(schema))(value);
45
+ const ttlSeconds = Duration.toSeconds(Duration.decode(ttl));
46
+ yield* cacheService.put(key, serialized, { expirationTtl: ttlSeconds });
47
+ return value;
48
+ });
49
+ /**
50
+ * Get a value from cache without computing.
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * const cached = yield* cacheGet(`user:${id}`, UserSchema)
55
+ * if (Option.isSome(cached)) {
56
+ * return cached.value
57
+ * }
58
+ * ```
59
+ */
60
+ export const cacheGet = (key, schema) => Effect.gen(function* () {
61
+ const cacheService = yield* CacheService;
62
+ const cached = yield* cacheService.get(key);
63
+ if (cached === null) {
64
+ return Option.none();
65
+ }
66
+ const decoded = yield* Schema.decodeUnknown(Schema.parseJson(schema))(cached);
67
+ return Option.some(decoded);
68
+ });
69
+ /**
70
+ * Set a value in cache.
71
+ *
72
+ * @example
73
+ * ```typescript
74
+ * yield* cacheSet(`user:${id}`, user, UserSchema, Duration.hours(1))
75
+ * ```
76
+ */
77
+ export const cacheSet = (key, value, schema, ttl) => Effect.gen(function* () {
78
+ const cacheService = yield* CacheService;
79
+ const serialized = yield* Schema.encode(Schema.parseJson(schema))(value);
80
+ const ttlSeconds = Duration.toSeconds(Duration.decode(ttl));
81
+ yield* cacheService.put(key, serialized, { expirationTtl: ttlSeconds });
82
+ });
83
+ /**
84
+ * Invalidate a cache key.
85
+ *
86
+ * @example
87
+ * ```typescript
88
+ * yield* cacheInvalidate(`user:${id}`)
89
+ * ```
90
+ */
91
+ export const cacheInvalidate = (key) => Effect.gen(function* () {
92
+ const cacheService = yield* CacheService;
93
+ yield* cacheService.delete(key);
94
+ });
95
+ /**
96
+ * Invalidate all cache keys with a given prefix.
97
+ *
98
+ * @example
99
+ * ```typescript
100
+ * yield* cacheInvalidatePrefix(`user:${userId}:`)
101
+ * ```
102
+ */
103
+ export const cacheInvalidatePrefix = (prefix) => Effect.gen(function* () {
104
+ const cacheService = yield* CacheService;
105
+ const list = yield* cacheService.list({ prefix });
106
+ yield* Effect.forEach(list.keys, (key) => cacheService.delete(key.name), { concurrency: 10 });
107
+ });
@@ -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 } from './services.js';
8
+ import { DatabaseService, AuthService, AuthUserService, HonertiaService, RequestService, ResponseFactoryService, BindingsService, CacheService } 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 | 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 | 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,EAQhB,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;AAsDD;;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,cAAc,EAChB,KAAK,EACL,KAAK,CACN,CAoEA;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"}
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,EAUb,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;AA2GD;;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,cAAc,EAChB,KAAK,EACL,KAAK,CACN,CA6EA;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"}
@@ -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, } from './services.js';
9
+ import { DatabaseService, AuthService, AuthUserService, HonertiaService, RequestService, ResponseFactoryService, BindingsService, CacheService, CacheClientError, } from './services.js';
10
10
  import { TestCaptureService } from './test-layers.js';
11
11
  /**
12
12
  * Symbol for storing Effect runtime in Hono context.
@@ -88,6 +88,41 @@ function createResponseFactory(c) {
88
88
  notFound: () => c.notFound(),
89
89
  };
90
90
  }
91
+ /**
92
+ * Create a CacheClient from Cloudflare KV binding.
93
+ */
94
+ function createKVCacheClient(kv) {
95
+ return {
96
+ get: (key) => Effect.tryPromise({
97
+ try: () => kv.get(key),
98
+ catch: (e) => new CacheClientError('Failed to get from cache', e),
99
+ }),
100
+ put: (key, value, options) => Effect.tryPromise({
101
+ try: () => kv.put(key, value, options),
102
+ catch: (e) => new CacheClientError('Failed to set cache', e),
103
+ }),
104
+ delete: (key) => Effect.tryPromise({
105
+ try: () => kv.delete(key),
106
+ catch: (e) => new CacheClientError('Failed to delete from cache', e),
107
+ }),
108
+ list: (options) => Effect.tryPromise({
109
+ try: () => kv.list(options),
110
+ catch: (e) => new CacheClientError('Failed to list cache keys', e),
111
+ }),
112
+ };
113
+ }
114
+ /**
115
+ * Create a no-op CacheClient that fails with helpful errors when KV is not configured.
116
+ */
117
+ function createUnconfiguredCacheClient() {
118
+ const error = new CacheClientError('CacheService requires KV binding. Add KV to your wrangler.toml and ensure it is available in c.env.KV');
119
+ return {
120
+ get: () => Effect.fail(error),
121
+ put: () => Effect.fail(error),
122
+ delete: () => Effect.fail(error),
123
+ list: () => Effect.fail(error),
124
+ };
125
+ }
91
126
  /**
92
127
  * Create a HonertiaRenderer from Hono context.
93
128
  */
@@ -115,6 +150,9 @@ export function buildContextLayer(c, config) {
115
150
  const honertiaLayer = Layer.succeed(HonertiaService, createHonertiaRenderer(c));
116
151
  // Bindings layer - always available, typed via module augmentation
117
152
  const bindingsLayer = Layer.succeed(BindingsService, (c.env ?? {}));
153
+ // Cache layer - backed by KV if available, otherwise unconfigured client
154
+ const kv = c.env?.KV;
155
+ const cacheLayer = Layer.succeed(CacheService, kv ? createKVCacheClient(kv) : createUnconfiguredCacheClient());
118
156
  // Database layer - provide helpful error proxy if not configured
119
157
  const db = c.var?.db;
120
158
  const databaseLayer = Layer.succeed(DatabaseService, (db ??
@@ -123,7 +161,7 @@ export function buildContextLayer(c, config) {
123
161
  const auth = c.var?.auth;
124
162
  const authLayer = Layer.succeed(AuthService, (auth ??
125
163
  createUnconfiguredServiceProxy('AuthService', 'auth: (c) => createAuth(...)', 'auth: (c) => betterAuth({ database: c.var.db, ... })', ErrorCodes.CFG_301_AUTH_NOT_CONFIGURED)));
126
- let baseLayer = Layer.mergeAll(requestLayer, responseLayer, honertiaLayer, bindingsLayer, databaseLayer, authLayer);
164
+ let baseLayer = Layer.mergeAll(requestLayer, responseLayer, honertiaLayer, bindingsLayer, cacheLayer, databaseLayer, authLayer);
127
165
  if (c.var?.authUser) {
128
166
  baseLayer = Layer.merge(baseLayer, Layer.succeed(AuthUserService, c.var.authUser));
129
167
  }
@@ -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, type AuthUser, type EmailClient, type HonertiaRenderer, type RequestContext, type ResponseFactory, type HonertiaDatabaseType, type HonertiaAuthType, type HonertiaBindingsType, type DatabaseType, type SchemaType, type AuthType, type BindingsType, } from './services.js';
6
+ export { DatabaseService, AuthService, AuthUserService, EmailService, HonertiaService, RequestService, ResponseFactoryService, BindingsService, CacheService, CacheClientError, type AuthUser, type EmailClient, type HonertiaRenderer, type RequestContext, type ResponseFactory, type CacheClient, type HonertiaDatabaseType, type HonertiaAuthType, type HonertiaBindingsType, 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,5 +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
25
  export { RequireAuthLayer, RequireGuestLayer, isAuthenticated, currentUser, requireAuth, requireGuest, shareAuth, shareAuthMiddleware, effectAuthRoutes, betterAuthFormAction, betterAuthLogoutAction, loadUser, type AuthRoutesConfig, type BetterAuthFormActionConfig, type BetterAuthLogoutConfig, type BetterAuthActionResult, } from './auth.js';
25
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":"AAAA;;;;GAIG;AAGH,OAAO,EACL,eAAe,EACf,WAAW,EACX,eAAe,EACf,YAAY,EACZ,eAAe,EACf,cAAc,EACd,sBAAsB,EACtB,eAAe,EACf,YAAY,EACZ,gBAAgB,EAChB,KAAK,QAAQ,EACb,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,QAAQ,EACb,KAAK,YAAY,GAClB,MAAM,eAAe,CAAA;AAGtB,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,aAAa,EACb,cAAc,EACd,SAAS,EACT,uBAAuB,EACvB,0BAA0B,EAC1B,QAAQ,EACR,iBAAiB,EACjB,iBAAiB,EACjB,KAAK,QAAQ,EACb,KAAK,sBAAsB,GAC5B,MAAM,aAAa,CAAA;AAGpB,YAAY,EACV,aAAa,EACb,YAAY,EACZ,cAAc,EACd,WAAW,EACX,YAAY,EACZ,cAAc,EACd,cAAc,IAAI,mBAAmB,EACrC,cAAc,EACd,OAAO,EACP,WAAW,EACX,YAAY,EACZ,UAAU,EACV,aAAa,EACb,SAAS,EACT,uBAAuB,EACvB,UAAU,EACV,mBAAmB,EACnB,sBAAsB,EACtB,gBAAgB,EAChB,eAAe,EACf,YAAY,GACb,MAAM,kBAAkB,CAAA;AAGzB,OAAO,EACL,UAAU,EACV,YAAY,EACZ,qBAAqB,EACrB,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,EACnB,KAAK,SAAS,GACf,MAAM,oBAAoB,CAAA;AAG3B,OAAO,EACL,kBAAkB,EAClB,sBAAsB,EACtB,qBAAqB,EACrB,kBAAkB,EAClB,eAAe,EACf,KAAK,cAAc,EACnB,KAAK,oBAAoB,EACzB,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,EAC5B,KAAK,YAAY,EACjB,KAAK,sBAAsB,GAC5B,MAAM,sBAAsB,CAAA;AAG7B,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,eAAe,EACf,aAAa,EACb,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,aAAa,EACb,YAAY,EACZ,KAAK,UAAU,EACf,KAAK,oBAAoB,GAC1B,MAAM,oBAAoB,CAAA;AAG3B,cAAc,aAAa,CAAA;AAG3B,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,6BAA6B,EAC7B,QAAQ,EACR,eAAe,EACf,WAAW,EACX,SAAS,EACT,KAAK,SAAS,EACd,KAAK,OAAO,EACZ,KAAK,qBAAqB,GAC3B,MAAM,iBAAiB,CAAA;AAGxB,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,aAAa,EACb,cAAc,GACf,MAAM,yBAAyB,CAAA;AAGhC,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EACf,KAAK,kBAAkB,GACxB,MAAM,aAAa,CAAA;AAGpB,OAAO,EACL,aAAa,EACb,MAAM,EACN,MAAM,EACN,eAAe,EACf,uBAAuB,GACxB,MAAM,cAAc,CAAA;AAGrB,OAAO,EACL,MAAM,EACN,SAAS,EACT,UAAU,EACV,aAAa,EACb,KAAK,MAAM,GACZ,MAAM,aAAa,CAAA;AAGpB,OAAO,EACL,QAAQ,EACR,MAAM,EACN,gBAAgB,EAChB,IAAI,EACJ,IAAI,EACJ,QAAQ,EACR,SAAS,EACT,SAAS,EACT,WAAW,EACX,YAAY,EACZ,KAAK,GACN,MAAM,gBAAgB,CAAA;AAGvB,OAAO,EACL,kBAAkB,EAClB,YAAY,EACZ,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,GACxB,MAAM,cAAc,CAAA;AAGrB,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,mBAAmB,EACnB,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,GACtB,MAAM,qBAAqB,CAAA;AAG5B,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,iBAAiB,EACjB,KAAK,YAAY,EACjB,KAAK,QAAQ,EACb,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,MAAM,EACX,KAAK,aAAa,GACnB,MAAM,cAAc,CAAA;AAGrB,OAAO,EACL,SAAS,EACT,kBAAkB,EAClB,KAAK,YAAY,GAClB,MAAM,kBAAkB,CAAA;AAGzB,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,KAAK,EACL,SAAS,EACT,aAAa,EACb,UAAU,EACV,KAAK,aAAa,EAClB,KAAK,UAAU,GAChB,MAAM,cAAc,CAAA;AAGrB,OAAO,EACL,UAAU,EACV,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,eAAe,EACf,qBAAqB,GACtB,MAAM,aAAa,CAAA;AAGpB,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,WAAW,EACX,WAAW,EACX,YAAY,EACZ,SAAS,EACT,mBAAmB,EACnB,gBAAgB,EAChB,oBAAoB,EACpB,sBAAsB,EACtB,QAAQ,EACR,KAAK,gBAAgB,EACrB,KAAK,0BAA0B,EAC/B,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,GAC5B,MAAM,WAAW,CAAA"}
@@ -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, } from './services.js';
7
+ export { DatabaseService, AuthService, AuthUserService, EmailService, HonertiaService, RequestService, ResponseFactoryService, BindingsService, CacheService, CacheClientError, } 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
@@ -37,5 +37,7 @@ export { describeRoute, createRouteTester, generateTestCases, } from './testing.
37
37
  export { TestLayer, TestCaptureService, } from './test-layers.js';
38
38
  // Route Model Binding
39
39
  export { BoundModels, BoundModelNotFound, bound, pluralize, parseBindings, toHonoPath, } from './binding.js';
40
+ // Cache
41
+ export { CacheError, cache, cacheGet, cacheSet, cacheInvalidate, cacheInvalidatePrefix, } from '../cache.js';
40
42
  // Auth
41
43
  export { RequireAuthLayer, RequireGuestLayer, isAuthenticated, currentUser, requireAuth, requireGuest, shareAuth, shareAuthMiddleware, effectAuthRoutes, betterAuthFormAction, betterAuthLogoutAction, loadUser, } from './auth.js';
@@ -212,5 +212,44 @@ export interface ResponseFactory {
212
212
  declare const ResponseFactoryService_base: Context.TagClass<ResponseFactoryService, "honertia/ResponseFactory", ResponseFactory>;
213
213
  export declare class ResponseFactoryService extends ResponseFactoryService_base {
214
214
  }
215
+ /**
216
+ * Cache Service - KV-backed caching for expensive operations
217
+ *
218
+ * Automatically provided by setupHonertia when KV binding is available.
219
+ * Falls back to a no-op implementation if KV is not configured.
220
+ *
221
+ * @example
222
+ * ```typescript
223
+ * // In your action
224
+ * const cacheService = yield* CacheService
225
+ * const cached = yield* cacheService.get('user:123')
226
+ * ```
227
+ */
228
+ export interface CacheClient {
229
+ get(key: string): Effect.Effect<string | null, CacheClientError>;
230
+ put(key: string, value: string, options?: {
231
+ expirationTtl?: number;
232
+ }): Effect.Effect<void, CacheClientError>;
233
+ delete(key: string): Effect.Effect<void, CacheClientError>;
234
+ list(options?: {
235
+ prefix?: string;
236
+ }): Effect.Effect<{
237
+ keys: Array<{
238
+ name: string;
239
+ }>;
240
+ }, CacheClientError>;
241
+ }
242
+ /**
243
+ * Error from cache client operations.
244
+ */
245
+ export declare class CacheClientError {
246
+ readonly reason: string;
247
+ readonly cause?: unknown | undefined;
248
+ readonly _tag = "CacheClientError";
249
+ constructor(reason: string, cause?: unknown | undefined);
250
+ }
251
+ declare const CacheService_base: Context.TagClass<CacheService, "honertia/Cache", CacheClient>;
252
+ export declare class CacheService extends CacheService_base {
253
+ }
215
254
  export {};
216
255
  //# sourceMappingURL=services.d.ts.map
@@ -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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"}
@@ -47,3 +47,17 @@ export class RequestService extends Context.Tag('honertia/Request')() {
47
47
  }
48
48
  export class ResponseFactoryService extends Context.Tag('honertia/ResponseFactory')() {
49
49
  }
50
+ /**
51
+ * Error from cache client operations.
52
+ */
53
+ export class CacheClientError {
54
+ reason;
55
+ cause;
56
+ _tag = 'CacheClientError';
57
+ constructor(reason, cause) {
58
+ this.reason = reason;
59
+ this.cause = cause;
60
+ }
61
+ }
62
+ export class CacheService extends Context.Tag('honertia/Cache')() {
63
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "honertia",
3
- "version": "0.1.31",
3
+ "version": "0.1.32",
4
4
  "description": "Inertia.js-style server-driven SPA adapter for Hono",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -30,6 +30,10 @@
30
30
  "./cli": {
31
31
  "types": "./dist/cli/index.d.ts",
32
32
  "import": "./dist/cli/index.js"
33
+ },
34
+ "./cache": {
35
+ "types": "./dist/cache.d.ts",
36
+ "import": "./dist/cache.js"
33
37
  }
34
38
  },
35
39
  "files": [