awaitly 1.35.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (241) hide show
  1. package/dist/{duration.d.ts → di-BDlT7InM.d.cts} +15 -1
  2. package/dist/{duration.d.cts → di-BbFFfO8y.d.ts} +15 -1
  3. package/dist/errors-DtXvrCiO.d.cts +708 -0
  4. package/dist/errors-DtXvrCiO.d.ts +708 -0
  5. package/dist/index.cjs +4594 -1
  6. package/dist/index.cjs.map +1 -1
  7. package/dist/index.d.cts +1970 -141
  8. package/dist/index.d.ts +1970 -141
  9. package/dist/index.js +4398 -1
  10. package/dist/index.js.map +1 -1
  11. package/dist/result.cjs +641 -1
  12. package/dist/result.cjs.map +1 -1
  13. package/dist/result.d.cts +29 -4
  14. package/dist/result.d.ts +29 -4
  15. package/dist/result.js +561 -1
  16. package/dist/result.js.map +1 -1
  17. package/dist/testing.cjs +4202 -8
  18. package/dist/testing.cjs.map +1 -1
  19. package/dist/testing.d.cts +2 -6
  20. package/dist/testing.d.ts +2 -6
  21. package/dist/testing.js +4154 -8
  22. package/dist/testing.js.map +1 -1
  23. package/dist/{run-entry-DGs0tySr.d.cts → types-B8NfNRGX.d.ts} +1078 -1502
  24. package/dist/{run-entry-BOuNyVoO.d.ts → types-BZ2f4MRR.d.cts} +1078 -1502
  25. package/dist/workflow.cjs +7096 -6
  26. package/dist/workflow.cjs.map +1 -1
  27. package/dist/workflow.d.cts +3346 -22
  28. package/dist/workflow.d.ts +3346 -22
  29. package/dist/workflow.js +6929 -6
  30. package/dist/workflow.js.map +1 -1
  31. package/package.json +3 -168
  32. package/dist/adapters.cjs +0 -7
  33. package/dist/adapters.cjs.map +0 -1
  34. package/dist/adapters.d.cts +0 -179
  35. package/dist/adapters.d.ts +0 -179
  36. package/dist/adapters.js +0 -7
  37. package/dist/adapters.js.map +0 -1
  38. package/dist/batch.cjs +0 -7
  39. package/dist/batch.cjs.map +0 -1
  40. package/dist/batch.d.cts +0 -200
  41. package/dist/batch.d.ts +0 -200
  42. package/dist/batch.js +0 -7
  43. package/dist/batch.js.map +0 -1
  44. package/dist/bind-deps.cjs +0 -2
  45. package/dist/bind-deps.cjs.map +0 -1
  46. package/dist/bind-deps.d.cts +0 -28
  47. package/dist/bind-deps.d.ts +0 -28
  48. package/dist/bind-deps.js +0 -2
  49. package/dist/bind-deps.js.map +0 -1
  50. package/dist/cache.cjs +0 -2
  51. package/dist/cache.cjs.map +0 -1
  52. package/dist/cache.d.cts +0 -269
  53. package/dist/cache.d.ts +0 -269
  54. package/dist/cache.js +0 -2
  55. package/dist/cache.js.map +0 -1
  56. package/dist/circuit-breaker.cjs +0 -7
  57. package/dist/circuit-breaker.cjs.map +0 -1
  58. package/dist/circuit-breaker.d.cts +0 -211
  59. package/dist/circuit-breaker.d.ts +0 -211
  60. package/dist/circuit-breaker.js +0 -7
  61. package/dist/circuit-breaker.js.map +0 -1
  62. package/dist/conditional.cjs +0 -2
  63. package/dist/conditional.cjs.map +0 -1
  64. package/dist/conditional.d.cts +0 -252
  65. package/dist/conditional.d.ts +0 -252
  66. package/dist/conditional.js +0 -2
  67. package/dist/conditional.js.map +0 -1
  68. package/dist/core.cjs +0 -7
  69. package/dist/core.cjs.map +0 -1
  70. package/dist/core.d.cts +0 -5
  71. package/dist/core.d.ts +0 -5
  72. package/dist/core.js +0 -7
  73. package/dist/core.js.map +0 -1
  74. package/dist/di-By77n4Fa.d.ts +0 -15
  75. package/dist/di-OJfsohf-.d.cts +0 -15
  76. package/dist/diagnostics.cjs +0 -8
  77. package/dist/diagnostics.cjs.map +0 -1
  78. package/dist/diagnostics.d.cts +0 -68
  79. package/dist/diagnostics.d.ts +0 -68
  80. package/dist/diagnostics.js +0 -8
  81. package/dist/diagnostics.js.map +0 -1
  82. package/dist/durable.cjs +0 -11
  83. package/dist/durable.cjs.map +0 -1
  84. package/dist/durable.d.cts +0 -9
  85. package/dist/durable.d.ts +0 -9
  86. package/dist/durable.js +0 -11
  87. package/dist/durable.js.map +0 -1
  88. package/dist/duration.cjs +0 -2
  89. package/dist/duration.cjs.map +0 -1
  90. package/dist/duration.js +0 -2
  91. package/dist/duration.js.map +0 -1
  92. package/dist/engine.cjs +0 -11
  93. package/dist/engine.cjs.map +0 -1
  94. package/dist/engine.d.cts +0 -115
  95. package/dist/engine.d.ts +0 -115
  96. package/dist/engine.js +0 -11
  97. package/dist/engine.js.map +0 -1
  98. package/dist/errors.cjs +0 -2
  99. package/dist/errors.cjs.map +0 -1
  100. package/dist/errors.d.cts +0 -361
  101. package/dist/errors.d.ts +0 -361
  102. package/dist/errors.js +0 -2
  103. package/dist/errors.js.map +0 -1
  104. package/dist/fetch.cjs +0 -7
  105. package/dist/fetch.cjs.map +0 -1
  106. package/dist/fetch.d.cts +0 -86
  107. package/dist/fetch.d.ts +0 -86
  108. package/dist/fetch.js +0 -7
  109. package/dist/fetch.js.map +0 -1
  110. package/dist/flow.cjs +0 -7
  111. package/dist/flow.cjs.map +0 -1
  112. package/dist/flow.d.cts +0 -163
  113. package/dist/flow.d.ts +0 -163
  114. package/dist/flow.js +0 -7
  115. package/dist/flow.js.map +0 -1
  116. package/dist/functional.cjs +0 -2
  117. package/dist/functional.cjs.map +0 -1
  118. package/dist/functional.d.cts +0 -444
  119. package/dist/functional.d.ts +0 -444
  120. package/dist/functional.js +0 -2
  121. package/dist/functional.js.map +0 -1
  122. package/dist/guards-4sV7mTqj.d.cts +0 -72
  123. package/dist/guards-BIX05ALH.d.ts +0 -72
  124. package/dist/hitl-DFn4Xa_l.d.cts +0 -468
  125. package/dist/hitl-DU5VpKq7.d.ts +0 -468
  126. package/dist/hitl.cjs +0 -7
  127. package/dist/hitl.cjs.map +0 -1
  128. package/dist/hitl.d.cts +0 -442
  129. package/dist/hitl.d.ts +0 -442
  130. package/dist/hitl.js +0 -7
  131. package/dist/hitl.js.map +0 -1
  132. package/dist/index-CnvBryQB.d.ts +0 -417
  133. package/dist/index-DEZEf8Fs.d.cts +0 -417
  134. package/dist/match-entry-DjI2bLpD.d.cts +0 -209
  135. package/dist/match-entry-DjI2bLpD.d.ts +0 -209
  136. package/dist/match.cjs +0 -2
  137. package/dist/match.cjs.map +0 -1
  138. package/dist/match.d.cts +0 -1
  139. package/dist/match.d.ts +0 -1
  140. package/dist/match.js +0 -2
  141. package/dist/match.js.map +0 -1
  142. package/dist/otel.cjs +0 -2
  143. package/dist/otel.cjs.map +0 -1
  144. package/dist/otel.d.cts +0 -188
  145. package/dist/otel.d.ts +0 -188
  146. package/dist/otel.js +0 -2
  147. package/dist/otel.js.map +0 -1
  148. package/dist/persistence-entry-B-8PjnSR.d.cts +0 -831
  149. package/dist/persistence-entry-D8zRkLiT.d.ts +0 -831
  150. package/dist/persistence.cjs +0 -2
  151. package/dist/persistence.cjs.map +0 -1
  152. package/dist/persistence.d.cts +0 -7
  153. package/dist/persistence.d.ts +0 -7
  154. package/dist/persistence.js +0 -2
  155. package/dist/persistence.js.map +0 -1
  156. package/dist/policies.cjs +0 -2
  157. package/dist/policies.cjs.map +0 -1
  158. package/dist/policies.d.cts +0 -379
  159. package/dist/policies.d.ts +0 -379
  160. package/dist/policies.js +0 -2
  161. package/dist/policies.js.map +0 -1
  162. package/dist/ratelimit.cjs +0 -7
  163. package/dist/ratelimit.cjs.map +0 -1
  164. package/dist/ratelimit.d.cts +0 -458
  165. package/dist/ratelimit.d.ts +0 -458
  166. package/dist/ratelimit.js +0 -7
  167. package/dist/ratelimit.js.map +0 -1
  168. package/dist/reliability.cjs +0 -11
  169. package/dist/reliability.cjs.map +0 -1
  170. package/dist/reliability.d.cts +0 -11
  171. package/dist/reliability.d.ts +0 -11
  172. package/dist/reliability.js +0 -11
  173. package/dist/reliability.js.map +0 -1
  174. package/dist/resolver.cjs +0 -7
  175. package/dist/resolver.cjs.map +0 -1
  176. package/dist/resolver.d.cts +0 -68
  177. package/dist/resolver.d.ts +0 -68
  178. package/dist/resolver.js +0 -7
  179. package/dist/resolver.js.map +0 -1
  180. package/dist/resource.cjs +0 -7
  181. package/dist/resource.cjs.map +0 -1
  182. package/dist/resource.d.cts +0 -174
  183. package/dist/resource.d.ts +0 -174
  184. package/dist/resource.js +0 -7
  185. package/dist/resource.js.map +0 -1
  186. package/dist/result/retry.cjs +0 -2
  187. package/dist/result/retry.cjs.map +0 -1
  188. package/dist/result/retry.d.cts +0 -70
  189. package/dist/result/retry.d.ts +0 -70
  190. package/dist/result/retry.js +0 -2
  191. package/dist/result/retry.js.map +0 -1
  192. package/dist/retry.cjs +0 -2
  193. package/dist/retry.cjs.map +0 -1
  194. package/dist/retry.d.cts +0 -388
  195. package/dist/retry.d.ts +0 -388
  196. package/dist/retry.js +0 -2
  197. package/dist/retry.js.map +0 -1
  198. package/dist/run.cjs +0 -7
  199. package/dist/run.cjs.map +0 -1
  200. package/dist/run.d.cts +0 -4
  201. package/dist/run.d.ts +0 -4
  202. package/dist/run.js +0 -7
  203. package/dist/run.js.map +0 -1
  204. package/dist/saga.cjs +0 -11
  205. package/dist/saga.cjs.map +0 -1
  206. package/dist/saga.d.cts +0 -164
  207. package/dist/saga.d.ts +0 -164
  208. package/dist/saga.js +0 -11
  209. package/dist/saga.js.map +0 -1
  210. package/dist/singleflight.cjs +0 -2
  211. package/dist/singleflight.cjs.map +0 -1
  212. package/dist/singleflight.d.cts +0 -145
  213. package/dist/singleflight.d.ts +0 -145
  214. package/dist/singleflight.js +0 -2
  215. package/dist/singleflight.js.map +0 -1
  216. package/dist/slugs.cjs +0 -2
  217. package/dist/slugs.cjs.map +0 -1
  218. package/dist/slugs.d.cts +0 -67
  219. package/dist/slugs.d.ts +0 -67
  220. package/dist/slugs.js +0 -2
  221. package/dist/slugs.js.map +0 -1
  222. package/dist/streaming.cjs +0 -9
  223. package/dist/streaming.cjs.map +0 -1
  224. package/dist/streaming.d.cts +0 -596
  225. package/dist/streaming.d.ts +0 -596
  226. package/dist/streaming.js +0 -9
  227. package/dist/streaming.js.map +0 -1
  228. package/dist/tagged-error.cjs +0 -2
  229. package/dist/tagged-error.cjs.map +0 -1
  230. package/dist/tagged-error.d.cts +0 -275
  231. package/dist/tagged-error.d.ts +0 -275
  232. package/dist/tagged-error.js +0 -2
  233. package/dist/tagged-error.js.map +0 -1
  234. package/dist/types-BziYHFkD.d.ts +0 -323
  235. package/dist/types-C5jLEUqY.d.cts +0 -323
  236. package/dist/webhook.cjs +0 -7
  237. package/dist/webhook.cjs.map +0 -1
  238. package/dist/webhook.d.cts +0 -499
  239. package/dist/webhook.d.ts +0 -499
  240. package/dist/webhook.js +0 -7
  241. package/dist/webhook.js.map +0 -1
@@ -1,2 +0,0 @@
1
- "use strict";var t=Object.defineProperty;var o=Object.getOwnPropertyDescriptor;var d=Object.getOwnPropertyNames;var n=Object.prototype.hasOwnProperty;var A=(e,s)=>{for(var p in s)t(e,p,{get:s[p],enumerable:!0})},b=(e,s,p,D)=>{if(s&&typeof s=="object"||typeof s=="function")for(let r of d(s))!n.call(e,r)&&r!==p&&t(e,r,{get:()=>s[r],enumerable:!(D=o(s,r))||D.enumerable});return e};var i=e=>b(t({},"__esModule",{value:!0}),e);var u={};A(u,{bindDeps:()=>g});module.exports=i(u);var g=e=>s=>p=>e(p,s);0&&(module.exports={bindDeps});
2
- //# sourceMappingURL=bind-deps.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/bind-deps-entry.ts","../src/bind-deps.ts"],"sourcesContent":["/**\n * awaitly/bind-deps\n *\n * Partial application utility for the fn(args, deps) pattern.\n */\nexport { bindDeps } from \"./bind-deps\";\n","/**\n * awaitly/bind-deps\n *\n * Partial application utility for the fn(args, deps) pattern.\n * Transforms a function from fn(args, deps) => out into a curried form:\n * (deps) => (args) => out\n *\n * Use at composition boundaries to bind deps once, then call with args.\n * Keep core implementations in the explicit fn(args, deps) form for testing.\n *\n * @example\n * ```typescript\n * import { bindDeps } from 'awaitly/bind-deps';\n *\n * // Core function stays explicit and testable\n * const notify = (args: { name: string }, deps: { send: SendFn }) =>\n * deps.send(args.name);\n *\n * // At composition boundary, bind deps once\n * const notifySlack = bindDeps(notify)(slackDeps);\n *\n * // Call site is clean\n * await notifySlack({ name: 'Alice' });\n * ```\n */\n\nexport const bindDeps =\n <Args, Deps, Out>(fn: (args: Args, deps: Deps) => Out) =>\n (deps: Deps) =>\n (args: Args) =>\n fn(args, deps);\n"],"mappings":"yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,cAAAE,IAAA,eAAAC,EAAAH,GC0BO,IAAMI,EACOC,GACjBC,GACAC,GACCF,EAAGE,EAAMD,CAAI","names":["bind_deps_entry_exports","__export","bindDeps","__toCommonJS","bindDeps","fn","deps","args"]}
@@ -1,28 +0,0 @@
1
- /**
2
- * awaitly/bind-deps
3
- *
4
- * Partial application utility for the fn(args, deps) pattern.
5
- * Transforms a function from fn(args, deps) => out into a curried form:
6
- * (deps) => (args) => out
7
- *
8
- * Use at composition boundaries to bind deps once, then call with args.
9
- * Keep core implementations in the explicit fn(args, deps) form for testing.
10
- *
11
- * @example
12
- * ```typescript
13
- * import { bindDeps } from 'awaitly/bind-deps';
14
- *
15
- * // Core function stays explicit and testable
16
- * const notify = (args: { name: string }, deps: { send: SendFn }) =>
17
- * deps.send(args.name);
18
- *
19
- * // At composition boundary, bind deps once
20
- * const notifySlack = bindDeps(notify)(slackDeps);
21
- *
22
- * // Call site is clean
23
- * await notifySlack({ name: 'Alice' });
24
- * ```
25
- */
26
- declare const bindDeps: <Args, Deps, Out>(fn: (args: Args, deps: Deps) => Out) => (deps: Deps) => (args: Args) => Out;
27
-
28
- export { bindDeps };
@@ -1,28 +0,0 @@
1
- /**
2
- * awaitly/bind-deps
3
- *
4
- * Partial application utility for the fn(args, deps) pattern.
5
- * Transforms a function from fn(args, deps) => out into a curried form:
6
- * (deps) => (args) => out
7
- *
8
- * Use at composition boundaries to bind deps once, then call with args.
9
- * Keep core implementations in the explicit fn(args, deps) form for testing.
10
- *
11
- * @example
12
- * ```typescript
13
- * import { bindDeps } from 'awaitly/bind-deps';
14
- *
15
- * // Core function stays explicit and testable
16
- * const notify = (args: { name: string }, deps: { send: SendFn }) =>
17
- * deps.send(args.name);
18
- *
19
- * // At composition boundary, bind deps once
20
- * const notifySlack = bindDeps(notify)(slackDeps);
21
- *
22
- * // Call site is clean
23
- * await notifySlack({ name: 'Alice' });
24
- * ```
25
- */
26
- declare const bindDeps: <Args, Deps, Out>(fn: (args: Args, deps: Deps) => Out) => (deps: Deps) => (args: Args) => Out;
27
-
28
- export { bindDeps };
package/dist/bind-deps.js DELETED
@@ -1,2 +0,0 @@
1
- var r=s=>e=>p=>s(p,e);export{r as bindDeps};
2
- //# sourceMappingURL=bind-deps.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/bind-deps.ts"],"sourcesContent":["/**\n * awaitly/bind-deps\n *\n * Partial application utility for the fn(args, deps) pattern.\n * Transforms a function from fn(args, deps) => out into a curried form:\n * (deps) => (args) => out\n *\n * Use at composition boundaries to bind deps once, then call with args.\n * Keep core implementations in the explicit fn(args, deps) form for testing.\n *\n * @example\n * ```typescript\n * import { bindDeps } from 'awaitly/bind-deps';\n *\n * // Core function stays explicit and testable\n * const notify = (args: { name: string }, deps: { send: SendFn }) =>\n * deps.send(args.name);\n *\n * // At composition boundary, bind deps once\n * const notifySlack = bindDeps(notify)(slackDeps);\n *\n * // Call site is clean\n * await notifySlack({ name: 'Alice' });\n * ```\n */\n\nexport const bindDeps =\n <Args, Deps, Out>(fn: (args: Args, deps: Deps) => Out) =>\n (deps: Deps) =>\n (args: Args) =>\n fn(args, deps);\n"],"mappings":"AA0BO,IAAMA,EACOC,GACjBC,GACAC,GACCF,EAAGE,EAAMD,CAAI","names":["bindDeps","fn","deps","args"]}
package/dist/cache.cjs DELETED
@@ -1,2 +0,0 @@
1
- "use strict";var g=Object.defineProperty;var M=Object.getOwnPropertyDescriptor;var S=Object.getOwnPropertyNames;var z=Object.prototype.hasOwnProperty;var O=(e,t)=>{for(var r in t)g(e,r,{get:t[r],enumerable:!0})},F=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of S(t))!z.call(e,i)&&i!==r&&g(e,i,{get:()=>t[i],enumerable:!(n=M(t,i))||n.enumerable});return e};var I=e=>F(g({},"__esModule",{value:!0}),e);var V={};O(V,{cached:()=>b,cachedFunction:()=>C,cachedWithTTL:()=>A,createCache:()=>P,once:()=>w});module.exports=I(V);function K(e){return{_tag:"Duration",millis:e}}function k(e){return{_tag:"Duration",millis:e*1e3}}function _(e){return{_tag:"Duration",millis:e*60*1e3}}function E(e){return{_tag:"Duration",millis:e*60*60*1e3}}function L(e){return{_tag:"Duration",millis:e*24*60*60*1e3}}function T(e){let t=e.trim().match(/^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)$/i);if(!t)return;let r=parseFloat(t[1]);switch(t[2].toLowerCase()){case"ms":return K(r);case"s":return k(r);case"m":return _(r);case"h":return E(r);case"d":return L(r);default:return}}function h(e){if(typeof e=="string"){let t=T(e);if(!t)throw new Error(`Invalid duration string: ${e}`);return t.millis}return e.millis}function b(e){let t={status:"empty"};return async()=>{if(t.status==="filled")return t.value;if(t.status==="pending")return t.promise;let r=Promise.resolve(e()).then(n=>(t={status:"filled",value:n},n));return t={status:"pending",promise:r},r}}function A(e,t){let r=h(t.ttl),n={status:"empty"};return async()=>{let i=Date.now();if(n.status==="filled"&&i<n.expiresAt)return n.value;if(n.status==="pending")return n.promise;let s=Promise.resolve(e()).then(c=>(n={status:"filled",value:c,expiresAt:Date.now()+r},c));return n={status:"pending",promise:s},s}}function C(e,t={}){let{keyFn:r=(...u)=>JSON.stringify(u),maxSize:n=1/0}=t,i=t.ttl?h(t.ttl):void 0,s=new Map,c=new Map,d=0,o=0;function l(){if(s.size<=n)return;let u=null,a=1/0;for(let[p,f]of s.entries())f.timestamp<a&&(a=f.timestamp,u=p);u&&s.delete(u)}let m=async(...u)=>{let a=r(...u),p=Date.now(),f=s.get(a);if(f)if(f.expiresAt&&p>=f.expiresAt)s.delete(a);else return d++,f.value;let x=c.get(a);if(x)return x;o++;let y=Promise.resolve(e(...u)).then(D=>{let v={value:D,timestamp:Date.now(),expiresAt:i?Date.now()+i:void 0};return s.set(a,v),c.delete(a),l(),D});c.set(a,y);try{return await y}catch(D){throw c.delete(a),D}};return m.clear=()=>{s.clear(),c.clear()},m.delete=(...u)=>{let a=r(...u);return s.delete(a)},m.has=(...u)=>{let a=r(...u),p=s.get(a);return p?p.expiresAt&&Date.now()>=p.expiresAt?(s.delete(a),!1):!0:!1},m.getStats=()=>({hits:d,misses:o,size:s.size}),m}function w(e){let t={status:"idle"},r=async()=>{if(t.status==="done")return t.value;if(t.status==="failed")throw t.error;if(t.status==="running")return t.promise;let n=Promise.resolve(e()).then(i=>(t={status:"done",value:i},i)).catch(i=>{throw t={status:"failed",error:i},i});return t={status:"running",promise:n},n};return Object.defineProperty(r,"called",{get:()=>t.status!=="idle"}),Object.defineProperty(r,"completed",{get:()=>t.status==="done"}),Object.defineProperty(r,"failed",{get:()=>t.status==="failed"}),r.reset=()=>{t={status:"idle"}},r}function P(e={}){let{maxSize:t=1/0}=e,r=e.defaultTTL?h(e.defaultTTL):void 0,n=new Map,i=0,s=0;function c(o){return o.expiresAt!==void 0&&Date.now()>=o.expiresAt}function d(){if(n.size<=t)return;let o=null,l=1/0;for(let[m,u]of n.entries())u.timestamp<l&&(l=u.timestamp,o=m);o!==null&&n.delete(o)}return{get(o){let l=n.get(o);if(!l){s++;return}if(c(l)){n.delete(o),s++;return}return i++,l.value},set(o,l,m){let u=m?.ttl?h(m.ttl):r,a=Date.now();n.set(o,{value:l,timestamp:a,expiresAt:u?a+u:void 0}),d()},has(o){let l=n.get(o);return l?c(l)?(n.delete(o),!1):!0:!1},delete(o){return n.delete(o)},clear(){n.clear()},get size(){return n.size},getStats(){return{hits:i,misses:s,size:n.size}}}}0&&(module.exports={cached,cachedFunction,cachedWithTTL,createCache,once});
2
- //# sourceMappingURL=cache.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/cache-entry.ts","../src/duration.ts","../src/cache.ts"],"sourcesContent":["/**\n * awaitly/cache\n *\n * Caching utilities for memoization and deduplication.\n *\n * @example\n * ```typescript\n * import { cached, cachedWithTTL, cachedFunction, once } from 'awaitly/cache';\n *\n * // Compute once, reuse forever\n * const getConfig = cached(() => loadConfig());\n *\n * // Expire after duration\n * const getUser = cachedWithTTL(() => fetchUser(id), { ttl: '5m' });\n *\n * // Memoize by arguments\n * const fetchUserMemo = cachedFunction((id: string) => fetchUser(id));\n *\n * // Execute exactly once\n * const initDb = once(() => connectToDatabase());\n * ```\n */\n\nexport {\n // Types\n type DurationInput,\n type CacheEntry,\n type CacheOptions,\n type CachedFunctionOptions,\n type CacheStats,\n type MemoizedFunction,\n type OnceFunction,\n type Cache,\n type CacheConfig,\n\n // Functions\n cached,\n cachedWithTTL,\n cachedFunction,\n once,\n createCache,\n} from \"./cache\";\n","/**\n * awaitly/duration\n *\n * Type-safe duration handling inspired by Effect's Duration module.\n * Prevents unit confusion (milliseconds vs seconds) with explicit constructors.\n */\n\n// =============================================================================\n// Duration Type\n// =============================================================================\n\n/**\n * A type-safe representation of a time duration.\n * Use the constructor functions (millis, seconds, etc.) to create durations.\n */\nexport interface Duration {\n readonly _tag: \"Duration\";\n readonly millis: number;\n}\n\n// =============================================================================\n// Constructors\n// =============================================================================\n\n/**\n * Create a Duration from milliseconds.\n *\n * @example\n * ```typescript\n * const d = Duration.millis(500)\n * ```\n */\nexport function millis(ms: number): Duration {\n return { _tag: \"Duration\", millis: ms };\n}\n\n/**\n * Create a Duration from seconds.\n *\n * @example\n * ```typescript\n * const d = Duration.seconds(5) // 5000ms\n * ```\n */\nexport function seconds(s: number): Duration {\n return { _tag: \"Duration\", millis: s * 1000 };\n}\n\n/**\n * Create a Duration from minutes.\n *\n * @example\n * ```typescript\n * const d = Duration.minutes(2) // 120000ms\n * ```\n */\nexport function minutes(m: number): Duration {\n return { _tag: \"Duration\", millis: m * 60 * 1000 };\n}\n\n/**\n * Create a Duration from hours.\n *\n * @example\n * ```typescript\n * const d = Duration.hours(1) // 3600000ms\n * ```\n */\nexport function hours(h: number): Duration {\n return { _tag: \"Duration\", millis: h * 60 * 60 * 1000 };\n}\n\n/**\n * Create a Duration from days.\n *\n * @example\n * ```typescript\n * const d = Duration.days(1) // 86400000ms\n * ```\n */\nexport function days(d: number): Duration {\n return { _tag: \"Duration\", millis: d * 24 * 60 * 60 * 1000 };\n}\n\n/**\n * Zero duration.\n */\nexport const zero: Duration = { _tag: \"Duration\", millis: 0 };\n\n/**\n * Infinite duration (represented as Infinity milliseconds).\n */\nexport const infinity: Duration = { _tag: \"Duration\", millis: Infinity };\n\n// =============================================================================\n// Conversions\n// =============================================================================\n\n/**\n * Convert a Duration to milliseconds.\n */\nexport function toMillis(duration: Duration): number {\n return duration.millis;\n}\n\n/**\n * Convert a Duration to seconds.\n */\nexport function toSeconds(duration: Duration): number {\n return duration.millis / 1000;\n}\n\n/**\n * Convert a Duration to minutes.\n */\nexport function toMinutes(duration: Duration): number {\n return duration.millis / (60 * 1000);\n}\n\n/**\n * Convert a Duration to hours.\n */\nexport function toHours(duration: Duration): number {\n return duration.millis / (60 * 60 * 1000);\n}\n\n/**\n * Convert a Duration to days.\n */\nexport function toDays(duration: Duration): number {\n return duration.millis / (24 * 60 * 60 * 1000);\n}\n\n// =============================================================================\n// Operations\n// =============================================================================\n\n/**\n * Add two durations.\n *\n * @example\n * ```typescript\n * const total = Duration.add(Duration.seconds(5), Duration.millis(500))\n * // 5500ms\n * ```\n */\nexport function add(a: Duration, b: Duration): Duration {\n return { _tag: \"Duration\", millis: a.millis + b.millis };\n}\n\n/**\n * Subtract duration b from duration a.\n * Result is clamped to zero (no negative durations).\n *\n * @example\n * ```typescript\n * const remaining = Duration.subtract(Duration.seconds(5), Duration.seconds(2))\n * // 3000ms\n * ```\n */\nexport function subtract(a: Duration, b: Duration): Duration {\n return { _tag: \"Duration\", millis: Math.max(0, a.millis - b.millis) };\n}\n\n/**\n * Multiply a duration by a factor.\n *\n * @example\n * ```typescript\n * const doubled = Duration.multiply(Duration.seconds(5), 2)\n * // 10000ms\n * ```\n */\nexport function multiply(duration: Duration, factor: number): Duration {\n return { _tag: \"Duration\", millis: duration.millis * factor };\n}\n\n/**\n * Divide a duration by a divisor.\n *\n * @example\n * ```typescript\n * const half = Duration.divide(Duration.seconds(10), 2)\n * // 5000ms\n * ```\n */\nexport function divide(duration: Duration, divisor: number): Duration {\n return { _tag: \"Duration\", millis: duration.millis / divisor };\n}\n\n// =============================================================================\n// Comparisons\n// =============================================================================\n\n/**\n * Check if duration a is less than duration b.\n */\nexport function lessThan(a: Duration, b: Duration): boolean {\n return a.millis < b.millis;\n}\n\n/**\n * Check if duration a is less than or equal to duration b.\n */\nexport function lessThanOrEqual(a: Duration, b: Duration): boolean {\n return a.millis <= b.millis;\n}\n\n/**\n * Check if duration a is greater than duration b.\n */\nexport function greaterThan(a: Duration, b: Duration): boolean {\n return a.millis > b.millis;\n}\n\n/**\n * Check if duration a is greater than or equal to duration b.\n */\nexport function greaterThanOrEqual(a: Duration, b: Duration): boolean {\n return a.millis >= b.millis;\n}\n\n/**\n * Check if two durations are equal.\n */\nexport function equals(a: Duration, b: Duration): boolean {\n return a.millis === b.millis;\n}\n\n/**\n * Get the minimum of two durations.\n */\nexport function min(a: Duration, b: Duration): Duration {\n return a.millis <= b.millis ? a : b;\n}\n\n/**\n * Get the maximum of two durations.\n */\nexport function max(a: Duration, b: Duration): Duration {\n return a.millis >= b.millis ? a : b;\n}\n\n/**\n * Clamp a duration between a minimum and maximum.\n */\nexport function clamp(duration: Duration, minimum: Duration, maximum: Duration): Duration {\n return min(max(duration, minimum), maximum);\n}\n\n// =============================================================================\n// Predicates\n// =============================================================================\n\n/**\n * Check if a duration is zero.\n */\nexport function isZero(duration: Duration): boolean {\n return duration.millis === 0;\n}\n\n/**\n * Check if a duration is infinite.\n */\nexport function isInfinite(duration: Duration): boolean {\n return duration.millis === Infinity;\n}\n\n/**\n * Check if a duration is finite and positive.\n */\nexport function isFinite(duration: Duration): boolean {\n return Number.isFinite(duration.millis) && duration.millis > 0;\n}\n\n/**\n * Type guard to check if a value is a Duration.\n */\nexport function isDuration(value: unknown): value is Duration {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"_tag\" in value &&\n value._tag === \"Duration\" &&\n \"millis\" in value &&\n typeof value.millis === \"number\"\n );\n}\n\n// =============================================================================\n// Formatting\n// =============================================================================\n\n/**\n * Format a duration as a human-readable string.\n *\n * @example\n * ```typescript\n * Duration.format(Duration.seconds(90)) // \"1m 30s\"\n * Duration.format(Duration.millis(500)) // \"500ms\"\n * ```\n */\nexport function format(duration: Duration): string {\n const ms = duration.millis;\n\n if (ms === Infinity) return \"∞\";\n if (ms === 0) return \"0ms\";\n\n const days = Math.floor(ms / (24 * 60 * 60 * 1000));\n const hours = Math.floor((ms % (24 * 60 * 60 * 1000)) / (60 * 60 * 1000));\n const minutes = Math.floor((ms % (60 * 60 * 1000)) / (60 * 1000));\n const seconds = Math.floor((ms % (60 * 1000)) / 1000);\n const millis = ms % 1000;\n\n const parts: string[] = [];\n if (days > 0) parts.push(`${days}d`);\n if (hours > 0) parts.push(`${hours}h`);\n if (minutes > 0) parts.push(`${minutes}m`);\n if (seconds > 0) parts.push(`${seconds}s`);\n if (millis > 0 && parts.length === 0) parts.push(`${millis}ms`);\n\n return parts.join(\" \") || \"0ms\";\n}\n\n// =============================================================================\n// Parsing\n// =============================================================================\n\n/**\n * Parse a duration from a string like \"100ms\", \"5s\", \"2m\", \"1h\", \"1d\".\n * Returns undefined if parsing fails.\n *\n * @example\n * ```typescript\n * Duration.parse(\"5s\") // Duration.seconds(5)\n * Duration.parse(\"100ms\") // Duration.millis(100)\n * Duration.parse(\"2m\") // Duration.minutes(2)\n * ```\n */\nexport function parse(input: string): Duration | undefined {\n const match = input.trim().match(/^(\\d+(?:\\.\\d+)?)\\s*(ms|s|m|h|d)$/i);\n if (!match) return undefined;\n\n const value = parseFloat(match[1]);\n const unit = match[2].toLowerCase();\n\n switch (unit) {\n case \"ms\":\n return millis(value);\n case \"s\":\n return seconds(value);\n case \"m\":\n return minutes(value);\n case \"h\":\n return hours(value);\n case \"d\":\n return days(value);\n default:\n return undefined;\n }\n}\n\n// =============================================================================\n// Namespace Export\n// =============================================================================\n\n/**\n * Duration namespace with all functions for convenient access.\n *\n * @example\n * ```typescript\n * import { Duration } from \"awaitly\";\n *\n * const timeout = Duration.seconds(30);\n * const delay = Duration.millis(100);\n * const total = Duration.add(timeout, delay);\n *\n * console.log(Duration.format(total)); // \"30s 100ms\"\n * ```\n */\nexport const Duration = {\n // Constructors\n millis,\n seconds,\n minutes,\n hours,\n days,\n zero,\n infinity,\n\n // Conversions\n toMillis,\n toSeconds,\n toMinutes,\n toHours,\n toDays,\n\n // Operations\n add,\n subtract,\n multiply,\n divide,\n\n // Comparisons\n lessThan,\n lessThanOrEqual,\n greaterThan,\n greaterThanOrEqual,\n equals,\n min,\n max,\n clamp,\n\n // Predicates\n isZero,\n isInfinite,\n isFinite,\n isDuration,\n\n // Formatting\n format,\n parse,\n} as const;\n\nexport type { Duration as DurationType };\n","/**\n * awaitly/cache\n *\n * Caching utilities for memoization and deduplication.\n * Inspired by Effect.js caching patterns.\n *\n * @example\n * ```typescript\n * import { cached, cachedWithTTL, cachedFunction, once } from 'awaitly/cache';\n *\n * // Compute once, reuse forever\n * const getConfig = cached(() => loadConfig());\n *\n * // Expire after duration\n * const getUser = cachedWithTTL(() => fetchUser(id), { ttl: '5m' });\n *\n * // Memoize by arguments\n * const fetchUserMemo = cachedFunction((id: string) => fetchUser(id));\n *\n * // Execute exactly once (for initialization)\n * const initDb = once(() => connectToDatabase());\n * ```\n */\n\nimport { Duration, parse as parseDuration } from \"./duration\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Duration input type - supports Duration objects or string shorthand.\n */\nexport type DurationInput = Duration | string;\n\n/**\n * Cache entry with metadata.\n */\nexport interface CacheEntry<T> {\n value: T;\n timestamp: number;\n expiresAt?: number;\n}\n\n/**\n * Cache options.\n */\nexport interface CacheOptions {\n /**\n * Time-to-live for cached values.\n * Accepts Duration or string shorthand like \"5m\", \"1h\", \"30s\".\n */\n ttl?: DurationInput;\n}\n\n/**\n * Cached function options.\n */\nexport interface CachedFunctionOptions<Args extends unknown[]> {\n /**\n * Custom key generator for arguments.\n * Default: JSON.stringify(args)\n */\n keyFn?: (...args: Args) => string;\n\n /**\n * Time-to-live for cached values.\n */\n ttl?: DurationInput;\n\n /**\n * Maximum cache size. When exceeded, oldest entries are evicted.\n * @default Infinity\n */\n maxSize?: number;\n}\n\n/**\n * Cache statistics.\n */\nexport interface CacheStats {\n hits: number;\n misses: number;\n size: number;\n}\n\n// =============================================================================\n// Helper Functions\n// =============================================================================\n\n/**\n * Convert DurationInput to milliseconds.\n */\nfunction toMs(duration: DurationInput): number {\n if (typeof duration === \"string\") {\n const parsed = parseDuration(duration);\n if (!parsed) {\n throw new Error(`Invalid duration string: ${duration}`);\n }\n return parsed.millis;\n }\n return duration.millis;\n}\n\n// =============================================================================\n// cached() - Compute once, reuse forever\n// =============================================================================\n\n/**\n * State for a cached value.\n */\ntype CachedState<T> =\n | { status: \"empty\" }\n | { status: \"pending\"; promise: Promise<T> }\n | { status: \"filled\"; value: T };\n\n/**\n * Create a cached computation that executes once and reuses the result.\n *\n * The function is called at most once, even with concurrent calls.\n * Subsequent calls return the cached value immediately.\n *\n * @param fn - Function to compute the cached value\n * @returns Function that returns the cached value\n *\n * @example\n * ```typescript\n * const getConfig = cached(async () => {\n * console.log('Loading config...');\n * return await loadConfigFromFile();\n * });\n *\n * // First call executes the function\n * const config1 = await getConfig(); // \"Loading config...\"\n *\n * // Subsequent calls return cached value\n * const config2 = await getConfig(); // No log, instant return\n * const config3 = await getConfig(); // No log, instant return\n * ```\n */\nexport function cached<T>(fn: () => T | Promise<T>): () => Promise<T> {\n let state: CachedState<T> = { status: \"empty\" };\n\n return async () => {\n if (state.status === \"filled\") {\n return state.value;\n }\n\n if (state.status === \"pending\") {\n return state.promise;\n }\n\n // Compute the value\n const promise = Promise.resolve(fn()).then((value) => {\n state = { status: \"filled\", value };\n return value;\n });\n\n state = { status: \"pending\", promise };\n return promise;\n };\n}\n\n// =============================================================================\n// cachedWithTTL() - Expire after duration\n// =============================================================================\n\n/**\n * State for a TTL-cached value.\n */\ntype CachedTTLState<T> =\n | { status: \"empty\" }\n | { status: \"pending\"; promise: Promise<T> }\n | { status: \"filled\"; value: T; expiresAt: number };\n\n/**\n * Create a cached computation that expires after a duration.\n *\n * The function is re-executed when the TTL expires.\n * Concurrent calls while computing share the same promise.\n *\n * @param fn - Function to compute the cached value\n * @param options - Cache options including TTL\n * @returns Function that returns the cached value\n *\n * @example\n * ```typescript\n * const getUser = cachedWithTTL(\n * async () => await fetchUser(userId),\n * { ttl: '5m' } // Cache for 5 minutes\n * );\n *\n * const user1 = await getUser(); // Fetches from API\n * const user2 = await getUser(); // Returns cached (within 5 min)\n *\n * // After 5 minutes...\n * const user3 = await getUser(); // Fetches again\n * ```\n */\nexport function cachedWithTTL<T>(\n fn: () => T | Promise<T>,\n options: { ttl: DurationInput }\n): () => Promise<T> {\n const ttlMs = toMs(options.ttl);\n let state: CachedTTLState<T> = { status: \"empty\" };\n\n return async () => {\n const now = Date.now();\n\n // Check if cached value is still valid\n if (state.status === \"filled\" && now < state.expiresAt) {\n return state.value;\n }\n\n // Check if already computing\n if (state.status === \"pending\") {\n return state.promise;\n }\n\n // Compute the value\n const promise = Promise.resolve(fn()).then((value) => {\n state = {\n status: \"filled\",\n value,\n expiresAt: Date.now() + ttlMs,\n };\n return value;\n });\n\n state = { status: \"pending\", promise };\n return promise;\n };\n}\n\n// =============================================================================\n// cachedFunction() - Memoize by arguments\n// =============================================================================\n\n/**\n * Cache entry for memoized functions.\n */\ninterface MemoEntry<T> {\n value: T;\n timestamp: number;\n expiresAt?: number;\n}\n\n/**\n * Memoized function interface.\n */\nexport interface MemoizedFunction<Args extends unknown[], T> {\n (...args: Args): Promise<T>;\n /** Clear the entire cache */\n clear(): void;\n /** Clear a specific cache entry */\n delete(...args: Args): boolean;\n /** Check if an entry exists */\n has(...args: Args): boolean;\n /** Get cache statistics */\n getStats(): CacheStats;\n}\n\n/**\n * Create a memoized function that caches results by arguments.\n *\n * Each unique set of arguments produces a cached result.\n * Supports TTL and max size limits.\n *\n * @param fn - Function to memoize\n * @param options - Memoization options\n * @returns Memoized function with cache control methods\n *\n * @example\n * ```typescript\n * const fetchUserMemo = cachedFunction(\n * async (id: string) => await fetchUser(id),\n * { ttl: '5m', maxSize: 100 }\n * );\n *\n * const user1 = await fetchUserMemo('user-1'); // Fetches\n * const user2 = await fetchUserMemo('user-2'); // Fetches\n * const user1Again = await fetchUserMemo('user-1'); // Cached!\n *\n * // Cache control\n * fetchUserMemo.delete('user-1'); // Remove specific entry\n * fetchUserMemo.clear(); // Clear all\n * console.log(fetchUserMemo.getStats()); // { hits: 1, misses: 2, size: 0 }\n * ```\n */\nexport function cachedFunction<Args extends unknown[], T>(\n fn: (...args: Args) => T | Promise<T>,\n options: CachedFunctionOptions<Args> = {}\n): MemoizedFunction<Args, T> {\n const {\n keyFn = (...args: Args) => JSON.stringify(args),\n maxSize = Infinity,\n } = options;\n const ttlMs = options.ttl ? toMs(options.ttl) : undefined;\n\n const cache = new Map<string, MemoEntry<T>>();\n const pending = new Map<string, Promise<T>>();\n let hits = 0;\n let misses = 0;\n\n /**\n * Evict oldest entries if over max size.\n */\n function evictOldest(): void {\n if (cache.size <= maxSize) return;\n\n // Find oldest entry\n let oldestKey: string | null = null;\n let oldestTime = Infinity;\n\n for (const [key, entry] of cache.entries()) {\n if (entry.timestamp < oldestTime) {\n oldestTime = entry.timestamp;\n oldestKey = key;\n }\n }\n\n if (oldestKey) {\n cache.delete(oldestKey);\n }\n }\n\n const memoized = async (...args: Args): Promise<T> => {\n const key = keyFn(...args);\n const now = Date.now();\n\n // Check cache\n const cached = cache.get(key);\n if (cached) {\n // Check if expired\n if (cached.expiresAt && now >= cached.expiresAt) {\n cache.delete(key);\n } else {\n hits++;\n return cached.value;\n }\n }\n\n // Check if already computing\n const pendingPromise = pending.get(key);\n if (pendingPromise) {\n return pendingPromise;\n }\n\n // Compute the value\n misses++;\n const promise = Promise.resolve(fn(...args)).then((value) => {\n const entry: MemoEntry<T> = {\n value,\n timestamp: Date.now(),\n expiresAt: ttlMs ? Date.now() + ttlMs : undefined,\n };\n cache.set(key, entry);\n pending.delete(key);\n evictOldest();\n return value;\n });\n\n pending.set(key, promise);\n\n try {\n return await promise;\n } catch (error) {\n pending.delete(key);\n throw error;\n }\n };\n\n memoized.clear = () => {\n cache.clear();\n pending.clear();\n };\n\n memoized.delete = (...args: Args) => {\n const key = keyFn(...args);\n return cache.delete(key);\n };\n\n memoized.has = (...args: Args) => {\n const key = keyFn(...args);\n const entry = cache.get(key);\n if (!entry) return false;\n if (entry.expiresAt && Date.now() >= entry.expiresAt) {\n cache.delete(key);\n return false;\n }\n return true;\n };\n\n memoized.getStats = () => ({\n hits,\n misses,\n size: cache.size,\n });\n\n return memoized;\n}\n\n// =============================================================================\n// once() - Execute exactly once\n// =============================================================================\n\n/**\n * State for a once-executed function.\n */\ntype OnceState<T> =\n | { status: \"idle\" }\n | { status: \"running\"; promise: Promise<T> }\n | { status: \"done\"; value: T }\n | { status: \"failed\"; error: unknown };\n\n/**\n * Once-executed function interface.\n */\nexport interface OnceFunction<T> {\n (): Promise<T>;\n /** Check if the function has been called */\n called: boolean;\n /** Check if execution completed successfully */\n completed: boolean;\n /** Check if execution failed */\n failed: boolean;\n /** Reset to allow re-execution */\n reset(): void;\n}\n\n/**\n * Create a function that executes exactly once.\n *\n * Useful for initialization code that should only run once.\n * Subsequent calls return the same result or re-throw the same error.\n *\n * @param fn - Function to execute once\n * @returns Function that executes once and returns the result\n *\n * @example\n * ```typescript\n * const initDb = once(async () => {\n * console.log('Connecting to database...');\n * const conn = await createConnection();\n * return conn;\n * });\n *\n * // First call executes\n * const db1 = await initDb(); // \"Connecting to database...\"\n *\n * // Subsequent calls return cached result\n * const db2 = await initDb(); // Instant, same connection\n * const db3 = await initDb(); // Instant, same connection\n *\n * console.log(initDb.called); // true\n * console.log(initDb.completed); // true\n * ```\n */\nexport function once<T>(fn: () => T | Promise<T>): OnceFunction<T> {\n let state: OnceState<T> = { status: \"idle\" };\n\n const onceFn = async (): Promise<T> => {\n if (state.status === \"done\") {\n return state.value;\n }\n\n if (state.status === \"failed\") {\n throw state.error;\n }\n\n if (state.status === \"running\") {\n return state.promise;\n }\n\n // Execute the function\n const promise = Promise.resolve(fn())\n .then((value) => {\n state = { status: \"done\", value };\n return value;\n })\n .catch((error) => {\n state = { status: \"failed\", error };\n throw error;\n });\n\n state = { status: \"running\", promise };\n return promise;\n };\n\n Object.defineProperty(onceFn, \"called\", {\n get: () => state.status !== \"idle\",\n });\n\n Object.defineProperty(onceFn, \"completed\", {\n get: () => state.status === \"done\",\n });\n\n Object.defineProperty(onceFn, \"failed\", {\n get: () => state.status === \"failed\",\n });\n\n onceFn.reset = () => {\n state = { status: \"idle\" };\n };\n\n return onceFn as OnceFunction<T>;\n}\n\n// =============================================================================\n// createCache() - General purpose cache\n// =============================================================================\n\n/**\n * General purpose cache interface.\n */\nexport interface Cache<K, V> {\n /** Get a value from the cache */\n get(key: K): V | undefined;\n /** Set a value in the cache */\n set(key: K, value: V, options?: { ttl?: DurationInput }): void;\n /** Check if a key exists */\n has(key: K): boolean;\n /** Delete a key from the cache */\n delete(key: K): boolean;\n /** Clear the entire cache */\n clear(): void;\n /** Get the cache size */\n size: number;\n /** Get cache statistics */\n getStats(): CacheStats;\n}\n\n/**\n * Cache configuration.\n */\nexport interface CacheConfig {\n /**\n * Default TTL for all entries.\n */\n defaultTTL?: DurationInput;\n\n /**\n * Maximum cache size.\n * @default Infinity\n */\n maxSize?: number;\n}\n\n/**\n * Create a general-purpose cache with TTL and size limits.\n *\n * @param config - Cache configuration\n * @returns A Cache instance\n *\n * @example\n * ```typescript\n * const cache = createCache<string, User>({\n * defaultTTL: '5m',\n * maxSize: 1000,\n * });\n *\n * cache.set('user:1', user);\n * cache.set('user:2', user2, { ttl: '1h' }); // Override TTL\n *\n * const user = cache.get('user:1');\n * ```\n */\nexport function createCache<K, V>(config: CacheConfig = {}): Cache<K, V> {\n const { maxSize = Infinity } = config;\n const defaultTTLMs = config.defaultTTL ? toMs(config.defaultTTL) : undefined;\n\n interface Entry {\n value: V;\n timestamp: number;\n expiresAt?: number;\n }\n\n const store = new Map<K, Entry>();\n let hits = 0;\n let misses = 0;\n\n /**\n * Check if an entry is expired.\n */\n function isExpired(entry: Entry): boolean {\n return entry.expiresAt !== undefined && Date.now() >= entry.expiresAt;\n }\n\n /**\n * Evict oldest entries if over max size.\n */\n function evictOldest(): void {\n if (store.size <= maxSize) return;\n\n let oldestKey: K | null = null;\n let oldestTime = Infinity;\n\n for (const [key, entry] of store.entries()) {\n if (entry.timestamp < oldestTime) {\n oldestTime = entry.timestamp;\n oldestKey = key;\n }\n }\n\n if (oldestKey !== null) {\n store.delete(oldestKey);\n }\n }\n\n return {\n get(key: K): V | undefined {\n const entry = store.get(key);\n if (!entry) {\n misses++;\n return undefined;\n }\n if (isExpired(entry)) {\n store.delete(key);\n misses++;\n return undefined;\n }\n hits++;\n return entry.value;\n },\n\n set(key: K, value: V, options?: { ttl?: DurationInput }): void {\n const ttlMs = options?.ttl ? toMs(options.ttl) : defaultTTLMs;\n const now = Date.now();\n\n store.set(key, {\n value,\n timestamp: now,\n expiresAt: ttlMs ? now + ttlMs : undefined,\n });\n\n evictOldest();\n },\n\n has(key: K): boolean {\n const entry = store.get(key);\n if (!entry) return false;\n if (isExpired(entry)) {\n store.delete(key);\n return false;\n }\n return true;\n },\n\n delete(key: K): boolean {\n return store.delete(key);\n },\n\n clear(): void {\n store.clear();\n },\n\n get size(): number {\n return store.size;\n },\n\n getStats(): CacheStats {\n return { hits, misses, size: store.size };\n },\n };\n}\n"],"mappings":"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","names":["cache_entry_exports","__export","cached","cachedFunction","cachedWithTTL","createCache","once","__toCommonJS","millis","ms","seconds","s","minutes","m","hours","h","days","d","parse","input","match","value","millis","seconds","minutes","hours","days","toMs","duration","parsed","parse","cached","fn","state","promise","value","cachedWithTTL","options","ttlMs","now","cachedFunction","keyFn","args","maxSize","cache","pending","hits","misses","evictOldest","oldestKey","oldestTime","key","entry","memoized","pendingPromise","error","once","onceFn","createCache","config","defaultTTLMs","store","isExpired"]}
package/dist/cache.d.cts DELETED
@@ -1,269 +0,0 @@
1
- import { Duration } from './duration.cjs';
2
-
3
- /**
4
- * awaitly/cache
5
- *
6
- * Caching utilities for memoization and deduplication.
7
- * Inspired by Effect.js caching patterns.
8
- *
9
- * @example
10
- * ```typescript
11
- * import { cached, cachedWithTTL, cachedFunction, once } from 'awaitly/cache';
12
- *
13
- * // Compute once, reuse forever
14
- * const getConfig = cached(() => loadConfig());
15
- *
16
- * // Expire after duration
17
- * const getUser = cachedWithTTL(() => fetchUser(id), { ttl: '5m' });
18
- *
19
- * // Memoize by arguments
20
- * const fetchUserMemo = cachedFunction((id: string) => fetchUser(id));
21
- *
22
- * // Execute exactly once (for initialization)
23
- * const initDb = once(() => connectToDatabase());
24
- * ```
25
- */
26
-
27
- /**
28
- * Duration input type - supports Duration objects or string shorthand.
29
- */
30
- type DurationInput = Duration | string;
31
- /**
32
- * Cache entry with metadata.
33
- */
34
- interface CacheEntry<T> {
35
- value: T;
36
- timestamp: number;
37
- expiresAt?: number;
38
- }
39
- /**
40
- * Cache options.
41
- */
42
- interface CacheOptions {
43
- /**
44
- * Time-to-live for cached values.
45
- * Accepts Duration or string shorthand like "5m", "1h", "30s".
46
- */
47
- ttl?: DurationInput;
48
- }
49
- /**
50
- * Cached function options.
51
- */
52
- interface CachedFunctionOptions<Args extends unknown[]> {
53
- /**
54
- * Custom key generator for arguments.
55
- * Default: JSON.stringify(args)
56
- */
57
- keyFn?: (...args: Args) => string;
58
- /**
59
- * Time-to-live for cached values.
60
- */
61
- ttl?: DurationInput;
62
- /**
63
- * Maximum cache size. When exceeded, oldest entries are evicted.
64
- * @default Infinity
65
- */
66
- maxSize?: number;
67
- }
68
- /**
69
- * Cache statistics.
70
- */
71
- interface CacheStats {
72
- hits: number;
73
- misses: number;
74
- size: number;
75
- }
76
- /**
77
- * Create a cached computation that executes once and reuses the result.
78
- *
79
- * The function is called at most once, even with concurrent calls.
80
- * Subsequent calls return the cached value immediately.
81
- *
82
- * @param fn - Function to compute the cached value
83
- * @returns Function that returns the cached value
84
- *
85
- * @example
86
- * ```typescript
87
- * const getConfig = cached(async () => {
88
- * console.log('Loading config...');
89
- * return await loadConfigFromFile();
90
- * });
91
- *
92
- * // First call executes the function
93
- * const config1 = await getConfig(); // "Loading config..."
94
- *
95
- * // Subsequent calls return cached value
96
- * const config2 = await getConfig(); // No log, instant return
97
- * const config3 = await getConfig(); // No log, instant return
98
- * ```
99
- */
100
- declare function cached<T>(fn: () => T | Promise<T>): () => Promise<T>;
101
- /**
102
- * Create a cached computation that expires after a duration.
103
- *
104
- * The function is re-executed when the TTL expires.
105
- * Concurrent calls while computing share the same promise.
106
- *
107
- * @param fn - Function to compute the cached value
108
- * @param options - Cache options including TTL
109
- * @returns Function that returns the cached value
110
- *
111
- * @example
112
- * ```typescript
113
- * const getUser = cachedWithTTL(
114
- * async () => await fetchUser(userId),
115
- * { ttl: '5m' } // Cache for 5 minutes
116
- * );
117
- *
118
- * const user1 = await getUser(); // Fetches from API
119
- * const user2 = await getUser(); // Returns cached (within 5 min)
120
- *
121
- * // After 5 minutes...
122
- * const user3 = await getUser(); // Fetches again
123
- * ```
124
- */
125
- declare function cachedWithTTL<T>(fn: () => T | Promise<T>, options: {
126
- ttl: DurationInput;
127
- }): () => Promise<T>;
128
- /**
129
- * Memoized function interface.
130
- */
131
- interface MemoizedFunction<Args extends unknown[], T> {
132
- (...args: Args): Promise<T>;
133
- /** Clear the entire cache */
134
- clear(): void;
135
- /** Clear a specific cache entry */
136
- delete(...args: Args): boolean;
137
- /** Check if an entry exists */
138
- has(...args: Args): boolean;
139
- /** Get cache statistics */
140
- getStats(): CacheStats;
141
- }
142
- /**
143
- * Create a memoized function that caches results by arguments.
144
- *
145
- * Each unique set of arguments produces a cached result.
146
- * Supports TTL and max size limits.
147
- *
148
- * @param fn - Function to memoize
149
- * @param options - Memoization options
150
- * @returns Memoized function with cache control methods
151
- *
152
- * @example
153
- * ```typescript
154
- * const fetchUserMemo = cachedFunction(
155
- * async (id: string) => await fetchUser(id),
156
- * { ttl: '5m', maxSize: 100 }
157
- * );
158
- *
159
- * const user1 = await fetchUserMemo('user-1'); // Fetches
160
- * const user2 = await fetchUserMemo('user-2'); // Fetches
161
- * const user1Again = await fetchUserMemo('user-1'); // Cached!
162
- *
163
- * // Cache control
164
- * fetchUserMemo.delete('user-1'); // Remove specific entry
165
- * fetchUserMemo.clear(); // Clear all
166
- * console.log(fetchUserMemo.getStats()); // { hits: 1, misses: 2, size: 0 }
167
- * ```
168
- */
169
- declare function cachedFunction<Args extends unknown[], T>(fn: (...args: Args) => T | Promise<T>, options?: CachedFunctionOptions<Args>): MemoizedFunction<Args, T>;
170
- /**
171
- * Once-executed function interface.
172
- */
173
- interface OnceFunction<T> {
174
- (): Promise<T>;
175
- /** Check if the function has been called */
176
- called: boolean;
177
- /** Check if execution completed successfully */
178
- completed: boolean;
179
- /** Check if execution failed */
180
- failed: boolean;
181
- /** Reset to allow re-execution */
182
- reset(): void;
183
- }
184
- /**
185
- * Create a function that executes exactly once.
186
- *
187
- * Useful for initialization code that should only run once.
188
- * Subsequent calls return the same result or re-throw the same error.
189
- *
190
- * @param fn - Function to execute once
191
- * @returns Function that executes once and returns the result
192
- *
193
- * @example
194
- * ```typescript
195
- * const initDb = once(async () => {
196
- * console.log('Connecting to database...');
197
- * const conn = await createConnection();
198
- * return conn;
199
- * });
200
- *
201
- * // First call executes
202
- * const db1 = await initDb(); // "Connecting to database..."
203
- *
204
- * // Subsequent calls return cached result
205
- * const db2 = await initDb(); // Instant, same connection
206
- * const db3 = await initDb(); // Instant, same connection
207
- *
208
- * console.log(initDb.called); // true
209
- * console.log(initDb.completed); // true
210
- * ```
211
- */
212
- declare function once<T>(fn: () => T | Promise<T>): OnceFunction<T>;
213
- /**
214
- * General purpose cache interface.
215
- */
216
- interface Cache<K, V> {
217
- /** Get a value from the cache */
218
- get(key: K): V | undefined;
219
- /** Set a value in the cache */
220
- set(key: K, value: V, options?: {
221
- ttl?: DurationInput;
222
- }): void;
223
- /** Check if a key exists */
224
- has(key: K): boolean;
225
- /** Delete a key from the cache */
226
- delete(key: K): boolean;
227
- /** Clear the entire cache */
228
- clear(): void;
229
- /** Get the cache size */
230
- size: number;
231
- /** Get cache statistics */
232
- getStats(): CacheStats;
233
- }
234
- /**
235
- * Cache configuration.
236
- */
237
- interface CacheConfig {
238
- /**
239
- * Default TTL for all entries.
240
- */
241
- defaultTTL?: DurationInput;
242
- /**
243
- * Maximum cache size.
244
- * @default Infinity
245
- */
246
- maxSize?: number;
247
- }
248
- /**
249
- * Create a general-purpose cache with TTL and size limits.
250
- *
251
- * @param config - Cache configuration
252
- * @returns A Cache instance
253
- *
254
- * @example
255
- * ```typescript
256
- * const cache = createCache<string, User>({
257
- * defaultTTL: '5m',
258
- * maxSize: 1000,
259
- * });
260
- *
261
- * cache.set('user:1', user);
262
- * cache.set('user:2', user2, { ttl: '1h' }); // Override TTL
263
- *
264
- * const user = cache.get('user:1');
265
- * ```
266
- */
267
- declare function createCache<K, V>(config?: CacheConfig): Cache<K, V>;
268
-
269
- export { type Cache, type CacheConfig, type CacheEntry, type CacheOptions, type CacheStats, type CachedFunctionOptions, type DurationInput, type MemoizedFunction, type OnceFunction, cached, cachedFunction, cachedWithTTL, createCache, once };