@vielzeug/toolkit 1.0.13 → 1.0.14
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/array/{sortBy.cjs → arrange.cjs} +2 -2
- package/dist/array/arrange.cjs.map +1 -0
- package/dist/array/arrange.js +6 -0
- package/dist/array/arrange.js.map +1 -0
- package/dist/array/chunk.cjs.map +1 -1
- package/dist/array/chunk.js.map +1 -1
- package/dist/array/list.cjs +1 -1
- package/dist/array/list.cjs.map +1 -1
- package/dist/array/list.js +86 -53
- package/dist/array/list.js.map +1 -1
- package/dist/array/remoteList.cjs +2 -0
- package/dist/array/remoteList.cjs.map +1 -0
- package/dist/array/remoteList.js +123 -0
- package/dist/array/remoteList.js.map +1 -0
- package/dist/date/expires.cjs.map +1 -1
- package/dist/date/expires.js.map +1 -1
- package/dist/date/interval.cjs.map +1 -1
- package/dist/date/interval.js.map +1 -1
- package/dist/date/timeDiff.cjs.map +1 -1
- package/dist/date/timeDiff.js.map +1 -1
- package/dist/function/memo.cjs.map +1 -1
- package/dist/function/memo.js.map +1 -1
- package/dist/function/parallel.cjs +2 -0
- package/dist/function/parallel.cjs.map +1 -0
- package/dist/function/parallel.js +28 -0
- package/dist/function/parallel.js.map +1 -0
- package/dist/function/proxy.cjs.map +1 -1
- package/dist/function/proxy.js.map +1 -1
- package/dist/function/prune.cjs +2 -0
- package/dist/function/prune.cjs.map +1 -0
- package/dist/function/prune.js +30 -0
- package/dist/function/prune.js.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +400 -51
- package/dist/index.js +218 -194
- package/dist/index.js.map +1 -1
- package/dist/math/abs.cjs +2 -0
- package/dist/math/abs.cjs.map +1 -0
- package/dist/math/abs.js +7 -0
- package/dist/math/abs.js.map +1 -0
- package/dist/math/add.cjs +2 -0
- package/dist/math/add.cjs.map +1 -0
- package/dist/math/add.js +9 -0
- package/dist/math/add.js.map +1 -0
- package/dist/math/allocate.cjs +2 -0
- package/dist/math/allocate.cjs.map +1 -0
- package/dist/math/allocate.js +29 -0
- package/dist/math/allocate.js.map +1 -0
- package/dist/math/distribute.cjs +2 -0
- package/dist/math/distribute.cjs.map +1 -0
- package/dist/math/distribute.js +18 -0
- package/dist/math/distribute.js.map +1 -0
- package/dist/math/divide.cjs +2 -0
- package/dist/math/divide.cjs.map +1 -0
- package/dist/math/divide.js +11 -0
- package/dist/math/divide.js.map +1 -0
- package/dist/math/multiply.cjs +2 -0
- package/dist/math/multiply.cjs.map +1 -0
- package/dist/math/multiply.js +9 -0
- package/dist/math/multiply.js.map +1 -0
- package/dist/math/subtract.cjs +2 -0
- package/dist/math/subtract.cjs.map +1 -0
- package/dist/math/subtract.js +9 -0
- package/dist/math/subtract.js.map +1 -0
- package/dist/money/currency.cjs +2 -0
- package/dist/money/currency.cjs.map +1 -0
- package/dist/money/currency.js +49 -0
- package/dist/money/currency.js.map +1 -0
- package/dist/money/exchange.cjs +2 -0
- package/dist/money/exchange.cjs.map +1 -0
- package/dist/money/exchange.js +13 -0
- package/dist/money/exchange.js.map +1 -0
- package/dist/object/merge.cjs.map +1 -1
- package/dist/object/merge.js.map +1 -1
- package/dist/object/parseJSON.cjs.map +1 -1
- package/dist/object/parseJSON.js.map +1 -1
- package/dist/object/path.cjs.map +1 -1
- package/dist/object/path.js.map +1 -1
- package/package.json +1 -1
- package/dist/array/sortBy.cjs.map +0 -1
- package/dist/array/sortBy.js +0 -6
- package/dist/array/sortBy.js.map +0 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("../function/compareBy.cjs"),o=(r,e)=>[...r].sort(t.compareBy(e));exports.
|
|
2
|
-
//# sourceMappingURL=
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("../function/compareBy.cjs"),o=(r,e)=>[...r].sort(t.compareBy(e));exports.arrange=o;
|
|
2
|
+
//# sourceMappingURL=arrange.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"arrange.cjs","sources":["../../src/array/arrange.ts"],"sourcesContent":["import { compareBy } from '../function/compareBy';\n\n/**\n * Arranges (sorts) an array of objects based on multiple selectors.\n *\n * @example\n * ```ts\n * const data = [\n * { name: 'Alice', age: 30 },\n * { name: 'Bob', age: 25 },\n * { name: 'Charlie', age: 35 },\n * { name: 'Alice', age: 25 },\n * { name: 'Bob', age: 30 },\n * { name: 'Charlie', age: 30 },\n * ].arrange(data, { name: 'asc', age: 'desc' }); // [ { name: 'Alice', age: 30 }, { name: 'Alice', age: 25 }, { name: 'Bob', age: 30 }, { name: 'Bob', age: 25 }, { name: 'Charlie', age: 35 }, { name: 'Charlie', age: 30 } ]\n * ```\n *\n * @param array - The array to sort.\n * @param selectors - An object where keys are the properties to sort by and values are 'asc' or 'desc'.\n * @returns A new sorted array.\n */\nexport const arrange = <T>(array: T[], selectors: Partial<Record<keyof T, 'asc' | 'desc'>>) => {\n return [...array].sort(compareBy(selectors));\n};\n"],"names":["arrange","array","selectors","compareBy"],"mappings":"6HAqBaA,EAAU,CAAIC,EAAYC,IAC9B,CAAC,GAAGD,CAAK,EAAE,KAAKE,EAAAA,UAAUD,CAAS,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"arrange.js","sources":["../../src/array/arrange.ts"],"sourcesContent":["import { compareBy } from '../function/compareBy';\n\n/**\n * Arranges (sorts) an array of objects based on multiple selectors.\n *\n * @example\n * ```ts\n * const data = [\n * { name: 'Alice', age: 30 },\n * { name: 'Bob', age: 25 },\n * { name: 'Charlie', age: 35 },\n * { name: 'Alice', age: 25 },\n * { name: 'Bob', age: 30 },\n * { name: 'Charlie', age: 30 },\n * ].arrange(data, { name: 'asc', age: 'desc' }); // [ { name: 'Alice', age: 30 }, { name: 'Alice', age: 25 }, { name: 'Bob', age: 30 }, { name: 'Bob', age: 25 }, { name: 'Charlie', age: 35 }, { name: 'Charlie', age: 30 } ]\n * ```\n *\n * @param array - The array to sort.\n * @param selectors - An object where keys are the properties to sort by and values are 'asc' or 'desc'.\n * @returns A new sorted array.\n */\nexport const arrange = <T>(array: T[], selectors: Partial<Record<keyof T, 'asc' | 'desc'>>) => {\n return [...array].sort(compareBy(selectors));\n};\n"],"names":["arrange","array","selectors","compareBy"],"mappings":";AAqBO,MAAMA,IAAU,CAAIC,GAAYC,MAC9B,CAAC,GAAGD,CAAK,EAAE,KAAKE,EAAUD,CAAS,CAAC;"}
|
package/dist/array/chunk.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chunk.cjs","sources":["../../src/array/chunk.ts"],"sourcesContent":["import { assert } from '../function/assert';\nimport { isArray } from '../typed/isArray';\nimport { isString } from '../typed/isString';\n\ntype ChunkOptions = {\n overlap?: boolean;\n pad?: string;\n};\n\ntype ChunkResult<T> = (T extends string ? string : T[])[];\n\n/**\n * Splits an array or string into chunks of a specified size.\n *\n * @example\n * ```ts\n * chunk([1, 2, 3, 4, 5], 2) // [[1, 2], [3, 4], [5]]\n * chunk(\"hello\", 2) // [\"he\", \"ll\", \"o\"]\n * chunk(\"hello\", 2, { overlap: true }) // [\" h\", \"he\", \"el\", \"ll\", \"lo\", \"o \"]\n * ```\n *\n * @param input - The input array or string to be chunked.\n * @param size - The size of each chunk.\n * @param [options] - Additional options for chunking.\n * @param [options.overlap] -\n * @param [options.pad] -\n *\n * @returns An array of chunks.\n *\n * @throws {RangeError} If the chunk size is invalid.\n * @throws {TypeError} If the input type is invalid.\n */\nexport function chunk<T>(input: T[] | string, size = 2, options: ChunkOptions = {}): ChunkResult<T> {\n assert(isArray(input as T[]) || isString(input), 'Argument must be an array or string.', {\n args: { input },\n type: TypeError,\n });\n\n assert(size >= 1, 'Chunk size must be at least 1.', {\n args: { size },\n type: RangeError,\n });\n\n const { overlap = false, pad = ' ' } = options;\n\n if (isString(input) && overlap) {\n const padded = pad + input + pad;\n const numChunks = padded.length - size + 1;\n return Array.from({ length: numChunks }, (_, i) => padded.slice(i, i + size)) as ChunkResult<T>;\n }\n\n return Array.from({ length: Math.ceil(input.length / size) }, (_, i) =>\n input.slice(i * size, i * size + size),\n ) as ChunkResult<T>;\n}\n"],"names":["chunk","input","size","options","assert","isArray","isString","overlap","pad","padded","numChunks","_","i"],"mappings":"+
|
|
1
|
+
{"version":3,"file":"chunk.cjs","sources":["../../src/array/chunk.ts"],"sourcesContent":["import { assert } from '../function/assert';\nimport { isArray } from '../typed/isArray';\nimport { isString } from '../typed/isString';\n\n// #region ChunkTypes\ntype ChunkOptions = {\n overlap?: boolean;\n pad?: string;\n};\n\ntype ChunkResult<T> = (T extends string ? string : T[])[];\n// #endregion ChunkTypes\n\n/**\n * Splits an array or string into chunks of a specified size.\n *\n * @example\n * ```ts\n * chunk([1, 2, 3, 4, 5], 2) // [[1, 2], [3, 4], [5]]\n * chunk(\"hello\", 2) // [\"he\", \"ll\", \"o\"]\n * chunk(\"hello\", 2, { overlap: true }) // [\" h\", \"he\", \"el\", \"ll\", \"lo\", \"o \"]\n * ```\n *\n * @param input - The input array or string to be chunked.\n * @param size - The size of each chunk.\n * @param [options] - Additional options for chunking.\n * @param [options.overlap] -\n * @param [options.pad] -\n *\n * @returns An array of chunks.\n *\n * @throws {RangeError} If the chunk size is invalid.\n * @throws {TypeError} If the input type is invalid.\n */\nexport function chunk<T>(input: T[] | string, size = 2, options: ChunkOptions = {}): ChunkResult<T> {\n assert(isArray(input as T[]) || isString(input), 'Argument must be an array or string.', {\n args: { input },\n type: TypeError,\n });\n\n assert(size >= 1, 'Chunk size must be at least 1.', {\n args: { size },\n type: RangeError,\n });\n\n const { overlap = false, pad = ' ' } = options;\n\n if (isString(input) && overlap) {\n const padded = pad + input + pad;\n const numChunks = padded.length - size + 1;\n return Array.from({ length: numChunks }, (_, i) => padded.slice(i, i + size)) as ChunkResult<T>;\n }\n\n return Array.from({ length: Math.ceil(input.length / size) }, (_, i) =>\n input.slice(i * size, i * size + size),\n ) as ChunkResult<T>;\n}\n"],"names":["chunk","input","size","options","assert","isArray","isString","overlap","pad","padded","numChunks","_","i"],"mappings":"+LAkCO,SAASA,EAASC,EAAqBC,EAAO,EAAGC,EAAwB,CAAA,EAAoB,CAClGC,EAAAA,OAAOC,EAAAA,QAAQJ,CAAY,GAAKK,EAAAA,SAASL,CAAK,EAAG,uCAAwC,CACvF,KAAM,CAAE,MAAAA,CAAA,EACR,KAAM,SAAA,CACP,EAEDG,SAAOF,GAAQ,EAAG,iCAAkC,CAClD,KAAM,CAAE,KAAAA,CAAA,EACR,KAAM,UAAA,CACP,EAED,KAAM,CAAE,QAAAK,EAAU,GAAO,IAAAC,EAAM,KAAQL,EAEvC,GAAIG,EAAAA,SAASL,CAAK,GAAKM,EAAS,CAC9B,MAAME,EAASD,EAAMP,EAAQO,EACvBE,EAAYD,EAAO,OAASP,EAAO,EACzC,OAAO,MAAM,KAAK,CAAE,OAAQQ,GAAa,CAACC,EAAGC,IAAMH,EAAO,MAAMG,EAAGA,EAAIV,CAAI,CAAC,CAC9E,CAEA,OAAO,MAAM,KAAK,CAAE,OAAQ,KAAK,KAAKD,EAAM,OAASC,CAAI,CAAA,EAAK,CAACS,EAAGC,IAChEX,EAAM,MAAMW,EAAIV,EAAMU,EAAIV,EAAOA,CAAI,CAAA,CAEzC"}
|
package/dist/array/chunk.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chunk.js","sources":["../../src/array/chunk.ts"],"sourcesContent":["import { assert } from '../function/assert';\nimport { isArray } from '../typed/isArray';\nimport { isString } from '../typed/isString';\n\ntype ChunkOptions = {\n overlap?: boolean;\n pad?: string;\n};\n\ntype ChunkResult<T> = (T extends string ? string : T[])[];\n\n/**\n * Splits an array or string into chunks of a specified size.\n *\n * @example\n * ```ts\n * chunk([1, 2, 3, 4, 5], 2) // [[1, 2], [3, 4], [5]]\n * chunk(\"hello\", 2) // [\"he\", \"ll\", \"o\"]\n * chunk(\"hello\", 2, { overlap: true }) // [\" h\", \"he\", \"el\", \"ll\", \"lo\", \"o \"]\n * ```\n *\n * @param input - The input array or string to be chunked.\n * @param size - The size of each chunk.\n * @param [options] - Additional options for chunking.\n * @param [options.overlap] -\n * @param [options.pad] -\n *\n * @returns An array of chunks.\n *\n * @throws {RangeError} If the chunk size is invalid.\n * @throws {TypeError} If the input type is invalid.\n */\nexport function chunk<T>(input: T[] | string, size = 2, options: ChunkOptions = {}): ChunkResult<T> {\n assert(isArray(input as T[]) || isString(input), 'Argument must be an array or string.', {\n args: { input },\n type: TypeError,\n });\n\n assert(size >= 1, 'Chunk size must be at least 1.', {\n args: { size },\n type: RangeError,\n });\n\n const { overlap = false, pad = ' ' } = options;\n\n if (isString(input) && overlap) {\n const padded = pad + input + pad;\n const numChunks = padded.length - size + 1;\n return Array.from({ length: numChunks }, (_, i) => padded.slice(i, i + size)) as ChunkResult<T>;\n }\n\n return Array.from({ length: Math.ceil(input.length / size) }, (_, i) =>\n input.slice(i * size, i * size + size),\n ) as ChunkResult<T>;\n}\n"],"names":["chunk","input","size","options","assert","isArray","isString","overlap","pad","padded","numChunks","_","i"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"chunk.js","sources":["../../src/array/chunk.ts"],"sourcesContent":["import { assert } from '../function/assert';\nimport { isArray } from '../typed/isArray';\nimport { isString } from '../typed/isString';\n\n// #region ChunkTypes\ntype ChunkOptions = {\n overlap?: boolean;\n pad?: string;\n};\n\ntype ChunkResult<T> = (T extends string ? string : T[])[];\n// #endregion ChunkTypes\n\n/**\n * Splits an array or string into chunks of a specified size.\n *\n * @example\n * ```ts\n * chunk([1, 2, 3, 4, 5], 2) // [[1, 2], [3, 4], [5]]\n * chunk(\"hello\", 2) // [\"he\", \"ll\", \"o\"]\n * chunk(\"hello\", 2, { overlap: true }) // [\" h\", \"he\", \"el\", \"ll\", \"lo\", \"o \"]\n * ```\n *\n * @param input - The input array or string to be chunked.\n * @param size - The size of each chunk.\n * @param [options] - Additional options for chunking.\n * @param [options.overlap] -\n * @param [options.pad] -\n *\n * @returns An array of chunks.\n *\n * @throws {RangeError} If the chunk size is invalid.\n * @throws {TypeError} If the input type is invalid.\n */\nexport function chunk<T>(input: T[] | string, size = 2, options: ChunkOptions = {}): ChunkResult<T> {\n assert(isArray(input as T[]) || isString(input), 'Argument must be an array or string.', {\n args: { input },\n type: TypeError,\n });\n\n assert(size >= 1, 'Chunk size must be at least 1.', {\n args: { size },\n type: RangeError,\n });\n\n const { overlap = false, pad = ' ' } = options;\n\n if (isString(input) && overlap) {\n const padded = pad + input + pad;\n const numChunks = padded.length - size + 1;\n return Array.from({ length: numChunks }, (_, i) => padded.slice(i, i + size)) as ChunkResult<T>;\n }\n\n return Array.from({ length: Math.ceil(input.length / size) }, (_, i) =>\n input.slice(i * size, i * size + size),\n ) as ChunkResult<T>;\n}\n"],"names":["chunk","input","size","options","assert","isArray","isString","overlap","pad","padded","numChunks","_","i"],"mappings":";;;AAkCO,SAASA,EAASC,GAAqBC,IAAO,GAAGC,IAAwB,CAAA,GAAoB;AAClG,EAAAC,EAAOC,EAAQJ,CAAY,KAAKK,EAASL,CAAK,GAAG,wCAAwC;AAAA,IACvF,MAAM,EAAE,OAAAA,EAAA;AAAA,IACR,MAAM;AAAA,EAAA,CACP,GAEDG,EAAOF,KAAQ,GAAG,kCAAkC;AAAA,IAClD,MAAM,EAAE,MAAAA,EAAA;AAAA,IACR,MAAM;AAAA,EAAA,CACP;AAED,QAAM,EAAE,SAAAK,IAAU,IAAO,KAAAC,IAAM,QAAQL;AAEvC,MAAIG,EAASL,CAAK,KAAKM,GAAS;AAC9B,UAAME,IAASD,IAAMP,IAAQO,GACvBE,IAAYD,EAAO,SAASP,IAAO;AACzC,WAAO,MAAM,KAAK,EAAE,QAAQQ,KAAa,CAACC,GAAGC,MAAMH,EAAO,MAAMG,GAAGA,IAAIV,CAAI,CAAC;AAAA,EAC9E;AAEA,SAAO,MAAM;AAAA,IAAK,EAAE,QAAQ,KAAK,KAAKD,EAAM,SAASC,CAAI,EAAA;AAAA,IAAK,CAACS,GAAGC,MAChEX,EAAM,MAAMW,IAAIV,GAAMU,IAAIV,IAAOA,CAAI;AAAA,EAAA;AAEzC;"}
|
package/dist/array/list.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const Q=require("./search.cjs");function j(S,i={}){const T=new Set,M={debounceMs:300,limit:10,searchTone:.5};let x=[...S],n=Math.max(1,i.limit??M.limit),m=i.filterFn??(()=>!0),u=i.sortFn;const y=i.searchFn??((t,e,o)=>Q.search([...t],e,o)),v=i.searchTone??M.searchTone;let c="",s=0,h=[];const d=()=>{for(const t of T)t()},g=()=>{let t=x;c&&(t=y(t,c,v)),m&&(t=t.filter(m)),t=u?[...t].sort(u):[...t];const e=Math.max(1,Math.ceil(t.length/n));s=Math.min(s,e-1),h=t},D=()=>{if(!h.length)return[];const t=s*n;return h.slice(t,t+n)},r=()=>{g(),d()};let F;const L=(t,e)=>{F&&clearTimeout(F),F=setTimeout(()=>{c=t,F=void 0,r()},e)};return g(),{batch(t){let e=n,o=m,l=u,f=c,b=x,p=s;const w=(a,E,O)=>Math.max(0,Math.min(a,Math.max(0,Math.ceil(E/O)-1)));t({goTo:a=>{p=w(a-1,h.length,e)},setData:a=>{b=[...a],p=0},setFilter:a=>{o=a},setLimit:a=>{e=Math.max(1,a)},setQuery:a=>{f=a,p=0},setSort:a=>{l=a}}),n=e,m=o,u=l,c=f,x=b,s=p,r()},get current(){return D()},goTo(t){const e=Math.max(1,Math.ceil(h.length/n));s=Math.max(0,Math.min(t-1,e-1)),d()},get meta(){const t=h.length,e=Math.max(1,Math.ceil(t/n)),o=t===0,l=Math.min(s+1,e),f=o?0:(l-1)*n+1;return{end:o?0:Math.min(l*n,t),isEmpty:o,isFirst:l<=1,isLast:l>=e,limit:n,page:l,pages:e,start:f,total:t}},next(){const t=Math.max(1,Math.ceil(h.length/n));s<t-1&&(s++,d())},prev(){s>0&&(s--,d())},reset(){n=Math.max(1,i.limit??M.limit),m=i.filterFn??(()=>!0),u=i.sortFn,c="",s=0,r()},search(t,e){c=t,s=0,e?.immediate?r():L(t,i.debounceMs??M.debounceMs)},setData(t){x=[...t],s=0,r()},setFilter(t){m=t,s=0,r()},setLimit(t){n=Math.max(1,t),s=0,r()},setSort(t){u=t,r()},subscribe(t){return T.add(t),()=>T.delete(t)}}}exports.list=j;
|
|
2
2
|
//# sourceMappingURL=list.cjs.map
|
package/dist/array/list.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list.cjs","sources":["../../src/array/list.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"list.cjs","sources":["../../src/array/list.ts"],"sourcesContent":["import type { Predicate, Sorter } from '../types';\nimport { search as defaultSearch } from './search';\n\n// #region Meta\nexport type Meta = Readonly<{\n end: number; // inclusive\n isEmpty: boolean;\n isFirst: boolean;\n isLast: boolean;\n limit: number;\n page: number; // 1-based\n pages: number;\n start: number; // 1-based\n total: number;\n}>;\n// #endregion Meta\n\n// #region List\nexport type List<T, F, S> = {\n readonly current: readonly T[];\n readonly meta: Meta;\n subscribe(listener: () => void): () => void;\n\n goTo(page: number): void;\n next(): void;\n prev(): void;\n reset(): void;\n search(query: string, opts?: { immediate?: boolean }): void;\n setData?(data: readonly T[]): void; // implemented by local\n setFilter(filter: F): void;\n setLimit(n: number): void;\n setSort(sort?: S): void;\n\n // Batch updates across properties in one recompute/refetch\n batch(\n mutator: (ctx: {\n setLimit(n: number): void;\n setFilter(f: F): void;\n setSort(s?: S): void;\n setQuery(q: string): void;\n setData?(d: readonly T[]): void; // local-only\n goTo(p: number): void; // 1-based\n }) => void,\n ): void;\n};\n// #endregion List\n\n// #region LocalConfig\ntype LocalConfig<T> = Readonly<{\n debounceMs?: number;\n filterFn?: Predicate<T>;\n limit?: number;\n searchFn?: (items: readonly T[], query: string, tone: number) => readonly T[];\n searchTone?: number;\n sortFn?: Sorter<T>;\n}>;\n// #endregion LocalConfig\n\nexport function list<T>(initialData: readonly T[], cfg: LocalConfig<T> = {}): List<T, Predicate<T>, Sorter<T>> {\n const listeners = new Set<() => void>();\n\n const DEFAULTS = { debounceMs: 300, limit: 10, searchTone: 0.5 } as const;\n\n let rawData: readonly T[] = [...initialData];\n let limit = Math.max(1, cfg.limit ?? DEFAULTS.limit);\n let filterFn: Predicate<T> = cfg.filterFn ?? (() => true);\n let sortFn: Sorter<T> | undefined = cfg.sortFn;\n const searchFn = cfg.searchFn ?? ((items: readonly T[], q: string, t: number) => defaultSearch([...items], q, t));\n const searchTone = cfg.searchTone ?? DEFAULTS.searchTone;\n\n let query = '';\n let offset = 0;\n let view: readonly T[] = [];\n\n const notify = () => {\n for (const l of listeners) {\n l();\n }\n };\n\n const recompute = () => {\n let arr = rawData;\n\n if (query) arr = searchFn(arr, query, searchTone);\n if (filterFn) arr = arr.filter(filterFn);\n arr = sortFn ? [...arr].sort(sortFn) : [...arr];\n\n const pages = Math.max(1, Math.ceil(arr.length / limit));\n offset = Math.min(offset, pages - 1);\n view = arr;\n };\n\n const slice = (): readonly T[] => {\n if (!view.length) return [];\n const start = offset * limit;\n return view.slice(start, start + limit);\n };\n\n const update = () => {\n recompute();\n notify();\n };\n\n let timer: ReturnType<typeof setTimeout> | undefined;\n const debouncedSearch = (q: string, ms: number) => {\n if (timer) clearTimeout(timer);\n timer = setTimeout(() => {\n query = q;\n timer = undefined;\n void update();\n }, ms);\n };\n\n // initial compute\n recompute();\n\n return {\n batch(mutator) {\n let nextLimit = limit;\n let nextFilter = filterFn;\n let nextSort = sortFn;\n let nextQuery = query;\n let nextData = rawData;\n let nextOffset = offset;\n\n const clamp = (i: number, total: number, lim: number) =>\n Math.max(0, Math.min(i, Math.max(0, Math.ceil(total / lim) - 1)));\n\n mutator({\n goTo: (p) => {\n nextOffset = clamp(p - 1, view.length, nextLimit);\n },\n setData: (d) => {\n nextData = [...d];\n nextOffset = 0;\n },\n setFilter: (f) => {\n nextFilter = f;\n },\n setLimit: (n) => {\n nextLimit = Math.max(1, n);\n },\n setQuery: (q) => {\n nextQuery = q;\n nextOffset = 0;\n },\n setSort: (s) => {\n nextSort = s;\n },\n });\n\n // apply once\n limit = nextLimit;\n filterFn = nextFilter;\n sortFn = nextSort;\n query = nextQuery;\n rawData = nextData;\n offset = nextOffset;\n\n update();\n },\n get current() {\n return slice();\n },\n goTo(page) {\n const pages = Math.max(1, Math.ceil(view.length / limit));\n offset = Math.max(0, Math.min(page - 1, pages - 1));\n notify();\n },\n get meta() {\n const total = view.length;\n const pages = Math.max(1, Math.ceil(total / limit));\n const isEmpty = total === 0;\n const page = Math.min(offset + 1, pages);\n const start = isEmpty ? 0 : (page - 1) * limit + 1;\n const end = isEmpty ? 0 : Math.min(page * limit, total);\n return {\n end,\n isEmpty,\n isFirst: page <= 1,\n isLast: page >= pages,\n limit,\n page,\n pages,\n start,\n total,\n };\n },\n next() {\n const pages = Math.max(1, Math.ceil(view.length / limit));\n if (offset < pages - 1) {\n offset++;\n notify();\n }\n },\n prev() {\n if (offset > 0) {\n offset--;\n notify();\n }\n },\n reset() {\n limit = Math.max(1, cfg.limit ?? DEFAULTS.limit);\n filterFn = cfg.filterFn ?? (() => true);\n sortFn = cfg.sortFn;\n query = '';\n offset = 0;\n update();\n },\n search(q, opts) {\n query = q;\n offset = 0;\n if (opts?.immediate) {\n update();\n } else {\n debouncedSearch(q, cfg.debounceMs ?? DEFAULTS.debounceMs);\n }\n },\n setData(data) {\n rawData = [...data];\n offset = 0;\n update();\n },\n setFilter(f) {\n filterFn = f;\n offset = 0;\n update();\n },\n setLimit(n) {\n limit = Math.max(1, n);\n offset = 0;\n update();\n },\n setSort(s) {\n sortFn = s;\n update();\n },\n subscribe(listener) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n };\n}\n"],"names":["list","initialData","cfg","listeners","DEFAULTS","rawData","limit","filterFn","sortFn","searchFn","items","q","t","defaultSearch","searchTone","query","offset","view","notify","l","recompute","arr","pages","slice","start","update","timer","debouncedSearch","ms","mutator","nextLimit","nextFilter","nextSort","nextQuery","nextData","nextOffset","clamp","i","total","lim","p","d","f","n","s","page","isEmpty","opts","data","listener"],"mappings":"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"}
|
package/dist/array/list.js
CHANGED
|
@@ -1,74 +1,107 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
1
|
+
import { search as A } from "./search.js";
|
|
2
|
+
function U(S, i = {}) {
|
|
3
|
+
const T = /* @__PURE__ */ new Set(), u = { debounceMs: 300, limit: 10, searchTone: 0.5 };
|
|
4
|
+
let x = [...S], a = Math.max(1, i.limit ?? u.limit), m = i.filterFn ?? (() => !0), M = i.sortFn;
|
|
5
|
+
const y = i.searchFn ?? ((t, e, o) => A([...t], e, o)), D = i.searchTone ?? u.searchTone;
|
|
6
|
+
let c = "", n = 0, h = [];
|
|
7
|
+
const d = () => {
|
|
8
|
+
for (const t of T)
|
|
9
|
+
t();
|
|
10
|
+
}, g = () => {
|
|
11
|
+
let t = x;
|
|
12
|
+
c && (t = y(t, c, D)), m && (t = t.filter(m)), t = M ? [...t].sort(M) : [...t];
|
|
13
|
+
const e = Math.max(1, Math.ceil(t.length / a));
|
|
14
|
+
n = Math.min(n, e - 1), h = t;
|
|
15
|
+
}, L = () => {
|
|
16
|
+
if (!h.length) return [];
|
|
17
|
+
const t = n * a;
|
|
18
|
+
return h.slice(t, t + a);
|
|
19
|
+
}, r = () => {
|
|
20
|
+
g(), d();
|
|
18
21
|
};
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
let F;
|
|
23
|
+
const v = (t, e) => {
|
|
24
|
+
F && clearTimeout(F), F = setTimeout(() => {
|
|
25
|
+
c = t, F = void 0, r();
|
|
26
|
+
}, e);
|
|
27
|
+
};
|
|
28
|
+
return g(), {
|
|
29
|
+
batch(t) {
|
|
30
|
+
let e = a, o = m, l = M, f = c, b = x, p = n;
|
|
31
|
+
const w = (s, E, Q) => Math.max(0, Math.min(s, Math.max(0, Math.ceil(E / Q) - 1)));
|
|
32
|
+
t({
|
|
33
|
+
goTo: (s) => {
|
|
34
|
+
p = w(s - 1, h.length, e);
|
|
35
|
+
},
|
|
36
|
+
setData: (s) => {
|
|
37
|
+
b = [...s], p = 0;
|
|
38
|
+
},
|
|
39
|
+
setFilter: (s) => {
|
|
40
|
+
o = s;
|
|
41
|
+
},
|
|
42
|
+
setLimit: (s) => {
|
|
43
|
+
e = Math.max(1, s);
|
|
44
|
+
},
|
|
45
|
+
setQuery: (s) => {
|
|
46
|
+
f = s, p = 0;
|
|
47
|
+
},
|
|
48
|
+
setSort: (s) => {
|
|
49
|
+
l = s;
|
|
50
|
+
}
|
|
51
|
+
}), a = e, m = o, M = l, c = f, x = b, n = p, r();
|
|
25
52
|
},
|
|
26
|
-
|
|
27
|
-
return
|
|
53
|
+
get current() {
|
|
54
|
+
return L();
|
|
28
55
|
},
|
|
29
|
-
|
|
30
|
-
|
|
56
|
+
goTo(t) {
|
|
57
|
+
const e = Math.max(1, Math.ceil(h.length / a));
|
|
58
|
+
n = Math.max(0, Math.min(t - 1, e - 1)), d();
|
|
31
59
|
},
|
|
32
60
|
get meta() {
|
|
61
|
+
const t = h.length, e = Math.max(1, Math.ceil(t / a)), o = t === 0, l = Math.min(n + 1, e), f = o ? 0 : (l - 1) * a + 1;
|
|
33
62
|
return {
|
|
34
|
-
end: Math.min(
|
|
35
|
-
isEmpty:
|
|
36
|
-
isFirst:
|
|
37
|
-
isLast:
|
|
38
|
-
limit:
|
|
39
|
-
page:
|
|
40
|
-
pages:
|
|
41
|
-
start:
|
|
42
|
-
total:
|
|
63
|
+
end: o ? 0 : Math.min(l * a, t),
|
|
64
|
+
isEmpty: o,
|
|
65
|
+
isFirst: l <= 1,
|
|
66
|
+
isLast: l >= e,
|
|
67
|
+
limit: a,
|
|
68
|
+
page: l,
|
|
69
|
+
pages: e,
|
|
70
|
+
start: f,
|
|
71
|
+
total: t
|
|
43
72
|
};
|
|
44
73
|
},
|
|
45
74
|
next() {
|
|
46
|
-
t
|
|
47
|
-
|
|
48
|
-
set page(e) {
|
|
49
|
-
const g = e - 1, p = g < 0 ? n.length + g : g;
|
|
50
|
-
t = Math.max(0, Math.min(p, n.length - 1));
|
|
51
|
-
},
|
|
52
|
-
get pages() {
|
|
53
|
-
return n;
|
|
75
|
+
const t = Math.max(1, Math.ceil(h.length / a));
|
|
76
|
+
n < t - 1 && (n++, d());
|
|
54
77
|
},
|
|
55
78
|
prev() {
|
|
56
|
-
|
|
79
|
+
n > 0 && (n--, d());
|
|
57
80
|
},
|
|
58
81
|
reset() {
|
|
59
|
-
|
|
82
|
+
a = Math.max(1, i.limit ?? u.limit), m = i.filterFn ?? (() => !0), M = i.sortFn, c = "", n = 0, r();
|
|
83
|
+
},
|
|
84
|
+
search(t, e) {
|
|
85
|
+
c = t, n = 0, e?.immediate ? r() : v(t, i.debounceMs ?? u.debounceMs);
|
|
86
|
+
},
|
|
87
|
+
setData(t) {
|
|
88
|
+
x = [...t], n = 0, r();
|
|
89
|
+
},
|
|
90
|
+
setFilter(t) {
|
|
91
|
+
m = t, n = 0, r();
|
|
92
|
+
},
|
|
93
|
+
setLimit(t) {
|
|
94
|
+
a = Math.max(1, t), n = 0, r();
|
|
60
95
|
},
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
return h = e, i();
|
|
96
|
+
setSort(t) {
|
|
97
|
+
M = t, r();
|
|
64
98
|
},
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
yield e;
|
|
99
|
+
subscribe(t) {
|
|
100
|
+
return T.add(t), () => T.delete(t);
|
|
68
101
|
}
|
|
69
102
|
};
|
|
70
103
|
}
|
|
71
104
|
export {
|
|
72
|
-
|
|
105
|
+
U as list
|
|
73
106
|
};
|
|
74
107
|
//# sourceMappingURL=list.js.map
|
package/dist/array/list.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list.js","sources":["../../src/array/list.ts"],"sourcesContent":["import { debounce } from '../function/debounce';\nimport type { Predicate } from '../types';\nimport { chunk } from './chunk';\nimport { search } from './search';\n\ntype Config<T> = {\n filterFn?: Predicate<T>;\n limit?: number;\n sortFn?: (a: T, b: T) => number;\n};\n\nconst DEFAULT_LIMIT = 10;\nconst DEFAULT_SEARCH_TONE = 0.5;\nconst MIN_LIMIT = 1;\n\nfunction getPages<T>(data: T[], size: number, sortFn: Config<T>['sortFn']): T[][] {\n const sortedData = sortFn ? [...data].sort(sortFn) : [...data];\n return chunk<T>(sortedData, size) as T[][];\n}\n\nfunction getData<T>(data: T[], currentFilterFn: Predicate<T>, query = ''): T[] {\n const searchResults = query ? search(data, query, DEFAULT_SEARCH_TONE) : data;\n return searchResults.filter(currentFilterFn) as T[];\n}\n\nexport function list<T>(initialData: T[], config: Config<T> = {}) {\n let { limit = DEFAULT_LIMIT, filterFn = () => true, sortFn } = config;\n let rawData = [...initialData];\n let offset = 0;\n let query = '';\n let data = getData(rawData, filterFn);\n let pages = getPages(data, limit, sortFn);\n\n const update = () => {\n data = getData(rawData, filterFn, query);\n pages = getPages(data, limit, sortFn);\n // Ensure offset is valid for the new page count\n // If pages is empty, offset should be 0. Max index is pages.length - 1\n const maxOffset = Math.max(0, pages.length - 1);\n offset = Math.max(0, Math.min(offset, maxOffset));\n return pages[offset] ?? [];\n };\n\n return {\n get current() {\n return pages[offset] ?? [];\n },\n set data(newData: T[]) {\n rawData = [...newData];\n offset = 0;\n update();\n },\n filter(predicate: (item: T) => boolean) {\n filterFn = predicate;\n offset = 0;\n return update();\n },\n set limit(newLimit: number) {\n limit = Math.max(MIN_LIMIT, newLimit);\n update();\n },\n get meta() {\n return {\n end: Math.min(limit * (offset + 1), data.length),\n isEmpty: data.length === 0,\n isFirst: offset === 0,\n isLast: offset === pages.length - 1,\n limit: limit,\n page: offset + 1,\n pages: Math.ceil(data.length / limit),\n start: Math.max(0, limit * offset + 1),\n total: data.length,\n };\n },\n next() {\n if (offset < pages.length - 1) {\n offset++;\n }\n },\n set page(page: number) {\n const index = page - 1;\n const value = index < 0 ? pages.length + index : index;\n offset = Math.max(0, Math.min(value, pages.length - 1));\n },\n get pages() {\n return pages;\n },\n prev() {\n if (offset > 0) {\n offset--;\n }\n },\n reset() {\n offset = 0;\n query = '';\n filterFn = config.filterFn ?? (() => true);\n return update();\n },\n search: debounce((str: string) => {\n query = str;\n return update();\n }),\n sort(fn?: (a: T, b: T) => number) {\n sortFn = fn;\n return update();\n },\n *[Symbol.iterator]() {\n for (const page of pages) {\n yield page;\n }\n },\n };\n}\n"],"names":["DEFAULT_LIMIT","DEFAULT_SEARCH_TONE","MIN_LIMIT","getPages","data","size","sortFn","sortedData","chunk","getData","currentFilterFn","query","search","list","initialData","config","limit","filterFn","rawData","offset","pages","update","maxOffset","newData","predicate","newLimit","page","index","value","debounce","str","fn"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"list.js","sources":["../../src/array/list.ts"],"sourcesContent":["import type { Predicate, Sorter } from '../types';\nimport { search as defaultSearch } from './search';\n\n// #region Meta\nexport type Meta = Readonly<{\n end: number; // inclusive\n isEmpty: boolean;\n isFirst: boolean;\n isLast: boolean;\n limit: number;\n page: number; // 1-based\n pages: number;\n start: number; // 1-based\n total: number;\n}>;\n// #endregion Meta\n\n// #region List\nexport type List<T, F, S> = {\n readonly current: readonly T[];\n readonly meta: Meta;\n subscribe(listener: () => void): () => void;\n\n goTo(page: number): void;\n next(): void;\n prev(): void;\n reset(): void;\n search(query: string, opts?: { immediate?: boolean }): void;\n setData?(data: readonly T[]): void; // implemented by local\n setFilter(filter: F): void;\n setLimit(n: number): void;\n setSort(sort?: S): void;\n\n // Batch updates across properties in one recompute/refetch\n batch(\n mutator: (ctx: {\n setLimit(n: number): void;\n setFilter(f: F): void;\n setSort(s?: S): void;\n setQuery(q: string): void;\n setData?(d: readonly T[]): void; // local-only\n goTo(p: number): void; // 1-based\n }) => void,\n ): void;\n};\n// #endregion List\n\n// #region LocalConfig\ntype LocalConfig<T> = Readonly<{\n debounceMs?: number;\n filterFn?: Predicate<T>;\n limit?: number;\n searchFn?: (items: readonly T[], query: string, tone: number) => readonly T[];\n searchTone?: number;\n sortFn?: Sorter<T>;\n}>;\n// #endregion LocalConfig\n\nexport function list<T>(initialData: readonly T[], cfg: LocalConfig<T> = {}): List<T, Predicate<T>, Sorter<T>> {\n const listeners = new Set<() => void>();\n\n const DEFAULTS = { debounceMs: 300, limit: 10, searchTone: 0.5 } as const;\n\n let rawData: readonly T[] = [...initialData];\n let limit = Math.max(1, cfg.limit ?? DEFAULTS.limit);\n let filterFn: Predicate<T> = cfg.filterFn ?? (() => true);\n let sortFn: Sorter<T> | undefined = cfg.sortFn;\n const searchFn = cfg.searchFn ?? ((items: readonly T[], q: string, t: number) => defaultSearch([...items], q, t));\n const searchTone = cfg.searchTone ?? DEFAULTS.searchTone;\n\n let query = '';\n let offset = 0;\n let view: readonly T[] = [];\n\n const notify = () => {\n for (const l of listeners) {\n l();\n }\n };\n\n const recompute = () => {\n let arr = rawData;\n\n if (query) arr = searchFn(arr, query, searchTone);\n if (filterFn) arr = arr.filter(filterFn);\n arr = sortFn ? [...arr].sort(sortFn) : [...arr];\n\n const pages = Math.max(1, Math.ceil(arr.length / limit));\n offset = Math.min(offset, pages - 1);\n view = arr;\n };\n\n const slice = (): readonly T[] => {\n if (!view.length) return [];\n const start = offset * limit;\n return view.slice(start, start + limit);\n };\n\n const update = () => {\n recompute();\n notify();\n };\n\n let timer: ReturnType<typeof setTimeout> | undefined;\n const debouncedSearch = (q: string, ms: number) => {\n if (timer) clearTimeout(timer);\n timer = setTimeout(() => {\n query = q;\n timer = undefined;\n void update();\n }, ms);\n };\n\n // initial compute\n recompute();\n\n return {\n batch(mutator) {\n let nextLimit = limit;\n let nextFilter = filterFn;\n let nextSort = sortFn;\n let nextQuery = query;\n let nextData = rawData;\n let nextOffset = offset;\n\n const clamp = (i: number, total: number, lim: number) =>\n Math.max(0, Math.min(i, Math.max(0, Math.ceil(total / lim) - 1)));\n\n mutator({\n goTo: (p) => {\n nextOffset = clamp(p - 1, view.length, nextLimit);\n },\n setData: (d) => {\n nextData = [...d];\n nextOffset = 0;\n },\n setFilter: (f) => {\n nextFilter = f;\n },\n setLimit: (n) => {\n nextLimit = Math.max(1, n);\n },\n setQuery: (q) => {\n nextQuery = q;\n nextOffset = 0;\n },\n setSort: (s) => {\n nextSort = s;\n },\n });\n\n // apply once\n limit = nextLimit;\n filterFn = nextFilter;\n sortFn = nextSort;\n query = nextQuery;\n rawData = nextData;\n offset = nextOffset;\n\n update();\n },\n get current() {\n return slice();\n },\n goTo(page) {\n const pages = Math.max(1, Math.ceil(view.length / limit));\n offset = Math.max(0, Math.min(page - 1, pages - 1));\n notify();\n },\n get meta() {\n const total = view.length;\n const pages = Math.max(1, Math.ceil(total / limit));\n const isEmpty = total === 0;\n const page = Math.min(offset + 1, pages);\n const start = isEmpty ? 0 : (page - 1) * limit + 1;\n const end = isEmpty ? 0 : Math.min(page * limit, total);\n return {\n end,\n isEmpty,\n isFirst: page <= 1,\n isLast: page >= pages,\n limit,\n page,\n pages,\n start,\n total,\n };\n },\n next() {\n const pages = Math.max(1, Math.ceil(view.length / limit));\n if (offset < pages - 1) {\n offset++;\n notify();\n }\n },\n prev() {\n if (offset > 0) {\n offset--;\n notify();\n }\n },\n reset() {\n limit = Math.max(1, cfg.limit ?? DEFAULTS.limit);\n filterFn = cfg.filterFn ?? (() => true);\n sortFn = cfg.sortFn;\n query = '';\n offset = 0;\n update();\n },\n search(q, opts) {\n query = q;\n offset = 0;\n if (opts?.immediate) {\n update();\n } else {\n debouncedSearch(q, cfg.debounceMs ?? DEFAULTS.debounceMs);\n }\n },\n setData(data) {\n rawData = [...data];\n offset = 0;\n update();\n },\n setFilter(f) {\n filterFn = f;\n offset = 0;\n update();\n },\n setLimit(n) {\n limit = Math.max(1, n);\n offset = 0;\n update();\n },\n setSort(s) {\n sortFn = s;\n update();\n },\n subscribe(listener) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n };\n}\n"],"names":["list","initialData","cfg","listeners","DEFAULTS","rawData","limit","filterFn","sortFn","searchFn","items","q","t","defaultSearch","searchTone","query","offset","view","notify","l","recompute","arr","pages","slice","start","update","timer","debouncedSearch","ms","mutator","nextLimit","nextFilter","nextSort","nextQuery","nextData","nextOffset","clamp","i","total","lim","p","d","f","n","page","isEmpty","opts","data","s","listener"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function Q(r){const y=new Set,b=Math.max(1,r.limit??10),O=r.debounceMs??300;let a=1,n=b,h="",u=r.initialFilter,d=r.initialSort,M=[],c=0,f=!1,x=null;const m=new Map,w=new Map,v=t=>JSON.stringify(t),F=()=>({filter:u,limit:n,page:a,search:h||void 0,sort:d}),p=t=>{M=t.items,c=t.total??0;const e=Math.max(1,Math.ceil(c/n));a=Math.min(Math.max(1,a),e)},L=()=>{for(const t of y)t()},P=async t=>{const e=v(t);if(m.has(e)){p(m.get(e));return}if(w.has(e)){await w.get(e),p(m.get(e));return}f=!0,x=null,L();const s=r.fetch(t).then(l=>{m.set(e,l),p(l)}).catch(l=>{x=l?.message??"Request failed",M=[],c=0}).finally(()=>{w.delete(e),f=!1,L()});w.set(e,s),await s},i=async()=>{await P(F())};let g;const k=()=>{g&&clearTimeout(g),g=setTimeout(()=>{g=void 0,i()},O)};return{async batch(t){let e=a,s=n,l=h,S=u,T=d;t({goTo:o=>{e=Math.max(1,o|0)},setFilter:o=>{S=o,e=1},setLimit:o=>{s=Math.max(1,o),e=1},setQuery:o=>{l=o,e=1},setSort:o=>{T=o,e=1}}),a=e,n=s,h=l,u=S,d=T,await i()},get current(){return M},async goTo(t){a=Math.max(1,t|0),await i()},invalidate(){m.clear()},get meta(){const t=c===0,e=Math.max(1,Math.ceil(c/n)),s=Math.min(a,e),l=t?0:(s-1)*n+1;return{end:t?0:Math.min(s*n,c),error:x,isEmpty:t,isFirst:s<=1,isLast:s>=e,limit:n,loading:f,page:s,pages:e,start:l,total:c}},async next(){a+=1,await i()},async prev(){a=Math.max(1,a-1),await i()},async refresh(){m.delete(v(F())),await i()},async reset(){a=1,n=b,h="",u=r.initialFilter,d=r.initialSort,m.clear(),await i()},async search(t,e){h=t,a=1,e?.immediate?await i():k()},async setFilter(t){u=t,a=1,await i()},async setLimit(t){n=Math.max(1,t),a=1,await i()},async setSort(t){d=t,a=1,await i()},subscribe(t){return y.add(t),y.size===1&&M.length===0&&!f&&i(),()=>y.delete(t)}}}exports.remoteList=Q;
|
|
2
|
+
//# sourceMappingURL=remoteList.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remoteList.cjs","sources":["../../src/array/remoteList.ts"],"sourcesContent":["// #region RemoteMeta\nexport type RemoteMeta = Readonly<{\n end: number; // inclusive\n error: string | null;\n isEmpty: boolean;\n isFirst: boolean;\n isLast: boolean;\n limit: number;\n loading: boolean;\n page: number; // 1-based\n pages: number;\n start: number; // 1-based\n total: number;\n}>;\n// #endregion RemoteMeta\n\n// #region RemoteList\nexport type RemoteList<T, F, S> = {\n readonly current: readonly T[];\n readonly meta: RemoteMeta;\n subscribe(listener: () => void): () => void;\n\n goTo(page: number): Promise<void>;\n invalidate?(): void;\n next(): Promise<void>;\n prev(): Promise<void>;\n refresh(): Promise<void>;\n reset(): Promise<void>;\n search(query: string, opts?: { immediate?: boolean }): Promise<void>;\n setFilter(filter: F): Promise<void>;\n setLimit(n: number): Promise<void>;\n setSort(sort?: S): Promise<void>;\n\n // Batch updates across properties in one recompute/refetch\n batch(\n mutator: (ctx: {\n setLimit(n: number): void;\n setFilter(f: F): void;\n setSort(s?: S): void;\n setQuery(q: string): void;\n setData?(d: readonly T[]): void; // local-only\n goTo(p: number): void; // 1-based\n }) => void,\n ): Promise<void>;\n};\n// #endregion RemoteList\n\n// #region RemoteConfig\ntype RemoteQuery<F, S> = Readonly<{\n filter?: F;\n limit: number;\n page: number; // 1-based\n search?: string;\n sort?: S;\n}>;\n\ntype RemoteResult<T> = Readonly<{ items: readonly T[]; total: number }>;\n\ntype RemoteConfig<T, F, S> = Readonly<{\n debounceMs?: number;\n fetch: (q: RemoteQuery<F, S>) => Promise<RemoteResult<T>>;\n initialFilter?: F;\n initialSort?: S;\n limit?: number;\n}>;\n// #endregion RemoteConfig\n\nexport function remoteList<T, F = Record<string, unknown>, S = { key?: string; dir?: 'asc' | 'desc' }>(\n cfg: RemoteConfig<T, F, S>,\n): RemoteList<T, F, S> {\n const listeners = new Set<() => void>();\n\n const limitDefault = Math.max(1, cfg.limit ?? 10);\n const debounceMs = cfg.debounceMs ?? 300;\n\n let page = 1;\n let limit = limitDefault;\n let search = '';\n let filter = cfg.initialFilter as F | undefined;\n let sort = cfg.initialSort as S | undefined;\n\n let items: readonly T[] = [];\n let total = 0;\n let loading = false;\n let error: string | null = null;\n\n const cache = new Map<string, RemoteResult<T>>();\n const inflight = new Map<string, Promise<void>>();\n\n const keyOf = (q: RemoteQuery<F, S>) => JSON.stringify(q);\n const queryOf = (): RemoteQuery<F, S> => ({\n filter,\n limit,\n page,\n search: search || undefined,\n sort,\n });\n\n const assign = (res: RemoteResult<T>) => {\n items = res.items;\n total = res.total ?? 0;\n const pages = Math.max(1, Math.ceil(total / limit));\n page = Math.min(Math.max(1, page), pages);\n };\n\n const notify = () => {\n for (const l of listeners) {\n l();\n }\n };\n\n const fetchQuery = async (q: RemoteQuery<F, S>) => {\n const k = keyOf(q);\n if (cache.has(k)) {\n assign(cache.get(k)!);\n return;\n }\n if (inflight.has(k)) {\n await inflight.get(k);\n assign(cache.get(k)!);\n return;\n }\n loading = true;\n error = null;\n notify();\n const p = cfg\n .fetch(q)\n .then((res) => {\n cache.set(k, res);\n assign(res);\n })\n .catch((e) => {\n error = e?.message ?? 'Request failed';\n items = [];\n total = 0;\n })\n .finally(() => {\n inflight.delete(k);\n loading = false;\n notify();\n });\n inflight.set(k, p);\n await p;\n };\n\n const update = async () => {\n await fetchQuery(queryOf());\n };\n\n // debounced search\n let timer: ReturnType<typeof setTimeout> | undefined;\n const debounced = () => {\n if (timer) clearTimeout(timer);\n timer = setTimeout(() => {\n timer = undefined;\n void update();\n }, debounceMs);\n };\n\n // initial fetch is on the first subscriber or call to refresh; up to you\n return {\n async batch(mutator) {\n // Stage local copies\n let nextPage = page;\n let nextLimit = limit;\n let nextSearch = search;\n let nextFilter = filter as F | undefined;\n let nextSort = sort as S | undefined;\n\n mutator({\n goTo: (p: number) => {\n nextPage = Math.max(1, p | 0);\n },\n setFilter: (f: F) => {\n nextFilter = f;\n nextPage = 1;\n },\n setLimit: (n) => {\n nextLimit = Math.max(1, n);\n nextPage = 1;\n },\n setQuery: (q: string) => {\n nextSearch = q;\n nextPage = 1;\n },\n setSort: (s?: S) => {\n nextSort = s;\n nextPage = 1;\n },\n });\n\n // Apply and update at once\n page = nextPage;\n limit = nextLimit;\n search = nextSearch;\n filter = nextFilter;\n sort = nextSort;\n\n await update();\n },\n get current() {\n return items;\n },\n async goTo(p) {\n page = Math.max(1, p | 0);\n await update();\n },\n invalidate() {\n cache.clear();\n },\n get meta() {\n const isEmpty = total === 0;\n const pages = Math.max(1, Math.ceil(total / limit));\n const safePage = Math.min(page, pages);\n const start = isEmpty ? 0 : (safePage - 1) * limit + 1;\n const end = isEmpty ? 0 : Math.min(safePage * limit, total);\n return {\n end,\n error,\n isEmpty,\n isFirst: safePage <= 1,\n isLast: safePage >= pages,\n limit,\n loading,\n page: safePage,\n pages,\n start,\n total,\n };\n },\n async next() {\n page += 1;\n await update();\n },\n async prev() {\n page = Math.max(1, page - 1);\n await update();\n },\n async refresh() {\n cache.delete(keyOf(queryOf()));\n await update();\n },\n async reset() {\n page = 1;\n limit = limitDefault;\n search = '';\n filter = cfg.initialFilter as F | undefined;\n sort = cfg.initialSort as S | undefined;\n cache.clear();\n await update();\n },\n async search(q, opts) {\n search = q;\n page = 1;\n if (opts?.immediate) await update();\n else debounced();\n },\n async setFilter(f) {\n filter = f;\n page = 1;\n await update();\n },\n async setLimit(n) {\n limit = Math.max(1, n);\n page = 1;\n await update();\n },\n async setSort(s) {\n sort = s;\n page = 1;\n await update();\n },\n subscribe(listener) {\n listeners.add(listener);\n // optional: trigger an initial load on the first subscription\n if (listeners.size === 1 && items.length === 0 && !loading) {\n void update();\n }\n return () => listeners.delete(listener);\n },\n };\n}\n"],"names":["remoteList","cfg","listeners","limitDefault","debounceMs","page","limit","search","filter","sort","items","total","loading","error","cache","inflight","keyOf","q","queryOf","assign","res","pages","notify","l","fetchQuery","k","p","e","update","timer","debounced","mutator","nextPage","nextLimit","nextSearch","nextFilter","nextSort","f","n","s","isEmpty","safePage","start","opts","listener"],"mappings":"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"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
function Q(r) {
|
|
2
|
+
const y = /* @__PURE__ */ new Set(), b = Math.max(1, r.limit ?? 10), O = r.debounceMs ?? 300;
|
|
3
|
+
let a = 1, n = b, m = "", u = r.initialFilter, d = r.initialSort, M = [], c = 0, f = !1, g = null;
|
|
4
|
+
const h = /* @__PURE__ */ new Map(), w = /* @__PURE__ */ new Map(), v = (t) => JSON.stringify(t), F = () => ({
|
|
5
|
+
filter: u,
|
|
6
|
+
limit: n,
|
|
7
|
+
page: a,
|
|
8
|
+
search: m || void 0,
|
|
9
|
+
sort: d
|
|
10
|
+
}), p = (t) => {
|
|
11
|
+
M = t.items, c = t.total ?? 0;
|
|
12
|
+
const e = Math.max(1, Math.ceil(c / n));
|
|
13
|
+
a = Math.min(Math.max(1, a), e);
|
|
14
|
+
}, L = () => {
|
|
15
|
+
for (const t of y)
|
|
16
|
+
t();
|
|
17
|
+
}, k = async (t) => {
|
|
18
|
+
const e = v(t);
|
|
19
|
+
if (h.has(e)) {
|
|
20
|
+
p(h.get(e));
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (w.has(e)) {
|
|
24
|
+
await w.get(e), p(h.get(e));
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
f = !0, g = null, L();
|
|
28
|
+
const s = r.fetch(t).then((l) => {
|
|
29
|
+
h.set(e, l), p(l);
|
|
30
|
+
}).catch((l) => {
|
|
31
|
+
g = l?.message ?? "Request failed", M = [], c = 0;
|
|
32
|
+
}).finally(() => {
|
|
33
|
+
w.delete(e), f = !1, L();
|
|
34
|
+
});
|
|
35
|
+
w.set(e, s), await s;
|
|
36
|
+
}, i = async () => {
|
|
37
|
+
await k(F());
|
|
38
|
+
};
|
|
39
|
+
let x;
|
|
40
|
+
const P = () => {
|
|
41
|
+
x && clearTimeout(x), x = setTimeout(() => {
|
|
42
|
+
x = void 0, i();
|
|
43
|
+
}, O);
|
|
44
|
+
};
|
|
45
|
+
return {
|
|
46
|
+
async batch(t) {
|
|
47
|
+
let e = a, s = n, l = m, S = u, T = d;
|
|
48
|
+
t({
|
|
49
|
+
goTo: (o) => {
|
|
50
|
+
e = Math.max(1, o | 0);
|
|
51
|
+
},
|
|
52
|
+
setFilter: (o) => {
|
|
53
|
+
S = o, e = 1;
|
|
54
|
+
},
|
|
55
|
+
setLimit: (o) => {
|
|
56
|
+
s = Math.max(1, o), e = 1;
|
|
57
|
+
},
|
|
58
|
+
setQuery: (o) => {
|
|
59
|
+
l = o, e = 1;
|
|
60
|
+
},
|
|
61
|
+
setSort: (o) => {
|
|
62
|
+
T = o, e = 1;
|
|
63
|
+
}
|
|
64
|
+
}), a = e, n = s, m = l, u = S, d = T, await i();
|
|
65
|
+
},
|
|
66
|
+
get current() {
|
|
67
|
+
return M;
|
|
68
|
+
},
|
|
69
|
+
async goTo(t) {
|
|
70
|
+
a = Math.max(1, t | 0), await i();
|
|
71
|
+
},
|
|
72
|
+
invalidate() {
|
|
73
|
+
h.clear();
|
|
74
|
+
},
|
|
75
|
+
get meta() {
|
|
76
|
+
const t = c === 0, e = Math.max(1, Math.ceil(c / n)), s = Math.min(a, e), l = t ? 0 : (s - 1) * n + 1;
|
|
77
|
+
return {
|
|
78
|
+
end: t ? 0 : Math.min(s * n, c),
|
|
79
|
+
error: g,
|
|
80
|
+
isEmpty: t,
|
|
81
|
+
isFirst: s <= 1,
|
|
82
|
+
isLast: s >= e,
|
|
83
|
+
limit: n,
|
|
84
|
+
loading: f,
|
|
85
|
+
page: s,
|
|
86
|
+
pages: e,
|
|
87
|
+
start: l,
|
|
88
|
+
total: c
|
|
89
|
+
};
|
|
90
|
+
},
|
|
91
|
+
async next() {
|
|
92
|
+
a += 1, await i();
|
|
93
|
+
},
|
|
94
|
+
async prev() {
|
|
95
|
+
a = Math.max(1, a - 1), await i();
|
|
96
|
+
},
|
|
97
|
+
async refresh() {
|
|
98
|
+
h.delete(v(F())), await i();
|
|
99
|
+
},
|
|
100
|
+
async reset() {
|
|
101
|
+
a = 1, n = b, m = "", u = r.initialFilter, d = r.initialSort, h.clear(), await i();
|
|
102
|
+
},
|
|
103
|
+
async search(t, e) {
|
|
104
|
+
m = t, a = 1, e?.immediate ? await i() : P();
|
|
105
|
+
},
|
|
106
|
+
async setFilter(t) {
|
|
107
|
+
u = t, a = 1, await i();
|
|
108
|
+
},
|
|
109
|
+
async setLimit(t) {
|
|
110
|
+
n = Math.max(1, t), a = 1, await i();
|
|
111
|
+
},
|
|
112
|
+
async setSort(t) {
|
|
113
|
+
d = t, a = 1, await i();
|
|
114
|
+
},
|
|
115
|
+
subscribe(t) {
|
|
116
|
+
return y.add(t), y.size === 1 && M.length === 0 && !f && i(), () => y.delete(t);
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
export {
|
|
121
|
+
Q as remoteList
|
|
122
|
+
};
|
|
123
|
+
//# sourceMappingURL=remoteList.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remoteList.js","sources":["../../src/array/remoteList.ts"],"sourcesContent":["// #region RemoteMeta\nexport type RemoteMeta = Readonly<{\n end: number; // inclusive\n error: string | null;\n isEmpty: boolean;\n isFirst: boolean;\n isLast: boolean;\n limit: number;\n loading: boolean;\n page: number; // 1-based\n pages: number;\n start: number; // 1-based\n total: number;\n}>;\n// #endregion RemoteMeta\n\n// #region RemoteList\nexport type RemoteList<T, F, S> = {\n readonly current: readonly T[];\n readonly meta: RemoteMeta;\n subscribe(listener: () => void): () => void;\n\n goTo(page: number): Promise<void>;\n invalidate?(): void;\n next(): Promise<void>;\n prev(): Promise<void>;\n refresh(): Promise<void>;\n reset(): Promise<void>;\n search(query: string, opts?: { immediate?: boolean }): Promise<void>;\n setFilter(filter: F): Promise<void>;\n setLimit(n: number): Promise<void>;\n setSort(sort?: S): Promise<void>;\n\n // Batch updates across properties in one recompute/refetch\n batch(\n mutator: (ctx: {\n setLimit(n: number): void;\n setFilter(f: F): void;\n setSort(s?: S): void;\n setQuery(q: string): void;\n setData?(d: readonly T[]): void; // local-only\n goTo(p: number): void; // 1-based\n }) => void,\n ): Promise<void>;\n};\n// #endregion RemoteList\n\n// #region RemoteConfig\ntype RemoteQuery<F, S> = Readonly<{\n filter?: F;\n limit: number;\n page: number; // 1-based\n search?: string;\n sort?: S;\n}>;\n\ntype RemoteResult<T> = Readonly<{ items: readonly T[]; total: number }>;\n\ntype RemoteConfig<T, F, S> = Readonly<{\n debounceMs?: number;\n fetch: (q: RemoteQuery<F, S>) => Promise<RemoteResult<T>>;\n initialFilter?: F;\n initialSort?: S;\n limit?: number;\n}>;\n// #endregion RemoteConfig\n\nexport function remoteList<T, F = Record<string, unknown>, S = { key?: string; dir?: 'asc' | 'desc' }>(\n cfg: RemoteConfig<T, F, S>,\n): RemoteList<T, F, S> {\n const listeners = new Set<() => void>();\n\n const limitDefault = Math.max(1, cfg.limit ?? 10);\n const debounceMs = cfg.debounceMs ?? 300;\n\n let page = 1;\n let limit = limitDefault;\n let search = '';\n let filter = cfg.initialFilter as F | undefined;\n let sort = cfg.initialSort as S | undefined;\n\n let items: readonly T[] = [];\n let total = 0;\n let loading = false;\n let error: string | null = null;\n\n const cache = new Map<string, RemoteResult<T>>();\n const inflight = new Map<string, Promise<void>>();\n\n const keyOf = (q: RemoteQuery<F, S>) => JSON.stringify(q);\n const queryOf = (): RemoteQuery<F, S> => ({\n filter,\n limit,\n page,\n search: search || undefined,\n sort,\n });\n\n const assign = (res: RemoteResult<T>) => {\n items = res.items;\n total = res.total ?? 0;\n const pages = Math.max(1, Math.ceil(total / limit));\n page = Math.min(Math.max(1, page), pages);\n };\n\n const notify = () => {\n for (const l of listeners) {\n l();\n }\n };\n\n const fetchQuery = async (q: RemoteQuery<F, S>) => {\n const k = keyOf(q);\n if (cache.has(k)) {\n assign(cache.get(k)!);\n return;\n }\n if (inflight.has(k)) {\n await inflight.get(k);\n assign(cache.get(k)!);\n return;\n }\n loading = true;\n error = null;\n notify();\n const p = cfg\n .fetch(q)\n .then((res) => {\n cache.set(k, res);\n assign(res);\n })\n .catch((e) => {\n error = e?.message ?? 'Request failed';\n items = [];\n total = 0;\n })\n .finally(() => {\n inflight.delete(k);\n loading = false;\n notify();\n });\n inflight.set(k, p);\n await p;\n };\n\n const update = async () => {\n await fetchQuery(queryOf());\n };\n\n // debounced search\n let timer: ReturnType<typeof setTimeout> | undefined;\n const debounced = () => {\n if (timer) clearTimeout(timer);\n timer = setTimeout(() => {\n timer = undefined;\n void update();\n }, debounceMs);\n };\n\n // initial fetch is on the first subscriber or call to refresh; up to you\n return {\n async batch(mutator) {\n // Stage local copies\n let nextPage = page;\n let nextLimit = limit;\n let nextSearch = search;\n let nextFilter = filter as F | undefined;\n let nextSort = sort as S | undefined;\n\n mutator({\n goTo: (p: number) => {\n nextPage = Math.max(1, p | 0);\n },\n setFilter: (f: F) => {\n nextFilter = f;\n nextPage = 1;\n },\n setLimit: (n) => {\n nextLimit = Math.max(1, n);\n nextPage = 1;\n },\n setQuery: (q: string) => {\n nextSearch = q;\n nextPage = 1;\n },\n setSort: (s?: S) => {\n nextSort = s;\n nextPage = 1;\n },\n });\n\n // Apply and update at once\n page = nextPage;\n limit = nextLimit;\n search = nextSearch;\n filter = nextFilter;\n sort = nextSort;\n\n await update();\n },\n get current() {\n return items;\n },\n async goTo(p) {\n page = Math.max(1, p | 0);\n await update();\n },\n invalidate() {\n cache.clear();\n },\n get meta() {\n const isEmpty = total === 0;\n const pages = Math.max(1, Math.ceil(total / limit));\n const safePage = Math.min(page, pages);\n const start = isEmpty ? 0 : (safePage - 1) * limit + 1;\n const end = isEmpty ? 0 : Math.min(safePage * limit, total);\n return {\n end,\n error,\n isEmpty,\n isFirst: safePage <= 1,\n isLast: safePage >= pages,\n limit,\n loading,\n page: safePage,\n pages,\n start,\n total,\n };\n },\n async next() {\n page += 1;\n await update();\n },\n async prev() {\n page = Math.max(1, page - 1);\n await update();\n },\n async refresh() {\n cache.delete(keyOf(queryOf()));\n await update();\n },\n async reset() {\n page = 1;\n limit = limitDefault;\n search = '';\n filter = cfg.initialFilter as F | undefined;\n sort = cfg.initialSort as S | undefined;\n cache.clear();\n await update();\n },\n async search(q, opts) {\n search = q;\n page = 1;\n if (opts?.immediate) await update();\n else debounced();\n },\n async setFilter(f) {\n filter = f;\n page = 1;\n await update();\n },\n async setLimit(n) {\n limit = Math.max(1, n);\n page = 1;\n await update();\n },\n async setSort(s) {\n sort = s;\n page = 1;\n await update();\n },\n subscribe(listener) {\n listeners.add(listener);\n // optional: trigger an initial load on the first subscription\n if (listeners.size === 1 && items.length === 0 && !loading) {\n void update();\n }\n return () => listeners.delete(listener);\n },\n };\n}\n"],"names":["remoteList","cfg","listeners","limitDefault","debounceMs","page","limit","search","filter","sort","items","total","loading","error","cache","inflight","keyOf","q","queryOf","assign","res","pages","notify","l","fetchQuery","k","p","e","update","timer","debounced","mutator","nextPage","nextLimit","nextSearch","nextFilter","nextSort","f","n","s","isEmpty","safePage","start","opts","listener"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"expires.cjs","sources":["../../src/date/expires.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"file":"expires.cjs","sources":["../../src/date/expires.ts"],"sourcesContent":["// #region Expires\nexport type Expires = 'EXPIRED' | 'SOON' | 'LATER' | 'NEVER' | 'UNKNOWN';\n// #endregion Expires\n\n/**\n * Determines the expiry status of a given date.\n *\n * @param date - The date to check, as a string or Date object.\n * @param days - Number of days before expiry to be considered \"SOON\" (default: 7).\n * @returns\n * - 'EXPIRED' if the date is in the past,\n * - 'SOON' if the date is within the next `days`,\n * - 'LATER' if the date is further in the future,\n * - 'NEVER' if the year is >= 9999,\n * - 'UNKNOWN' if the date is invalid.\n */\nexport function expires(date: string | Date, days = 7): Expires {\n const target = typeof date === 'string' ? new Date(date) : date;\n const targetTime = target.getTime();\n\n if (Number.isNaN(targetTime)) return 'UNKNOWN';\n if (target.getFullYear() >= 9999) return 'NEVER';\n\n const now = Date.now();\n const diff = targetTime - now;\n\n if (diff <= 0) return 'EXPIRED';\n if (diff <= days * 24 * 60 * 60 * 1000) return 'SOON';\n\n return 'LATER';\n}\n"],"names":["expires","date","days","target","targetTime","now","diff"],"mappings":"gFAgBO,SAASA,EAAQC,EAAqBC,EAAO,EAAY,CAC9D,MAAMC,EAAS,OAAOF,GAAS,SAAW,IAAI,KAAKA,CAAI,EAAIA,EACrDG,EAAaD,EAAO,QAAA,EAE1B,GAAI,OAAO,MAAMC,CAAU,EAAG,MAAO,UACrC,GAAID,EAAO,eAAiB,KAAM,MAAO,QAEzC,MAAME,EAAM,KAAK,IAAA,EACXC,EAAOF,EAAaC,EAE1B,OAAIC,GAAQ,EAAU,UAClBA,GAAQJ,EAAO,GAAK,GAAK,GAAK,IAAa,OAExC,OACT"}
|
package/dist/date/expires.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"expires.js","sources":["../../src/date/expires.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"file":"expires.js","sources":["../../src/date/expires.ts"],"sourcesContent":["// #region Expires\nexport type Expires = 'EXPIRED' | 'SOON' | 'LATER' | 'NEVER' | 'UNKNOWN';\n// #endregion Expires\n\n/**\n * Determines the expiry status of a given date.\n *\n * @param date - The date to check, as a string or Date object.\n * @param days - Number of days before expiry to be considered \"SOON\" (default: 7).\n * @returns\n * - 'EXPIRED' if the date is in the past,\n * - 'SOON' if the date is within the next `days`,\n * - 'LATER' if the date is further in the future,\n * - 'NEVER' if the year is >= 9999,\n * - 'UNKNOWN' if the date is invalid.\n */\nexport function expires(date: string | Date, days = 7): Expires {\n const target = typeof date === 'string' ? new Date(date) : date;\n const targetTime = target.getTime();\n\n if (Number.isNaN(targetTime)) return 'UNKNOWN';\n if (target.getFullYear() >= 9999) return 'NEVER';\n\n const now = Date.now();\n const diff = targetTime - now;\n\n if (diff <= 0) return 'EXPIRED';\n if (diff <= days * 24 * 60 * 60 * 1000) return 'SOON';\n\n return 'LATER';\n}\n"],"names":["expires","date","days","target","targetTime","now","diff"],"mappings":"AAgBO,SAASA,EAAQC,GAAqBC,IAAO,GAAY;AAC9D,QAAMC,IAAS,OAAOF,KAAS,WAAW,IAAI,KAAKA,CAAI,IAAIA,GACrDG,IAAaD,EAAO,QAAA;AAE1B,MAAI,OAAO,MAAMC,CAAU,EAAG,QAAO;AACrC,MAAID,EAAO,iBAAiB,KAAM,QAAO;AAEzC,QAAME,IAAM,KAAK,IAAA,GACXC,IAAOF,IAAaC;AAE1B,SAAIC,KAAQ,IAAU,YAClBA,KAAQJ,IAAO,KAAK,KAAK,KAAK,MAAa,SAExC;AACT;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interval.cjs","sources":["../../src/date/interval.ts"],"sourcesContent":["import { assert } from '../function/assert';\nimport { isDate } from '../typed/isDate';\n\ntype IntervalType = 'D' | 'W' | 'M' | 'MS' | 'ME' | 'Y' | 'YS' | 'YE';\ntype IntervalOptions = {\n interval?: IntervalType;\n steps?: number;\n latest?: boolean;\n};\n\n/**\n * Generates an array of dates between a start and end date, with a specified interval and step size.\n *\n * @example\n * ```ts\n * const options = { interval: 'D', steps: 1, latest: false };\n *\n * interval('2022-01-01', '2022-01-31', options); // Returns an array of dates for every day in January 2022\n * ```\n *\n * @param start - The start date (Date object or ISO string).\n * @param end - The end date (Date object or ISO string).\n * @param options - Options for an interval and steps.\n *\n * @returns An array of generated dates.\n */\nexport function interval(\n start: Date | string,\n end: Date | string,\n { interval = 'D', steps = 1, latest = false }: IntervalOptions = {},\n): Date[] {\n const startDate = new Date(start);\n const endDate = new Date(end);\n\n assert([isDate(startDate), isDate(endDate)], 'Invalid date format. Use a valid Date object or ISO string.', {\n args: { end, start },\n type: TypeError,\n });\n\n const dateArray: Date[] = [];\n let currentDate = new Date(incrementDate(startDate, interval, 0));\n\n while (currentDate <= endDate) {\n dateArray.push(currentDate);\n currentDate = new Date(incrementDate(currentDate, interval, steps));\n }\n\n if (latest && dateArray.length > 0 && dateArray[dateArray.length - 1] < endDate) {\n dateArray.push(endDate);\n }\n\n return dateArray;\n}\n\nfunction incrementDate(date: Date, interval: IntervalType, steps: number): Date {\n const year = date.getFullYear();\n const month = date.getMonth();\n const day = date.getDate();\n\n switch (interval) {\n case 'D':\n return new Date(Date.UTC(year, month, day + steps));\n case 'W':\n return new Date(Date.UTC(year, month, day + 7 * steps));\n case 'M':\n return new Date(Date.UTC(year, month + steps, day));\n case 'MS':\n return new Date(Date.UTC(year, month + steps, 1));\n case 'ME':\n return new Date(Date.UTC(year, month + steps + 1, 0));\n case 'Y':\n return new Date(Date.UTC(year + steps, month, day));\n case 'YS':\n return new Date(Date.UTC(year + steps, 0, 1));\n case 'YE':\n return new Date(Date.UTC(year + steps, 11, 31));\n }\n}\n"],"names":["interval","start","end","steps","latest","startDate","endDate","assert","isDate","dateArray","currentDate","incrementDate","date","year","month","day"],"mappings":"
|
|
1
|
+
{"version":3,"file":"interval.cjs","sources":["../../src/date/interval.ts"],"sourcesContent":["import { assert } from '../function/assert';\nimport { isDate } from '../typed/isDate';\n\n// #region IntervalTypes\ntype IntervalType = 'D' | 'W' | 'M' | 'MS' | 'ME' | 'Y' | 'YS' | 'YE';\ntype IntervalOptions = {\n interval?: IntervalType;\n steps?: number;\n latest?: boolean;\n};\n// #endregion IntervalTypes\n\n/**\n * Generates an array of dates between a start and end date, with a specified interval and step size.\n *\n * @example\n * ```ts\n * const options = { interval: 'D', steps: 1, latest: false };\n *\n * interval('2022-01-01', '2022-01-31', options); // Returns an array of dates for every day in January 2022\n * ```\n *\n * @param start - The start date (Date object or ISO string).\n * @param end - The end date (Date object or ISO string).\n * @param options - Options for an interval and steps.\n *\n * @returns An array of generated dates.\n */\nexport function interval(\n start: Date | string,\n end: Date | string,\n { interval = 'D', steps = 1, latest = false }: IntervalOptions = {},\n): Date[] {\n const startDate = new Date(start);\n const endDate = new Date(end);\n\n assert([isDate(startDate), isDate(endDate)], 'Invalid date format. Use a valid Date object or ISO string.', {\n args: { end, start },\n type: TypeError,\n });\n\n const dateArray: Date[] = [];\n let currentDate = new Date(incrementDate(startDate, interval, 0));\n\n while (currentDate <= endDate) {\n dateArray.push(currentDate);\n currentDate = new Date(incrementDate(currentDate, interval, steps));\n }\n\n if (latest && dateArray.length > 0 && dateArray[dateArray.length - 1] < endDate) {\n dateArray.push(endDate);\n }\n\n return dateArray;\n}\n\nfunction incrementDate(date: Date, interval: IntervalType, steps: number): Date {\n const year = date.getFullYear();\n const month = date.getMonth();\n const day = date.getDate();\n\n switch (interval) {\n case 'D':\n return new Date(Date.UTC(year, month, day + steps));\n case 'W':\n return new Date(Date.UTC(year, month, day + 7 * steps));\n case 'M':\n return new Date(Date.UTC(year, month + steps, day));\n case 'MS':\n return new Date(Date.UTC(year, month + steps, 1));\n case 'ME':\n return new Date(Date.UTC(year, month + steps + 1, 0));\n case 'Y':\n return new Date(Date.UTC(year + steps, month, day));\n case 'YS':\n return new Date(Date.UTC(year + steps, 0, 1));\n case 'YE':\n return new Date(Date.UTC(year + steps, 11, 31));\n }\n}\n"],"names":["interval","start","end","steps","latest","startDate","endDate","assert","isDate","dateArray","currentDate","incrementDate","date","year","month","day"],"mappings":"2JA4BO,SAASA,EACdC,EACAC,EACA,CAAE,SAAAF,EAAW,IAAK,MAAAG,EAAQ,EAAG,OAAAC,EAAS,EAAA,EAA2B,CAAA,EACzD,CACR,MAAMC,EAAY,IAAI,KAAKJ,CAAK,EAC1BK,EAAU,IAAI,KAAKJ,CAAG,EAE5BK,EAAAA,OAAO,CAACC,EAAAA,OAAOH,CAAS,EAAGG,EAAAA,OAAOF,CAAO,CAAC,EAAG,8DAA+D,CAC1G,KAAM,CAAE,IAAAJ,EAAK,MAAAD,CAAA,EACb,KAAM,SAAA,CACP,EAED,MAAMQ,EAAoB,CAAA,EAC1B,IAAIC,EAAc,IAAI,KAAKC,EAAcN,EAAWL,EAAU,CAAC,CAAC,EAEhE,KAAOU,GAAeJ,GACpBG,EAAU,KAAKC,CAAW,EAC1BA,EAAc,IAAI,KAAKC,EAAcD,EAAaV,EAAUG,CAAK,CAAC,EAGpE,OAAIC,GAAUK,EAAU,OAAS,GAAKA,EAAUA,EAAU,OAAS,CAAC,EAAIH,GACtEG,EAAU,KAAKH,CAAO,EAGjBG,CACT,CAEA,SAASE,EAAcC,EAAYZ,EAAwBG,EAAqB,CAC9E,MAAMU,EAAOD,EAAK,YAAA,EACZE,EAAQF,EAAK,SAAA,EACbG,EAAMH,EAAK,QAAA,EAEjB,OAAQZ,EAAAA,CACN,IAAK,IACH,OAAO,IAAI,KAAK,KAAK,IAAIa,EAAMC,EAAOC,EAAMZ,CAAK,CAAC,EACpD,IAAK,IACH,OAAO,IAAI,KAAK,KAAK,IAAIU,EAAMC,EAAOC,EAAM,EAAIZ,CAAK,CAAC,EACxD,IAAK,IACH,OAAO,IAAI,KAAK,KAAK,IAAIU,EAAMC,EAAQX,EAAOY,CAAG,CAAC,EACpD,IAAK,KACH,OAAO,IAAI,KAAK,KAAK,IAAIF,EAAMC,EAAQX,EAAO,CAAC,CAAC,EAClD,IAAK,KACH,OAAO,IAAI,KAAK,KAAK,IAAIU,EAAMC,EAAQX,EAAQ,EAAG,CAAC,CAAC,EACtD,IAAK,IACH,OAAO,IAAI,KAAK,KAAK,IAAIU,EAAOV,EAAOW,EAAOC,CAAG,CAAC,EACpD,IAAK,KACH,OAAO,IAAI,KAAK,KAAK,IAAIF,EAAOV,EAAO,EAAG,CAAC,CAAC,EAC9C,IAAK,KACH,OAAO,IAAI,KAAK,KAAK,IAAIU,EAAOV,EAAO,GAAI,EAAE,CAAC,CAAA,CAEpD"}
|