@timber-js/app 0.2.0-alpha.166 → 0.2.0-alpha.168

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.
Files changed (188) hide show
  1. package/dist/_chunks/{actions-CDPfMp_I.js → actions-O_LsyCE4.js} +3 -3
  2. package/dist/_chunks/{actions-CDPfMp_I.js.map → actions-O_LsyCE4.js.map} +1 -1
  3. package/dist/_chunks/{cache-api-DygSeKCB.js → cache-api-B-lhk9p4.js} +107 -58
  4. package/dist/_chunks/cache-api-B-lhk9p4.js.map +1 -0
  5. package/dist/_chunks/{cli-schema-sync-EXGYPhI2.js → cli-schema-sync-B73L6pMq.js} +5 -3
  6. package/dist/_chunks/cli-schema-sync-B73L6pMq.js.map +1 -0
  7. package/dist/_chunks/cloudflare-AHoWYTYr.js +1188 -0
  8. package/dist/_chunks/cloudflare-AHoWYTYr.js.map +1 -0
  9. package/dist/_chunks/json-lossy-check-ClNvBM_3.js +63 -0
  10. package/dist/_chunks/json-lossy-check-ClNvBM_3.js.map +1 -0
  11. package/dist/_chunks/{logger-t3uxAmbX.js → logger-AWfuX-KJ.js} +2 -19
  12. package/dist/_chunks/logger-AWfuX-KJ.js.map +1 -0
  13. package/dist/_chunks/{walkers-DBVzXuWc.js → walkers-CoOC8Hga.js} +2 -2
  14. package/dist/_chunks/{walkers-DBVzXuWc.js.map → walkers-CoOC8Hga.js.map} +1 -1
  15. package/dist/adapters/cloudflare-dev.js +1 -1
  16. package/dist/adapters/cloudflare-kv-cache.d.ts.map +1 -1
  17. package/dist/adapters/cloudflare-kv-cache.js +10 -3
  18. package/dist/adapters/cloudflare-kv-cache.js.map +1 -1
  19. package/dist/adapters/cloudflare.d.ts +12 -1
  20. package/dist/adapters/cloudflare.d.ts.map +1 -1
  21. package/dist/adapters/cloudflare.js +2 -461
  22. package/dist/adapters/types.d.ts +2 -0
  23. package/dist/adapters/types.d.ts.map +1 -1
  24. package/dist/cache/cache-api.d.ts +33 -11
  25. package/dist/cache/cache-api.d.ts.map +1 -1
  26. package/dist/cache/index.d.ts +1 -1
  27. package/dist/cache/index.d.ts.map +1 -1
  28. package/dist/cache/index.js +1 -1
  29. package/dist/cache/json-lossy-check.d.ts +11 -0
  30. package/dist/cache/json-lossy-check.d.ts.map +1 -0
  31. package/dist/cache/redis-handler.d.ts +42 -0
  32. package/dist/cache/redis-handler.d.ts.map +1 -1
  33. package/dist/cache/singleflight.d.ts.map +1 -1
  34. package/dist/cache/stores/cloudflare-kv.d.ts +1 -1
  35. package/dist/cache/stores/cloudflare-kv.d.ts.map +1 -1
  36. package/dist/cache/stores/memory.d.ts +1 -1
  37. package/dist/cache/stores/memory.d.ts.map +1 -1
  38. package/dist/cache/stores/redis.d.ts +1 -1
  39. package/dist/cache/stores/redis.d.ts.map +1 -1
  40. package/dist/cache/stores/vercel.d.ts +1 -1
  41. package/dist/cache/stores/vercel.d.ts.map +1 -1
  42. package/dist/cache/tag-aware-handler.d.ts.map +1 -1
  43. package/dist/cache/timber-cache.d.ts.map +1 -1
  44. package/dist/cdn/cloudflare-purge.d.ts.map +1 -1
  45. package/dist/cdn/fastly-purge.d.ts.map +1 -1
  46. package/dist/cdn/workers-cache-purge.d.ts.map +1 -1
  47. package/dist/cli.d.ts +1 -1
  48. package/dist/cli.d.ts.map +1 -1
  49. package/dist/cli.js +3 -2
  50. package/dist/cli.js.map +1 -1
  51. package/dist/client/browser-entry/post-hydration.d.ts.map +1 -1
  52. package/dist/client/browser-entry/router-init.d.ts +1 -0
  53. package/dist/client/browser-entry/router-init.d.ts.map +1 -1
  54. package/dist/client/child-segment-context.d.ts +2 -2
  55. package/dist/client/child-segment-context.d.ts.map +1 -1
  56. package/dist/client/error-boundary.d.ts.map +1 -1
  57. package/dist/client/history.d.ts +10 -0
  58. package/dist/client/history.d.ts.map +1 -1
  59. package/dist/client/internal.js +141 -116
  60. package/dist/client/internal.js.map +1 -1
  61. package/dist/client/router.d.ts +6 -0
  62. package/dist/client/router.d.ts.map +1 -1
  63. package/dist/client/rsc-fetch.d.ts.map +1 -1
  64. package/dist/client/segment-cache.d.ts.map +1 -1
  65. package/dist/client/segment-update-context.d.ts +3 -3
  66. package/dist/client/segment-update-context.d.ts.map +1 -1
  67. package/dist/client/slot-outlet.d.ts +1 -1
  68. package/dist/codec.d.ts.map +1 -1
  69. package/dist/codec.js +2 -1
  70. package/dist/codec.js.map +1 -1
  71. package/dist/config-types.d.ts +16 -37
  72. package/dist/config-types.d.ts.map +1 -1
  73. package/dist/config-validation.d.ts.map +1 -1
  74. package/dist/dev-tools/instrumentation.d.ts.map +1 -1
  75. package/dist/fonts/pipeline.d.ts +19 -0
  76. package/dist/fonts/pipeline.d.ts.map +1 -1
  77. package/dist/fonts/transform.d.ts.map +1 -1
  78. package/dist/fonts/virtual-modules.d.ts.map +1 -1
  79. package/dist/index.d.ts.map +1 -1
  80. package/dist/index.js +378 -109
  81. package/dist/index.js.map +1 -1
  82. package/dist/plugin-context.d.ts.map +1 -1
  83. package/dist/plugins/adapter-build.d.ts +1 -0
  84. package/dist/plugins/adapter-build.d.ts.map +1 -1
  85. package/dist/plugins/cache.d.ts +8 -8
  86. package/dist/plugins/cache.d.ts.map +1 -1
  87. package/dist/plugins/entries.d.ts +27 -3
  88. package/dist/plugins/entries.d.ts.map +1 -1
  89. package/dist/plugins/fonts.d.ts.map +1 -1
  90. package/dist/plugins/server-bundle.d.ts.map +1 -1
  91. package/dist/routing/index.js +2 -2
  92. package/dist/server/action-client.d.ts +1 -1
  93. package/dist/server/action-client.d.ts.map +1 -1
  94. package/dist/server/als-registry.d.ts +7 -0
  95. package/dist/server/als-registry.d.ts.map +1 -1
  96. package/dist/server/body-limits.d.ts.map +1 -1
  97. package/dist/server/deny-boundary.d.ts +3 -1
  98. package/dist/server/deny-boundary.d.ts.map +1 -1
  99. package/dist/server/form-data.d.ts.map +1 -1
  100. package/dist/server/html-injector-core.d.ts.map +1 -1
  101. package/dist/server/index.js +20 -3
  102. package/dist/server/index.js.map +1 -1
  103. package/dist/server/internal.js +28 -41
  104. package/dist/server/internal.js.map +1 -1
  105. package/dist/server/middleware-runner.d.ts +0 -17
  106. package/dist/server/middleware-runner.d.ts.map +1 -1
  107. package/dist/server/param-coercion.d.ts.map +1 -1
  108. package/dist/server/pipeline-phases.d.ts +6 -3
  109. package/dist/server/pipeline-phases.d.ts.map +1 -1
  110. package/dist/server/pipeline.d.ts +18 -0
  111. package/dist/server/pipeline.d.ts.map +1 -1
  112. package/dist/server/prebuilt/capture-state.d.ts.map +1 -1
  113. package/dist/server/prebuilt/synthetic-store.d.ts.map +1 -1
  114. package/dist/server/primitives.d.ts.map +1 -1
  115. package/dist/server/render-timeout.d.ts.map +1 -1
  116. package/dist/server/route-element-builder.d.ts +9 -0
  117. package/dist/server/route-element-builder.d.ts.map +1 -1
  118. package/dist/server/rsc-entry/action-middleware-runner.d.ts +14 -14
  119. package/dist/server/rsc-entry/index.d.ts.map +1 -1
  120. package/dist/server/rsc-entry/render-route.d.ts +1 -0
  121. package/dist/server/rsc-entry/render-route.d.ts.map +1 -1
  122. package/dist/server/rsc-entry/revalidate-renderer.d.ts.map +1 -1
  123. package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
  124. package/dist/server/rsc-entry/rsc-stream.d.ts +8 -0
  125. package/dist/server/rsc-entry/rsc-stream.d.ts.map +1 -1
  126. package/dist/server/rsc-entry/wrap-action-dispatch.d.ts +44 -74
  127. package/dist/server/rsc-entry/wrap-action-dispatch.d.ts.map +1 -1
  128. package/dist/server/safe-load.d.ts.map +1 -1
  129. package/dist/server/ssr-entry.d.ts.map +1 -1
  130. package/dist/shared/redirect-type.d.ts +2 -2
  131. package/dist/shared/redirect-type.d.ts.map +1 -1
  132. package/dist/shims/font-google.d.ts.map +1 -1
  133. package/dist/shims/image.d.ts +120 -120
  134. package/dist/shims/image.d.ts.map +1 -1
  135. package/docs/api/32-api-cache.mdx +116 -4
  136. package/docs/api/34-api-config.mdx +0 -26
  137. package/docs/learn/09-caching.mdx +126 -10
  138. package/docs/learn/12-client-navigation.mdx +9 -1
  139. package/docs/learn/13-configuration.mdx +6 -8
  140. package/docs/learn/14-deploying.mdx +18 -18
  141. package/docs/more/03-coming-from-nextjs.mdx +2 -2
  142. package/docs/more/04-metadata-and-fonts.mdx +1 -1
  143. package/docs/more/50-ai-agent-instructions.mdx +5 -5
  144. package/package.json +6 -5
  145. package/src/adapters/cloudflare-kv-cache.ts +27 -6
  146. package/src/adapters/cloudflare.ts +63 -25
  147. package/src/adapters/types.ts +2 -0
  148. package/src/cache/cache-api.ts +84 -84
  149. package/src/cache/index.ts +1 -1
  150. package/src/cache/json-lossy-check.ts +75 -0
  151. package/src/cache/redis-handler.ts +65 -14
  152. package/src/cache/timber-cache.ts +10 -2
  153. package/src/client/browser-entry/index.ts +2 -0
  154. package/src/client/browser-entry/post-hydration.ts +16 -9
  155. package/src/client/browser-entry/router-init.ts +2 -0
  156. package/src/client/history.ts +11 -0
  157. package/src/client/router.ts +224 -208
  158. package/src/codec.ts +3 -1
  159. package/src/config-types.ts +16 -37
  160. package/src/config-validation.ts +7 -5
  161. package/src/fonts/pipeline.ts +32 -0
  162. package/src/fonts/transform.ts +30 -24
  163. package/src/plugins/adapter-build.ts +132 -13
  164. package/src/plugins/cache.ts +45 -30
  165. package/src/plugins/entries.ts +40 -43
  166. package/src/plugins/fonts.ts +30 -0
  167. package/src/plugins/server-bundle.ts +7 -15
  168. package/src/routing/scanner.ts +7 -0
  169. package/src/server/action-client.ts +1 -1
  170. package/src/server/als-registry.ts +7 -0
  171. package/src/server/deny-boundary.ts +6 -3
  172. package/src/server/middleware-runner.ts +0 -44
  173. package/src/server/param-coercion.ts +1 -0
  174. package/src/server/pipeline-phases.ts +11 -13
  175. package/src/server/pipeline.ts +51 -3
  176. package/src/server/route-element-builder.ts +59 -9
  177. package/src/server/rsc-entry/action-middleware-runner.ts +14 -14
  178. package/src/server/rsc-entry/index.ts +30 -40
  179. package/src/server/rsc-entry/render-route.ts +6 -2
  180. package/src/server/rsc-entry/rsc-payload.ts +21 -4
  181. package/src/server/rsc-entry/rsc-stream.ts +8 -0
  182. package/src/server/rsc-entry/wrap-action-dispatch.ts +109 -372
  183. package/dist/_chunks/cache-api-DygSeKCB.js.map +0 -1
  184. package/dist/_chunks/cli-schema-sync-EXGYPhI2.js.map +0 -1
  185. package/dist/_chunks/logger-t3uxAmbX.js.map +0 -1
  186. package/dist/_chunks/tree-match-D2l830j2.js +0 -102
  187. package/dist/_chunks/tree-match-D2l830j2.js.map +0 -1
  188. package/dist/adapters/cloudflare.js.map +0 -1
@@ -1,461 +1,2 @@
1
- import { t as runSharedBuildSteps } from "../_chunks/build-output-helper-DF5uScqS.js";
2
- import { join, relative } from "node:path";
3
- import { readFile, readdir, rm, writeFile } from "node:fs/promises";
4
- import { AsyncLocalStorage } from "node:async_hooks";
5
- import { execFile } from "node:child_process";
6
- //#region src/adapters/cloudflare.ts
7
- var BINDINGS_ALS_KEY = Symbol.for("timber:cf-bindings-als");
8
- function getBindingsAls() {
9
- let als = globalThis[BINDINGS_ALS_KEY];
10
- if (!als) {
11
- als = new AsyncLocalStorage();
12
- globalThis[BINDINGS_ALS_KEY] = als;
13
- }
14
- return als;
15
- }
16
- /**
17
- * Get Cloudflare Worker bindings for the current request.
18
- *
19
- * Returns the `env` object passed to the Worker's `fetch` handler,
20
- * giving direct access to KV, D1, Durable Objects, R2, Queues, and
21
- * any other bindings configured in `wrangler.jsonc`.
22
- *
23
- * Must be called within a request context (server component, middleware,
24
- * server action). Throws outside a request.
25
- *
26
- * @example
27
- * ```ts
28
- * import { getCloudflareBindings } from '@timber-js/app/adapters/cloudflare'
29
- *
30
- * export default async function Page() {
31
- * const { MY_KV, MY_DB } = getCloudflareBindings()
32
- * const data = await MY_KV.get('key')
33
- * return <div>{data}</div>
34
- * }
35
- * ```
36
- */
37
- function getCloudflareBindings() {
38
- const env = getBindingsAls().getStore();
39
- if (!env) throw new Error("getCloudflareBindings() called outside a Cloudflare Workers request context. It can only be called from server components, middleware, or server actions when running on the Cloudflare adapter.");
40
- return env;
41
- }
42
- /**
43
- * Run a function with Cloudflare bindings available via getCloudflareBindings().
44
- * @internal Used by cloudflareDevBindings() to wrap dev-server requests.
45
- */
46
- function runWithBindings(env, fn) {
47
- return getBindingsAls().run(env, fn);
48
- }
49
- /**
50
- * Create a Cloudflare Workers adapter.
51
- *
52
- * @example
53
- * ```ts
54
- * import { cloudflare } from '@timber-js/app/adapters/cloudflare'
55
- *
56
- * export default {
57
- * output: 'server',
58
- * adapter: cloudflare(),
59
- * }
60
- * ```
61
- */
62
- function cloudflare(options = {}) {
63
- return {
64
- name: "cloudflare",
65
- async buildOutput(config, buildDir) {
66
- const outDir = join(buildDir, "cloudflare");
67
- await rm(join(outDir, "prebuilt"), {
68
- recursive: true,
69
- force: true
70
- }).catch(() => {});
71
- await rm(join(outDir, "static", "prebuilt"), {
72
- recursive: true,
73
- force: true
74
- }).catch(() => {});
75
- await runSharedBuildSteps({
76
- config,
77
- buildDir,
78
- outDir,
79
- publicDirName: "static"
80
- });
81
- let hasWorkerHandlers = false;
82
- if (options.workerHandlers) {
83
- const handlersEntry = join(process.cwd(), options.workerHandlers);
84
- const { build: viteBuild } = await import("vite");
85
- await viteBuild({
86
- configFile: false,
87
- logLevel: "info",
88
- build: {
89
- lib: {
90
- entry: handlersEntry,
91
- formats: ["es"],
92
- fileName: () => "_worker-handlers.js"
93
- },
94
- outDir,
95
- emptyOutDir: false,
96
- minify: false,
97
- rollupOptions: { external: [/^node:/, /^cloudflare:/] }
98
- }
99
- });
100
- hasWorkerHandlers = true;
101
- }
102
- const prebuiltData = await readPrebuiltData(buildDir);
103
- await writeFile(join(outDir, "_timber-prebuilt-source.js"), generatePrebuiltSourceModule(prebuiltData));
104
- await rm(join(outDir, "prebuilt"), {
105
- recursive: true,
106
- force: true
107
- }).catch(() => {});
108
- await writeFile(join(outDir, "_timber-runtime-init.js"), generateRuntimeInitModule());
109
- const workerEntry = generateWorkerEntry(outDir, outDir, !!config.manifestInit, hasWorkerHandlers);
110
- await writeFile(join(outDir, "_worker.js"), workerEntry);
111
- const wranglerConfig = generateWranglerConfig(config, options);
112
- await writeFile(join(outDir, "wrangler.jsonc"), JSON.stringify(wranglerConfig, null, 2));
113
- },
114
- async preview(_config, buildDir) {
115
- const cmd = generatePreviewCommand(buildDir);
116
- await spawnPreviewProcess(cmd.command, cmd.args, cmd.cwd);
117
- }
118
- };
119
- }
120
- /** @internal Exported for testing. */
121
- function generateWorkerEntry(buildDir, outDir, hasManifestInit = false, hasWorkerHandlers = false) {
122
- let rscEntryRelative = relative(outDir, join(buildDir, "rsc", "index.js"));
123
- if (!rscEntryRelative.startsWith(".")) rscEntryRelative = "./" + rscEntryRelative;
124
- return `// Generated by @timber-js/app/adapters/cloudflare
125
- // Do not edit — this file is regenerated on each build.
126
-
127
- import { AsyncLocalStorage } from 'node:async_hooks'
128
- // Side-effect imports: evaluated IN ORDER before the _worker.js body runs.
129
- // _timber-runtime-init.js sets TIMBER_RUNTIME before createRequestHandler()
130
- // runs (during the RSC entry's top-level await) so the auto-wire for
131
- // WorkersCachePurge can detect the Cloudflare runtime (TIM-1170).
132
- // _timber-prebuilt-source.js sets globalThis.__TIMBER_PREBUILT_SOURCE__
133
- // for cache.component payloads.
134
- import './_timber-runtime-init.js'
135
- import './_timber-prebuilt-source.js'
136
- ${hasManifestInit ? "import './_timber-manifest-init.js'\n" : ""}import handler, { runWithWaitUntil, runWithWorkersCachePurge } from '${rscEntryRelative}'
137
- ${hasWorkerHandlers ? "import * as workerHandlers from './_worker-handlers.js'\n" : ""}
138
-
139
- // Bind Cloudflare env to ALS so getCloudflareBindings() works at runtime.
140
- // Uses the same Symbol.for key as getCloudflareBindings() reads from.
141
- const ALS_KEY = Symbol.for('timber:cf-bindings-als')
142
- let bindingsAls = globalThis[ALS_KEY]
143
- if (!bindingsAls) {
144
- bindingsAls = new AsyncLocalStorage()
145
- globalThis[ALS_KEY] = bindingsAls
146
- }
147
-
148
- // Register timber's platform tracer so pipeline phase spans (timber.proxy,
149
- // timber.middleware, timber.render, timber.access, timber.action, …) appear
150
- // as native Cloudflare spans in the Observability dashboard, nested under
151
- // the auto-instrumented fetch/KV/D1 spans. Requires observability.traces
152
- // enabled in wrangler.jsonc (generated by default).
153
- //
154
- // Lazily imported and guarded: runtimes without the custom spans API
155
- // (older compatibility dates, older local workerd) fall back to OTEL-only
156
- // emission. withSpan() in the framework composes both channels.
157
- const TRACER_KEY = Symbol.for('timber:platform-tracer')
158
- let tracerInit
159
- function initPlatformTracer() {
160
- tracerInit ??= import('cloudflare:workers')
161
- .then(({ tracing }) => {
162
- if (tracing && typeof tracing.enterSpan === 'function') {
163
- globalThis[TRACER_KEY] ??= {
164
- enterSpan: (name, fn) => tracing.enterSpan(name, fn),
165
- }
166
- }
167
- })
168
- .catch(() => {})
169
- return tracerInit
170
- }
171
-
172
- export default {
173
- ${hasWorkerHandlers ? " ...workerHandlers,\n" : ""} async fetch(request, env, ctx) {
174
- // One-time on first request; resolved-promise await thereafter.
175
- await initPlatformTracer()
176
-
177
- // Edge-cache GET/HEAD responses via the Cache API. RSC navigation
178
- // requests (Accept: text/x-component) are excluded — they vary by
179
- // X-Timber-State-Tree which the cache key doesn't capture.
180
- const isRsc = request.headers.get('Accept')?.includes('text/x-component')
181
- const reqCc = request.headers.get('Cache-Control') || ''
182
- const bypassCache = reqCc.includes('no-cache') || reqCc.includes('max-age=0')
183
- || request.headers.has('Pragma')
184
- const cacheable = !isRsc && !bypassCache
185
- && (request.method === 'GET' || request.method === 'HEAD')
186
- const cache = cacheable && typeof caches !== 'undefined' ? caches.default : null
187
- if (cache) {
188
- const cached = await cache.match(request)
189
- if (cached) return cached
190
- }
191
-
192
- // Bridge ctx.cache into the per-request ALS so WorkersCachePurge can
193
- // call ctx.cache.purge({ tags }) during cache.invalidate/revalidateTag.
194
- // Workers Cache is available on all Cloudflare plans — no API token needed.
195
- const workersCacheCtx = ctx.cache && typeof ctx.cache.purge === 'function' ? ctx.cache : null
196
- const runHandler = () =>
197
- runWithWaitUntil((promise) => ctx.waitUntil(promise), () => handler(request))
198
- const response = await bindingsAls.run(env, () =>
199
- workersCacheCtx
200
- ? runWithWorkersCachePurge(workersCacheCtx, runHandler)
201
- : runHandler()
202
- )
203
-
204
- // Cache public responses at the edge. A response is edge-cacheable if
205
- // it has 'public' or 's-maxage' (a shared-cache TTL directive) and no
206
- // 'no-store' or 'private'. Cloudflare's cache.put() only accepts GET
207
- // requests and rejects 206, so only store GET 200s. HEAD requests
208
- // benefit from cache.match() which handles HEAD→GET matching automatically.
209
- // See design/25-production-deployments.md §"CDN / Edge Cache".
210
- if (cache && request.method === 'GET' && response.status === 200) {
211
- const cc = response.headers.get('Cache-Control')
212
- const vary = response.headers.get('Vary')
213
- if (cc && !cc.includes('no-store') && !cc.includes('private')
214
- && (cc.includes('public') || cc.includes('s-maxage'))
215
- && vary !== '*' && !response.headers.has('Set-Cookie')) {
216
- ctx.waitUntil(cache.put(request, response.clone()))
217
- }
218
- }
219
-
220
- return response
221
- }
222
- }
223
- `;
224
- }
225
- /**
226
- * Discover and read prebuilt flight payload files from the build directory.
227
- * Returns null when no prebuilt artifacts exist (no cache.component usage).
228
- *
229
- * @internal Exported for testing.
230
- */
231
- async function readPrebuiltData(buildDir) {
232
- const prebuiltDir = join(buildDir, "prebuilt");
233
- let manifestJson;
234
- try {
235
- manifestJson = await readFile(join(prebuiltDir, "manifest.json"), "utf-8");
236
- } catch {
237
- return null;
238
- }
239
- const payloads = [];
240
- try {
241
- const componentDirs = await readdir(prebuiltDir, { withFileTypes: true });
242
- for (const dir of componentDirs) {
243
- if (!dir.isDirectory()) continue;
244
- const files = await readdir(join(prebuiltDir, dir.name));
245
- for (const file of files) if (file.endsWith(".flight")) {
246
- const filePath = `${dir.name}/${file}`;
247
- const bytes = await readFile(join(prebuiltDir, filePath));
248
- payloads.push({
249
- file: filePath,
250
- bytes
251
- });
252
- }
253
- }
254
- } catch {}
255
- return {
256
- manifestJson,
257
- payloads
258
- };
259
- }
260
- /**
261
- * Generate the prebuilt payload source module for Cloudflare Workers.
262
- *
263
- * Inlines manifest JSON and flight payload bytes directly into the module
264
- * so they're part of the worker's JS bundle — no external Text/Data module
265
- * imports needed. This avoids depending on wrangler's find_additional_modules
266
- * to discover non-JS files, which doesn't reliably resolve Text/Data imports.
267
- *
268
- * @internal Exported for testing.
269
- */
270
- function generatePrebuiltSourceModule(data) {
271
- if (!data) return `// Generated by @timber-js/app/adapters/cloudflare
272
- // No prebuilt artifacts — all cache.component wrappers render dynamically.
273
- globalThis.__TIMBER_PREBUILT_SOURCE__ = {
274
- async loadManifest() { return null },
275
- async readPayload() { return null },
276
- }
277
- `;
278
- const entries = [];
279
- for (const { file, bytes } of data.payloads) {
280
- const b64 = bytes.toString("base64");
281
- entries.push(` ${JSON.stringify(file)}: ${JSON.stringify(b64)}`);
282
- }
283
- return `// Generated by @timber-js/app/adapters/cloudflare
284
- // Prebuilt payload source — inlines cache.component flight payloads
285
- // directly into the worker bundle (TIM-1162).
286
- const _manifest = ${data.manifestJson.trim()}
287
-
288
- function _decode(b64) {
289
- const bin = atob(b64)
290
- const buf = new Uint8Array(bin.length)
291
- for (let i = 0; i < bin.length; i++) buf[i] = bin.charCodeAt(i)
292
- return buf
293
- }
294
-
295
- const _payloads = {
296
- ${entries.join(",\n")}
297
- }
298
-
299
- globalThis.__TIMBER_PREBUILT_SOURCE__ = {
300
- async loadManifest() { return _manifest },
301
- async readPayload(file) {
302
- const b64 = _payloads[file]
303
- return b64 ? _decode(b64) : null
304
- },
305
- }
306
- `;
307
- }
308
- /**
309
- * Generate the runtime init module that sets TIMBER_RUNTIME before the
310
- * RSC entry evaluates. Imported as a side-effect module in _worker.js.
311
- *
312
- * @internal Exported for testing.
313
- */
314
- function generateRuntimeInitModule() {
315
- return `// Generated by @timber-js/app/adapters/cloudflare
316
- // Sets TIMBER_RUNTIME before the RSC handler evaluates (TIM-1170).
317
- // Must be a separate module imported before rsc/index.js so that ESM
318
- // evaluation order guarantees the env var is set when
319
- // createRequestHandler() runs its auto-wire logic.
320
- globalThis.process ??= { env: {} }
321
- process.env.TIMBER_RUNTIME = 'cloudflare'
322
- `;
323
- }
324
- /**
325
- * Convert declarative `CloudflareBindings` into wrangler.jsonc binding sections.
326
- *
327
- * Maps:
328
- * - `kv` → `kv_namespaces`
329
- * - `d1` → `d1_databases`
330
- * - `r2` → `r2_buckets`
331
- * - `queues` → `queues` (producers + consumers)
332
- * - `durableObjects` → `durable_objects`
333
- *
334
- * Empty arrays are omitted from the output.
335
- *
336
- * @internal Exported for testing.
337
- */
338
- function generateBindingsConfig(bindings) {
339
- if (!bindings) return {};
340
- const result = {};
341
- if (bindings.kv && bindings.kv.length > 0) result.kv_namespaces = bindings.kv.map((kv) => ({
342
- binding: kv.name,
343
- id: kv.id ?? ""
344
- }));
345
- if (bindings.d1 && bindings.d1.length > 0) result.d1_databases = bindings.d1.map((d1) => ({
346
- binding: d1.name,
347
- database_id: d1.database_id
348
- }));
349
- if (bindings.r2 && bindings.r2.length > 0) result.r2_buckets = bindings.r2.map((r2) => ({
350
- binding: r2.name,
351
- bucket_name: r2.bucket_name
352
- }));
353
- if (bindings.queues) {
354
- const queues = {};
355
- if (bindings.queues.producers && bindings.queues.producers.length > 0) queues.producers = bindings.queues.producers.map((p) => ({
356
- binding: p.name,
357
- queue: p.queue
358
- }));
359
- if (bindings.queues.consumers && bindings.queues.consumers.length > 0) queues.consumers = bindings.queues.consumers.map((c) => {
360
- const consumer = { queue: c.queue };
361
- if (c.max_batch_size !== void 0) consumer.max_batch_size = c.max_batch_size;
362
- if (c.max_retries !== void 0) consumer.max_retries = c.max_retries;
363
- if (c.dead_letter_queue !== void 0) consumer.dead_letter_queue = c.dead_letter_queue;
364
- return consumer;
365
- });
366
- if (Object.keys(queues).length > 0) result.queues = queues;
367
- }
368
- if (bindings.durableObjects && bindings.durableObjects.length > 0) result.durable_objects = { bindings: bindings.durableObjects.map((dobj) => {
369
- const entry = {
370
- name: dobj.name,
371
- class_name: dobj.class_name
372
- };
373
- if (dobj.script_name) entry.script_name = dobj.script_name;
374
- return entry;
375
- }) };
376
- return result;
377
- }
378
- /**
379
- * One-level deep merge: for each key in `override`, if both the base and
380
- * override values are plain objects (not arrays, not null), merge their
381
- * keys with override winning on conflicts. Everything else (primitives,
382
- * arrays, null) is replaced outright — matching the intuitive behavior
383
- * where `kv_namespaces: [...]` replaces fully but
384
- * `durable_objects: { migrations }` merges with generated `bindings`.
385
- */
386
- function shallowDeepMerge(base, override) {
387
- const result = { ...base };
388
- for (const key of Object.keys(override)) {
389
- const baseVal = result[key];
390
- const overVal = override[key];
391
- if (isPlainObject(baseVal) && isPlainObject(overVal)) result[key] = {
392
- ...baseVal,
393
- ...overVal
394
- };
395
- else result[key] = overVal;
396
- }
397
- return result;
398
- }
399
- /** True for `{}` literals — false for arrays, null, Date, etc. */
400
- function isPlainObject(val) {
401
- return val !== null && typeof val === "object" && !Array.isArray(val);
402
- }
403
- /** @internal Exported for testing. */
404
- function generateWranglerConfig(config, options) {
405
- const base = {
406
- name: "timber-app",
407
- main: "_worker.js",
408
- compatibility_date: options.compatibilityDate ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
409
- compatibility_flags: options.compatibilityFlags ?? ["nodejs_compat"],
410
- no_bundle: true,
411
- find_additional_modules: true,
412
- rules: [{
413
- type: "ESModule",
414
- globs: ["**/*.js"]
415
- }],
416
- assets: {
417
- directory: "./static",
418
- binding: "ASSETS"
419
- },
420
- observability: { traces: { enabled: true } }
421
- };
422
- const bindingsConfig = generateBindingsConfig(options.bindings);
423
- const merged = {
424
- ...base,
425
- ...bindingsConfig
426
- };
427
- if (options.wrangler) return shallowDeepMerge(merged, options.wrangler);
428
- return merged;
429
- }
430
- /** @internal Exported for testing. */
431
- function generatePreviewCommand(buildDir) {
432
- const cfDir = join(buildDir, "cloudflare");
433
- return {
434
- command: "wrangler",
435
- args: [
436
- "dev",
437
- "--local",
438
- "--config",
439
- join(cfDir, "wrangler.jsonc")
440
- ],
441
- cwd: cfDir
442
- };
443
- }
444
- /**
445
- * Spawn a long-running preview process and pipe stdio to the parent.
446
- * Resolves when the process exits.
447
- */
448
- function spawnPreviewProcess(command, args, cwd) {
449
- return new Promise((resolve, reject) => {
450
- const child = execFile(command, args, { cwd }, (err) => {
451
- if (err) reject(err);
452
- else resolve();
453
- });
454
- child.stdout?.pipe(process.stdout);
455
- child.stderr?.pipe(process.stderr);
456
- });
457
- }
458
- //#endregion
459
- export { cloudflare, generateBindingsConfig, generatePrebuiltSourceModule, generatePreviewCommand, generateRuntimeInitModule, generateWorkerEntry, generateWranglerConfig, getCloudflareBindings, readPrebuiltData, runWithBindings };
460
-
461
- //# sourceMappingURL=cloudflare.js.map
1
+ import { a as generateRuntimeInitModule, c as getCloudflareBindings, d as runWithBindings, i as generatePreviewCommand, l as readPrebuiltData, n as generateBindingsConfig, o as generateWorkerEntry, r as generatePrebuiltSourceModule, s as generateWranglerConfig, t as cloudflare, u as readUserWranglerConfig } from "../_chunks/cloudflare-AHoWYTYr.js";
2
+ export { cloudflare, generateBindingsConfig, generatePrebuiltSourceModule, generatePreviewCommand, generateRuntimeInitModule, generateWorkerEntry, generateWranglerConfig, getCloudflareBindings, readPrebuiltData, readUserWranglerConfig, runWithBindings };
@@ -3,6 +3,8 @@
3
3
  * A subset of the resolved timber.config.ts relevant to adapters.
4
4
  */
5
5
  export interface TimberConfig {
6
+ /** Absolute path to the project root (Vite root). */
7
+ root?: string;
6
8
  output: 'server' | 'static';
7
9
  clientJavascriptDisabled?: boolean;
8
10
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/adapters/types.ts"],"names":[],"mappings":"AAMA;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC5B,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IACpC,8DAA8D;IAC9D,IAAI,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,WAAW,CAAC,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEnE;;;OAGG;IACH,OAAO,CAAC,CAAC,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEhE;;;;;;;;;OASG;IACH,cAAc,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;CACxC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/adapters/types.ts"],"names":[],"mappings":"AAMA;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,qDAAqD;IACrD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC5B,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IACpC,8DAA8D;IAC9D,IAAI,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,WAAW,CAAC,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEnE;;;OAGG;IACH,OAAO,CAAC,CAAC,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEhE;;;;;;;;;OASG;IACH,cAAc,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;CACxC"}
@@ -1,26 +1,48 @@
1
1
  import type { CacheOptions } from './index';
2
2
  import type { PrebuiltComponentOptions } from '../server/prebuilt-runtime';
3
3
  /**
4
- * Public caching API: `cache(fn, opts)`.
5
- *
6
- * Wraps an async function with cross-request caching. Uses the configured
7
- * cache handler (defaults to MemoryCacheHandler, overridable via timber.config.ts).
4
+ * Cache namespace — `cache.data`, `cache.component`, `cache.invalidate`.
8
5
  *
9
6
  * ```ts
10
7
  * import { cache } from '@timber-js/app/cache';
11
8
  *
12
- * const getUser = cache(
9
+ * const getUser = cache.data(
13
10
  * async (id: string) => db.users.findUnique({ where: { id } }),
14
11
  * { ttl: 60, tags: (id) => [`user:${id}`] }
15
12
  * );
16
13
  * ```
17
14
  */
18
- export declare function cache<Fn extends (...args: any[]) => Promise<any>>(fn: Fn, opts: CacheOptions<Fn>, stableId?: string): Fn;
19
- export declare namespace cache {
20
- var invalidate: (opts: {
15
+ export declare const cache: {
16
+ /**
17
+ * Wrap an async function with cross-request caching.
18
+ *
19
+ * The configured cache handler (defaults to MemoryCacheHandler, overridable
20
+ * via timber.config.ts) is re-resolved on every call, so module-scope
21
+ * wrappers created before `setCacheHandler()` still pick up the configured
22
+ * handler (TIM-1029).
23
+ */
24
+ data<Fn extends (...args: any[]) => Promise<any>>(fn: Fn, opts: CacheOptions<Fn>, stableId?: string): Fn;
25
+ /**
26
+ * Invalidate cache entries by tag or key.
27
+ *
28
+ * ```ts
29
+ * cache.invalidate({ tag: 'products' });
30
+ * cache.invalidate({ key: 'user:abc' });
31
+ * ```
32
+ */
33
+ invalidate(opts: {
21
34
  key?: string;
22
35
  tag?: string;
23
- }) => Promise<void>;
24
- var component: <C extends (props: any) => unknown>(componentFn: C, _options?: PrebuiltComponentOptions) => C;
25
- }
36
+ }): Promise<void>;
37
+ /**
38
+ * Runtime fallback for `cache.component(...)` callsites the timber-prebuilt
39
+ * transform does not rewrite — nested/computed/argument-position forms (see
40
+ * plugins/prebuilt.ts). Only module-scope `const X = cache.component(...)`
41
+ * declarations are transformed and eligible for prebuilding; everything else
42
+ * lands here, warns once per component, and renders dynamically. Same
43
+ * safety-net pattern as the data cache's runtime fnId fallback (TIM-1054):
44
+ * an untraceable callsite degrades in performance, never in correctness.
45
+ */
46
+ component<C extends (props: any) => unknown>(componentFn: C, _options?: PrebuiltComponentOptions): C;
47
+ };
26
48
  //# sourceMappingURL=cache-api.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cache-api.d.ts","sourceRoot":"","sources":["../../src/cache/cache-api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AAO3E;;;;;;;;;;;;;;GAcG;AAEH,wBAAgB,KAAK,CAAC,EAAE,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,EAC/D,EAAE,EAAE,EAAE,EACN,IAAI,EAAE,YAAY,CAAC,EAAE,CAAC,EACtB,QAAQ,CAAC,EAAE,MAAM,GAChB,EAAE,CAMJ;yBAVe,KAAK;2BAoB8B;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,KAAG,OAAO,CAAC,IAAI,CAAC;oBAgD5D,CAAC,SAAS,CAAC,KAAK,EAAE,GAAG,KAAK,OAAO,eACvD,CAAC,aACH,wBAAwB,KAClC,CAAC"}
1
+ {"version":3,"file":"cache-api.d.ts","sourceRoot":"","sources":["../../src/cache/cache-api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AAS3E;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,KAAK;IAChB;;;;;;;OAOG;IAEH,IAAI,CAAC,EAAE,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,MAC1C,EAAE,QACA,YAAY,CAAC,EAAE,CAAC,aACX,MAAM,GAChB,EAAE;IAIL;;;;;;;OAOG;IACG,UAAU,OAAO;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAiCrE;;;;;;;;OAQG;IAEH,SAAS,CAAC,CAAC,SAAS,CAAC,KAAK,EAAE,GAAG,KAAK,OAAO,eAC5B,CAAC,aACH,wBAAwB,GAClC,CAAC;CAYL,CAAC"}
@@ -65,7 +65,7 @@ export declare class MemoryCacheHandler implements CacheHandler {
65
65
  private evictOldest;
66
66
  }
67
67
  export { RedisCacheHandler } from './redis-handler';
68
- export type { RedisClient } from './redis-handler';
68
+ export type { RedisClient, RedisWriteOp } from './redis-handler';
69
69
  export { cache } from './cache-api';
70
70
  export { setCacheHandler, getCacheHandler } from './handler-store';
71
71
  export { setDefaultSingleflightTimeout } from './timber-cache';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cache/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cache/index.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,3 @@
1
- import { _ as setDefaultSingleflightTimeout, f as MemoryCacheHandler, g as setCacheHandler, h as getCacheHandler, m as RedisCacheHandler, p as TagAwareCacheHandler, t as cache } from "../_chunks/cache-api-DygSeKCB.js";
1
+ import { _ as setDefaultSingleflightTimeout, f as MemoryCacheHandler, g as setCacheHandler, h as getCacheHandler, m as RedisCacheHandler, p as TagAwareCacheHandler, t as cache } from "../_chunks/cache-api-B-lhk9p4.js";
2
2
  import { t as estimateByteSize } from "../_chunks/sizeof-lXffQ1Vr.js";
3
3
  export { MemoryCacheHandler, RedisCacheHandler, TagAwareCacheHandler, cache, estimateByteSize, getCacheHandler, setCacheHandler, setDefaultSingleflightTimeout };
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Dev-mode warning when a cache value contains types that lose fidelity
3
+ * through JSON round-trip. Only runs in development — no-op in production.
4
+ *
5
+ * Detects: Date (→ ISO string), Map/Set (→ {}), BigInt (→ throws).
6
+ * Emits once per process to avoid log spam.
7
+ */
8
+ export declare function warnLossyJsonValue(value: unknown, cacheKey: string): void;
9
+ /** Reset warning state — for tests only. */
10
+ export declare function _resetLossyWarning(): void;
11
+ //# sourceMappingURL=json-lossy-check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-lossy-check.d.ts","sourceRoot":"","sources":["../../src/cache/json-lossy-check.ts"],"names":[],"mappings":"AAIA;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAiBzE;AA2CD,4CAA4C;AAC5C,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC"}
@@ -26,6 +26,19 @@ import type { CacheHandler } from './index';
26
26
  * srem: (k, ...m) => redis.srem(k, ...m),
27
27
  * smembers: (k) => redis.smembers(k),
28
28
  * ttl: (k) => redis.ttl(k),
29
+ * execMulti: async (ops) => {
30
+ * const p = redis.multi();
31
+ * for (const op of ops) {
32
+ * switch (op.op) {
33
+ * case 'set': p.set(op.key, op.value, 'EX', op.ttlSeconds); break;
34
+ * case 'sadd': p.sadd(op.key, ...op.members); break;
35
+ * case 'expireNX': p.expire(op.key, op.seconds, 'NX'); break;
36
+ * case 'expireGT': p.expire(op.key, op.seconds, 'GT'); break;
37
+ * case 'del': p.del(op.key); break;
38
+ * }
39
+ * }
40
+ * await p.exec();
41
+ * },
29
42
  * };
30
43
  * ```
31
44
  *
@@ -66,6 +79,27 @@ import type { CacheHandler } from './index';
66
79
  * };
67
80
  * ```
68
81
  */
82
+ export type RedisWriteOp = {
83
+ op: 'set';
84
+ key: string;
85
+ value: string;
86
+ ttlSeconds: number;
87
+ } | {
88
+ op: 'sadd';
89
+ key: string;
90
+ members: string[];
91
+ } | {
92
+ op: 'expireNX';
93
+ key: string;
94
+ seconds: number;
95
+ } | {
96
+ op: 'expireGT';
97
+ key: string;
98
+ seconds: number;
99
+ } | {
100
+ op: 'del';
101
+ key: string;
102
+ };
69
103
  export interface RedisClient {
70
104
  /** `GET key` — the stored string, or null when absent. */
71
105
  get(key: string): Promise<string | null>;
@@ -98,6 +132,13 @@ export interface RedisClient {
98
132
  * not exist, -1 if the key exists but has no expiry.
99
133
  */
100
134
  ttl(key: string): Promise<number>;
135
+ /**
136
+ * Execute a batch of write operations atomically via `MULTI/EXEC`.
137
+ * When provided, `set()` uses this to make tag-index writes and the data
138
+ * write atomic — no `invalidate({tag})` can interleave. Optional: when
139
+ * absent, operations run sequentially (correct but not race-free).
140
+ */
141
+ execMulti?(ops: RedisWriteOp[]): Promise<void>;
101
142
  }
102
143
  /**
103
144
  * Redis-backed CacheHandler for distributed caching.
@@ -127,6 +168,7 @@ export declare class RedisCacheHandler implements CacheHandler {
127
168
  tags: string[];
128
169
  generation?: number;
129
170
  }): Promise<void>;
171
+ private execOp;
130
172
  invalidate(opts: {
131
173
  key?: string;
132
174
  tag?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"redis-handler.d.ts","sourceRoot":"","sources":["../../src/cache/redis-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkEG;AACH,MAAM,WAAW,WAAW;IAC1B,0DAA0D;IAC1D,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACzC;;;OAGG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtE;;;OAGG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACzD;;;;OAIG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACzD,uEAAuE;IACvE,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACnC,6DAA6D;IAC7D,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1D,kEAAkE;IAClE,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1D,6CAA6C;IAC7C,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACzC;;;OAGG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACnC;AAKD;;;;;;;;;;GAUG;AACH,qBAAa,iBAAkB,YAAW,YAAY;IACpD,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,MAAM,CAAS;gBAEX,MAAM,EAAE,WAAW,EAAE,IAAI,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE;IAK3D,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,MAAM;IAIR,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IASpE,GAAG,CACP,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,OAAO,EACd,IAAI,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,EAAE,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,GACzD,OAAO,CAAC,IAAI,CAAC;IA6BV,UAAU,CAAC,IAAI,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAyCtE"}
1
+ {"version":3,"file":"redis-handler.d.ts","sourceRoot":"","sources":["../../src/cache/redis-handler.ts"],"names":[],"mappings":"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"}