@ts-utilities/core 1.3.5 → 1.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- const e=e=>!e,t=e=>e==null,n=e=>typeof e==`boolean`,r=e=>typeof e==`string`,i=e=>{if(typeof e==`number`)return Number.isFinite(e);if(typeof e==`string`){let t=Number(e);return Number.isFinite(t)}return!1},a=e=>Array.isArray(e),o=e=>typeof e==`function`,s=e=>{if(e==null)return!0;switch(typeof e){case`string`:case`number`:case`bigint`:case`boolean`:case`symbol`:return!0;default:return!1}};function c(e){if(typeof e!=`object`||!e||Object.prototype.toString.call(e)!==`[object Object]`)return!1;let t=Object.getPrototypeOf(e);return t===null||t===Object.prototype}function l(e,...t){let n,r={},i=t[t.length-1];i&&typeof i==`object`&&!Array.isArray(i)&&(i.arrayMerge!==void 0||i.clone!==void 0||i.customMerge!==void 0||i.functionMerge!==void 0||i.maxDepth!==void 0)?(r={...r,...i},n=t.slice(0,-1)):n=t;let{arrayMerge:a=`replace`,clone:o=!0,functionMerge:s=`replace`,maxDepth:l=100,customMerge:u}=r,d=new WeakMap;return f(e,n,0);function f(e,t,n){if(n>=l)return console.warn(`[deepmerge] Maximum depth ${l} exceeded. Returning target as-is.`),e;if(!c(e)&&!Array.isArray(e)){for(let n of t)if(n!==void 0){if(u){let t=u(``,e,n);if(t!==void 0)return t}return typeof e==`function`&&typeof n==`function`&&s===`compose`?(...t)=>{e(...t),n(...t)}:n}return e}let r=o?Array.isArray(e)?[...e]:{...e}:e;for(let e of t)if(e!=null&&!d.has(e))if(d.set(e,r),Array.isArray(r)&&Array.isArray(e))r=p(r,e,a);else if(c(r)&&c(e)){let t=new Set([...Object.keys(r),...Object.keys(e),...Object.getOwnPropertySymbols(r),...Object.getOwnPropertySymbols(e)]);for(let i of t){let t=r[i],o=e[i];u&&u(i,t,o)!==void 0?r[i]=u(i,t,o):typeof t==`function`&&typeof o==`function`?s===`compose`?r[i]=(...e)=>{t(...e),o(...e)}:r[i]=o:c(t)&&c(o)?r[i]=f(t,[o],n+1):Array.isArray(t)&&Array.isArray(o)?r[i]=p(t,o,a):o!==void 0&&(r[i]=o)}}else r=e;return r}function p(e,t,n){if(typeof n==`function`)return n(e,t);switch(n){case`concat`:return[...e,...t];case`merge`:let n=Math.max(e.length,t.length),r=[];for(let i=0;i<n;i++)i<e.length&&i<t.length?c(e[i])&&c(t[i])?r[i]=f(e[i],[t[i]],0):r[i]=t[i]:i<e.length?r[i]=e[i]:r[i]=t[i];return r;case`replace`:default:return[...t]}}}function u(e,t,n){if(Array.isArray(t))return t.map(t=>u(e,t,n));if(typeof t==`object`&&t&&!Array.isArray(t)){let r={};for(let i in t)t.hasOwnProperty(i)&&(r[i]=u(e,t[i],n));return r}if(typeof t!=`string`)return n;let r=(()=>t===``?[]:t.split(`.`).filter(e=>e!==``))(),i=e;for(let e of r){if(i==null)return n;let t=typeof e==`string`&&Array.isArray(i)&&/^\d+$/.test(e)?Number.parseInt(e,10):e;i=i[t]}return i===void 0?n:i}function d(e){return f(e)}function f(e){if(e!==null){if(typeof e!=`object`||!e)return e;if(Array.isArray(e))return e.map(f);if(c(e)){let t={};for(let n in e)t[n]=f(e[n]);return t}return e}}function p(e,t){return e==null?e:Object.assign(e,t)}function m(e){return(e.split(`.`).pop()||e).replace(/([a-z])([A-Z])/g,`$1 $2`).split(/[-_|�\s]+/).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(` `)}const h=`àáãäâèéëêìíïîòóöôùúüûñç·/_,:;`,g=`aaaaaeeeeiiiioooouuuunc------`,_=e=>{if(typeof e!=`string`)throw TypeError(`Input must be a string`);let t=e.trim().toLowerCase(),n={};for(let e=0;e<29;e++)n[h.charAt(e)]=`aaaaaeeeeiiiioooouuuunc------`.charAt(e);return t=t.replace(RegExp(`[${h}]`,`g`),e=>n[e]||e),t.replace(/[^a-z0-9 -]/g,``).replace(/\s+/g,`-`).replace(/-+/g,`-`).replace(/^-+/,``).replace(/-+$/,``)||``},v=(e=1e3,t)=>new Promise(n=>{if(t?.aborted)return n();let r=setTimeout(()=>{a(),n()},e);function i(){clearTimeout(r),a(),n()}function a(){t?.removeEventListener(`abort`,i)}t&&t.addEventListener(`abort`,i,{once:!0})});function y(e,t=100,n){if(typeof e!=`function`)throw TypeError(`Expected the first parameter to be a function, got \`${typeof e}\`.`);if(t<0)throw RangeError("`wait` must not be negative.");let r=n?.immediate??!1,i,a,o,s;function c(){return s=e.apply(o,a),a=void 0,o=void 0,s}let l=function(...e){return a=e,o=this,i===void 0&&r&&(s=c.call(this)),i!==void 0&&clearTimeout(i),i=setTimeout(c.bind(this),t),s};return Object.defineProperty(l,`isPending`,{get(){return i!==void 0}}),l}function b(e,t=100,n){if(typeof e!=`function`)throw TypeError(`Expected the first parameter to be a function, got \`${typeof e}\`.`);if(t<0)throw RangeError("`wait` must not be negative.");let r=n?.leading??!0,i=n?.trailing??!0,a,o,s,c,l;function u(){c=Date.now(),l=e.apply(s,o),o=void 0,s=void 0}function d(){a=void 0,i&&o&&u()}let f=function(...e){let n=c?Date.now()-c:1/0;return o=e,s=this,n>=t?r?u():a=setTimeout(d,t):!a&&i&&(a=setTimeout(d,t-n)),l};return Object.defineProperty(f,`isPending`,{get(){return a!==void 0}}),f}function x(e,...t){let n=t.length===1&&Array.isArray(t[0])?t[0]:t,r=0;return e.replace(/%s/g,()=>{let e=n[r++];return e===void 0?``:String(e)})}function S(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}function C(e,t={}){if(!e)return``;let{lowercase:n=!0,removeAccents:r=!0,removeNonAlphanumeric:i=!0}=t,a=e.normalize(`NFC`);return r&&(a=a.normalize(`NFD`).replace(/\p{M}/gu,``)),i&&(a=a.replace(/^[^\p{L}\p{N}]*|[^\p{L}\p{N}]*$/gu,``)),n&&(a=a.toLocaleLowerCase()),a}function w(e,...t){return typeof e==`function`?e(...t):e}async function T(e,{interval:t=5e3,timeout:n=300*1e3,jitter:r=!0,signal:i}={}){let a=Date.now(),o=i?.aborted??!1,s=()=>{o=!0};i?.addEventListener(`abort`,s,{once:!0});try{for(let s=0;;s++){if(o)throw Error(`Polling aborted`);let s=await e();if(s)return s;if(Date.now()-a>=n)throw Error(`Polling timed out`,{cause:`Polling timed out after ${n}ms`});await v(r?t+(Math.random()-.5)*t*.2:t,i)}}catch(e){throw e}finally{i?.removeEventListener(`abort`,s)}}async function E(e,t={}){let{concurrency:n=1/0,timeout:r=1/0,signal:i,retry:a=0,retryDelay:o=0,throwOnFirstError:s=!1,ignoreErrors:c=!1}=t,l=Array.isArray(e),u={};l?e.forEach((e,t)=>{u[t.toString()]=e}):Object.entries(e).forEach(([e,t])=>{u[e]=t});let d=Object.keys(u);if(d.length===0)return{results:l?[]:{},errors:[],succeeded:0,failed:0,duration:0};let f=Date.now(),p=[],m=[],h=0,g=0,_=0,y=!1,b=s?null:void 0,x=async e=>{let t=0;for(;t<=a&&!y;)try{let t=u[e],n=await(typeof t==`function`?t():t);p.push([e,n]),g++;return}catch(e){if(t===a){let t=e instanceof Error?e:Error(String(e));m.push(t),_++,!c&&s&&!b&&(b=t,y=!0);return}t++,o>0&&await v(o,i)}},S=Array.from({length:Math.min(n,d.length)},async()=>{for(;!y;){let e=h++;if(e>=d.length)return;if(i?.aborted){y=!0;return}await x(d[e])}}),C=Promise.all(S);if(r===1/0?await C:await Promise.race([C,v(r,i).then(()=>{throw y=!0,Error(`Concurrence timed out after ${r}ms`)})]),b&&s)throw b;return l?{results:p.sort((e,t)=>Number(e[0])-Number(t[0])).map(([,e])=>e),errors:m,succeeded:g,failed:_,duration:Date.now()-f}:{results:Object.fromEntries(p),errors:m,succeeded:g,failed:_,duration:Date.now()-f}}function D(e,t={}){let{retry:n=0,delay:r=0,debug:i=!1}=t,a=Date.now(),o=async t=>{try{if(await e(),i){let e=Date.now()-a;console.log(`⚡[schedule.ts] Completed in ${e}ms`)}}catch(e){if(i&&console.log(`⚡[schedule.ts] err:`,e),t>0)i&&console.log(`⚡[schedule.ts] Retrying in ${r}ms...`),setTimeout(()=>o(t-1),r);else if(i){let e=Date.now()-a;console.log(`⚡[schedule.ts] Failed after ${e}ms`)}}};setTimeout(()=>o(n),0)}function O(e){if(e instanceof Promise)return e.then(e=>[null,e]).catch(e=>[e,null]);try{return[null,e()]}catch(t){return console.log(`\x1b[31m🛡 [shield]\x1b[0m ${e.name} failed →`,t),[t,null]}}exports.convertToNormalCase=m,exports.convertToSlug=_,exports.debounce=y,exports.deepmerge=l,exports.escapeRegExp=S,exports.extendProps=p,exports.extract=u,exports.hydrate=d,exports.isArray=a,exports.isBoolean=n,exports.isFalsy=e,exports.isFiniteNumber=i,exports.isFunction=o,exports.isNullish=t,exports.isPlainObject=c,exports.isPrimitive=s,exports.isString=r,exports.normalizeText=C,exports.poll=T,exports.printf=x,exports.schedule=D,exports.shield=O,exports.sleep=v,exports.throttle=b,exports.unwrap=w,exports.withConcurrency=E;
1
+ const e=e=>!e,t=e=>e==null,n=e=>typeof e==`boolean`,r=e=>typeof e==`string`,i=e=>{if(typeof e==`number`)return Number.isFinite(e);if(typeof e==`string`){let t=Number(e);return Number.isFinite(t)}return!1},a=e=>Array.isArray(e),o=e=>typeof e==`function`,s=e=>{if(e==null)return!0;switch(typeof e){case`string`:case`number`:case`bigint`:case`boolean`:case`symbol`:return!0;default:return!1}};function c(e){if(typeof e!=`object`||!e||Object.prototype.toString.call(e)!==`[object Object]`)return!1;let t=Object.getPrototypeOf(e);return t===null||t===Object.prototype}function l(e,...t){let n,r={},i=t[t.length-1];i&&typeof i==`object`&&!Array.isArray(i)&&(i.arrayMerge!==void 0||i.clone!==void 0||i.customMerge!==void 0||i.functionMerge!==void 0||i.maxDepth!==void 0)?(r={...r,...i},n=t.slice(0,-1)):n=t;let{arrayMerge:a=`replace`,clone:o=!0,functionMerge:s=`replace`,maxDepth:l=100,customMerge:u}=r,d=new WeakMap;return f(e,n,0);function f(e,t,n){if(n>=l)return console.warn(`[deepmerge] Maximum depth ${l} exceeded. Returning target as-is.`),e;if(!c(e)&&!Array.isArray(e)){for(let n of t)if(n!==void 0){if(u){let t=u(``,e,n);if(t!==void 0)return t}return typeof e==`function`&&typeof n==`function`&&s===`compose`?(...t)=>{e(...t),n(...t)}:n}return e}let r=o?Array.isArray(e)?[...e]:{...e}:e;for(let e of t)if(e!=null&&!d.has(e))if(d.set(e,r),Array.isArray(r)&&Array.isArray(e))r=p(r,e,a);else if(c(r)&&c(e)){let t=new Set([...Object.keys(r),...Object.keys(e),...Object.getOwnPropertySymbols(r),...Object.getOwnPropertySymbols(e)]);for(let i of t){let t=r[i],o=e[i];u&&u(i,t,o)!==void 0?r[i]=u(i,t,o):typeof t==`function`&&typeof o==`function`?s===`compose`?r[i]=(...e)=>{t(...e),o(...e)}:r[i]=o:c(t)&&c(o)?r[i]=f(t,[o],n+1):Array.isArray(t)&&Array.isArray(o)?r[i]=p(t,o,a):o!==void 0&&(r[i]=o)}}else r=e;return r}function p(e,t,n){if(typeof n==`function`)return n(e,t);switch(n){case`concat`:return[...e,...t];case`merge`:let n=Math.max(e.length,t.length),r=[];for(let i=0;i<n;i++)i<e.length&&i<t.length?c(e[i])&&c(t[i])?r[i]=f(e[i],[t[i]],0):r[i]=t[i]:i<e.length?r[i]=e[i]:r[i]=t[i];return r;case`replace`:default:return[...t]}}}function u(e,t,n){if(Array.isArray(t))return t.map(t=>u(e,t,n));if(typeof t==`object`&&t&&!Array.isArray(t)){let r={};for(let i in t)t.hasOwnProperty(i)&&(r[i]=u(e,t[i],n));return r}if(typeof t!=`string`)return n;let r=(()=>t===``?[]:t.split(`.`).filter(e=>e!==``))(),i=e;for(let e of r){if(i==null)return n;let t=typeof e==`string`&&Array.isArray(i)&&/^\d+$/.test(e)?Number.parseInt(e,10):e;i=i[t]}return i===void 0?n:i}function d(e){return f(e)}function f(e){if(e!==null){if(typeof e!=`object`||!e)return e;if(Array.isArray(e))return e.map(f);if(c(e)){let t={};for(let n in e)t[n]=f(e[n]);return t}return e}}function p(e,t){return e==null?e:Object.assign(e,t)}function m(e){return(e.split(`.`).pop()||e).replace(/([a-z])([A-Z])/g,`$1 $2`).split(/[-_|�\s]+/).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(` `)}const h=`àáãäâèéëêìíïîòóöôùúüûñç·/_,:;`,g=`aaaaaeeeeiiiioooouuuunc------`,_=e=>{if(typeof e!=`string`)throw TypeError(`Input must be a string`);let t=e.trim().toLowerCase(),n={};for(let e=0;e<29;e++)n[h.charAt(e)]=`aaaaaeeeeiiiioooouuuunc------`.charAt(e);return t=t.replace(RegExp(`[${h}]`,`g`),e=>n[e]||e),t.replace(/[^a-z0-9 -]/g,``).replace(/\s+/g,`-`).replace(/-+/g,`-`).replace(/^-+/,``).replace(/-+$/,``)||``},v=(e=1e3,t)=>new Promise(n=>{if(t?.aborted)return n();let r=setTimeout(()=>{a(),n()},e);function i(){clearTimeout(r),a(),n()}function a(){t?.removeEventListener(`abort`,i)}t&&t.addEventListener(`abort`,i,{once:!0})});function y(e,t=100,n){if(typeof e!=`function`)throw TypeError(`Expected the first parameter to be a function, got \`${typeof e}\`.`);if(t<0)throw RangeError("`wait` must not be negative.");let r=n?.immediate??!1,i,a,o,s;function c(){return s=e.apply(o,a),a=void 0,o=void 0,s}let l=function(...e){return a=e,o=this,i===void 0&&r&&(s=c.call(this)),i!==void 0&&clearTimeout(i),i=setTimeout(c.bind(this),t),s};return Object.defineProperty(l,`isPending`,{get(){return i!==void 0}}),l}function b(e,t=100,n){if(typeof e!=`function`)throw TypeError(`Expected the first parameter to be a function, got \`${typeof e}\`.`);if(t<0)throw RangeError("`wait` must not be negative.");let r=n?.leading??!0,i=n?.trailing??!0,a,o,s,c,l;function u(){c=Date.now(),l=e.apply(s,o),o=void 0,s=void 0}function d(){a=void 0,i&&o&&u()}let f=function(...e){let n=c?Date.now()-c:1/0;return o=e,s=this,n>=t?r?u():a=setTimeout(d,t):!a&&i&&(a=setTimeout(d,t-n)),l};return Object.defineProperty(f,`isPending`,{get(){return a!==void 0}}),f}function x(e,...t){let n=t.length===1&&Array.isArray(t[0])?t[0]:t,r=0;return e.replace(/%s/g,()=>{let e=n[r++];return e===void 0?``:String(e)})}function S(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}function C(e,t={}){if(!e)return``;let{lowercase:n=!0,removeAccents:r=!0,removeNonAlphanumeric:i=!0}=t,a=e.normalize(`NFC`);return r&&(a=a.normalize(`NFD`).replace(/\p{M}/gu,``)),i&&(a=a.replace(/^[^\p{L}\p{N}]*|[^\p{L}\p{N}]*$/gu,``)),n&&(a=a.toLocaleLowerCase()),a}function w(e,...t){return typeof e==`function`?e(...t):e}const T=Symbol(`poll.signal.abort`),E=Symbol(`poll.signal.retry`);async function*D(e,{interval:t=5e3,timeout:n=300*1e3,jitter:r=!0,signal:i}={}){let a=Date.now();for(;;){if(i?.aborted)throw new DOMException(`Polling aborted`,`AbortError`);let o=await e();if(o===T)return;if(o===E){await v(r?t+(Math.random()-.5)*t*.2:t,i);continue}if(o){yield o;return}if(Date.now()-a>=n)throw Error(`Polling timed out after ${n}ms`);yield null,await v(r?t+(Math.random()-.5)*t*.2:t,i)}}async function O(e,t={}){for await(let n of D(e,t))if(n)return n}const k={wait:O,watch:D,signal:{abort:T,retry:E}};async function A(e,t={}){let{concurrency:n=1/0,timeout:r=1/0,signal:i,retry:a=0,retryDelay:o=0,throwOnFirstError:s=!1,ignoreErrors:c=!1}=t,l=Array.isArray(e),u={};l?e.forEach((e,t)=>{u[t.toString()]=e}):Object.entries(e).forEach(([e,t])=>{u[e]=t});let d=Object.keys(u);if(d.length===0)return{results:l?[]:{},errors:[],succeeded:0,failed:0,duration:0};let f=Date.now(),p=[],m=[],h=0,g=0,_=0,y=!1,b=s?null:void 0,x=async e=>{let t=0;for(;t<=a&&!y;)try{let t=u[e],n=await(typeof t==`function`?t():t);p.push([e,n]),g++;return}catch(e){if(t===a){let t=e instanceof Error?e:Error(String(e));m.push(t),_++,!c&&s&&!b&&(b=t,y=!0);return}t++,o>0&&await v(o,i)}},S=Array.from({length:Math.min(n,d.length)},async()=>{for(;!y;){let e=h++;if(e>=d.length)return;if(i?.aborted){y=!0;return}await x(d[e])}}),C=Promise.all(S);if(r===1/0?await C:await Promise.race([C,v(r,i).then(()=>{throw y=!0,Error(`Concurrence timed out after ${r}ms`)})]),b&&s)throw b;return l?{results:p.sort((e,t)=>Number(e[0])-Number(t[0])).map(([,e])=>e),errors:m,succeeded:g,failed:_,duration:Date.now()-f}:{results:Object.fromEntries(p),errors:m,succeeded:g,failed:_,duration:Date.now()-f}}function j(e,t={}){let{retry:n=0,delay:r=0,debug:i=!1,waitUntil:a}=t,o=Date.now(),s=async t=>{try{if(await w(e),i){let e=Date.now()-o;console.log(`⚡[schedule.ts] Completed in ${e}ms`)}}catch(e){if(i&&console.log(`⚡[schedule.ts] err:`,e),t>0)i&&console.log(`⚡[schedule.ts] Retrying in ${r}ms...`),setTimeout(()=>s(t-1),r);else if(i){let e=Date.now()-o;console.log(`⚡[schedule.ts] Failed after ${e}ms`)}}},c=new Promise(e=>{setTimeout(()=>s(n).then(e),0)});a&&a(c)}function M(e){if(e instanceof Promise)return e.then(e=>[null,e]).catch(e=>[e,null]);try{return[null,e()]}catch(t){return console.log(`\x1b[31m🛡 [shield]\x1b[0m ${e.name} failed →`,t),[t,null]}}exports.convertToNormalCase=m,exports.convertToSlug=_,exports.debounce=y,exports.deepmerge=l,exports.escapeRegExp=S,exports.extendProps=p,exports.extract=u,exports.hydrate=d,exports.isArray=a,exports.isBoolean=n,exports.isFalsy=e,exports.isFiniteNumber=i,exports.isFunction=o,exports.isNullish=t,exports.isPlainObject=c,exports.isPrimitive=s,exports.isString=r,exports.normalizeText=C,exports.poll=k,exports.printf=x,exports.schedule=j,exports.shield=M,exports.sleep=v,exports.throttle=b,exports.unwrap=w,exports.withConcurrency=A;
package/dist/index.d.cts CHANGED
@@ -994,17 +994,93 @@ declare function hydrate<T>(data: T): Hydrate<T>;
994
994
  declare function extendProps<T, P$1 extends object>(base: T, props: P$1): T extends null | undefined ? T : T & P$1;
995
995
  //#endregion
996
996
  //#region src/functions/poll.d.ts
997
+ type PollOptions = Partial<{
998
+ interval: number;
999
+ timeout: number;
1000
+ signal: AbortSignal;
1001
+ jitter: boolean;
1002
+ }>;
1003
+ declare const ABORT: unique symbol;
1004
+ declare const RETRY: unique symbol;
1005
+ type PollControl = typeof ABORT | typeof RETRY;
1006
+ /**
1007
+ * Streams an async `cond` function, yielding `null` while waiting and the
1008
+ * final truthy value `T` when resolved.
1009
+ *
1010
+ * Designed for use in tRPC subscriptions or any async generator context where
1011
+ * you want to stream progress to the client instead of blocking.
1012
+ *
1013
+ * Keeps SSE/WebSocket connections alive on edge runtimes (e.g. Vercel) by
1014
+ * continuously emitting values rather than holding a silent open connection.
1015
+ *
1016
+ * @template T The type of the successful result.
1017
+ *
1018
+ * @param cond
1019
+ * A function returning a Promise that resolves to:
1020
+ * - a truthy value `T` → stop polling and yield it
1021
+ * - `poll.signal.abort` → stop polling cleanly with no error
1022
+ * - `poll.signal.retry` → skip this tick silently, no null emitted
1023
+ * - falsy/null/undefined → yield `null` and continue polling
1024
+ *
1025
+ * @param options
1026
+ * Configuration options:
1027
+ * - `interval` (number) — Time between polls in ms (default: 5000 ms)
1028
+ * - `timeout` (number) — Max total duration before failing (default: 5 min)
1029
+ * - `jitter` (boolean) — Add small random offset (±10%) to intervals to avoid sync bursts (default: true)
1030
+ * - `signal` (AbortSignal) — Optional abort signal to cancel polling
1031
+ *
1032
+ * @throws `AbortError` if aborted via signal
1033
+ * @throws `Error` if timed out
1034
+ *
1035
+ * @example
1036
+ * ```ts
1037
+ * // tRPC subscription — stream null while job is pending, emit result when done
1038
+ * syncImageJob: privateProcedure
1039
+ * .input(z.object({ id: z.coerce.number() }))
1040
+ * .subscription(async function* ({ ctx, input, signal }) {
1041
+ * yield* poll.watch(
1042
+ * async () => {
1043
+ * const job = await getJob(input.id);
1044
+ * if (job?.is_cancelled) return poll.signal.abort; // stop cleanly
1045
+ * if (job?.is_paused) return poll.signal.retry; // skip this tick
1046
+ * return job?.is_finished ? job : null;
1047
+ * },
1048
+ * { interval: 15000, signal },
1049
+ * );
1050
+ * }),
1051
+ * ```
1052
+ *
1053
+ * @example
1054
+ * ```ts
1055
+ * // Manually iterate
1056
+ * for await (const result of poll.watch(() => getJobStatus(), { interval: 3000 })) {
1057
+ * if (result === null) console.log('still waiting...');
1058
+ * else console.log('done!', result);
1059
+ * }
1060
+ * ```
1061
+ */
1062
+ declare function watch<T>(cond: () => Promise<T | null | false | undefined | PollControl>, {
1063
+ interval,
1064
+ timeout,
1065
+ jitter,
1066
+ signal
1067
+ }?: PollOptions): AsyncGenerator<T | null>;
997
1068
  /**
998
1069
  * Repeatedly polls an async `cond` function UNTIL it returns a TRUTHY value,
999
1070
  * or until the operation times out or is aborted.
1000
1071
  *
1001
- * Designed for waiting on async jobs, external state, or delayed availability.
1072
+ * Designed for waiting on async jobs, external state, or delayed availability
1073
+ * where you only care about the final result and not intermediate states.
1074
+ *
1075
+ * Use `poll.watch` instead if you need to stream progress (e.g. tRPC subscriptions).
1002
1076
  *
1003
1077
  * @template T The type of the successful result.
1004
1078
  *
1005
1079
  * @param cond
1006
1080
  * A function returning a Promise that resolves to:
1007
1081
  * - a truthy value `T` → stop polling and return it
1082
+ * - `poll.signal.abort` → stop polling cleanly, resolves with undefined
1083
+ * - `poll.signal.retry` → skip this tick silently
1008
1084
  * - falsy/null/undefined → continue polling
1009
1085
  *
1010
1086
  * @param options
@@ -1014,14 +1090,14 @@ declare function extendProps<T, P$1 extends object>(base: T, props: P$1): T exte
1014
1090
  * - `jitter` (boolean) — Add small random offset (±10%) to intervals to avoid sync bursts (default: true)
1015
1091
  * - `signal` (AbortSignal) — Optional abort signal to cancel polling
1016
1092
  *
1017
- * @returns
1018
- * Resolves with the truthy value `T` when successful.
1019
- * Throws `AbortError` if aborted
1093
+ * @returns Resolves with the truthy value `T` when successful.
1094
+ * @throws `AbortError` if aborted via signal
1095
+ * @throws `Error` if timed out
1020
1096
  *
1021
1097
  * @example
1022
1098
  * ```ts
1023
- * // Poll for job completion
1024
- * const job = await poll(async () => {
1099
+ * // Wait for a job to complete
1100
+ * const job = await poll.wait(async () => {
1025
1101
  * const status = await getJobStatus();
1026
1102
  * return status === 'done' ? status : null;
1027
1103
  * }, { interval: 3000, timeout: 60000 });
@@ -1029,27 +1105,24 @@ declare function extendProps<T, P$1 extends object>(base: T, props: P$1): T exte
1029
1105
  *
1030
1106
  * @example
1031
1107
  * ```ts
1032
- * // Wait for API endpoint to be ready
1033
- * const apiReady = await poll(async () => {
1034
- * try {
1035
- * await fetch('/api/health');
1036
- * return true;
1037
- * } catch {
1038
- * return null;
1039
- * }
1040
- * }, { interval: 1000, timeout: 30000 });
1108
+ * // Abort early based on domain logic
1109
+ * const job = await poll.wait(async () => {
1110
+ * const job = await getJob(id);
1111
+ * if (job?.is_cancelled) return poll.signal.abort;
1112
+ * return job?.is_finished ? job : null;
1113
+ * }, { timeout: 60000 });
1041
1114
  * ```
1042
1115
  *
1043
1116
  * @example
1044
1117
  * ```ts
1045
- * // Poll with abort signal for cancellation
1118
+ * // Cancel polling with an AbortSignal
1046
1119
  * const controller = new AbortController();
1047
- * setTimeout(() => controller.abort(), 10000); // Cancel after 10s
1120
+ * setTimeout(() => controller.abort(), 10000);
1048
1121
  *
1049
1122
  * try {
1050
- * const result = await poll(
1123
+ * const result = await poll.wait(
1051
1124
  * () => checkExternalService(),
1052
- * { interval: 2000, signal: controller.signal }
1125
+ * { interval: 2000, signal: controller.signal },
1053
1126
  * );
1054
1127
  * } catch (err) {
1055
1128
  * if (err.name === 'AbortError') {
@@ -1057,27 +1130,18 @@ declare function extendProps<T, P$1 extends object>(base: T, props: P$1): T exte
1057
1130
  * }
1058
1131
  * }
1059
1132
  * ```
1060
- *
1061
- * @example
1062
- * ```ts
1063
- * // Poll for user action completion
1064
- * const userConfirmed = await poll(async () => {
1065
- * const confirmations = await getPendingConfirmations();
1066
- * return confirmations.length > 0 ? confirmations[0] : null;
1067
- * }, { interval: 5000, timeout: 300000 }); // 5 min timeout
1068
- * ```
1069
1133
  */
1070
- declare function poll<T>(cond: () => Promise<T | null | false | undefined>, {
1071
- interval,
1072
- timeout,
1073
- jitter,
1074
- signal
1075
- }?: Partial<{
1076
- interval: number;
1077
- timeout: number;
1078
- signal: AbortSignal;
1079
- jitter: boolean;
1080
- }>): Promise<T>;
1134
+ declare function wait<T>(cond: () => Promise<T | null | false | undefined | PollControl>, options?: PollOptions): Promise<T | undefined>;
1135
+ declare const poll: {
1136
+ readonly wait: typeof wait;
1137
+ readonly watch: typeof watch;
1138
+ readonly signal: {
1139
+ /** Stop polling cleanly with no error */
1140
+ readonly abort: typeof ABORT;
1141
+ /** Skip this tick silently without emitting null */
1142
+ readonly retry: typeof RETRY;
1143
+ };
1144
+ };
1081
1145
  //#endregion
1082
1146
  //#region src/functions/concurrency.d.ts
1083
1147
  type TaskType<T> = Promise<T> | (() => Promise<T>);
@@ -1106,7 +1170,7 @@ declare function withConcurrency<T extends Record<string, TaskType<any>>>(tasks:
1106
1170
  /**
1107
1171
  * A task function that can be synchronous or asynchronous.
1108
1172
  */
1109
- type Task = () => Promise<void> | void;
1173
+ type Task = MaybeFunction<Promise<void> | void>;
1110
1174
  /**
1111
1175
  * Options for configuring the schedule function.
1112
1176
  */
@@ -1115,10 +1179,23 @@ interface ScheduleOpts {
1115
1179
  retry?: number;
1116
1180
  /** Delay in milliseconds between retries. Defaults to 0. */
1117
1181
  delay?: number;
1118
- /** Maximum time in milliseconds to wait for the task to complete. */
1119
- timeout?: number;
1120
1182
  /** Enable debug logging. Defaults to false. */
1121
1183
  debug?: boolean;
1184
+ /**
1185
+ * Pass `ctx.waitUntil` on serverless/edge runtimes (Vercel, Cloudflare, etc.)
1186
+ * to keep the execution context alive until the task settles.
1187
+ * Without this, the runtime may shut down before the task completes.
1188
+ *
1189
+ * @example
1190
+ * ```ts
1191
+ * // Vercel Edge / Next.js route handler
1192
+ * schedule(() => sendAnalytics(), { waitUntil: ctx.waitUntil });
1193
+ *
1194
+ * // Cloudflare Worker
1195
+ * schedule(() => logToR2(), { waitUntil: ctx.waitUntil });
1196
+ * ```
1197
+ */
1198
+ waitUntil?: (promise: Promise<unknown>) => void;
1122
1199
  }
1123
1200
  /**
1124
1201
  * Runs a function asynchronously in the background without blocking the main thread.
@@ -1127,8 +1204,11 @@ interface ScheduleOpts {
1127
1204
  * Useful for non-critical operations like analytics, logging, or background processing.
1128
1205
  * Logs execution time and retry attempts to the console.
1129
1206
  *
1207
+ * On serverless/edge runtimes, pass `waitUntil` from the execution context to prevent
1208
+ * the runtime from shutting down before the task completes.
1209
+ *
1130
1210
  * @param task - The function to execute asynchronously
1131
- * @param options - Configuration options for retries and timing
1211
+ * @param options - Configuration options for retries, timing, and runtime context
1132
1212
  *
1133
1213
  * @example
1134
1214
  * ```ts
@@ -1142,6 +1222,12 @@ interface ScheduleOpts {
1142
1222
  * () => sendAnalytics(),
1143
1223
  * { retry: 3, delay: 1000 }
1144
1224
  * );
1225
+ *
1226
+ * // Serverless/edge runtime
1227
+ * schedule(
1228
+ * () => sendAnalytics(),
1229
+ * { retry: 3, delay: 1000, waitUntil: ctx.waitUntil }
1230
+ * );
1145
1231
  * ```
1146
1232
  */
1147
1233
  declare function schedule(task: Task, options?: ScheduleOpts): void;
package/dist/index.d.ts CHANGED
@@ -994,17 +994,93 @@ declare function hydrate<T>(data: T): Hydrate<T>;
994
994
  declare function extendProps<T, P$1 extends object>(base: T, props: P$1): T extends null | undefined ? T : T & P$1;
995
995
  //#endregion
996
996
  //#region src/functions/poll.d.ts
997
+ type PollOptions = Partial<{
998
+ interval: number;
999
+ timeout: number;
1000
+ signal: AbortSignal;
1001
+ jitter: boolean;
1002
+ }>;
1003
+ declare const ABORT: unique symbol;
1004
+ declare const RETRY: unique symbol;
1005
+ type PollControl = typeof ABORT | typeof RETRY;
1006
+ /**
1007
+ * Streams an async `cond` function, yielding `null` while waiting and the
1008
+ * final truthy value `T` when resolved.
1009
+ *
1010
+ * Designed for use in tRPC subscriptions or any async generator context where
1011
+ * you want to stream progress to the client instead of blocking.
1012
+ *
1013
+ * Keeps SSE/WebSocket connections alive on edge runtimes (e.g. Vercel) by
1014
+ * continuously emitting values rather than holding a silent open connection.
1015
+ *
1016
+ * @template T The type of the successful result.
1017
+ *
1018
+ * @param cond
1019
+ * A function returning a Promise that resolves to:
1020
+ * - a truthy value `T` → stop polling and yield it
1021
+ * - `poll.signal.abort` → stop polling cleanly with no error
1022
+ * - `poll.signal.retry` → skip this tick silently, no null emitted
1023
+ * - falsy/null/undefined → yield `null` and continue polling
1024
+ *
1025
+ * @param options
1026
+ * Configuration options:
1027
+ * - `interval` (number) — Time between polls in ms (default: 5000 ms)
1028
+ * - `timeout` (number) — Max total duration before failing (default: 5 min)
1029
+ * - `jitter` (boolean) — Add small random offset (±10%) to intervals to avoid sync bursts (default: true)
1030
+ * - `signal` (AbortSignal) — Optional abort signal to cancel polling
1031
+ *
1032
+ * @throws `AbortError` if aborted via signal
1033
+ * @throws `Error` if timed out
1034
+ *
1035
+ * @example
1036
+ * ```ts
1037
+ * // tRPC subscription — stream null while job is pending, emit result when done
1038
+ * syncImageJob: privateProcedure
1039
+ * .input(z.object({ id: z.coerce.number() }))
1040
+ * .subscription(async function* ({ ctx, input, signal }) {
1041
+ * yield* poll.watch(
1042
+ * async () => {
1043
+ * const job = await getJob(input.id);
1044
+ * if (job?.is_cancelled) return poll.signal.abort; // stop cleanly
1045
+ * if (job?.is_paused) return poll.signal.retry; // skip this tick
1046
+ * return job?.is_finished ? job : null;
1047
+ * },
1048
+ * { interval: 15000, signal },
1049
+ * );
1050
+ * }),
1051
+ * ```
1052
+ *
1053
+ * @example
1054
+ * ```ts
1055
+ * // Manually iterate
1056
+ * for await (const result of poll.watch(() => getJobStatus(), { interval: 3000 })) {
1057
+ * if (result === null) console.log('still waiting...');
1058
+ * else console.log('done!', result);
1059
+ * }
1060
+ * ```
1061
+ */
1062
+ declare function watch<T>(cond: () => Promise<T | null | false | undefined | PollControl>, {
1063
+ interval,
1064
+ timeout,
1065
+ jitter,
1066
+ signal
1067
+ }?: PollOptions): AsyncGenerator<T | null>;
997
1068
  /**
998
1069
  * Repeatedly polls an async `cond` function UNTIL it returns a TRUTHY value,
999
1070
  * or until the operation times out or is aborted.
1000
1071
  *
1001
- * Designed for waiting on async jobs, external state, or delayed availability.
1072
+ * Designed for waiting on async jobs, external state, or delayed availability
1073
+ * where you only care about the final result and not intermediate states.
1074
+ *
1075
+ * Use `poll.watch` instead if you need to stream progress (e.g. tRPC subscriptions).
1002
1076
  *
1003
1077
  * @template T The type of the successful result.
1004
1078
  *
1005
1079
  * @param cond
1006
1080
  * A function returning a Promise that resolves to:
1007
1081
  * - a truthy value `T` → stop polling and return it
1082
+ * - `poll.signal.abort` → stop polling cleanly, resolves with undefined
1083
+ * - `poll.signal.retry` → skip this tick silently
1008
1084
  * - falsy/null/undefined → continue polling
1009
1085
  *
1010
1086
  * @param options
@@ -1014,14 +1090,14 @@ declare function extendProps<T, P$1 extends object>(base: T, props: P$1): T exte
1014
1090
  * - `jitter` (boolean) — Add small random offset (±10%) to intervals to avoid sync bursts (default: true)
1015
1091
  * - `signal` (AbortSignal) — Optional abort signal to cancel polling
1016
1092
  *
1017
- * @returns
1018
- * Resolves with the truthy value `T` when successful.
1019
- * Throws `AbortError` if aborted
1093
+ * @returns Resolves with the truthy value `T` when successful.
1094
+ * @throws `AbortError` if aborted via signal
1095
+ * @throws `Error` if timed out
1020
1096
  *
1021
1097
  * @example
1022
1098
  * ```ts
1023
- * // Poll for job completion
1024
- * const job = await poll(async () => {
1099
+ * // Wait for a job to complete
1100
+ * const job = await poll.wait(async () => {
1025
1101
  * const status = await getJobStatus();
1026
1102
  * return status === 'done' ? status : null;
1027
1103
  * }, { interval: 3000, timeout: 60000 });
@@ -1029,27 +1105,24 @@ declare function extendProps<T, P$1 extends object>(base: T, props: P$1): T exte
1029
1105
  *
1030
1106
  * @example
1031
1107
  * ```ts
1032
- * // Wait for API endpoint to be ready
1033
- * const apiReady = await poll(async () => {
1034
- * try {
1035
- * await fetch('/api/health');
1036
- * return true;
1037
- * } catch {
1038
- * return null;
1039
- * }
1040
- * }, { interval: 1000, timeout: 30000 });
1108
+ * // Abort early based on domain logic
1109
+ * const job = await poll.wait(async () => {
1110
+ * const job = await getJob(id);
1111
+ * if (job?.is_cancelled) return poll.signal.abort;
1112
+ * return job?.is_finished ? job : null;
1113
+ * }, { timeout: 60000 });
1041
1114
  * ```
1042
1115
  *
1043
1116
  * @example
1044
1117
  * ```ts
1045
- * // Poll with abort signal for cancellation
1118
+ * // Cancel polling with an AbortSignal
1046
1119
  * const controller = new AbortController();
1047
- * setTimeout(() => controller.abort(), 10000); // Cancel after 10s
1120
+ * setTimeout(() => controller.abort(), 10000);
1048
1121
  *
1049
1122
  * try {
1050
- * const result = await poll(
1123
+ * const result = await poll.wait(
1051
1124
  * () => checkExternalService(),
1052
- * { interval: 2000, signal: controller.signal }
1125
+ * { interval: 2000, signal: controller.signal },
1053
1126
  * );
1054
1127
  * } catch (err) {
1055
1128
  * if (err.name === 'AbortError') {
@@ -1057,27 +1130,18 @@ declare function extendProps<T, P$1 extends object>(base: T, props: P$1): T exte
1057
1130
  * }
1058
1131
  * }
1059
1132
  * ```
1060
- *
1061
- * @example
1062
- * ```ts
1063
- * // Poll for user action completion
1064
- * const userConfirmed = await poll(async () => {
1065
- * const confirmations = await getPendingConfirmations();
1066
- * return confirmations.length > 0 ? confirmations[0] : null;
1067
- * }, { interval: 5000, timeout: 300000 }); // 5 min timeout
1068
- * ```
1069
1133
  */
1070
- declare function poll<T>(cond: () => Promise<T | null | false | undefined>, {
1071
- interval,
1072
- timeout,
1073
- jitter,
1074
- signal
1075
- }?: Partial<{
1076
- interval: number;
1077
- timeout: number;
1078
- signal: AbortSignal;
1079
- jitter: boolean;
1080
- }>): Promise<T>;
1134
+ declare function wait<T>(cond: () => Promise<T | null | false | undefined | PollControl>, options?: PollOptions): Promise<T | undefined>;
1135
+ declare const poll: {
1136
+ readonly wait: typeof wait;
1137
+ readonly watch: typeof watch;
1138
+ readonly signal: {
1139
+ /** Stop polling cleanly with no error */
1140
+ readonly abort: typeof ABORT;
1141
+ /** Skip this tick silently without emitting null */
1142
+ readonly retry: typeof RETRY;
1143
+ };
1144
+ };
1081
1145
  //#endregion
1082
1146
  //#region src/functions/concurrency.d.ts
1083
1147
  type TaskType<T> = Promise<T> | (() => Promise<T>);
@@ -1106,7 +1170,7 @@ declare function withConcurrency<T extends Record<string, TaskType<any>>>(tasks:
1106
1170
  /**
1107
1171
  * A task function that can be synchronous or asynchronous.
1108
1172
  */
1109
- type Task = () => Promise<void> | void;
1173
+ type Task = MaybeFunction<Promise<void> | void>;
1110
1174
  /**
1111
1175
  * Options for configuring the schedule function.
1112
1176
  */
@@ -1115,10 +1179,23 @@ interface ScheduleOpts {
1115
1179
  retry?: number;
1116
1180
  /** Delay in milliseconds between retries. Defaults to 0. */
1117
1181
  delay?: number;
1118
- /** Maximum time in milliseconds to wait for the task to complete. */
1119
- timeout?: number;
1120
1182
  /** Enable debug logging. Defaults to false. */
1121
1183
  debug?: boolean;
1184
+ /**
1185
+ * Pass `ctx.waitUntil` on serverless/edge runtimes (Vercel, Cloudflare, etc.)
1186
+ * to keep the execution context alive until the task settles.
1187
+ * Without this, the runtime may shut down before the task completes.
1188
+ *
1189
+ * @example
1190
+ * ```ts
1191
+ * // Vercel Edge / Next.js route handler
1192
+ * schedule(() => sendAnalytics(), { waitUntil: ctx.waitUntil });
1193
+ *
1194
+ * // Cloudflare Worker
1195
+ * schedule(() => logToR2(), { waitUntil: ctx.waitUntil });
1196
+ * ```
1197
+ */
1198
+ waitUntil?: (promise: Promise<unknown>) => void;
1122
1199
  }
1123
1200
  /**
1124
1201
  * Runs a function asynchronously in the background without blocking the main thread.
@@ -1127,8 +1204,11 @@ interface ScheduleOpts {
1127
1204
  * Useful for non-critical operations like analytics, logging, or background processing.
1128
1205
  * Logs execution time and retry attempts to the console.
1129
1206
  *
1207
+ * On serverless/edge runtimes, pass `waitUntil` from the execution context to prevent
1208
+ * the runtime from shutting down before the task completes.
1209
+ *
1130
1210
  * @param task - The function to execute asynchronously
1131
- * @param options - Configuration options for retries and timing
1211
+ * @param options - Configuration options for retries, timing, and runtime context
1132
1212
  *
1133
1213
  * @example
1134
1214
  * ```ts
@@ -1142,6 +1222,12 @@ interface ScheduleOpts {
1142
1222
  * () => sendAnalytics(),
1143
1223
  * { retry: 3, delay: 1000 }
1144
1224
  * );
1225
+ *
1226
+ * // Serverless/edge runtime
1227
+ * schedule(
1228
+ * () => sendAnalytics(),
1229
+ * { retry: 3, delay: 1000, waitUntil: ctx.waitUntil }
1230
+ * );
1145
1231
  * ```
1146
1232
  */
1147
1233
  declare function schedule(task: Task, options?: ScheduleOpts): void;
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- const e=e=>!e,t=e=>e==null,n=e=>typeof e==`boolean`,r=e=>typeof e==`string`,i=e=>{if(typeof e==`number`)return Number.isFinite(e);if(typeof e==`string`){let t=Number(e);return Number.isFinite(t)}return!1},a=e=>Array.isArray(e),o=e=>typeof e==`function`,s=e=>{if(e==null)return!0;switch(typeof e){case`string`:case`number`:case`bigint`:case`boolean`:case`symbol`:return!0;default:return!1}};function c(e){if(typeof e!=`object`||!e||Object.prototype.toString.call(e)!==`[object Object]`)return!1;let t=Object.getPrototypeOf(e);return t===null||t===Object.prototype}function l(e,...t){let n,r={},i=t[t.length-1];i&&typeof i==`object`&&!Array.isArray(i)&&(i.arrayMerge!==void 0||i.clone!==void 0||i.customMerge!==void 0||i.functionMerge!==void 0||i.maxDepth!==void 0)?(r={...r,...i},n=t.slice(0,-1)):n=t;let{arrayMerge:a=`replace`,clone:o=!0,functionMerge:s=`replace`,maxDepth:l=100,customMerge:u}=r,d=new WeakMap;return f(e,n,0);function f(e,t,n){if(n>=l)return console.warn(`[deepmerge] Maximum depth ${l} exceeded. Returning target as-is.`),e;if(!c(e)&&!Array.isArray(e)){for(let n of t)if(n!==void 0){if(u){let t=u(``,e,n);if(t!==void 0)return t}return typeof e==`function`&&typeof n==`function`&&s===`compose`?(...t)=>{e(...t),n(...t)}:n}return e}let r=o?Array.isArray(e)?[...e]:{...e}:e;for(let e of t)if(e!=null&&!d.has(e))if(d.set(e,r),Array.isArray(r)&&Array.isArray(e))r=p(r,e,a);else if(c(r)&&c(e)){let t=new Set([...Object.keys(r),...Object.keys(e),...Object.getOwnPropertySymbols(r),...Object.getOwnPropertySymbols(e)]);for(let i of t){let t=r[i],o=e[i];u&&u(i,t,o)!==void 0?r[i]=u(i,t,o):typeof t==`function`&&typeof o==`function`?s===`compose`?r[i]=(...e)=>{t(...e),o(...e)}:r[i]=o:c(t)&&c(o)?r[i]=f(t,[o],n+1):Array.isArray(t)&&Array.isArray(o)?r[i]=p(t,o,a):o!==void 0&&(r[i]=o)}}else r=e;return r}function p(e,t,n){if(typeof n==`function`)return n(e,t);switch(n){case`concat`:return[...e,...t];case`merge`:let n=Math.max(e.length,t.length),r=[];for(let i=0;i<n;i++)i<e.length&&i<t.length?c(e[i])&&c(t[i])?r[i]=f(e[i],[t[i]],0):r[i]=t[i]:i<e.length?r[i]=e[i]:r[i]=t[i];return r;case`replace`:default:return[...t]}}}function u(e,t,n){if(Array.isArray(t))return t.map(t=>u(e,t,n));if(typeof t==`object`&&t&&!Array.isArray(t)){let r={};for(let i in t)t.hasOwnProperty(i)&&(r[i]=u(e,t[i],n));return r}if(typeof t!=`string`)return n;let r=(()=>t===``?[]:t.split(`.`).filter(e=>e!==``))(),i=e;for(let e of r){if(i==null)return n;let t=typeof e==`string`&&Array.isArray(i)&&/^\d+$/.test(e)?Number.parseInt(e,10):e;i=i[t]}return i===void 0?n:i}function d(e){return f(e)}function f(e){if(e!==null){if(typeof e!=`object`||!e)return e;if(Array.isArray(e))return e.map(f);if(c(e)){let t={};for(let n in e)t[n]=f(e[n]);return t}return e}}function p(e,t){return e==null?e:Object.assign(e,t)}function m(e){return(e.split(`.`).pop()||e).replace(/([a-z])([A-Z])/g,`$1 $2`).split(/[-_|�\s]+/).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(` `)}const h=`àáãäâèéëêìíïîòóöôùúüûñç·/_,:;`,g=e=>{if(typeof e!=`string`)throw TypeError(`Input must be a string`);let t=e.trim().toLowerCase(),n={};for(let e=0;e<29;e++)n[h.charAt(e)]=`aaaaaeeeeiiiioooouuuunc------`.charAt(e);return t=t.replace(RegExp(`[${h}]`,`g`),e=>n[e]||e),t.replace(/[^a-z0-9 -]/g,``).replace(/\s+/g,`-`).replace(/-+/g,`-`).replace(/^-+/,``).replace(/-+$/,``)||``},_=(e=1e3,t)=>new Promise(n=>{if(t?.aborted)return n();let r=setTimeout(()=>{a(),n()},e);function i(){clearTimeout(r),a(),n()}function a(){t?.removeEventListener(`abort`,i)}t&&t.addEventListener(`abort`,i,{once:!0})});function v(e,t=100,n){if(typeof e!=`function`)throw TypeError(`Expected the first parameter to be a function, got \`${typeof e}\`.`);if(t<0)throw RangeError("`wait` must not be negative.");let r=n?.immediate??!1,i,a,o,s;function c(){return s=e.apply(o,a),a=void 0,o=void 0,s}let l=function(...e){return a=e,o=this,i===void 0&&r&&(s=c.call(this)),i!==void 0&&clearTimeout(i),i=setTimeout(c.bind(this),t),s};return Object.defineProperty(l,`isPending`,{get(){return i!==void 0}}),l}function y(e,t=100,n){if(typeof e!=`function`)throw TypeError(`Expected the first parameter to be a function, got \`${typeof e}\`.`);if(t<0)throw RangeError("`wait` must not be negative.");let r=n?.leading??!0,i=n?.trailing??!0,a,o,s,c,l;function u(){c=Date.now(),l=e.apply(s,o),o=void 0,s=void 0}function d(){a=void 0,i&&o&&u()}let f=function(...e){let n=c?Date.now()-c:1/0;return o=e,s=this,n>=t?r?u():a=setTimeout(d,t):!a&&i&&(a=setTimeout(d,t-n)),l};return Object.defineProperty(f,`isPending`,{get(){return a!==void 0}}),f}function b(e,...t){let n=t.length===1&&Array.isArray(t[0])?t[0]:t,r=0;return e.replace(/%s/g,()=>{let e=n[r++];return e===void 0?``:String(e)})}function x(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}function S(e,t={}){if(!e)return``;let{lowercase:n=!0,removeAccents:r=!0,removeNonAlphanumeric:i=!0}=t,a=e.normalize(`NFC`);return r&&(a=a.normalize(`NFD`).replace(/\p{M}/gu,``)),i&&(a=a.replace(/^[^\p{L}\p{N}]*|[^\p{L}\p{N}]*$/gu,``)),n&&(a=a.toLocaleLowerCase()),a}function C(e,...t){return typeof e==`function`?e(...t):e}async function w(e,{interval:t=5e3,timeout:n=300*1e3,jitter:r=!0,signal:i}={}){let a=Date.now(),o=i?.aborted??!1,s=()=>{o=!0};i?.addEventListener(`abort`,s,{once:!0});try{for(let s=0;;s++){if(o)throw Error(`Polling aborted`);let s=await e();if(s)return s;if(Date.now()-a>=n)throw Error(`Polling timed out`,{cause:`Polling timed out after ${n}ms`});await _(r?t+(Math.random()-.5)*t*.2:t,i)}}catch(e){throw e}finally{i?.removeEventListener(`abort`,s)}}async function T(e,t={}){let{concurrency:n=1/0,timeout:r=1/0,signal:i,retry:a=0,retryDelay:o=0,throwOnFirstError:s=!1,ignoreErrors:c=!1}=t,l=Array.isArray(e),u={};l?e.forEach((e,t)=>{u[t.toString()]=e}):Object.entries(e).forEach(([e,t])=>{u[e]=t});let d=Object.keys(u);if(d.length===0)return{results:l?[]:{},errors:[],succeeded:0,failed:0,duration:0};let f=Date.now(),p=[],m=[],h=0,g=0,v=0,y=!1,b=s?null:void 0,x=async e=>{let t=0;for(;t<=a&&!y;)try{let t=u[e],n=await(typeof t==`function`?t():t);p.push([e,n]),g++;return}catch(e){if(t===a){let t=e instanceof Error?e:Error(String(e));m.push(t),v++,!c&&s&&!b&&(b=t,y=!0);return}t++,o>0&&await _(o,i)}},S=Array.from({length:Math.min(n,d.length)},async()=>{for(;!y;){let e=h++;if(e>=d.length)return;if(i?.aborted){y=!0;return}await x(d[e])}}),C=Promise.all(S);if(r===1/0?await C:await Promise.race([C,_(r,i).then(()=>{throw y=!0,Error(`Concurrence timed out after ${r}ms`)})]),b&&s)throw b;return l?{results:p.sort((e,t)=>Number(e[0])-Number(t[0])).map(([,e])=>e),errors:m,succeeded:g,failed:v,duration:Date.now()-f}:{results:Object.fromEntries(p),errors:m,succeeded:g,failed:v,duration:Date.now()-f}}function E(e,t={}){let{retry:n=0,delay:r=0,debug:i=!1}=t,a=Date.now(),o=async t=>{try{if(await e(),i){let e=Date.now()-a;console.log(`⚡[schedule.ts] Completed in ${e}ms`)}}catch(e){if(i&&console.log(`⚡[schedule.ts] err:`,e),t>0)i&&console.log(`⚡[schedule.ts] Retrying in ${r}ms...`),setTimeout(()=>o(t-1),r);else if(i){let e=Date.now()-a;console.log(`⚡[schedule.ts] Failed after ${e}ms`)}}};setTimeout(()=>o(n),0)}function D(e){if(e instanceof Promise)return e.then(e=>[null,e]).catch(e=>[e,null]);try{return[null,e()]}catch(t){return console.log(`\x1b[31m🛡 [shield]\x1b[0m ${e.name} failed →`,t),[t,null]}}export{m as convertToNormalCase,g as convertToSlug,v as debounce,l as deepmerge,x as escapeRegExp,p as extendProps,u as extract,d as hydrate,a as isArray,n as isBoolean,e as isFalsy,i as isFiniteNumber,o as isFunction,t as isNullish,c as isPlainObject,s as isPrimitive,r as isString,S as normalizeText,w as poll,b as printf,E as schedule,D as shield,_ as sleep,y as throttle,C as unwrap,T as withConcurrency};
1
+ const e=e=>!e,t=e=>e==null,n=e=>typeof e==`boolean`,r=e=>typeof e==`string`,i=e=>{if(typeof e==`number`)return Number.isFinite(e);if(typeof e==`string`){let t=Number(e);return Number.isFinite(t)}return!1},a=e=>Array.isArray(e),o=e=>typeof e==`function`,s=e=>{if(e==null)return!0;switch(typeof e){case`string`:case`number`:case`bigint`:case`boolean`:case`symbol`:return!0;default:return!1}};function c(e){if(typeof e!=`object`||!e||Object.prototype.toString.call(e)!==`[object Object]`)return!1;let t=Object.getPrototypeOf(e);return t===null||t===Object.prototype}function l(e,...t){let n,r={},i=t[t.length-1];i&&typeof i==`object`&&!Array.isArray(i)&&(i.arrayMerge!==void 0||i.clone!==void 0||i.customMerge!==void 0||i.functionMerge!==void 0||i.maxDepth!==void 0)?(r={...r,...i},n=t.slice(0,-1)):n=t;let{arrayMerge:a=`replace`,clone:o=!0,functionMerge:s=`replace`,maxDepth:l=100,customMerge:u}=r,d=new WeakMap;return f(e,n,0);function f(e,t,n){if(n>=l)return console.warn(`[deepmerge] Maximum depth ${l} exceeded. Returning target as-is.`),e;if(!c(e)&&!Array.isArray(e)){for(let n of t)if(n!==void 0){if(u){let t=u(``,e,n);if(t!==void 0)return t}return typeof e==`function`&&typeof n==`function`&&s===`compose`?(...t)=>{e(...t),n(...t)}:n}return e}let r=o?Array.isArray(e)?[...e]:{...e}:e;for(let e of t)if(e!=null&&!d.has(e))if(d.set(e,r),Array.isArray(r)&&Array.isArray(e))r=p(r,e,a);else if(c(r)&&c(e)){let t=new Set([...Object.keys(r),...Object.keys(e),...Object.getOwnPropertySymbols(r),...Object.getOwnPropertySymbols(e)]);for(let i of t){let t=r[i],o=e[i];u&&u(i,t,o)!==void 0?r[i]=u(i,t,o):typeof t==`function`&&typeof o==`function`?s===`compose`?r[i]=(...e)=>{t(...e),o(...e)}:r[i]=o:c(t)&&c(o)?r[i]=f(t,[o],n+1):Array.isArray(t)&&Array.isArray(o)?r[i]=p(t,o,a):o!==void 0&&(r[i]=o)}}else r=e;return r}function p(e,t,n){if(typeof n==`function`)return n(e,t);switch(n){case`concat`:return[...e,...t];case`merge`:let n=Math.max(e.length,t.length),r=[];for(let i=0;i<n;i++)i<e.length&&i<t.length?c(e[i])&&c(t[i])?r[i]=f(e[i],[t[i]],0):r[i]=t[i]:i<e.length?r[i]=e[i]:r[i]=t[i];return r;case`replace`:default:return[...t]}}}function u(e,t,n){if(Array.isArray(t))return t.map(t=>u(e,t,n));if(typeof t==`object`&&t&&!Array.isArray(t)){let r={};for(let i in t)t.hasOwnProperty(i)&&(r[i]=u(e,t[i],n));return r}if(typeof t!=`string`)return n;let r=(()=>t===``?[]:t.split(`.`).filter(e=>e!==``))(),i=e;for(let e of r){if(i==null)return n;let t=typeof e==`string`&&Array.isArray(i)&&/^\d+$/.test(e)?Number.parseInt(e,10):e;i=i[t]}return i===void 0?n:i}function d(e){return f(e)}function f(e){if(e!==null){if(typeof e!=`object`||!e)return e;if(Array.isArray(e))return e.map(f);if(c(e)){let t={};for(let n in e)t[n]=f(e[n]);return t}return e}}function p(e,t){return e==null?e:Object.assign(e,t)}function m(e){return(e.split(`.`).pop()||e).replace(/([a-z])([A-Z])/g,`$1 $2`).split(/[-_|�\s]+/).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(` `)}const h=`àáãäâèéëêìíïîòóöôùúüûñç·/_,:;`,g=e=>{if(typeof e!=`string`)throw TypeError(`Input must be a string`);let t=e.trim().toLowerCase(),n={};for(let e=0;e<29;e++)n[h.charAt(e)]=`aaaaaeeeeiiiioooouuuunc------`.charAt(e);return t=t.replace(RegExp(`[${h}]`,`g`),e=>n[e]||e),t.replace(/[^a-z0-9 -]/g,``).replace(/\s+/g,`-`).replace(/-+/g,`-`).replace(/^-+/,``).replace(/-+$/,``)||``},_=(e=1e3,t)=>new Promise(n=>{if(t?.aborted)return n();let r=setTimeout(()=>{a(),n()},e);function i(){clearTimeout(r),a(),n()}function a(){t?.removeEventListener(`abort`,i)}t&&t.addEventListener(`abort`,i,{once:!0})});function v(e,t=100,n){if(typeof e!=`function`)throw TypeError(`Expected the first parameter to be a function, got \`${typeof e}\`.`);if(t<0)throw RangeError("`wait` must not be negative.");let r=n?.immediate??!1,i,a,o,s;function c(){return s=e.apply(o,a),a=void 0,o=void 0,s}let l=function(...e){return a=e,o=this,i===void 0&&r&&(s=c.call(this)),i!==void 0&&clearTimeout(i),i=setTimeout(c.bind(this),t),s};return Object.defineProperty(l,`isPending`,{get(){return i!==void 0}}),l}function y(e,t=100,n){if(typeof e!=`function`)throw TypeError(`Expected the first parameter to be a function, got \`${typeof e}\`.`);if(t<0)throw RangeError("`wait` must not be negative.");let r=n?.leading??!0,i=n?.trailing??!0,a,o,s,c,l;function u(){c=Date.now(),l=e.apply(s,o),o=void 0,s=void 0}function d(){a=void 0,i&&o&&u()}let f=function(...e){let n=c?Date.now()-c:1/0;return o=e,s=this,n>=t?r?u():a=setTimeout(d,t):!a&&i&&(a=setTimeout(d,t-n)),l};return Object.defineProperty(f,`isPending`,{get(){return a!==void 0}}),f}function b(e,...t){let n=t.length===1&&Array.isArray(t[0])?t[0]:t,r=0;return e.replace(/%s/g,()=>{let e=n[r++];return e===void 0?``:String(e)})}function x(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}function S(e,t={}){if(!e)return``;let{lowercase:n=!0,removeAccents:r=!0,removeNonAlphanumeric:i=!0}=t,a=e.normalize(`NFC`);return r&&(a=a.normalize(`NFD`).replace(/\p{M}/gu,``)),i&&(a=a.replace(/^[^\p{L}\p{N}]*|[^\p{L}\p{N}]*$/gu,``)),n&&(a=a.toLocaleLowerCase()),a}function C(e,...t){return typeof e==`function`?e(...t):e}const w=Symbol(`poll.signal.abort`),T=Symbol(`poll.signal.retry`);async function*E(e,{interval:t=5e3,timeout:n=300*1e3,jitter:r=!0,signal:i}={}){let a=Date.now();for(;;){if(i?.aborted)throw new DOMException(`Polling aborted`,`AbortError`);let o=await e();if(o===w)return;if(o===T){await _(r?t+(Math.random()-.5)*t*.2:t,i);continue}if(o){yield o;return}if(Date.now()-a>=n)throw Error(`Polling timed out after ${n}ms`);yield null,await _(r?t+(Math.random()-.5)*t*.2:t,i)}}async function D(e,t={}){for await(let n of E(e,t))if(n)return n}const O={wait:D,watch:E,signal:{abort:w,retry:T}};async function k(e,t={}){let{concurrency:n=1/0,timeout:r=1/0,signal:i,retry:a=0,retryDelay:o=0,throwOnFirstError:s=!1,ignoreErrors:c=!1}=t,l=Array.isArray(e),u={};l?e.forEach((e,t)=>{u[t.toString()]=e}):Object.entries(e).forEach(([e,t])=>{u[e]=t});let d=Object.keys(u);if(d.length===0)return{results:l?[]:{},errors:[],succeeded:0,failed:0,duration:0};let f=Date.now(),p=[],m=[],h=0,g=0,v=0,y=!1,b=s?null:void 0,x=async e=>{let t=0;for(;t<=a&&!y;)try{let t=u[e],n=await(typeof t==`function`?t():t);p.push([e,n]),g++;return}catch(e){if(t===a){let t=e instanceof Error?e:Error(String(e));m.push(t),v++,!c&&s&&!b&&(b=t,y=!0);return}t++,o>0&&await _(o,i)}},S=Array.from({length:Math.min(n,d.length)},async()=>{for(;!y;){let e=h++;if(e>=d.length)return;if(i?.aborted){y=!0;return}await x(d[e])}}),C=Promise.all(S);if(r===1/0?await C:await Promise.race([C,_(r,i).then(()=>{throw y=!0,Error(`Concurrence timed out after ${r}ms`)})]),b&&s)throw b;return l?{results:p.sort((e,t)=>Number(e[0])-Number(t[0])).map(([,e])=>e),errors:m,succeeded:g,failed:v,duration:Date.now()-f}:{results:Object.fromEntries(p),errors:m,succeeded:g,failed:v,duration:Date.now()-f}}function A(e,t={}){let{retry:n=0,delay:r=0,debug:i=!1,waitUntil:a}=t,o=Date.now(),s=async t=>{try{if(await C(e),i){let e=Date.now()-o;console.log(`⚡[schedule.ts] Completed in ${e}ms`)}}catch(e){if(i&&console.log(`⚡[schedule.ts] err:`,e),t>0)i&&console.log(`⚡[schedule.ts] Retrying in ${r}ms...`),setTimeout(()=>s(t-1),r);else if(i){let e=Date.now()-o;console.log(`⚡[schedule.ts] Failed after ${e}ms`)}}},c=new Promise(e=>{setTimeout(()=>s(n).then(e),0)});a&&a(c)}function j(e){if(e instanceof Promise)return e.then(e=>[null,e]).catch(e=>[e,null]);try{return[null,e()]}catch(t){return console.log(`\x1b[31m🛡 [shield]\x1b[0m ${e.name} failed →`,t),[t,null]}}export{m as convertToNormalCase,g as convertToSlug,v as debounce,l as deepmerge,x as escapeRegExp,p as extendProps,u as extract,d as hydrate,a as isArray,n as isBoolean,e as isFalsy,i as isFiniteNumber,o as isFunction,t as isNullish,c as isPlainObject,s as isPrimitive,r as isString,S as normalizeText,O as poll,b as printf,A as schedule,j as shield,_ as sleep,y as throttle,C as unwrap,k as withConcurrency};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ts-utilities/core",
3
- "version": "1.3.5",
3
+ "version": "1.3.7",
4
4
  "author": "Sohan Emon <sohanemon@outlook.com>",
5
5
  "description": "Core utilities for JavaScript/TypeScript projects",
6
6
  "type": "module",