@farm.js/core 0.1.0-beta.97 → 0.1.0-beta.98

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 (248) hide show
  1. package/dist/after.cjs.map +1 -0
  2. package/dist/agent-runtime.cjs +7 -3
  3. package/dist/agent-runtime.cjs.map +1 -0
  4. package/dist/agent-runtime.d.mts +2 -2
  5. package/dist/agent-runtime.d.ts +2 -2
  6. package/dist/agent-runtime.mjs +2 -1
  7. package/dist/agent-runtime.mjs.map +1 -1
  8. package/dist/api/route-manager.cjs.map +1 -0
  9. package/dist/api/route-manager.d.mts +2 -2
  10. package/dist/api/route-manager.d.ts +2 -2
  11. package/dist/api/runtime.cjs.map +1 -0
  12. package/dist/api.cjs +26 -1
  13. package/dist/api.cjs.map +1 -0
  14. package/dist/api.d.mts +3 -3
  15. package/dist/api.d.ts +3 -3
  16. package/dist/api.mjs +2 -2
  17. package/dist/{app-CIOx3LvY.d.ts → app-CJKz2mbi.d.ts} +4 -4
  18. package/dist/{app-DBd6437H.d.mts → app-DMEGFLR2.d.mts} +4 -4
  19. package/dist/app-markdown.cjs.map +1 -0
  20. package/dist/app-markdown.d.mts +3 -3
  21. package/dist/app-markdown.d.ts +3 -3
  22. package/dist/cache.cjs.map +1 -0
  23. package/dist/{chunk-WD2EBM3I.mjs → chunk-3CQJV3NC.mjs} +2 -2
  24. package/dist/{chunk-I3OOUWO5.mjs → chunk-3GDRV7SG.mjs} +2 -2
  25. package/dist/{chunk-X4SFHKFH.mjs → chunk-6KJY25DQ.mjs} +5 -5
  26. package/dist/{chunk-FP5Q67FO.mjs → chunk-AAEZP2EY.mjs} +9 -9
  27. package/dist/{chunk-WMJGA32P.mjs → chunk-BXVOHG6B.mjs} +27 -2
  28. package/dist/{chunk-WMJGA32P.mjs.map → chunk-BXVOHG6B.mjs.map} +1 -1
  29. package/dist/{chunk-BZAIS65C.mjs → chunk-DIHNOVZZ.mjs} +40 -13
  30. package/dist/chunk-DIHNOVZZ.mjs.map +1 -0
  31. package/dist/{chunk-ISSFLGJ5.mjs → chunk-FDWECN3W.mjs} +8 -4
  32. package/dist/chunk-FDWECN3W.mjs.map +1 -0
  33. package/dist/chunk-FDWXXTWR.mjs +114 -0
  34. package/dist/chunk-FDWXXTWR.mjs.map +1 -0
  35. package/dist/{chunk-J3MPQ4MQ.mjs → chunk-IHADQ6J7.mjs} +2 -2
  36. package/dist/{chunk-J3MPQ4MQ.mjs.map → chunk-IHADQ6J7.mjs.map} +1 -1
  37. package/dist/{chunk-FSIXEIOB.mjs → chunk-L7FSTPGS.mjs} +2 -2
  38. package/dist/{chunk-AAC66ILR.mjs → chunk-LXXG3K2Y.mjs} +61 -2
  39. package/dist/chunk-LXXG3K2Y.mjs.map +1 -0
  40. package/dist/{chunk-GJ7W6LNK.mjs → chunk-N5RK4LK6.mjs} +3 -3
  41. package/dist/{chunk-WKDAA6V2.mjs → chunk-NBF56FXB.mjs} +20 -5
  42. package/dist/chunk-NBF56FXB.mjs.map +1 -0
  43. package/dist/chunk-RKUPOYQV.mjs +28 -0
  44. package/dist/chunk-RKUPOYQV.mjs.map +1 -0
  45. package/dist/{chunk-2SKCXYLB.mjs → chunk-RL3THP3L.mjs} +54 -5
  46. package/dist/chunk-RL3THP3L.mjs.map +1 -0
  47. package/dist/{chunk-52BR7LTX.mjs → chunk-STSPSJUI.mjs} +3 -3
  48. package/dist/{chunk-EPDIF432.mjs → chunk-UOGX6UNO.mjs} +37 -17
  49. package/dist/chunk-UOGX6UNO.mjs.map +1 -0
  50. package/dist/{chunk-YIBV77KZ.mjs → chunk-URQELPSH.mjs} +2 -2
  51. package/dist/{chunk-UMUWR5XA.mjs → chunk-UWQ4EEW7.mjs} +2 -2
  52. package/dist/{chunk-IZBX2TEU.mjs → chunk-VQG3QWWK.mjs} +2 -2
  53. package/dist/{chunk-D7F7YZ7L.mjs → chunk-WA77FWAO.mjs} +8 -8
  54. package/dist/{chunk-35NPLKQJ.mjs → chunk-XWM2C267.mjs} +21 -105
  55. package/dist/chunk-XWM2C267.mjs.map +1 -0
  56. package/dist/client/lifecycle.cjs.map +1 -0
  57. package/dist/client/lifecycle.mjs +2 -2
  58. package/dist/{client-plugins-B6-07YmP.d.mts → client-plugins-CyTLPqjP.d.mts} +38 -2
  59. package/dist/{client-plugins-By6MVUX-.d.ts → client-plugins-D-UgzngJ.d.ts} +38 -2
  60. package/dist/client-plugins.cjs +1 -1
  61. package/dist/client-plugins.cjs.map +1 -0
  62. package/dist/client-plugins.d.mts +2 -2
  63. package/dist/client-plugins.d.ts +2 -2
  64. package/dist/client-plugins.mjs +2 -2
  65. package/dist/client.cjs +61 -16
  66. package/dist/client.cjs.map +1 -0
  67. package/dist/client.d.mts +5 -5
  68. package/dist/client.d.ts +5 -5
  69. package/dist/client.mjs +7 -7
  70. package/dist/config.cjs.map +1 -0
  71. package/dist/config.d.mts +2 -2
  72. package/dist/config.d.ts +2 -2
  73. package/dist/cron.cjs +48 -12
  74. package/dist/cron.cjs.map +1 -0
  75. package/dist/cron.mjs +2 -1
  76. package/dist/deferred.cjs.map +1 -0
  77. package/dist/deployment.cjs.map +1 -0
  78. package/dist/{devtools-XWZX6CCL.mjs → devtools-KQ2PXSIC.mjs} +8 -6
  79. package/dist/{devtools-XWZX6CCL.mjs.map → devtools-KQ2PXSIC.mjs.map} +1 -1
  80. package/dist/docs.cjs +7 -3
  81. package/dist/docs.cjs.map +1 -0
  82. package/dist/docs.mjs +8 -6
  83. package/dist/env-types.cjs.map +1 -0
  84. package/dist/env.cjs.map +1 -0
  85. package/dist/environment/vite.cjs.map +1 -0
  86. package/dist/environment.cjs.map +1 -0
  87. package/dist/font.cjs.map +1 -0
  88. package/dist/headers.cjs.map +1 -0
  89. package/dist/i18n/client.cjs.map +1 -0
  90. package/dist/i18n/index.cjs.map +1 -0
  91. package/dist/i18n/server.cjs.map +1 -0
  92. package/dist/image/server.cjs +19 -4
  93. package/dist/image/server.cjs.map +1 -0
  94. package/dist/image/server.d.mts +6 -0
  95. package/dist/image/server.d.ts +6 -0
  96. package/dist/image/server.mjs +1 -1
  97. package/dist/image/sharp.cjs +1 -0
  98. package/dist/image/sharp.cjs.map +1 -0
  99. package/dist/image/sharp.mjs +1 -1
  100. package/dist/image.cjs.map +1 -0
  101. package/dist/index.cjs +370 -143
  102. package/dist/index.cjs.map +1 -0
  103. package/dist/index.d.mts +10 -10
  104. package/dist/index.d.ts +10 -10
  105. package/dist/index.mjs +29 -21
  106. package/dist/index.mjs.map +1 -1
  107. package/dist/instrumentation.cjs.map +1 -0
  108. package/dist/{integration-orm-LLW6WZNK.mjs → integration-orm-G5BPBT5X.mjs} +3 -3
  109. package/dist/integrations.cjs +168 -5
  110. package/dist/integrations.cjs.map +1 -0
  111. package/dist/integrations.d.mts +2 -2
  112. package/dist/integrations.d.ts +2 -2
  113. package/dist/integrations.mjs +10 -3
  114. package/dist/internal/build-runtime.cjs +178 -51
  115. package/dist/internal/build-runtime.cjs.map +1 -0
  116. package/dist/internal/build-runtime.d.mts +4 -4
  117. package/dist/internal/build-runtime.d.ts +4 -4
  118. package/dist/internal/build-runtime.mjs +13 -11
  119. package/dist/internal/client-runtime.cjs +1 -1
  120. package/dist/internal/client-runtime.cjs.map +1 -0
  121. package/dist/internal/client-runtime.mjs +2 -2
  122. package/dist/internal/config-runtime.cjs +48 -42
  123. package/dist/internal/config-runtime.cjs.map +1 -0
  124. package/dist/internal/config-runtime.d.mts +3 -3
  125. package/dist/internal/config-runtime.d.ts +3 -3
  126. package/dist/internal/config-runtime.mjs +7 -5
  127. package/dist/internal/isolated-boundary.cjs.map +1 -0
  128. package/dist/internal/metadata-image-runtime.cjs.map +1 -0
  129. package/dist/internal/product-telemetry-runtime.cjs +6 -2
  130. package/dist/internal/product-telemetry-runtime.cjs.map +1 -0
  131. package/dist/internal/product-telemetry-runtime.mjs +6 -2
  132. package/dist/internal/product-telemetry-runtime.mjs.map +1 -1
  133. package/dist/internal/production-node-env.cjs.map +1 -0
  134. package/dist/internal/production-runtime.cjs.map +1 -0
  135. package/dist/internal/production-runtime.d.mts +8 -8
  136. package/dist/internal/production-runtime.d.ts +8 -8
  137. package/dist/markdown.cjs.map +1 -0
  138. package/dist/{metadata-route-8_V3UonT.d.ts → metadata-route-BpU8K9RI.d.ts} +1 -1
  139. package/dist/{metadata-route-CWP_HjGU.d.mts → metadata-route-D1AYgWni.d.mts} +1 -1
  140. package/dist/middleware.cjs.map +1 -0
  141. package/dist/middleware.d.mts +4 -4
  142. package/dist/middleware.d.ts +4 -4
  143. package/dist/navigation.cjs.map +1 -0
  144. package/dist/{nitro-EGRNOB5P.mjs → nitro-IITKBMUI.mjs} +4 -3
  145. package/dist/{nitro-EGRNOB5P.mjs.map → nitro-IITKBMUI.mjs.map} +1 -1
  146. package/dist/observability.cjs.map +1 -0
  147. package/dist/plugin.cjs +143 -50
  148. package/dist/plugin.cjs.map +1 -0
  149. package/dist/plugin.d.mts +2 -2
  150. package/dist/plugin.d.ts +2 -2
  151. package/dist/plugin.mjs +10 -8
  152. package/dist/{production-runtime-BGRH1dKG.d.mts → production-runtime-BIKA-BVT.d.mts} +2 -2
  153. package/dist/{production-runtime-CdmzsDax.d.ts → production-runtime-DtCUEELY.d.ts} +2 -2
  154. package/dist/query/client.cjs.map +1 -0
  155. package/dist/query/index.cjs.map +1 -0
  156. package/dist/query/parsers.cjs.map +1 -0
  157. package/dist/query/server.cjs.map +1 -0
  158. package/dist/{render-plan-DLxJYAA5.d.mts → render-plan-Dimb23ux.d.mts} +1 -1
  159. package/dist/{render-plan-0GeHiGVZ.d.ts → render-plan-rg3uiSo3.d.ts} +1 -1
  160. package/dist/renderer/react/client.cjs.map +1 -0
  161. package/dist/renderer/react/server.cjs.map +1 -0
  162. package/dist/renderer/react/vite.cjs.map +1 -0
  163. package/dist/renderer-client.cjs +26 -1
  164. package/dist/renderer-client.cjs.map +1 -0
  165. package/dist/renderer-client.d.mts +4 -4
  166. package/dist/renderer-client.d.ts +4 -4
  167. package/dist/renderer-client.mjs +2 -2
  168. package/dist/renderer.cjs.map +1 -0
  169. package/dist/{request-BstWbZ6O.d.ts → request-ColD-ChW.d.ts} +1 -1
  170. package/dist/{request-BCNXSXLh.d.mts → request-D5zvbU2Z.d.mts} +1 -1
  171. package/dist/request.cjs.map +1 -0
  172. package/dist/request.d.mts +3 -3
  173. package/dist/request.d.ts +3 -3
  174. package/dist/router.cjs.map +1 -0
  175. package/dist/routes.cjs.map +1 -0
  176. package/dist/routes.d.mts +2 -2
  177. package/dist/routes.d.ts +2 -2
  178. package/dist/server-action-security.cjs +117 -104
  179. package/dist/server-action-security.cjs.map +1 -0
  180. package/dist/server-action-security.mjs +2 -1
  181. package/dist/server-fn-client.cjs.map +1 -0
  182. package/dist/server-fn.cjs.map +1 -0
  183. package/dist/server-fn.mjs +3 -2
  184. package/dist/server-plugins.cjs.map +1 -0
  185. package/dist/server-plugins.d.mts +3 -3
  186. package/dist/server-plugins.d.ts +3 -3
  187. package/dist/server-query-client.cjs +26 -1
  188. package/dist/server-query-client.cjs.map +1 -0
  189. package/dist/server-query-client.mjs +3 -3
  190. package/dist/server-query.cjs.map +1 -0
  191. package/dist/server-query.mjs +4 -3
  192. package/dist/server.cjs +221 -116
  193. package/dist/server.cjs.map +1 -0
  194. package/dist/server.d.mts +8 -8
  195. package/dist/server.d.ts +8 -8
  196. package/dist/server.mjs +13 -11
  197. package/dist/server.mjs.map +1 -1
  198. package/dist/{spa-router-BeyAVSc6.d.mts → spa-router-BlilYGAN.d.mts} +1 -1
  199. package/dist/{spa-router-COgu4txb.d.ts → spa-router-kXORcsT_.d.ts} +1 -1
  200. package/dist/storage.cjs +7 -3
  201. package/dist/storage.cjs.map +1 -0
  202. package/dist/storage.mjs +1 -1
  203. package/dist/testing.cjs.map +1 -0
  204. package/dist/testing.d.mts +2 -2
  205. package/dist/testing.d.ts +2 -2
  206. package/dist/testing.mjs +2 -1
  207. package/dist/testing.mjs.map +1 -1
  208. package/dist/theme/client.cjs.map +1 -0
  209. package/dist/theme/index.cjs.map +1 -0
  210. package/dist/theme/runtime.cjs.map +1 -0
  211. package/dist/theme/server.cjs.map +1 -0
  212. package/dist/{utils-DkU2c_1B.d.ts → utils-BhvF7Hhl.d.ts} +1 -1
  213. package/dist/{utils-DL2VwDQl.d.mts → utils-uF32evNX.d.mts} +1 -1
  214. package/dist/version.cjs +1 -1
  215. package/dist/version.cjs.map +1 -0
  216. package/dist/version.mjs +1 -1
  217. package/dist/vite.cjs +143 -50
  218. package/dist/vite.cjs.map +1 -0
  219. package/dist/vite.d.mts +2 -2
  220. package/dist/vite.d.ts +2 -2
  221. package/dist/vite.mjs +10 -8
  222. package/dist/{workflows-ChyK_oTu.d.mts → workflows-BantWytQ.d.mts} +7 -0
  223. package/dist/{workflows-ChyK_oTu.d.ts → workflows-BantWytQ.d.ts} +7 -0
  224. package/dist/workflows.cjs +67 -4
  225. package/dist/workflows.cjs.map +1 -0
  226. package/dist/workflows.d.mts +1 -1
  227. package/dist/workflows.d.ts +1 -1
  228. package/dist/workflows.mjs +2 -1
  229. package/package.json +17 -17
  230. package/dist/chunk-2SKCXYLB.mjs.map +0 -1
  231. package/dist/chunk-35NPLKQJ.mjs.map +0 -1
  232. package/dist/chunk-AAC66ILR.mjs.map +0 -1
  233. package/dist/chunk-BZAIS65C.mjs.map +0 -1
  234. package/dist/chunk-EPDIF432.mjs.map +0 -1
  235. package/dist/chunk-ISSFLGJ5.mjs.map +0 -1
  236. package/dist/chunk-WKDAA6V2.mjs.map +0 -1
  237. /package/dist/{chunk-WD2EBM3I.mjs.map → chunk-3CQJV3NC.mjs.map} +0 -0
  238. /package/dist/{chunk-I3OOUWO5.mjs.map → chunk-3GDRV7SG.mjs.map} +0 -0
  239. /package/dist/{chunk-X4SFHKFH.mjs.map → chunk-6KJY25DQ.mjs.map} +0 -0
  240. /package/dist/{chunk-FP5Q67FO.mjs.map → chunk-AAEZP2EY.mjs.map} +0 -0
  241. /package/dist/{chunk-FSIXEIOB.mjs.map → chunk-L7FSTPGS.mjs.map} +0 -0
  242. /package/dist/{chunk-GJ7W6LNK.mjs.map → chunk-N5RK4LK6.mjs.map} +0 -0
  243. /package/dist/{chunk-52BR7LTX.mjs.map → chunk-STSPSJUI.mjs.map} +0 -0
  244. /package/dist/{chunk-YIBV77KZ.mjs.map → chunk-URQELPSH.mjs.map} +0 -0
  245. /package/dist/{chunk-UMUWR5XA.mjs.map → chunk-UWQ4EEW7.mjs.map} +0 -0
  246. /package/dist/{chunk-IZBX2TEU.mjs.map → chunk-VQG3QWWK.mjs.map} +0 -0
  247. /package/dist/{chunk-D7F7YZ7L.mjs.map → chunk-WA77FWAO.mjs.map} +0 -0
  248. /package/dist/{integration-orm-LLW6WZNK.mjs.map → integration-orm-G5BPBT5X.mjs.map} +0 -0
@@ -233,6 +233,7 @@ var _FarmClientDataCache = class _FarmClientDataCache {
233
233
  const watched = /* @__PURE__ */ new Set();
234
234
  for (const key of this.listeners.keys()) watched.add(this.resolveKey(key));
235
235
  let remaining = false;
236
+ const swept = /* @__PURE__ */ new Set();
236
237
  for (const [key, entry] of this.entries) {
237
238
  if (entry.gcAt === void 0) continue;
238
239
  if (now < entry.gcAt || entry.fetching || this.inflight.has(key) || watched.has(key)) {
@@ -241,9 +242,28 @@ var _FarmClientDataCache = class _FarmClientDataCache {
241
242
  }
242
243
  this.entries.delete(key);
243
244
  this.persistence?.onDelete(key);
245
+ swept.add(key);
244
246
  }
247
+ if (swept.size > 0) this.sweepEntryMetadata(swept);
245
248
  if (remaining) this.scheduleGcSweep();
246
249
  }
250
+ /**
251
+ * Entry eviction alone leaves the per-key metadata behind. `invalidatedAt`
252
+ * marks and provisional aliases are created per query invocation, so with
253
+ * dynamic keys they accumulate for the lifetime of the page even though the
254
+ * entries they describe are long gone.
255
+ */
256
+ sweepEntryMetadata(swept) {
257
+ for (const key of swept) this.invalidatedAt.delete(key);
258
+ for (const [alias, target] of this.aliases) {
259
+ if (this.entries.has(alias) || this.listeners.has(alias)) continue;
260
+ const resolved = this.resolveKey(target);
261
+ if (!swept.has(resolved)) continue;
262
+ if (this.entries.has(resolved) || this.listeners.has(resolved)) continue;
263
+ this.aliases.delete(alias);
264
+ this.invalidatedAt.delete(alias);
265
+ }
266
+ }
247
267
  emit(key, event) {
248
268
  this.notifyListeners(key, event);
249
269
  for (const [alias, target] of this.aliases) {
@@ -254,7 +274,12 @@ var _FarmClientDataCache = class _FarmClientDataCache {
254
274
  }
255
275
  notifyListeners(key, event) {
256
276
  for (const listener of this.listeners.get(key) ?? []) {
257
- listener(event);
277
+ try {
278
+ listener(event);
279
+ } catch (error) {
280
+ const detail = error instanceof Error ? error.stack ?? error.message : String(error);
281
+ console.warn(`[farm:client-cache] cache listener failed: ${detail}`);
282
+ }
258
283
  }
259
284
  }
260
285
  };
@@ -278,4 +303,4 @@ export {
278
303
  getFarmClientDataCache,
279
304
  normalizeFarmClientCacheKey
280
305
  };
281
- //# sourceMappingURL=chunk-WMJGA32P.mjs.map
306
+ //# sourceMappingURL=chunk-BXVOHG6B.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client-cache.ts"],"sourcesContent":["/// <reference lib=\"es2021.weakref\" />\nimport { createRouteDataCacheKey, type RouteDataCacheKey } from \"./cache\";\nimport { subscribeFarmCacheInvalidation } from \"./cache-invalidation\";\n\nexport type FarmClientCacheKey = RouteDataCacheKey;\n\nexport type FarmClientCacheStatus = \"idle\" | \"pending\" | \"success\" | \"error\";\n\nexport type FarmClientCacheEntry<TData = unknown> = {\n data: TData;\n updatedAt: number;\n staleAt: number;\n gcAt?: number;\n invalidatedAt?: number;\n status?: FarmClientCacheStatus;\n error?: Error | null;\n fetching?: boolean;\n /** Marks an entry the persistence layer may write to its adapter. */\n persist?: boolean;\n};\n\n/** @internal Observation seam for the client cache persistence engine. */\nexport type FarmClientCachePersistenceSink = {\n onSet(key: string, entry: FarmClientCacheEntry): void;\n onDelete(key: string): void;\n onClear(): void;\n};\n\ntype FarmClientCacheListener = (event?: \"invalidate\") => void;\n\nconst invalidationTrackers = new WeakMap<FarmClientDataCache, Set<Set<string>>>();\n\n/** Internal request-lifetime tracking, including keys learned only from a response. */\nexport function trackFarmClientCacheInvalidations(cache: FarmClientDataCache) {\n let trackers = invalidationTrackers.get(cache);\n if (!trackers) {\n trackers = new Set();\n invalidationTrackers.set(cache, trackers);\n }\n const keys = new Set<string>();\n trackers.add(keys);\n return {\n has(key: string) {\n const resolved = cache.resolveKey(key);\n for (const invalidated of keys) {\n if (cache.resolveKey(invalidated) === resolved) return true;\n }\n return false;\n },\n dispose() {\n if (!trackers.delete(keys)) return;\n keys.clear();\n if (trackers.size === 0) invalidationTrackers.delete(cache);\n },\n };\n}\n\nconst cacheFinalizer =\n typeof FinalizationRegistry === \"function\"\n ? new FinalizationRegistry<() => void>((unsubscribe) => unsubscribe())\n : undefined;\n\n// This closure must only capture a weak reference, never the cache itself.\nfunction subscribeWeakCache(reference: WeakRef<FarmClientDataCache>): () => void {\n const unsubscribe = subscribeFarmCacheInvalidation((key) => {\n const cache = reference.deref();\n if (cache) cache.invalidate(key);\n else dispose();\n });\n function dispose() {\n unsubscribe();\n cacheFinalizer?.unregister(reference);\n }\n return dispose;\n}\n\nconst DEFAULT_GC_SWEEP_INTERVAL_MS = 30_000;\n\nexport class FarmClientDataCache {\n private entries = new Map<string, FarmClientCacheEntry>();\n private aliases = new Map<string, string>();\n private invalidatedAt = new Map<string, number>();\n private listeners = new Map<string, Set<FarmClientCacheListener>>();\n private inflight = new Map<string, Promise<unknown>>();\n private unsubscribeInvalidation: (() => void) | undefined;\n private gcTimer: ReturnType<typeof setTimeout> | undefined;\n private readonly gcSweepIntervalMs: number | false;\n private persistence: FarmClientCachePersistenceSink | undefined;\n\n constructor(\n options: { subscribeToInvalidation?: boolean; gcSweepIntervalMs?: number | false } = {},\n ) {\n this.gcSweepIntervalMs = options.gcSweepIntervalMs ?? DEFAULT_GC_SWEEP_INTERVAL_MS;\n if (options.subscribeToInvalidation !== false) {\n if (typeof WeakRef === \"function\") {\n const reference = new WeakRef(this);\n const unsubscribe = subscribeWeakCache(reference);\n cacheFinalizer?.register(this, unsubscribe, reference);\n this.unsubscribeInvalidation = unsubscribe;\n } else {\n // Keep invalidation working on older runtimes without weak references.\n this.unsubscribeInvalidation = subscribeFarmCacheInvalidation((key) =>\n this.invalidate(key),\n );\n }\n }\n }\n\n get size(): number {\n return this.entries.size;\n }\n\n /** @internal Attach or detach the persistence engine's observation sink. */\n attachPersistence(sink: FarmClientCachePersistenceSink | undefined): void {\n this.persistence = sink;\n }\n\n resolveKey(key: string): string {\n let resolved = key;\n const seen = new Set<string>();\n\n while (this.aliases.has(resolved) && !seen.has(resolved)) {\n seen.add(resolved);\n resolved = this.aliases.get(resolved)!;\n }\n\n return resolved;\n }\n\n get<TData = unknown>(key: string, now = Date.now()): FarmClientCacheEntry<TData> | undefined {\n const resolved = this.resolveKey(key);\n const entry = this.entries.get(resolved) as FarmClientCacheEntry<TData> | undefined;\n if (!entry) return undefined;\n\n if (entry.gcAt !== undefined && now >= entry.gcAt) {\n this.entries.delete(resolved);\n this.persistence?.onDelete(resolved);\n this.emit(resolved);\n return undefined;\n }\n\n return entry;\n }\n\n set<TData>(key: string, entry: FarmClientCacheEntry<TData>): this {\n const resolved = this.resolveKey(key);\n const invalidatedAt = this.invalidatedAt.get(resolved);\n const nextEntry =\n invalidatedAt !== undefined && invalidatedAt > entry.updatedAt\n ? { ...entry, staleAt: 0, invalidatedAt }\n : { ...entry, invalidatedAt: undefined };\n\n if (invalidatedAt === undefined || entry.updatedAt >= invalidatedAt) {\n this.invalidatedAt.delete(resolved);\n }\n\n this.entries.set(resolved, nextEntry);\n if (nextEntry.gcAt !== undefined) this.scheduleGcSweep();\n this.persistence?.onSet(resolved, nextEntry);\n this.emit(resolved);\n return this;\n }\n\n delete(key: string): boolean {\n const resolved = this.resolveKey(key);\n const deleted = this.entries.delete(resolved);\n this.inflight.delete(resolved);\n if (deleted) this.persistence?.onDelete(resolved);\n this.emit(resolved);\n return deleted;\n }\n\n clear(): void {\n const keys = new Set([...this.entries.keys(), ...this.listeners.keys()]);\n this.entries.clear();\n this.aliases.clear();\n this.invalidatedAt.clear();\n this.inflight.clear();\n this.persistence?.onClear();\n for (const key of keys) this.emit(key);\n }\n\n dispose(): void {\n this.unsubscribeInvalidation?.();\n this.unsubscribeInvalidation = undefined;\n if (this.gcTimer !== undefined) {\n clearTimeout(this.gcTimer);\n this.gcTimer = undefined;\n }\n this.clear();\n }\n\n isStale(key: string, now = Date.now()): boolean {\n const entry = this.get(key, now);\n return !entry || entry.invalidatedAt !== undefined || now >= entry.staleAt;\n }\n\n invalidate(key: string, now = Date.now()): void {\n const resolved = this.resolveKey(key);\n for (const keys of invalidationTrackers.get(this) ?? []) keys.add(resolved);\n this.invalidatedAt.set(resolved, now);\n\n const entry = this.entries.get(resolved);\n if (entry) {\n this.entries.set(resolved, {\n ...entry,\n staleAt: 0,\n invalidatedAt: now,\n });\n }\n\n this.emit(resolved, \"invalidate\");\n }\n\n alias(alias: string, key: string): void {\n const resolved = this.resolveKey(key);\n if (alias === resolved) return;\n\n const aliasEntry = this.entries.get(alias);\n const aliasInvalidatedAt = this.invalidatedAt.get(alias) ?? aliasEntry?.invalidatedAt;\n const resolvedInvalidatedAt = this.invalidatedAt.get(resolved);\n if (aliasEntry && !this.entries.has(resolved)) {\n this.entries.set(resolved, aliasEntry);\n this.persistence?.onSet(resolved, aliasEntry);\n }\n\n if (this.entries.delete(alias)) this.persistence?.onDelete(alias);\n this.invalidatedAt.delete(alias);\n const invalidatedAt = [aliasInvalidatedAt, resolvedInvalidatedAt].reduce<number | undefined>(\n (latest, value) =>\n value === undefined ? latest : latest === undefined ? value : Math.max(latest, value),\n undefined,\n );\n const resolvedEntry = this.entries.get(resolved);\n if (\n invalidatedAt !== undefined &&\n (!resolvedEntry || invalidatedAt > resolvedEntry.updatedAt)\n ) {\n this.invalidatedAt.set(resolved, invalidatedAt);\n if (resolvedEntry) {\n this.entries.set(resolved, {\n ...resolvedEntry,\n staleAt: 0,\n invalidatedAt,\n });\n }\n } else if (invalidatedAt !== undefined) {\n this.invalidatedAt.delete(resolved);\n if (resolvedEntry?.invalidatedAt !== undefined) {\n this.entries.set(resolved, { ...resolvedEntry, invalidatedAt: undefined });\n }\n }\n\n const aliasInflight = this.inflight.get(alias);\n if (aliasInflight && !this.inflight.has(resolved)) {\n this.inflight.set(resolved, aliasInflight);\n }\n this.inflight.delete(alias);\n this.aliases.set(alias, resolved);\n this.emit(alias);\n this.emit(resolved, this.invalidatedAt.has(resolved) ? \"invalidate\" : undefined);\n }\n\n subscribe(key: string, listener: FarmClientCacheListener): () => void {\n let listeners = this.listeners.get(key);\n if (!listeners) {\n listeners = new Set();\n this.listeners.set(key, listeners);\n }\n\n listeners.add(listener);\n return () => {\n listeners!.delete(listener);\n // Only drop the map entry if it still holds this exact set. A repeated or\n // stale unsubscribe (called after the key was drained and resubscribed)\n // must not evict a newer subscriber's live listener set.\n if (listeners!.size === 0 && this.listeners.get(key) === listeners) {\n this.listeners.delete(key);\n }\n };\n }\n\n getInflight<TData>(key: string): Promise<TData> | undefined {\n return this.inflight.get(this.resolveKey(key)) as Promise<TData> | undefined;\n }\n\n setInflight<TData>(key: string, promise: Promise<TData>): void {\n this.inflight.set(this.resolveKey(key), promise);\n }\n\n deleteInflight(key: string): void {\n this.inflight.delete(this.resolveKey(key));\n }\n\n private scheduleGcSweep(): void {\n if (this.gcSweepIntervalMs === false || this.gcTimer !== undefined) return;\n const timer = setTimeout(() => {\n this.gcTimer = undefined;\n this.sweepExpiredEntries();\n }, this.gcSweepIntervalMs);\n // Cache cleanup must never keep a Node.js process (SSR, tests) alive.\n (timer as unknown as { unref?: () => void }).unref?.();\n this.gcTimer = timer;\n }\n\n private sweepExpiredEntries(now = Date.now()): void {\n const watched = new Set<string>();\n for (const key of this.listeners.keys()) watched.add(this.resolveKey(key));\n\n let remaining = false;\n for (const [key, entry] of this.entries) {\n if (entry.gcAt === undefined) continue;\n if (now < entry.gcAt || entry.fetching || this.inflight.has(key) || watched.has(key)) {\n remaining = true;\n continue;\n }\n // Only unwatched entries are swept, so eviction is unobservable: a read\n // of this key would already evict it lazily before returning data.\n this.entries.delete(key);\n this.persistence?.onDelete(key);\n }\n\n if (remaining) this.scheduleGcSweep();\n }\n\n private emit(key: string, event?: \"invalidate\"): void {\n this.notifyListeners(key, event);\n for (const [alias, target] of this.aliases) {\n if (this.resolveKey(target) === key) {\n this.notifyListeners(alias, event);\n }\n }\n }\n\n private notifyListeners(key: string, event?: \"invalidate\"): void {\n for (const listener of this.listeners.get(key) ?? []) {\n listener(event);\n }\n }\n}\n\nconst FARM_CLIENT_DATA_CACHE = Symbol.for(\"farm.clientDataCache\");\nconst clientCacheGlobal = globalThis as typeof globalThis & {\n [FARM_CLIENT_DATA_CACHE]?: FarmClientDataCache;\n};\nconst sharedFarmClientDataCache = (clientCacheGlobal[FARM_CLIENT_DATA_CACHE] ??=\n new FarmClientDataCache());\n\nexport function getFarmClientDataCache(): FarmClientDataCache {\n return sharedFarmClientDataCache;\n}\n\nexport function normalizeFarmClientCacheKey(key: FarmClientCacheKey): string {\n return typeof key === \"string\" ? key : createRouteDataCacheKey(key);\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/client-cache.ts"],"sourcesContent":["/// <reference lib=\"es2021.weakref\" />\nimport { createRouteDataCacheKey, type RouteDataCacheKey } from \"./cache\";\nimport { subscribeFarmCacheInvalidation } from \"./cache-invalidation\";\n\nexport type FarmClientCacheKey = RouteDataCacheKey;\n\nexport type FarmClientCacheStatus = \"idle\" | \"pending\" | \"success\" | \"error\";\n\nexport type FarmClientCacheEntry<TData = unknown> = {\n data: TData;\n updatedAt: number;\n staleAt: number;\n gcAt?: number;\n invalidatedAt?: number;\n status?: FarmClientCacheStatus;\n error?: Error | null;\n fetching?: boolean;\n /** Marks an entry the persistence layer may write to its adapter. */\n persist?: boolean;\n};\n\n/** @internal Observation seam for the client cache persistence engine. */\nexport type FarmClientCachePersistenceSink = {\n onSet(key: string, entry: FarmClientCacheEntry): void;\n onDelete(key: string): void;\n onClear(): void;\n};\n\ntype FarmClientCacheListener = (event?: \"invalidate\") => void;\n\nconst invalidationTrackers = new WeakMap<FarmClientDataCache, Set<Set<string>>>();\n\n/** Internal request-lifetime tracking, including keys learned only from a response. */\nexport function trackFarmClientCacheInvalidations(cache: FarmClientDataCache) {\n let trackers = invalidationTrackers.get(cache);\n if (!trackers) {\n trackers = new Set();\n invalidationTrackers.set(cache, trackers);\n }\n const keys = new Set<string>();\n trackers.add(keys);\n return {\n has(key: string) {\n const resolved = cache.resolveKey(key);\n for (const invalidated of keys) {\n if (cache.resolveKey(invalidated) === resolved) return true;\n }\n return false;\n },\n dispose() {\n if (!trackers.delete(keys)) return;\n keys.clear();\n if (trackers.size === 0) invalidationTrackers.delete(cache);\n },\n };\n}\n\nconst cacheFinalizer =\n typeof FinalizationRegistry === \"function\"\n ? new FinalizationRegistry<() => void>((unsubscribe) => unsubscribe())\n : undefined;\n\n// This closure must only capture a weak reference, never the cache itself.\nfunction subscribeWeakCache(reference: WeakRef<FarmClientDataCache>): () => void {\n const unsubscribe = subscribeFarmCacheInvalidation((key) => {\n const cache = reference.deref();\n if (cache) cache.invalidate(key);\n else dispose();\n });\n function dispose() {\n unsubscribe();\n cacheFinalizer?.unregister(reference);\n }\n return dispose;\n}\n\nconst DEFAULT_GC_SWEEP_INTERVAL_MS = 30_000;\n\nexport class FarmClientDataCache {\n private entries = new Map<string, FarmClientCacheEntry>();\n private aliases = new Map<string, string>();\n private invalidatedAt = new Map<string, number>();\n private listeners = new Map<string, Set<FarmClientCacheListener>>();\n private inflight = new Map<string, Promise<unknown>>();\n private unsubscribeInvalidation: (() => void) | undefined;\n private gcTimer: ReturnType<typeof setTimeout> | undefined;\n private readonly gcSweepIntervalMs: number | false;\n private persistence: FarmClientCachePersistenceSink | undefined;\n\n constructor(\n options: { subscribeToInvalidation?: boolean; gcSweepIntervalMs?: number | false } = {},\n ) {\n this.gcSweepIntervalMs = options.gcSweepIntervalMs ?? DEFAULT_GC_SWEEP_INTERVAL_MS;\n if (options.subscribeToInvalidation !== false) {\n if (typeof WeakRef === \"function\") {\n const reference = new WeakRef(this);\n const unsubscribe = subscribeWeakCache(reference);\n cacheFinalizer?.register(this, unsubscribe, reference);\n this.unsubscribeInvalidation = unsubscribe;\n } else {\n // Keep invalidation working on older runtimes without weak references.\n this.unsubscribeInvalidation = subscribeFarmCacheInvalidation((key) =>\n this.invalidate(key),\n );\n }\n }\n }\n\n get size(): number {\n return this.entries.size;\n }\n\n /** @internal Attach or detach the persistence engine's observation sink. */\n attachPersistence(sink: FarmClientCachePersistenceSink | undefined): void {\n this.persistence = sink;\n }\n\n resolveKey(key: string): string {\n let resolved = key;\n const seen = new Set<string>();\n\n while (this.aliases.has(resolved) && !seen.has(resolved)) {\n seen.add(resolved);\n resolved = this.aliases.get(resolved)!;\n }\n\n return resolved;\n }\n\n get<TData = unknown>(key: string, now = Date.now()): FarmClientCacheEntry<TData> | undefined {\n const resolved = this.resolveKey(key);\n const entry = this.entries.get(resolved) as FarmClientCacheEntry<TData> | undefined;\n if (!entry) return undefined;\n\n if (entry.gcAt !== undefined && now >= entry.gcAt) {\n this.entries.delete(resolved);\n this.persistence?.onDelete(resolved);\n this.emit(resolved);\n return undefined;\n }\n\n return entry;\n }\n\n set<TData>(key: string, entry: FarmClientCacheEntry<TData>): this {\n const resolved = this.resolveKey(key);\n const invalidatedAt = this.invalidatedAt.get(resolved);\n const nextEntry =\n invalidatedAt !== undefined && invalidatedAt > entry.updatedAt\n ? { ...entry, staleAt: 0, invalidatedAt }\n : { ...entry, invalidatedAt: undefined };\n\n if (invalidatedAt === undefined || entry.updatedAt >= invalidatedAt) {\n this.invalidatedAt.delete(resolved);\n }\n\n this.entries.set(resolved, nextEntry);\n if (nextEntry.gcAt !== undefined) this.scheduleGcSweep();\n this.persistence?.onSet(resolved, nextEntry);\n this.emit(resolved);\n return this;\n }\n\n delete(key: string): boolean {\n const resolved = this.resolveKey(key);\n const deleted = this.entries.delete(resolved);\n this.inflight.delete(resolved);\n if (deleted) this.persistence?.onDelete(resolved);\n this.emit(resolved);\n return deleted;\n }\n\n clear(): void {\n const keys = new Set([...this.entries.keys(), ...this.listeners.keys()]);\n this.entries.clear();\n this.aliases.clear();\n this.invalidatedAt.clear();\n this.inflight.clear();\n this.persistence?.onClear();\n for (const key of keys) this.emit(key);\n }\n\n dispose(): void {\n this.unsubscribeInvalidation?.();\n this.unsubscribeInvalidation = undefined;\n if (this.gcTimer !== undefined) {\n clearTimeout(this.gcTimer);\n this.gcTimer = undefined;\n }\n this.clear();\n }\n\n isStale(key: string, now = Date.now()): boolean {\n const entry = this.get(key, now);\n return !entry || entry.invalidatedAt !== undefined || now >= entry.staleAt;\n }\n\n invalidate(key: string, now = Date.now()): void {\n const resolved = this.resolveKey(key);\n for (const keys of invalidationTrackers.get(this) ?? []) keys.add(resolved);\n this.invalidatedAt.set(resolved, now);\n\n const entry = this.entries.get(resolved);\n if (entry) {\n this.entries.set(resolved, {\n ...entry,\n staleAt: 0,\n invalidatedAt: now,\n });\n }\n\n this.emit(resolved, \"invalidate\");\n }\n\n alias(alias: string, key: string): void {\n const resolved = this.resolveKey(key);\n if (alias === resolved) return;\n\n const aliasEntry = this.entries.get(alias);\n const aliasInvalidatedAt = this.invalidatedAt.get(alias) ?? aliasEntry?.invalidatedAt;\n const resolvedInvalidatedAt = this.invalidatedAt.get(resolved);\n if (aliasEntry && !this.entries.has(resolved)) {\n this.entries.set(resolved, aliasEntry);\n this.persistence?.onSet(resolved, aliasEntry);\n }\n\n if (this.entries.delete(alias)) this.persistence?.onDelete(alias);\n this.invalidatedAt.delete(alias);\n const invalidatedAt = [aliasInvalidatedAt, resolvedInvalidatedAt].reduce<number | undefined>(\n (latest, value) =>\n value === undefined ? latest : latest === undefined ? value : Math.max(latest, value),\n undefined,\n );\n const resolvedEntry = this.entries.get(resolved);\n if (\n invalidatedAt !== undefined &&\n (!resolvedEntry || invalidatedAt > resolvedEntry.updatedAt)\n ) {\n this.invalidatedAt.set(resolved, invalidatedAt);\n if (resolvedEntry) {\n this.entries.set(resolved, {\n ...resolvedEntry,\n staleAt: 0,\n invalidatedAt,\n });\n }\n } else if (invalidatedAt !== undefined) {\n this.invalidatedAt.delete(resolved);\n if (resolvedEntry?.invalidatedAt !== undefined) {\n this.entries.set(resolved, { ...resolvedEntry, invalidatedAt: undefined });\n }\n }\n\n const aliasInflight = this.inflight.get(alias);\n if (aliasInflight && !this.inflight.has(resolved)) {\n this.inflight.set(resolved, aliasInflight);\n }\n this.inflight.delete(alias);\n this.aliases.set(alias, resolved);\n this.emit(alias);\n this.emit(resolved, this.invalidatedAt.has(resolved) ? \"invalidate\" : undefined);\n }\n\n subscribe(key: string, listener: FarmClientCacheListener): () => void {\n let listeners = this.listeners.get(key);\n if (!listeners) {\n listeners = new Set();\n this.listeners.set(key, listeners);\n }\n\n listeners.add(listener);\n return () => {\n listeners!.delete(listener);\n // Only drop the map entry if it still holds this exact set. A repeated or\n // stale unsubscribe (called after the key was drained and resubscribed)\n // must not evict a newer subscriber's live listener set.\n if (listeners!.size === 0 && this.listeners.get(key) === listeners) {\n this.listeners.delete(key);\n }\n };\n }\n\n getInflight<TData>(key: string): Promise<TData> | undefined {\n return this.inflight.get(this.resolveKey(key)) as Promise<TData> | undefined;\n }\n\n setInflight<TData>(key: string, promise: Promise<TData>): void {\n this.inflight.set(this.resolveKey(key), promise);\n }\n\n deleteInflight(key: string): void {\n this.inflight.delete(this.resolveKey(key));\n }\n\n private scheduleGcSweep(): void {\n if (this.gcSweepIntervalMs === false || this.gcTimer !== undefined) return;\n const timer = setTimeout(() => {\n this.gcTimer = undefined;\n this.sweepExpiredEntries();\n }, this.gcSweepIntervalMs);\n // Cache cleanup must never keep a Node.js process (SSR, tests) alive.\n (timer as unknown as { unref?: () => void }).unref?.();\n this.gcTimer = timer;\n }\n\n private sweepExpiredEntries(now = Date.now()): void {\n const watched = new Set<string>();\n for (const key of this.listeners.keys()) watched.add(this.resolveKey(key));\n\n let remaining = false;\n const swept = new Set<string>();\n for (const [key, entry] of this.entries) {\n if (entry.gcAt === undefined) continue;\n if (now < entry.gcAt || entry.fetching || this.inflight.has(key) || watched.has(key)) {\n remaining = true;\n continue;\n }\n // Only unwatched entries are swept, so eviction is unobservable: a read\n // of this key would already evict it lazily before returning data.\n this.entries.delete(key);\n this.persistence?.onDelete(key);\n swept.add(key);\n }\n\n if (swept.size > 0) this.sweepEntryMetadata(swept);\n if (remaining) this.scheduleGcSweep();\n }\n\n /**\n * Entry eviction alone leaves the per-key metadata behind. `invalidatedAt`\n * marks and provisional aliases are created per query invocation, so with\n * dynamic keys they accumulate for the lifetime of the page even though the\n * entries they describe are long gone.\n */\n private sweepEntryMetadata(swept: Set<string>): void {\n for (const key of swept) this.invalidatedAt.delete(key);\n\n for (const [alias, target] of this.aliases) {\n // Keep any alias that is still addressable: one that has its own entry,\n // that something is subscribed to, or whose target is still live.\n if (this.entries.has(alias) || this.listeners.has(alias)) continue;\n const resolved = this.resolveKey(target);\n if (!swept.has(resolved)) continue;\n if (this.entries.has(resolved) || this.listeners.has(resolved)) continue;\n this.aliases.delete(alias);\n this.invalidatedAt.delete(alias);\n }\n }\n\n private emit(key: string, event?: \"invalidate\"): void {\n this.notifyListeners(key, event);\n for (const [alias, target] of this.aliases) {\n if (this.resolveKey(target) === key) {\n this.notifyListeners(alias, event);\n }\n }\n }\n\n private notifyListeners(key: string, event?: \"invalidate\"): void {\n for (const listener of this.listeners.get(key) ?? []) {\n // One subscriber must not be able to break the others, or to make an\n // ordinary cache write or invalidation throw in its caller. This matches\n // the isolation the global invalidation bus already provides.\n try {\n listener(event);\n } catch (error) {\n const detail = error instanceof Error ? (error.stack ?? error.message) : String(error);\n console.warn(`[farm:client-cache] cache listener failed: ${detail}`);\n }\n }\n }\n}\n\nconst FARM_CLIENT_DATA_CACHE = Symbol.for(\"farm.clientDataCache\");\nconst clientCacheGlobal = globalThis as typeof globalThis & {\n [FARM_CLIENT_DATA_CACHE]?: FarmClientDataCache;\n};\nconst sharedFarmClientDataCache = (clientCacheGlobal[FARM_CLIENT_DATA_CACHE] ??=\n new FarmClientDataCache());\n\nexport function getFarmClientDataCache(): FarmClientDataCache {\n return sharedFarmClientDataCache;\n}\n\nexport function normalizeFarmClientCacheKey(key: FarmClientCacheKey): string {\n return typeof key === \"string\" ? key : createRouteDataCacheKey(key);\n}\n"],"mappings":"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","names":[]}
@@ -1,3 +1,7 @@
1
+ import {
2
+ getFarmRuntimeBindings,
3
+ readFarmEnvironmentValue
4
+ } from "./chunk-RKUPOYQV.mjs";
1
5
  import {
2
6
  toPosixPath
3
7
  } from "./chunk-LBRRZEEK.mjs";
@@ -61,9 +65,10 @@ async function prepareFarmCronForNitro(config) {
61
65
  await fs.rm(generatedDir, { recursive: true, force: true });
62
66
  await fs.mkdir(generatedDir, { recursive: true });
63
67
  const tasks = {};
68
+ const wrapperNames = resolveCronWrapperFileNames(cron.jobs.map((job) => job.name));
64
69
  for (const job of cron.jobs) {
65
70
  const taskName = getFarmCronTaskName(job.name);
66
- const wrapperPath = toPosixPath(path.join(generatedDir, `${safeFileName(job.name)}.mjs`));
71
+ const wrapperPath = toPosixPath(path.join(generatedDir, `${wrapperNames.get(job.name)}.mjs`));
67
72
  await fs.writeFile(wrapperPath, createNitroCronTaskWrapper(job, cron.secretEnv), "utf8");
68
73
  tasks[taskName] = {
69
74
  handler: wrapperPath,
@@ -153,9 +158,9 @@ function mergeScheduledTasks(...maps) {
153
158
  __name(mergeScheduledTasks, "mergeScheduledTasks");
154
159
  function isCronRequestAuthorized(request, options = {}) {
155
160
  const secretEnv = options.secretEnv || DEFAULT_FARM_CRON_SECRET_ENV;
156
- const secret = options.secret || readEnvironmentValue(secretEnv);
161
+ const secret = options.secret || readFarmEnvironmentValue(secretEnv);
157
162
  if (!secret) {
158
- return options.allowUnsecured === true || !getFarmRuntimeBindings() && readEnvironmentValue("NODE_ENV") !== "production";
163
+ return options.allowUnsecured === true || !getFarmRuntimeBindings() && readFarmEnvironmentValue("NODE_ENV") !== "production";
159
164
  }
160
165
  const authorization = request.headers.get("authorization") || "";
161
166
  const bearer = /^Bearer (.*)$/i.exec(authorization)?.[1] || "";
@@ -365,17 +370,39 @@ export default defineTask({
365
370
  `.trim();
366
371
  }
367
372
  __name(createNitroCronTaskWrapper, "createNitroCronTaskWrapper");
368
- function readEnvironmentValue(name) {
369
- const runtimeValue = getFarmRuntimeBindings()?.[name];
370
- if (typeof runtimeValue === "string") return runtimeValue;
371
- return typeof process !== "undefined" ? process.env?.[name] : void 0;
373
+ function resolveCronWrapperFileNames(names) {
374
+ const groups = /* @__PURE__ */ new Map();
375
+ for (const name of names) {
376
+ const key = safeFileName(name).toLowerCase();
377
+ groups.set(key, (groups.get(key) ?? 0) + 1);
378
+ }
379
+ const resolved = /* @__PURE__ */ new Map();
380
+ const claimed = /* @__PURE__ */ new Map();
381
+ for (const name of names) {
382
+ const base = safeFileName(name);
383
+ const key = base.toLowerCase();
384
+ const fileName = (groups.get(key) ?? 0) > 1 ? `${key}-${cronNameFingerprint(name)}` : base;
385
+ const claimedBy = claimed.get(fileName.toLowerCase());
386
+ if (claimedBy !== void 0) {
387
+ throw new Error(
388
+ `Farm cron jobs ${JSON.stringify(claimedBy)} and ${JSON.stringify(name)} generate the same wrapper file ${JSON.stringify(`${fileName}.mjs`)}. Rename one of them.`
389
+ );
390
+ }
391
+ claimed.set(fileName.toLowerCase(), name);
392
+ resolved.set(name, fileName);
393
+ }
394
+ return resolved;
372
395
  }
373
- __name(readEnvironmentValue, "readEnvironmentValue");
374
- function getFarmRuntimeBindings() {
375
- const runtimeBindings = globalThis.__env__;
376
- return runtimeBindings && typeof runtimeBindings === "object" ? runtimeBindings : void 0;
396
+ __name(resolveCronWrapperFileNames, "resolveCronWrapperFileNames");
397
+ function cronNameFingerprint(value) {
398
+ let hash = 2166136261;
399
+ for (let index = 0; index < value.length; index += 1) {
400
+ hash ^= value.charCodeAt(index);
401
+ hash = Math.imul(hash, 16777619);
402
+ }
403
+ return (hash >>> 0).toString(16).padStart(8, "0");
377
404
  }
378
- __name(getFarmRuntimeBindings, "getFarmRuntimeBindings");
405
+ __name(cronNameFingerprint, "cronNameFingerprint");
379
406
  function safeFileName(value) {
380
407
  return value.replace(/[^a-zA-Z0-9._-]+/g, "-") || "cron";
381
408
  }
@@ -395,4 +422,4 @@ export {
395
422
  isCronRequestAuthorized,
396
423
  cronRoute
397
424
  };
398
- //# sourceMappingURL=chunk-BZAIS65C.mjs.map
425
+ //# sourceMappingURL=chunk-DIHNOVZZ.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cron.ts"],"sourcesContent":["import { toPosixPath } from \"./utils\";\nimport {\n getFarmRuntimeBindings,\n readFarmEnvironmentValue as readEnvironmentValue,\n} from \"./utils/runtime-env\";\n\nexport type FarmCronSchedule = string | string[];\n\nexport interface FarmCronJobConfig {\n /** Portable five-field cron expression, or a list of expressions. */\n schedule: FarmCronSchedule;\n /** Farm API route invoked with GET when the schedule fires. */\n path: string;\n /** Human-readable purpose shown by local tooling and deployment manifests. */\n description?: string;\n /** Disable this schedule without removing its configuration. */\n enabled?: boolean;\n}\n\nexport type FarmCronUserConfig = Record<string, FarmCronJobConfig>;\n\nexport interface FarmCronJob {\n name: string;\n schedule: string[];\n path: string;\n description?: string;\n}\n\nexport interface FarmCronResolvedConfig {\n enabled: boolean;\n secretEnv: typeof DEFAULT_FARM_CRON_SECRET_ENV;\n jobs: FarmCronJob[];\n}\n\nexport interface PreparedFarmCron {\n jobs: FarmCronJob[];\n tasks: Record<string, { handler: string; description: string }>;\n scheduledTasks: Record<string, string | string[]>;\n manifestPath?: string;\n}\n\nexport interface FarmCronRouteOptions {\n /** Environment variable containing the bearer token. */\n secretEnv?: string;\n /** Inline token, primarily useful in tests. Environment variables are preferred. */\n secret?: string;\n /** Allow a production route without a secret. Defaults to false. */\n allowUnsecured?: boolean;\n}\n\nexport interface FarmCronCloudflareConfig {\n deployConfig: true;\n wrangler: {\n triggers: {\n crons: string[];\n };\n };\n}\n\nexport const DEFAULT_FARM_CRON_SECRET_ENV = \"CRON_SECRET\";\nexport const FARM_CRON_MANIFEST = \"cron-manifest.json\";\n\nconst CRON_FIELD_RANGES = [\n [0, 59, \"minute\"],\n [0, 23, \"hour\"],\n [1, 31, \"day of month\"],\n [1, 12, \"month\"],\n [0, 6, \"day of week\"],\n] as const;\n\nexport function resolveCronConfig(\n cron: FarmCronResolvedConfig | FarmCronUserConfig | false | undefined,\n): FarmCronResolvedConfig {\n if (isResolvedCronConfig(cron)) return cron;\n if (!cron) {\n return {\n enabled: false,\n secretEnv: DEFAULT_FARM_CRON_SECRET_ENV,\n jobs: [],\n };\n }\n\n const jobs: FarmCronJob[] = [];\n for (const [name, job] of Object.entries(cron)) {\n if (!job || typeof job !== \"object\" || Array.isArray(job)) {\n throw new TypeError(`Farm cron ${JSON.stringify(name)} must be a configuration object.`);\n }\n if (job.enabled === false) continue;\n jobs.push(normalizeCronJob(name, job));\n }\n\n return {\n enabled: jobs.length > 0,\n secretEnv: DEFAULT_FARM_CRON_SECRET_ENV,\n jobs,\n };\n}\n\nexport async function prepareFarmCronForNitro(config: {\n root?: string;\n distDir?: string;\n cron?: FarmCronResolvedConfig | FarmCronUserConfig | false;\n}): Promise<PreparedFarmCron> {\n const cron = isResolvedCronConfig(config.cron) ? config.cron : resolveCronConfig(config.cron);\n const root = config.root || process.cwd();\n const distDir = config.distDir || \".farm\";\n const fs = await import(\"node:fs/promises\");\n const path = await import(\"node:path\");\n const manifestPath = path.join(root, distDir, FARM_CRON_MANIFEST);\n const generatedDir = path.join(root, distDir, \".nitro\", \"farm-cron\");\n\n if (!cron.enabled) {\n await Promise.all([\n fs.rm(manifestPath, { force: true }),\n fs.rm(generatedDir, { recursive: true, force: true }),\n ]);\n return {\n jobs: [],\n tasks: {},\n scheduledTasks: {},\n };\n }\n\n await fs.rm(generatedDir, { recursive: true, force: true });\n await fs.mkdir(generatedDir, { recursive: true });\n\n const tasks: PreparedFarmCron[\"tasks\"] = {};\n const wrapperNames = resolveCronWrapperFileNames(cron.jobs.map((job) => job.name));\n for (const job of cron.jobs) {\n const taskName = getFarmCronTaskName(job.name);\n const wrapperPath = toPosixPath(path.join(generatedDir, `${wrapperNames.get(job.name)}.mjs`));\n await fs.writeFile(wrapperPath, createNitroCronTaskWrapper(job, cron.secretEnv), \"utf8\");\n tasks[taskName] = {\n handler: wrapperPath,\n description: job.description || `Farm cron ${job.name}`,\n };\n }\n\n const manifest = createFarmCronManifest(cron);\n await fs.mkdir(path.dirname(manifestPath), { recursive: true });\n await fs.writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\\n`, \"utf8\");\n\n return {\n jobs: cron.jobs,\n tasks,\n scheduledTasks: createCronScheduledTasks(cron.jobs),\n manifestPath,\n };\n}\n\nexport function createFarmCronManifest(cron: FarmCronResolvedConfig) {\n return {\n schemaVersion: 1,\n secretEnv: cron.secretEnv,\n jobs: cron.jobs.map((job) => ({\n name: job.name,\n schedule: job.schedule,\n path: job.path,\n description: job.description || null,\n })),\n };\n}\n\nexport function createFarmCronVercelCrons(\n jobs: FarmCronJob[],\n): Array<{ path: string; schedule: string }> {\n return jobs.flatMap((job) =>\n job.schedule.map((schedule) => ({\n path: job.path,\n schedule,\n })),\n );\n}\n\nexport function applyFarmCronVercelCrons(\n vercelConfig: Record<string, any>,\n jobs: FarmCronJob[],\n): Record<string, any> {\n const existingCrons = Array.isArray(vercelConfig.crons) ? vercelConfig.crons : [];\n const seen = new Set(existingCrons.map((cron) => `${cron.path}:${cron.schedule}`));\n const crons = [...existingCrons];\n\n for (const cron of createFarmCronVercelCrons(jobs)) {\n const key = `${cron.path}:${cron.schedule}`;\n if (seen.has(key)) continue;\n seen.add(key);\n crons.push(cron);\n }\n\n return crons.length > 0 ? { ...vercelConfig, crons } : vercelConfig;\n}\n\nexport function createFarmCronCloudflareTriggers(jobs: FarmCronJob[]): string[] {\n return [...new Set(jobs.flatMap((job) => job.schedule))];\n}\n\nexport function createFarmCronCloudflareConfig(\n jobs: FarmCronJob[],\n): FarmCronCloudflareConfig | undefined {\n const crons = createFarmCronCloudflareTriggers(jobs);\n if (crons.length === 0) return undefined;\n\n return {\n deployConfig: true,\n wrangler: {\n triggers: { crons },\n },\n };\n}\n\nexport function mergeScheduledTasks(\n ...maps: Array<Record<string, string | string[]> | undefined>\n): Record<string, string | string[]> {\n const merged = new Map<string, string[]>();\n\n for (const map of maps) {\n for (const [schedule, taskNames] of Object.entries(map || {})) {\n const current = merged.get(schedule) || [];\n for (const taskName of Array.isArray(taskNames) ? taskNames : [taskNames]) {\n if (!current.includes(taskName)) current.push(taskName);\n }\n merged.set(schedule, current);\n }\n }\n\n return Object.fromEntries(\n [...merged].map(([schedule, taskNames]) => [\n schedule,\n taskNames.length === 1 ? taskNames[0] : taskNames,\n ]),\n );\n}\n\nexport function isCronRequestAuthorized(\n request: Request,\n options: FarmCronRouteOptions = {},\n): boolean {\n const secretEnv = options.secretEnv || DEFAULT_FARM_CRON_SECRET_ENV;\n const secret = options.secret || readEnvironmentValue(secretEnv);\n if (!secret) {\n return (\n options.allowUnsecured === true ||\n (!getFarmRuntimeBindings() && readEnvironmentValue(\"NODE_ENV\") !== \"production\")\n );\n }\n\n const authorization = request.headers.get(\"authorization\") || \"\";\n const bearer = /^Bearer (.*)$/i.exec(authorization)?.[1] || \"\";\n return bearer === secret || request.headers.get(\"x-farm-cron-secret\") === secret;\n}\n\nexport function cronRoute<TArgs extends unknown[], TResult>(\n handler: (request: Request, ...args: TArgs) => TResult,\n options: FarmCronRouteOptions = {},\n): (request: Request, ...args: TArgs) => TResult | Promise<Response> {\n return (request, ...args) => {\n if (!isCronRequestAuthorized(request, options)) {\n return Promise.resolve(\n Response.json({ error: \"Unauthorized cron request.\" }, { status: 401 }),\n );\n }\n return handler(request, ...args);\n };\n}\n\nfunction normalizeCronJob(name: string, job: FarmCronJobConfig): FarmCronJob {\n if (!/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(name)) {\n throw new TypeError(\n `Farm cron name ${JSON.stringify(name)} must start with a letter or number and contain only letters, numbers, dots, underscores, or hyphens.`,\n );\n }\n if (!job || typeof job !== \"object\" || Array.isArray(job)) {\n throw new TypeError(`Farm cron ${JSON.stringify(name)} must be a configuration object.`);\n }\n\n const path = normalizeCronPath(name, job.path);\n const rawSchedule = Array.isArray(job.schedule) ? job.schedule : [job.schedule];\n const schedule = [...new Set(rawSchedule.map((value) => normalizeCronExpression(name, value)))];\n if (schedule.length === 0) {\n throw new TypeError(`Farm cron ${JSON.stringify(name)} must define at least one schedule.`);\n }\n\n return {\n name,\n schedule,\n path,\n description: job.description?.trim() || undefined,\n };\n}\n\nfunction normalizeCronPath(name: string, value: unknown): string {\n if (typeof value !== \"string\" || !value.trim().startsWith(\"/\")) {\n throw new TypeError(`Farm cron ${JSON.stringify(name)} path must start with \"/\".`);\n }\n\n const hasUnstableCharacters = (candidate: string) =>\n candidate.includes(\"\\\\\") ||\n Array.from(candidate).some((character) => {\n const code = character.charCodeAt(0);\n return code <= 31 || (code >= 127 && code <= 159);\n });\n if (hasUnstableCharacters(value)) {\n throw new TypeError(\n `Farm cron ${JSON.stringify(name)} path cannot contain backslashes or control characters.`,\n );\n }\n\n const path = value.trim();\n if (path.startsWith(\"//\") || path.includes(\"?\") || path.includes(\"#\")) {\n throw new TypeError(\n `Farm cron ${JSON.stringify(name)} path must be an application pathname without a host, query, or hash.`,\n );\n }\n for (const segment of path.split(\"/\")) {\n let decoded = segment;\n try {\n decoded = decodeURIComponent(segment);\n } catch {\n // Malformed escapes remain literal and cannot conceal a separator or dot segment.\n }\n if (hasUnstableCharacters(decoded)) {\n throw new TypeError(\n `Farm cron ${JSON.stringify(name)} path cannot contain backslashes or control characters.`,\n );\n }\n if (decoded.includes(\"/\")) {\n throw new TypeError(\n `Farm cron ${JSON.stringify(name)} path cannot contain percent-encoded path separators.`,\n );\n }\n if (decoded === \".\" || decoded === \"..\") {\n throw new TypeError(\n `Farm cron ${JSON.stringify(name)} path cannot contain \".\" or \"..\" path segments.`,\n );\n }\n }\n return path.length > 1 ? path.replace(/\\/+$/, \"\") : path;\n}\n\nfunction normalizeCronExpression(name: string, value: unknown): string {\n if (typeof value !== \"string\") {\n throw new TypeError(`Farm cron ${JSON.stringify(name)} schedule must be a string.`);\n }\n\n const expression = value.trim().replace(/\\s+/g, \" \");\n const fields = expression.split(\" \");\n if (fields.length !== CRON_FIELD_RANGES.length) {\n throw new TypeError(\n `Farm cron ${JSON.stringify(name)} schedule ${JSON.stringify(expression)} must use five fields: minute hour day-of-month month day-of-week.`,\n );\n }\n\n fields.forEach((field, index) => {\n const [minimum, maximum, label] = CRON_FIELD_RANGES[index];\n validateCronField(name, expression, field, minimum, maximum, label);\n });\n if (fields[2] !== \"*\" && fields[4] !== \"*\") {\n throw new TypeError(\n `Farm cron ${JSON.stringify(name)} schedule ${JSON.stringify(expression)} cannot constrain both day-of-month and day-of-week.`,\n );\n }\n return expression;\n}\n\nfunction validateCronField(\n name: string,\n expression: string,\n field: string,\n minimum: number,\n maximum: number,\n label: string,\n): void {\n for (const segment of field.split(\",\")) {\n const [range, stepText, ...extra] = segment.split(\"/\");\n if (!range || extra.length > 0 || (stepText !== undefined && !isIntegerInRange(stepText, 1))) {\n throwInvalidCronField(name, expression, label);\n }\n\n if (range === \"*\") continue;\n const bounds = range.split(\"-\");\n if (bounds.length > 2 || !bounds.every((value) => isIntegerInRange(value, minimum, maximum))) {\n throwInvalidCronField(name, expression, label);\n }\n if (bounds.length === 2 && Number(bounds[0]) > Number(bounds[1])) {\n throwInvalidCronField(name, expression, label);\n }\n }\n}\n\nfunction isIntegerInRange(value: string, minimum: number, maximum = Number.MAX_SAFE_INTEGER) {\n if (!/^\\d+$/.test(value)) return false;\n const parsed = Number(value);\n return parsed >= minimum && parsed <= maximum;\n}\n\nfunction throwInvalidCronField(name: string, expression: string, label: string): never {\n throw new TypeError(\n `Farm cron ${JSON.stringify(name)} schedule ${JSON.stringify(expression)} has an invalid ${label} field.`,\n );\n}\n\nfunction isResolvedCronConfig(value: unknown): value is FarmCronResolvedConfig {\n return (\n !!value &&\n typeof value === \"object\" &&\n \"enabled\" in value &&\n typeof (value as FarmCronResolvedConfig).enabled === \"boolean\" &&\n \"jobs\" in value &&\n Array.isArray((value as FarmCronResolvedConfig).jobs) &&\n \"secretEnv\" in value\n );\n}\n\nfunction createCronScheduledTasks(jobs: FarmCronJob[]): Record<string, string | string[]> {\n const scheduledTasks: Record<string, string | string[]> = {};\n for (const job of jobs) {\n const taskName = getFarmCronTaskName(job.name);\n for (const schedule of job.schedule) {\n scheduledTasks[schedule] = mergeTaskNames(scheduledTasks[schedule], taskName);\n }\n }\n return scheduledTasks;\n}\n\nfunction mergeTaskNames(\n current: string | string[] | undefined,\n taskName: string,\n): string | string[] {\n if (!current) return taskName;\n const names = Array.isArray(current) ? current : [current];\n return names.includes(taskName) ? names : [...names, taskName];\n}\n\nfunction getFarmCronTaskName(name: string): string {\n return `farm:cron:${name}`;\n}\n\nfunction createNitroCronTaskWrapper(job: FarmCronJob, secretEnv: string): string {\n return `\nimport { defineTask, useNitroApp } from \"nitro/runtime\";\n\nconst name = ${JSON.stringify(job.name)};\nconst path = ${JSON.stringify(job.path)};\nconst secretEnv = ${JSON.stringify(secretEnv)};\n\nfunction readSecret(event) {\n const runtimeEnv = event?.context?.cloudflare?.env || event?.context?.env || {};\n return runtimeEnv[secretEnv] || (typeof process !== \"undefined\" ? process.env?.[secretEnv] : \"\") || \"\";\n}\n\nexport default defineTask({\n meta: {\n name: ${JSON.stringify(getFarmCronTaskName(job.name))},\n description: ${JSON.stringify(job.description || `Farm cron ${job.name}`)}\n },\n async run(event) {\n const headers = new Headers({\n \"x-farm-cron-name\": name,\n \"x-farm-cron-scheduled-at\": String(event?.payload?.scheduledTime || Date.now())\n });\n const secret = readSecret(event);\n if (secret) headers.set(\"authorization\", \"Bearer \" + secret);\n\n const response = await useNitroApp().fetch(path, { method: \"GET\", headers });\n const contentType = response.headers.get(\"content-type\") || \"\";\n const body = response.status === 204\n ? null\n : contentType.includes(\"application/json\")\n ? await response.json().catch(() => null)\n : await response.text();\n\n if (!response.ok) {\n const detail = typeof body === \"string\" ? body : JSON.stringify(body);\n throw new Error(\n \"Farm cron \" + JSON.stringify(name) + \" received \" + response.status +\n \" from \" + path + (detail ? \": \" + detail : \"\")\n );\n }\n\n return { result: body };\n }\n});\n`.trim();\n}\n\n/**\n * Wrapper file names for a set of cron job names.\n *\n * Cron names are case-sensitive and `Daily` and `daily` are both valid, distinct\n * jobs — but macOS and Windows use case-insensitive filesystems by default, so\n * their wrappers would overwrite one another and both jobs would run whichever\n * file was written last. Names are only disambiguated when they actually\n * collide case-insensitively, so ordinary names such as `dailyCleanup` keep a\n * readable wrapper; every name in a colliding group gets a digest of the exact\n * job name appended, which keeps the result independent of configuration order.\n */\nfunction resolveCronWrapperFileNames(names: readonly string[]): Map<string, string> {\n const groups = new Map<string, number>();\n for (const name of names) {\n const key = safeFileName(name).toLowerCase();\n groups.set(key, (groups.get(key) ?? 0) + 1);\n }\n\n const resolved = new Map<string, string>();\n const claimed = new Map<string, string>();\n for (const name of names) {\n const base = safeFileName(name);\n const key = base.toLowerCase();\n const fileName = (groups.get(key) ?? 0) > 1 ? `${key}-${cronNameFingerprint(name)}` : base;\n const claimedBy = claimed.get(fileName.toLowerCase());\n if (claimedBy !== undefined) {\n throw new Error(\n `Farm cron jobs ${JSON.stringify(claimedBy)} and ${JSON.stringify(name)} generate the same wrapper file ${JSON.stringify(`${fileName}.mjs`)}. Rename one of them.`,\n );\n }\n claimed.set(fileName.toLowerCase(), name);\n resolved.set(name, fileName);\n }\n return resolved;\n}\n\n/**\n * FNV-1a. This module is bundled into server runtimes without node:crypto, and\n * the digest only needs to separate file names.\n */\nfunction cronNameFingerprint(value: string): string {\n let hash = 0x811c9dc5;\n for (let index = 0; index < value.length; index += 1) {\n hash ^= value.charCodeAt(index);\n hash = Math.imul(hash, 0x01000193);\n }\n return (hash >>> 0).toString(16).padStart(8, \"0\");\n}\n\nfunction safeFileName(value: string): string {\n return value.replace(/[^a-zA-Z0-9._-]+/g, \"-\") || \"cron\";\n}\n"],"mappings":"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","names":[]}
@@ -439,9 +439,13 @@ function getStorage(namespace) {
439
439
  const namespaced = prefixStorage(globalStorage, base);
440
440
  return {
441
441
  ...namespaced,
442
- async clear() {
443
- const keys = await namespaced.getKeys();
444
- await Promise.all(keys.map((key) => namespaced.removeItem(key)));
442
+ // Scope the wipe to the namespace instead of the whole storage, and keep
443
+ // the caller's base so `clear("sessions")` cannot take unrelated keys with
444
+ // it. Dropping the argument silently widens a targeted clear into a
445
+ // namespace-wide delete.
446
+ async clear(base2, opts) {
447
+ const keys = await namespaced.getKeys(base2);
448
+ await Promise.all(keys.map((key) => namespaced.removeItem(key, opts)));
445
449
  }
446
450
  };
447
451
  }
@@ -483,4 +487,4 @@ export {
483
487
  getStorage,
484
488
  disposeStorage
485
489
  };
486
- //# sourceMappingURL=chunk-ISSFLGJ5.mjs.map
490
+ //# sourceMappingURL=chunk-FDWECN3W.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/storage/index.ts"],"sourcesContent":["import { createStorage, prefixStorage, builtinDrivers, type BuiltinDriverName } from \"unstorage\";\nimport type { Driver, Storage, TransactionOptions } from \"unstorage\";\nimport type { Database } from \"db0\";\nimport memoryDriver from \"unstorage/drivers/memory\";\nimport type {\n FarmStorageClient,\n FarmStorageClientConfig,\n FarmStorageConfigObject,\n FarmStorageLocalDriverConfig,\n FarmStorageDatabaseConfig,\n FarmStorageDatabaseDriverMap,\n FarmStorageDatabaseDriverName,\n FarmStorageRuntimeClient,\n FarmStorageMountConfig,\n FarmStorageMounts,\n FarmStorageUserConfig,\n} from \"./types\";\n\nexport type {\n FarmStorageBuiltinDriverConfig,\n FarmStorageClient,\n FarmStorageClientConfig,\n FarmStorageConfigObject,\n FarmStorageCustomDriverConfig,\n FarmStorageDatabaseConfig,\n FarmStorageDatabaseDriverName,\n FarmStorageLocalDriverConfig,\n FarmStorageRuntimeClient,\n FarmStorageMountConfig,\n FarmStorageMounts,\n FarmStorageUserConfig,\n} from \"./types\";\nexport type {\n BuiltinDriverName,\n BuiltinDriverOptions,\n Driver,\n Storage,\n StorageMeta,\n StorageValue,\n TransactionOptions,\n} from \"unstorage\";\nexport type { ConnectorName, ConnectorOptions, Database } from \"db0\";\n\ntype DriverFactoryModule = {\n default: (options?: Record<string, any>) => Driver;\n};\n\ntype DriverResolver = () => Driver | Promise<Driver>;\ntype ModuleLoader = <T = any>(specifier: string) => Promise<T>;\n\nconst loadModule: ModuleLoader = (specifier) => import(/* @vite-ignore */ specifier);\n\nconst DATABASE_CONNECTORS: {\n [K in FarmStorageDatabaseDriverName]: {\n connector: FarmStorageDatabaseDriverMap[K];\n load: () => Promise<{ default: (options?: any) => any }>;\n };\n} = {\n sqlite: {\n connector: \"node-sqlite\",\n load: () => loadModule(\"db0/connectors/node-sqlite\"),\n },\n \"node-sqlite\": {\n connector: \"node-sqlite\",\n load: () => loadModule(\"db0/connectors/node-sqlite\"),\n },\n sqlite3: {\n connector: \"sqlite3\",\n load: () => loadModule(\"db0/connectors/sqlite3\"),\n },\n \"better-sqlite3\": {\n connector: \"better-sqlite3\",\n load: () => loadModule(\"db0/connectors/better-sqlite3\"),\n },\n postgres: {\n connector: \"postgresql\",\n load: () => loadModule(\"db0/connectors/postgresql\"),\n },\n postgresql: {\n connector: \"postgresql\",\n load: () => loadModule(\"db0/connectors/postgresql\"),\n },\n pg: {\n connector: \"postgresql\",\n load: () => loadModule(\"db0/connectors/postgresql\"),\n },\n mysql: {\n connector: \"mysql2\",\n load: () => loadModule(\"db0/connectors/mysql2\"),\n },\n mysql2: {\n connector: \"mysql2\",\n load: () => loadModule(\"db0/connectors/mysql2\"),\n },\n pglite: {\n connector: \"pglite\",\n load: () => loadModule(\"db0/connectors/pglite\"),\n },\n planetscale: {\n connector: \"planetscale\",\n load: () => loadModule(\"db0/connectors/planetscale\"),\n },\n libsql: {\n connector: \"libsql\",\n load: () => loadModule(\"db0/connectors/libsql/node\"),\n },\n \"libsql-node\": {\n connector: \"libsql-node\",\n load: () => loadModule(\"db0/connectors/libsql/node\"),\n },\n \"libsql-http\": {\n connector: \"libsql-http\",\n load: () => loadModule(\"db0/connectors/libsql/http\"),\n },\n};\n\nfunction createMemoryStorage(): Storage {\n return createStorage({\n driver: memoryDriver(),\n });\n}\n\nconst GLOBAL_STORAGE_KEY = Symbol.for(\"farmjs.storage.global\");\n\nfunction readGlobalStorage(): Storage | undefined {\n return (globalThis as unknown as Record<PropertyKey, Storage | undefined>)[GLOBAL_STORAGE_KEY];\n}\n\nfunction writeGlobalStorage(storage: Storage): Storage {\n (globalThis as unknown as Record<PropertyKey, Storage | undefined>)[GLOBAL_STORAGE_KEY] = storage;\n return storage;\n}\n\nfunction getActiveGlobalStorage(): Storage {\n const existing = readGlobalStorage();\n if (existing) {\n return existing;\n }\n return writeGlobalStorage(createMemoryStorage());\n}\n\nlet globalStorage = getActiveGlobalStorage();\n\nfunction normalizeMountKey(base?: string): string {\n return (base || \"\")\n .trim()\n .replace(/^[:/\\\\]+|[:/\\\\]+$/g, \"\")\n .replace(/[\\\\/]+/g, \":\");\n}\n\nfunction isDriverInstance(value: unknown): value is Driver {\n return (\n !!value &&\n typeof value === \"object\" &&\n typeof (value as Driver).getItem === \"function\" &&\n typeof (value as Driver).getKeys === \"function\"\n );\n}\n\nfunction isStorageInstance(value: unknown): value is Storage {\n return (\n !!value &&\n typeof value === \"object\" &&\n typeof (value as Storage).mount === \"function\" &&\n typeof (value as Storage).getMount === \"function\"\n );\n}\n\nfunction isStorageClient(value: unknown): value is FarmStorageClient {\n return (\n !!value &&\n typeof value === \"object\" &&\n (value as FarmStorageClient).kind === \"farm-storage-client\" &&\n typeof (value as FarmStorageClient).resolveDriver === \"function\" &&\n typeof (value as FarmStorageClient).createStorage === \"function\"\n );\n}\n\nfunction isDatabaseDriverName(name: string): name is FarmStorageDatabaseDriverName {\n return name in DATABASE_CONNECTORS;\n}\n\nfunction extractDriverOptions(config: FarmStorageClientConfig): Record<string, any> | undefined {\n const source = config as Record<string, any>;\n const {\n client: _client,\n driver: _driver,\n mounts: _mounts,\n options,\n tableName: _tableName,\n ...inlineOptions\n } = source;\n const hasInlineOptions = Object.keys(inlineOptions).length > 0;\n\n if (options && typeof options === \"object\" && !Array.isArray(options)) {\n return hasInlineOptions ? { ...inlineOptions, ...options } : options;\n }\n\n if (hasInlineOptions) {\n return inlineOptions;\n }\n\n return options;\n}\n\nasync function loadBuiltinDriver(\n name: BuiltinDriverName,\n options?: Record<string, any>,\n): Promise<Driver> {\n const modulePath = builtinDrivers[name];\n\n if (!modulePath) {\n throw new Error(`Unsupported storage driver \"${name}\".`);\n }\n\n const module = (await loadModule(modulePath)) as DriverFactoryModule;\n return module.default(options);\n}\n\nasync function loadDatabaseDriver(config: FarmStorageDatabaseConfig): Promise<Driver> {\n const connectorEntry = DATABASE_CONNECTORS[config.driver as FarmStorageDatabaseDriverName];\n const [{ createDatabase }, connectorModule, db0DriverModule] = await Promise.all([\n loadModule<typeof import(\"db0\")>(\"db0\"),\n connectorEntry.load(),\n loadModule<typeof import(\"unstorage/drivers/db0\")>(\"unstorage/drivers/db0\"),\n ]);\n\n const database = createDatabase(connectorModule.default(extractDriverOptions(config) || {}));\n const driver = db0DriverModule.default({\n database,\n tableName: config.tableName,\n });\n\n // The db0 driver has no dispose, so this connection is Farm's to close. Databases\n // handed to `databaseStorage` stay owned by the caller and are left open.\n return {\n ...driver,\n async dispose() {\n await driver.dispose?.();\n await database.dispose();\n },\n };\n}\n\nasync function resolveDriver(config?: FarmStorageMountConfig): Promise<Driver> {\n if (config && isStorageClient(config)) {\n return config.resolveDriver();\n }\n\n const driverInput = config?.driver ?? \"memory\";\n\n if (typeof driverInput === \"function\") {\n return await driverInput();\n }\n\n if (isDriverInstance(driverInput)) {\n return driverInput;\n }\n\n if (isDatabaseDriverName(driverInput)) {\n return loadDatabaseDriver(config as FarmStorageDatabaseConfig);\n }\n\n if (driverInput === \"local\") {\n return loadBuiltinDriver(\"fs-lite\", extractDriverOptions(config as FarmStorageMountConfig));\n }\n\n return loadBuiltinDriver(\n driverInput as BuiltinDriverName,\n extractDriverOptions(config as FarmStorageClientConfig),\n );\n}\n\nfunction createStorageClientFromResolver(resolveDriver: DriverResolver): FarmStorageClient {\n let driverPromise: Promise<Driver> | undefined;\n let storagePromise: Promise<Storage> | undefined;\n\n const ensureDriver = () => {\n if (driverPromise) return driverPromise;\n\n const pending = Promise.resolve().then(resolveDriver);\n driverPromise = pending;\n void pending.catch(() => {\n if (driverPromise === pending) {\n driverPromise = undefined;\n storagePromise = undefined;\n }\n });\n return pending;\n };\n\n const ensureStorage = () => {\n if (storagePromise) return storagePromise;\n\n const pending = ensureDriver().then((driver) =>\n createStorage({\n driver,\n }),\n );\n storagePromise = pending;\n void pending.catch(() => {\n if (storagePromise === pending) storagePromise = undefined;\n });\n return pending;\n };\n\n const target = {\n kind: \"farm-storage-client\" as const,\n ready: ensureStorage,\n createStorage: ensureStorage,\n resolveDriver: ensureDriver,\n };\n\n return new Proxy(target as FarmStorageClient, {\n get(currentTarget, prop, receiver) {\n if (prop === \"then\") {\n return undefined;\n }\n\n if (Reflect.has(currentTarget, prop)) {\n return Reflect.get(currentTarget, prop, receiver);\n }\n\n return (...args: any[]) =>\n ensureStorage().then(async (storage) => {\n const member = Reflect.get(storage as object, prop);\n\n if (typeof member !== \"function\") {\n return member;\n }\n\n const result = Reflect.apply(member, storage, args);\n\n if (prop === \"dispose\") {\n return Promise.resolve(result).finally(() => {\n // Disposing the storage also disposed the driver, so drop both\n // cached promises: a later call must re-run the driver factory\n // instead of rebuilding storage over the dead driver.\n storagePromise = undefined;\n driverPromise = undefined;\n });\n }\n\n return result;\n });\n },\n });\n}\n\nexport function defineStorageClient(resolveDriver: DriverResolver): FarmStorageClient {\n return createStorageClientFromResolver(resolveDriver);\n}\n\nexport function createStorageClient(config: FarmStorageClientConfig): FarmStorageClient {\n return defineStorageClient(() => resolveDriver(config));\n}\n\nexport function driverStorage(\n driver: Driver | (() => Driver | Promise<Driver>),\n): FarmStorageClient {\n return defineStorageClient(() => (typeof driver === \"function\" ? driver() : driver));\n}\n\nexport function databaseStorage(\n database: Database,\n options: { tableName?: string } = {},\n): FarmStorageClient {\n return defineStorageClient(async () => {\n const db0DriverModule =\n await loadModule<typeof import(\"unstorage/drivers/db0\")>(\"unstorage/drivers/db0\");\n return db0DriverModule.default({\n database,\n tableName: options.tableName,\n });\n });\n}\n\nexport function memoryStorage(): FarmStorageClient {\n return createStorageClient({ driver: \"memory\" });\n}\n\nexport function localStorage(options: Omit<FarmStorageLocalDriverConfig, \"driver\"> = {}) {\n return createStorageClient({\n driver: \"local\",\n ...options,\n });\n}\n\nexport function sqliteStorage(options: Omit<FarmStorageDatabaseConfig, \"driver\">) {\n return createStorageClient({\n driver: \"sqlite\",\n ...options,\n });\n}\n\nexport function mysqlStorage(options: Omit<FarmStorageDatabaseConfig, \"driver\">) {\n return createStorageClient({\n driver: \"mysql\",\n ...options,\n });\n}\n\nexport function mysql2Storage(options: Omit<FarmStorageDatabaseConfig, \"driver\">) {\n return mysqlStorage(options);\n}\n\nexport function postgresStorage(options: Omit<FarmStorageDatabaseConfig, \"driver\">) {\n return createStorageClient({\n driver: \"postgres\",\n ...options,\n });\n}\n\nexport function pgStorage(options: Omit<FarmStorageDatabaseConfig, \"driver\">) {\n return postgresStorage(options);\n}\n\nexport function pgliteStorage(options: Omit<FarmStorageDatabaseConfig, \"driver\">) {\n return createStorageClient({\n driver: \"pglite\",\n ...options,\n });\n}\n\nexport function planetscaleStorage(options: Omit<FarmStorageDatabaseConfig, \"driver\">) {\n return createStorageClient({\n driver: \"planetscale\",\n ...options,\n });\n}\n\nexport function libsqlStorage(options: Omit<FarmStorageDatabaseConfig, \"driver\">) {\n return createStorageClient({\n driver: \"libsql\",\n ...options,\n });\n}\n\nexport function redisStorage(\n options: Omit<Extract<FarmStorageClientConfig, { driver: \"redis\" }>, \"driver\">,\n) {\n return createStorageClient({\n driver: \"redis\",\n ...options,\n });\n}\n\nexport function s3Storage(\n options: Omit<Extract<FarmStorageClientConfig, { driver: \"s3\" }>, \"driver\">,\n) {\n return createStorageClient({\n driver: \"s3\",\n ...options,\n });\n}\n\nexport function mongodbStorage(\n options: Omit<Extract<FarmStorageClientConfig, { driver: \"mongodb\" }>, \"driver\">,\n) {\n return createStorageClient({\n driver: \"mongodb\",\n ...options,\n });\n}\n\nexport function upstashStorage(\n options: Omit<Extract<FarmStorageClientConfig, { driver: \"upstash\" }>, \"driver\">,\n) {\n return createStorageClient({\n driver: \"upstash\",\n ...options,\n });\n}\n\nexport function netlifyBlobsStorage(\n options: Omit<Extract<FarmStorageClientConfig, { driver: \"netlify-blobs\" }>, \"driver\">,\n) {\n return createStorageClient({\n driver: \"netlify-blobs\",\n ...options,\n });\n}\n\nexport function vercelKVStorage(\n options: Omit<Extract<FarmStorageClientConfig, { driver: \"vercel-kv\" }>, \"driver\">,\n) {\n return createStorageClient({\n driver: \"vercel-kv\",\n ...options,\n });\n}\n\nexport function vercelBlobStorage(\n options: Omit<Extract<FarmStorageClientConfig, { driver: \"vercel-blob\" }>, \"driver\">,\n) {\n return createStorageClient({\n driver: \"vercel-blob\",\n ...options,\n });\n}\n\nasync function mountNamespaces(storage: Storage, mounts?: FarmStorageMounts): Promise<void> {\n if (!mounts) {\n return;\n }\n\n for (const [base, config] of Object.entries(mounts)) {\n storage.mount(normalizeMountKey(base), await resolveDriver(config));\n }\n}\n\nexport async function createFarmStorage(config: FarmStorageUserConfig = {}): Promise<Storage> {\n if (isStorageInstance(config)) {\n return config;\n }\n\n if (isStorageClient(config)) {\n return config.createStorage();\n }\n\n const rootConfig = isStorageClient(config.client) ? config.client : config;\n const storage = createStorage({\n driver: await resolveDriver(rootConfig),\n });\n\n try {\n await mountNamespaces(storage, config.mounts);\n } catch (error) {\n await storage.dispose().catch(() => {});\n throw error;\n }\n return storage;\n}\n\nexport async function resolveStorageRuntimeClient(\n config: FarmStorageUserConfig | undefined,\n): Promise<unknown | undefined> {\n if (!config || isStorageInstance(config) || isStorageClient(config)) {\n return undefined;\n }\n\n const client = config.client;\n if (!client || isStorageInstance(client) || isStorageClient(client)) {\n return undefined;\n }\n\n return typeof client === \"function\" ? await client() : client;\n}\n\nexport async function initStorage(config: FarmStorageUserConfig = {}): Promise<Storage> {\n globalStorage = getActiveGlobalStorage();\n const nextStorage = await createFarmStorage(config);\n const previousStorage = globalStorage;\n globalStorage = writeGlobalStorage(nextStorage);\n\n if (previousStorage !== nextStorage) {\n await previousStorage.dispose().catch(() => {});\n }\n\n return globalStorage;\n}\n\nexport function getStorage(namespace?: string): Storage {\n globalStorage = getActiveGlobalStorage();\n const base = normalizeMountKey(namespace);\n if (!base) {\n return globalStorage;\n }\n\n const namespaced = prefixStorage(globalStorage, base);\n return {\n ...namespaced,\n // Scope the wipe to the namespace instead of the whole storage, and keep\n // the caller's base so `clear(\"sessions\")` cannot take unrelated keys with\n // it. Dropping the argument silently widens a targeted clear into a\n // namespace-wide delete.\n async clear(base?: string, opts?: TransactionOptions) {\n const keys = await namespaced.getKeys(base);\n await Promise.all(keys.map((key) => namespaced.removeItem(key, opts)));\n },\n } as Storage;\n}\n\nexport async function disposeStorage(): Promise<void> {\n globalStorage = getActiveGlobalStorage();\n const previousStorage = globalStorage;\n globalStorage = writeGlobalStorage(createMemoryStorage());\n await previousStorage.dispose().catch(() => {});\n}\n"],"mappings":";;;;;AAAA,SAAS,eAAe,eAAe,sBAA8C;AAGrF,OAAO,kBAAkB;AA+CzB,IAAM,aAA2B,wBAAC,cAAc;AAAA;AAAA,EAA0B;AAAA,GAAzC;AAEjC,IAAM,sBAKF;AAAA,EACF,QAAQ;AAAA,IACN,WAAW;AAAA,IACX,MAAM,6BAAM,WAAW,4BAA4B,GAA7C;AAAA,EACR;AAAA,EACA,eAAe;AAAA,IACb,WAAW;AAAA,IACX,MAAM,6BAAM,WAAW,4BAA4B,GAA7C;AAAA,EACR;AAAA,EACA,SAAS;AAAA,IACP,WAAW;AAAA,IACX,MAAM,6BAAM,WAAW,wBAAwB,GAAzC;AAAA,EACR;AAAA,EACA,kBAAkB;AAAA,IAChB,WAAW;AAAA,IACX,MAAM,6BAAM,WAAW,+BAA+B,GAAhD;AAAA,EACR;AAAA,EACA,UAAU;AAAA,IACR,WAAW;AAAA,IACX,MAAM,6BAAM,WAAW,2BAA2B,GAA5C;AAAA,EACR;AAAA,EACA,YAAY;AAAA,IACV,WAAW;AAAA,IACX,MAAM,6BAAM,WAAW,2BAA2B,GAA5C;AAAA,EACR;AAAA,EACA,IAAI;AAAA,IACF,WAAW;AAAA,IACX,MAAM,6BAAM,WAAW,2BAA2B,GAA5C;AAAA,EACR;AAAA,EACA,OAAO;AAAA,IACL,WAAW;AAAA,IACX,MAAM,6BAAM,WAAW,uBAAuB,GAAxC;AAAA,EACR;AAAA,EACA,QAAQ;AAAA,IACN,WAAW;AAAA,IACX,MAAM,6BAAM,WAAW,uBAAuB,GAAxC;AAAA,EACR;AAAA,EACA,QAAQ;AAAA,IACN,WAAW;AAAA,IACX,MAAM,6BAAM,WAAW,uBAAuB,GAAxC;AAAA,EACR;AAAA,EACA,aAAa;AAAA,IACX,WAAW;AAAA,IACX,MAAM,6BAAM,WAAW,4BAA4B,GAA7C;AAAA,EACR;AAAA,EACA,QAAQ;AAAA,IACN,WAAW;AAAA,IACX,MAAM,6BAAM,WAAW,4BAA4B,GAA7C;AAAA,EACR;AAAA,EACA,eAAe;AAAA,IACb,WAAW;AAAA,IACX,MAAM,6BAAM,WAAW,4BAA4B,GAA7C;AAAA,EACR;AAAA,EACA,eAAe;AAAA,IACb,WAAW;AAAA,IACX,MAAM,6BAAM,WAAW,4BAA4B,GAA7C;AAAA,EACR;AACF;AAEA,SAAS,sBAA+B;AACtC,SAAO,cAAc;AAAA,IACnB,QAAQ,aAAa;AAAA,EACvB,CAAC;AACH;AAJS;AAMT,IAAM,qBAAqB,uBAAO,IAAI,uBAAuB;AAE7D,SAAS,oBAAyC;AAChD,SAAQ,WAAmE,kBAAkB;AAC/F;AAFS;AAIT,SAAS,mBAAmB,SAA2B;AACrD,EAAC,WAAmE,kBAAkB,IAAI;AAC1F,SAAO;AACT;AAHS;AAKT,SAAS,yBAAkC;AACzC,QAAM,WAAW,kBAAkB;AACnC,MAAI,UAAU;AACZ,WAAO;AAAA,EACT;AACA,SAAO,mBAAmB,oBAAoB,CAAC;AACjD;AANS;AAQT,IAAI,gBAAgB,uBAAuB;AAE3C,SAAS,kBAAkB,MAAuB;AAChD,UAAQ,QAAQ,IACb,KAAK,EACL,QAAQ,sBAAsB,EAAE,EAChC,QAAQ,WAAW,GAAG;AAC3B;AALS;AAOT,SAAS,iBAAiB,OAAiC;AACzD,SACE,CAAC,CAAC,SACF,OAAO,UAAU,YACjB,OAAQ,MAAiB,YAAY,cACrC,OAAQ,MAAiB,YAAY;AAEzC;AAPS;AAST,SAAS,kBAAkB,OAAkC;AAC3D,SACE,CAAC,CAAC,SACF,OAAO,UAAU,YACjB,OAAQ,MAAkB,UAAU,cACpC,OAAQ,MAAkB,aAAa;AAE3C;AAPS;AAST,SAAS,gBAAgB,OAA4C;AACnE,SACE,CAAC,CAAC,SACF,OAAO,UAAU,YAChB,MAA4B,SAAS,yBACtC,OAAQ,MAA4B,kBAAkB,cACtD,OAAQ,MAA4B,kBAAkB;AAE1D;AARS;AAUT,SAAS,qBAAqB,MAAqD;AACjF,SAAO,QAAQ;AACjB;AAFS;AAIT,SAAS,qBAAqB,QAAkE;AAC9F,QAAM,SAAS;AACf,QAAM;AAAA,IACJ,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR;AAAA,IACA,WAAW;AAAA,IACX,GAAG;AAAA,EACL,IAAI;AACJ,QAAM,mBAAmB,OAAO,KAAK,aAAa,EAAE,SAAS;AAE7D,MAAI,WAAW,OAAO,YAAY,YAAY,CAAC,MAAM,QAAQ,OAAO,GAAG;AACrE,WAAO,mBAAmB,EAAE,GAAG,eAAe,GAAG,QAAQ,IAAI;AAAA,EAC/D;AAEA,MAAI,kBAAkB;AACpB,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AArBS;AAuBT,eAAe,kBACb,MACA,SACiB;AACjB,QAAM,aAAa,eAAe,IAAI;AAEtC,MAAI,CAAC,YAAY;AACf,UAAM,IAAI,MAAM,+BAA+B,IAAI,IAAI;AAAA,EACzD;AAEA,QAAM,SAAU,MAAM,WAAW,UAAU;AAC3C,SAAO,OAAO,QAAQ,OAAO;AAC/B;AAZe;AAcf,eAAe,mBAAmB,QAAoD;AACpF,QAAM,iBAAiB,oBAAoB,OAAO,MAAuC;AACzF,QAAM,CAAC,EAAE,eAAe,GAAG,iBAAiB,eAAe,IAAI,MAAM,QAAQ,IAAI;AAAA,IAC/E,WAAiC,KAAK;AAAA,IACtC,eAAe,KAAK;AAAA,IACpB,WAAmD,uBAAuB;AAAA,EAC5E,CAAC;AAED,QAAM,WAAW,eAAe,gBAAgB,QAAQ,qBAAqB,MAAM,KAAK,CAAC,CAAC,CAAC;AAC3F,QAAM,SAAS,gBAAgB,QAAQ;AAAA,IACrC;AAAA,IACA,WAAW,OAAO;AAAA,EACpB,CAAC;AAID,SAAO;AAAA,IACL,GAAG;AAAA,IACH,MAAM,UAAU;AACd,YAAM,OAAO,UAAU;AACvB,YAAM,SAAS,QAAQ;AAAA,IACzB;AAAA,EACF;AACF;AAvBe;AAyBf,eAAe,cAAc,QAAkD;AAC7E,MAAI,UAAU,gBAAgB,MAAM,GAAG;AACrC,WAAO,OAAO,cAAc;AAAA,EAC9B;AAEA,QAAM,cAAc,QAAQ,UAAU;AAEtC,MAAI,OAAO,gBAAgB,YAAY;AACrC,WAAO,MAAM,YAAY;AAAA,EAC3B;AAEA,MAAI,iBAAiB,WAAW,GAAG;AACjC,WAAO;AAAA,EACT;AAEA,MAAI,qBAAqB,WAAW,GAAG;AACrC,WAAO,mBAAmB,MAAmC;AAAA,EAC/D;AAEA,MAAI,gBAAgB,SAAS;AAC3B,WAAO,kBAAkB,WAAW,qBAAqB,MAAgC,CAAC;AAAA,EAC5F;AAEA,SAAO;AAAA,IACL;AAAA,IACA,qBAAqB,MAAiC;AAAA,EACxD;AACF;AA3Be;AA6Bf,SAAS,gCAAgCA,gBAAkD;AACzF,MAAI;AACJ,MAAI;AAEJ,QAAM,eAAe,6BAAM;AACzB,QAAI,cAAe,QAAO;AAE1B,UAAM,UAAU,QAAQ,QAAQ,EAAE,KAAKA,cAAa;AACpD,oBAAgB;AAChB,SAAK,QAAQ,MAAM,MAAM;AACvB,UAAI,kBAAkB,SAAS;AAC7B,wBAAgB;AAChB,yBAAiB;AAAA,MACnB;AAAA,IACF,CAAC;AACD,WAAO;AAAA,EACT,GAZqB;AAcrB,QAAM,gBAAgB,6BAAM;AAC1B,QAAI,eAAgB,QAAO;AAE3B,UAAM,UAAU,aAAa,EAAE;AAAA,MAAK,CAAC,WACnC,cAAc;AAAA,QACZ;AAAA,MACF,CAAC;AAAA,IACH;AACA,qBAAiB;AACjB,SAAK,QAAQ,MAAM,MAAM;AACvB,UAAI,mBAAmB,QAAS,kBAAiB;AAAA,IACnD,CAAC;AACD,WAAO;AAAA,EACT,GAbsB;AAetB,QAAM,SAAS;AAAA,IACb,MAAM;AAAA,IACN,OAAO;AAAA,IACP,eAAe;AAAA,IACf,eAAe;AAAA,EACjB;AAEA,SAAO,IAAI,MAAM,QAA6B;AAAA,IAC5C,IAAI,eAAe,MAAM,UAAU;AACjC,UAAI,SAAS,QAAQ;AACnB,eAAO;AAAA,MACT;AAEA,UAAI,QAAQ,IAAI,eAAe,IAAI,GAAG;AACpC,eAAO,QAAQ,IAAI,eAAe,MAAM,QAAQ;AAAA,MAClD;AAEA,aAAO,IAAI,SACT,cAAc,EAAE,KAAK,OAAO,YAAY;AACtC,cAAM,SAAS,QAAQ,IAAI,SAAmB,IAAI;AAElD,YAAI,OAAO,WAAW,YAAY;AAChC,iBAAO;AAAA,QACT;AAEA,cAAM,SAAS,QAAQ,MAAM,QAAQ,SAAS,IAAI;AAElD,YAAI,SAAS,WAAW;AACtB,iBAAO,QAAQ,QAAQ,MAAM,EAAE,QAAQ,MAAM;AAI3C,6BAAiB;AACjB,4BAAgB;AAAA,UAClB,CAAC;AAAA,QACH;AAEA,eAAO;AAAA,MACT,CAAC;AAAA,IACL;AAAA,EACF,CAAC;AACH;AA1ES;AA4EF,SAAS,oBAAoBA,gBAAkD;AACpF,SAAO,gCAAgCA,cAAa;AACtD;AAFgB;AAIT,SAAS,oBAAoB,QAAoD;AACtF,SAAO,oBAAoB,MAAM,cAAc,MAAM,CAAC;AACxD;AAFgB;AAIT,SAAS,cACd,QACmB;AACnB,SAAO,oBAAoB,MAAO,OAAO,WAAW,aAAa,OAAO,IAAI,MAAO;AACrF;AAJgB;AAMT,SAAS,gBACd,UACA,UAAkC,CAAC,GAChB;AACnB,SAAO,oBAAoB,YAAY;AACrC,UAAM,kBACJ,MAAM,WAAmD,uBAAuB;AAClF,WAAO,gBAAgB,QAAQ;AAAA,MAC7B;AAAA,MACA,WAAW,QAAQ;AAAA,IACrB,CAAC;AAAA,EACH,CAAC;AACH;AAZgB;AAcT,SAAS,gBAAmC;AACjD,SAAO,oBAAoB,EAAE,QAAQ,SAAS,CAAC;AACjD;AAFgB;AAIT,SAAS,aAAa,UAAwD,CAAC,GAAG;AACvF,SAAO,oBAAoB;AAAA,IACzB,QAAQ;AAAA,IACR,GAAG;AAAA,EACL,CAAC;AACH;AALgB;AAOT,SAAS,cAAc,SAAoD;AAChF,SAAO,oBAAoB;AAAA,IACzB,QAAQ;AAAA,IACR,GAAG;AAAA,EACL,CAAC;AACH;AALgB;AAOT,SAAS,aAAa,SAAoD;AAC/E,SAAO,oBAAoB;AAAA,IACzB,QAAQ;AAAA,IACR,GAAG;AAAA,EACL,CAAC;AACH;AALgB;AAOT,SAAS,cAAc,SAAoD;AAChF,SAAO,aAAa,OAAO;AAC7B;AAFgB;AAIT,SAAS,gBAAgB,SAAoD;AAClF,SAAO,oBAAoB;AAAA,IACzB,QAAQ;AAAA,IACR,GAAG;AAAA,EACL,CAAC;AACH;AALgB;AAOT,SAAS,UAAU,SAAoD;AAC5E,SAAO,gBAAgB,OAAO;AAChC;AAFgB;AAIT,SAAS,cAAc,SAAoD;AAChF,SAAO,oBAAoB;AAAA,IACzB,QAAQ;AAAA,IACR,GAAG;AAAA,EACL,CAAC;AACH;AALgB;AAOT,SAAS,mBAAmB,SAAoD;AACrF,SAAO,oBAAoB;AAAA,IACzB,QAAQ;AAAA,IACR,GAAG;AAAA,EACL,CAAC;AACH;AALgB;AAOT,SAAS,cAAc,SAAoD;AAChF,SAAO,oBAAoB;AAAA,IACzB,QAAQ;AAAA,IACR,GAAG;AAAA,EACL,CAAC;AACH;AALgB;AAOT,SAAS,aACd,SACA;AACA,SAAO,oBAAoB;AAAA,IACzB,QAAQ;AAAA,IACR,GAAG;AAAA,EACL,CAAC;AACH;AAPgB;AAST,SAAS,UACd,SACA;AACA,SAAO,oBAAoB;AAAA,IACzB,QAAQ;AAAA,IACR,GAAG;AAAA,EACL,CAAC;AACH;AAPgB;AAST,SAAS,eACd,SACA;AACA,SAAO,oBAAoB;AAAA,IACzB,QAAQ;AAAA,IACR,GAAG;AAAA,EACL,CAAC;AACH;AAPgB;AAST,SAAS,eACd,SACA;AACA,SAAO,oBAAoB;AAAA,IACzB,QAAQ;AAAA,IACR,GAAG;AAAA,EACL,CAAC;AACH;AAPgB;AAST,SAAS,oBACd,SACA;AACA,SAAO,oBAAoB;AAAA,IACzB,QAAQ;AAAA,IACR,GAAG;AAAA,EACL,CAAC;AACH;AAPgB;AAST,SAAS,gBACd,SACA;AACA,SAAO,oBAAoB;AAAA,IACzB,QAAQ;AAAA,IACR,GAAG;AAAA,EACL,CAAC;AACH;AAPgB;AAST,SAAS,kBACd,SACA;AACA,SAAO,oBAAoB;AAAA,IACzB,QAAQ;AAAA,IACR,GAAG;AAAA,EACL,CAAC;AACH;AAPgB;AAShB,eAAe,gBAAgB,SAAkB,QAA2C;AAC1F,MAAI,CAAC,QAAQ;AACX;AAAA,EACF;AAEA,aAAW,CAAC,MAAM,MAAM,KAAK,OAAO,QAAQ,MAAM,GAAG;AACnD,YAAQ,MAAM,kBAAkB,IAAI,GAAG,MAAM,cAAc,MAAM,CAAC;AAAA,EACpE;AACF;AARe;AAUf,eAAsB,kBAAkB,SAAgC,CAAC,GAAqB;AAC5F,MAAI,kBAAkB,MAAM,GAAG;AAC7B,WAAO;AAAA,EACT;AAEA,MAAI,gBAAgB,MAAM,GAAG;AAC3B,WAAO,OAAO,cAAc;AAAA,EAC9B;AAEA,QAAM,aAAa,gBAAgB,OAAO,MAAM,IAAI,OAAO,SAAS;AACpE,QAAM,UAAU,cAAc;AAAA,IAC5B,QAAQ,MAAM,cAAc,UAAU;AAAA,EACxC,CAAC;AAED,MAAI;AACF,UAAM,gBAAgB,SAAS,OAAO,MAAM;AAAA,EAC9C,SAAS,OAAO;AACd,UAAM,QAAQ,QAAQ,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AACtC,UAAM;AAAA,EACR;AACA,SAAO;AACT;AArBsB;AAuBtB,eAAsB,4BACpB,QAC8B;AAC9B,MAAI,CAAC,UAAU,kBAAkB,MAAM,KAAK,gBAAgB,MAAM,GAAG;AACnE,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,OAAO;AACtB,MAAI,CAAC,UAAU,kBAAkB,MAAM,KAAK,gBAAgB,MAAM,GAAG;AACnE,WAAO;AAAA,EACT;AAEA,SAAO,OAAO,WAAW,aAAa,MAAM,OAAO,IAAI;AACzD;AAbsB;AAetB,eAAsB,YAAY,SAAgC,CAAC,GAAqB;AACtF,kBAAgB,uBAAuB;AACvC,QAAM,cAAc,MAAM,kBAAkB,MAAM;AAClD,QAAM,kBAAkB;AACxB,kBAAgB,mBAAmB,WAAW;AAE9C,MAAI,oBAAoB,aAAa;AACnC,UAAM,gBAAgB,QAAQ,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EAChD;AAEA,SAAO;AACT;AAXsB;AAaf,SAAS,WAAW,WAA6B;AACtD,kBAAgB,uBAAuB;AACvC,QAAM,OAAO,kBAAkB,SAAS;AACxC,MAAI,CAAC,MAAM;AACT,WAAO;AAAA,EACT;AAEA,QAAM,aAAa,cAAc,eAAe,IAAI;AACpD,SAAO;AAAA,IACL,GAAG;AAAA;AAAA;AAAA;AAAA;AAAA,IAKH,MAAM,MAAMC,OAAe,MAA2B;AACpD,YAAM,OAAO,MAAM,WAAW,QAAQA,KAAI;AAC1C,YAAM,QAAQ,IAAI,KAAK,IAAI,CAAC,QAAQ,WAAW,WAAW,KAAK,IAAI,CAAC,CAAC;AAAA,IACvE;AAAA,EACF;AACF;AAnBgB;AAqBhB,eAAsB,iBAAgC;AACpD,kBAAgB,uBAAuB;AACvC,QAAM,kBAAkB;AACxB,kBAAgB,mBAAmB,oBAAoB,CAAC;AACxD,QAAM,gBAAgB,QAAQ,EAAE,MAAM,MAAM;AAAA,EAAC,CAAC;AAChD;AALsB;","names":["resolveDriver","base"]}
@@ -0,0 +1,114 @@
1
+ import {
2
+ __name
3
+ } from "./chunk-CGILA3WO.mjs";
4
+
5
+ // src/request-origin.ts
6
+ function getRequestSourceOrigin(request) {
7
+ const origin = request.headers.get("origin")?.trim();
8
+ if (origin) {
9
+ return parseSourceOrigin(origin);
10
+ }
11
+ const referer = request.headers.get("referer")?.trim();
12
+ if (referer) {
13
+ return parseSourceOrigin(referer);
14
+ }
15
+ return { ok: true, origin: null };
16
+ }
17
+ __name(getRequestSourceOrigin, "getRequestSourceOrigin");
18
+ function parseSourceOrigin(value) {
19
+ if (value === "null") {
20
+ return { ok: false, reason: "opaque-origin" };
21
+ }
22
+ try {
23
+ const parsed = new URL(value);
24
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
25
+ return { ok: false, reason: "invalid-origin" };
26
+ }
27
+ return { ok: true, origin: parsed.origin };
28
+ } catch {
29
+ return { ok: false, reason: "invalid-origin" };
30
+ }
31
+ }
32
+ __name(parseSourceOrigin, "parseSourceOrigin");
33
+ function matchesHostHeader(sourceOrigin, request) {
34
+ const host = request.headers.get("host")?.trim().toLowerCase();
35
+ if (!host) return false;
36
+ try {
37
+ const source = new URL(sourceOrigin);
38
+ const target = new URL(request.url);
39
+ return source.protocol === target.protocol && source.host.toLowerCase() === host;
40
+ } catch {
41
+ return false;
42
+ }
43
+ }
44
+ __name(matchesHostHeader, "matchesHostHeader");
45
+ function matchesAllowedOrigin(sourceOrigin, pattern) {
46
+ const source = new URL(sourceOrigin);
47
+ if (!pattern.includes("*")) {
48
+ return pattern.includes("://") ? source.origin === pattern : source.host === pattern;
49
+ }
50
+ const schemeEnd = pattern.indexOf("://");
51
+ const scheme = schemeEnd === -1 ? null : pattern.slice(0, schemeEnd + 1);
52
+ const hostPattern = pattern.slice(schemeEnd === -1 ? 0 : schemeEnd + 3);
53
+ const [wildcardHost, port] = splitHostAndPort(hostPattern);
54
+ const baseHost = wildcardHost.slice(2);
55
+ if (scheme && source.protocol !== scheme) return false;
56
+ if (port && getEffectivePort(source) !== port) return false;
57
+ if (!port && source.port) return false;
58
+ return source.hostname.endsWith(`.${baseHost}`) && source.hostname !== baseHost;
59
+ }
60
+ __name(matchesAllowedOrigin, "matchesAllowedOrigin");
61
+ function normalizeAllowedOriginPattern(value, label) {
62
+ const pattern = value.trim().toLowerCase();
63
+ if (!pattern) {
64
+ throw new TypeError(`${label} cannot contain empty values`);
65
+ }
66
+ if (pattern.includes("*")) {
67
+ if (!/^(?:https?:\/\/)?\*\.[a-z0-9.-]+(?::\d+)?$/.test(pattern)) {
68
+ throw new TypeError(`Invalid ${label} pattern: ${JSON.stringify(value)}`);
69
+ }
70
+ return pattern;
71
+ }
72
+ if (pattern.includes("://")) {
73
+ let parsed;
74
+ try {
75
+ parsed = new URL(pattern);
76
+ } catch {
77
+ throw new TypeError(`Invalid ${label} value: ${JSON.stringify(value)}`);
78
+ }
79
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:" || parsed.username || parsed.password || parsed.pathname !== "/" || parsed.search || parsed.hash) {
80
+ throw new TypeError(`${label} must contain origins without paths: ${JSON.stringify(value)}`);
81
+ }
82
+ return parsed.origin;
83
+ }
84
+ if (/[/@?#]/.test(pattern)) {
85
+ throw new TypeError(`${label} must contain origins or hosts: ${JSON.stringify(value)}`);
86
+ }
87
+ try {
88
+ return new URL(`http://${pattern}`).host;
89
+ } catch {
90
+ throw new TypeError(`Invalid ${label} value: ${JSON.stringify(value)}`);
91
+ }
92
+ }
93
+ __name(normalizeAllowedOriginPattern, "normalizeAllowedOriginPattern");
94
+ function splitHostAndPort(value) {
95
+ const separator = value.lastIndexOf(":");
96
+ if (separator === -1) return [value, null];
97
+ return [value.slice(0, separator), value.slice(separator + 1)];
98
+ }
99
+ __name(splitHostAndPort, "splitHostAndPort");
100
+ function getEffectivePort(url) {
101
+ if (url.port) return url.port;
102
+ if (url.protocol === "https:") return "443";
103
+ if (url.protocol === "http:") return "80";
104
+ return "";
105
+ }
106
+ __name(getEffectivePort, "getEffectivePort");
107
+
108
+ export {
109
+ getRequestSourceOrigin,
110
+ matchesHostHeader,
111
+ matchesAllowedOrigin,
112
+ normalizeAllowedOriginPattern
113
+ };
114
+ //# sourceMappingURL=chunk-FDWXXTWR.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/request-origin.ts"],"sourcesContent":["/**\n * Shared request-origin primitives.\n *\n * Server actions and integration auth routes both need to decide whether a\n * state-changing request actually came from the app's own origin. The matching\n * rules (Origin/Referer resolution, Host fallback, wildcard subdomain patterns)\n * are security-sensitive and must not drift between the two, so they live here\n * and are consumed by both rather than reimplemented.\n *\n * These helpers never throw for untrusted input: callers map the failure\n * reasons onto their own error shapes.\n */\n\nexport type RequestOriginFailureReason = \"opaque-origin\" | \"invalid-origin\";\n\nexport type RequestSourceOriginResult =\n /** `origin` is null when the request carried no Origin or Referer header. */\n { ok: true; origin: string | null } | { ok: false; reason: RequestOriginFailureReason };\n\n/** Resolve the origin a request claims to come from, preferring Origin over Referer. */\nexport function getRequestSourceOrigin(request: Request): RequestSourceOriginResult {\n const origin = request.headers.get(\"origin\")?.trim();\n if (origin) {\n return parseSourceOrigin(origin);\n }\n\n const referer = request.headers.get(\"referer\")?.trim();\n if (referer) {\n return parseSourceOrigin(referer);\n }\n\n return { ok: true, origin: null };\n}\n\nfunction parseSourceOrigin(value: string): RequestSourceOriginResult {\n if (value === \"null\") {\n return { ok: false, reason: \"opaque-origin\" };\n }\n\n try {\n const parsed = new URL(value);\n if (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n return { ok: false, reason: \"invalid-origin\" };\n }\n return { ok: true, origin: parsed.origin };\n } catch {\n return { ok: false, reason: \"invalid-origin\" };\n }\n}\n\n/**\n * Accept a source origin that matches the Host header even when `request.url`\n * was rebuilt behind a proxy with a different visible origin.\n */\nexport function matchesHostHeader(sourceOrigin: string, request: Request): boolean {\n const host = request.headers.get(\"host\")?.trim().toLowerCase();\n if (!host) return false;\n\n try {\n const source = new URL(sourceOrigin);\n const target = new URL(request.url);\n return source.protocol === target.protocol && source.host.toLowerCase() === host;\n } catch {\n return false;\n }\n}\n\nexport function matchesAllowedOrigin(sourceOrigin: string, pattern: string): boolean {\n const source = new URL(sourceOrigin);\n if (!pattern.includes(\"*\")) {\n return pattern.includes(\"://\") ? source.origin === pattern : source.host === pattern;\n }\n\n const schemeEnd = pattern.indexOf(\"://\");\n const scheme = schemeEnd === -1 ? null : pattern.slice(0, schemeEnd + 1);\n const hostPattern = pattern.slice(schemeEnd === -1 ? 0 : schemeEnd + 3);\n const [wildcardHost, port] = splitHostAndPort(hostPattern);\n const baseHost = wildcardHost.slice(2);\n\n if (scheme && source.protocol !== scheme) return false;\n if (port && getEffectivePort(source) !== port) return false;\n if (!port && source.port) return false;\n\n return source.hostname.endsWith(`.${baseHost}`) && source.hostname !== baseHost;\n}\n\n/**\n * Validate and canonicalize a configured origin pattern. `label` names the\n * configuration field so the thrown message points at the user's own setting.\n */\nexport function normalizeAllowedOriginPattern(value: string, label: string): string {\n const pattern = value.trim().toLowerCase();\n if (!pattern) {\n throw new TypeError(`${label} cannot contain empty values`);\n }\n\n if (pattern.includes(\"*\")) {\n if (!/^(?:https?:\\/\\/)?\\*\\.[a-z0-9.-]+(?::\\d+)?$/.test(pattern)) {\n throw new TypeError(`Invalid ${label} pattern: ${JSON.stringify(value)}`);\n }\n return pattern;\n }\n\n if (pattern.includes(\"://\")) {\n let parsed: URL;\n try {\n parsed = new URL(pattern);\n } catch {\n throw new TypeError(`Invalid ${label} value: ${JSON.stringify(value)}`);\n }\n\n if (\n (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") ||\n parsed.username ||\n parsed.password ||\n parsed.pathname !== \"/\" ||\n parsed.search ||\n parsed.hash\n ) {\n throw new TypeError(`${label} must contain origins without paths: ${JSON.stringify(value)}`);\n }\n return parsed.origin;\n }\n\n if (/[/@?#]/.test(pattern)) {\n throw new TypeError(`${label} must contain origins or hosts: ${JSON.stringify(value)}`);\n }\n\n try {\n return new URL(`http://${pattern}`).host;\n } catch {\n throw new TypeError(`Invalid ${label} value: ${JSON.stringify(value)}`);\n }\n}\n\nfunction splitHostAndPort(value: string): [string, string | null] {\n const separator = value.lastIndexOf(\":\");\n if (separator === -1) return [value, null];\n return [value.slice(0, separator), value.slice(separator + 1)];\n}\n\nfunction getEffectivePort(url: URL): string {\n if (url.port) return url.port;\n if (url.protocol === \"https:\") return \"443\";\n if (url.protocol === \"http:\") return \"80\";\n return \"\";\n}\n"],"mappings":"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","names":[]}
@@ -1,5 +1,5 @@
1
1
  // package.json
2
- var version = "0.1.0-beta.97";
2
+ var version = "0.1.0-beta.98";
3
3
 
4
4
  // src/version.ts
5
5
  var FARM_VERSION = version;
@@ -7,4 +7,4 @@ var FARM_VERSION = version;
7
7
  export {
8
8
  FARM_VERSION
9
9
  };
10
- //# sourceMappingURL=chunk-J3MPQ4MQ.mjs.map
10
+ //# sourceMappingURL=chunk-IHADQ6J7.mjs.map