@synnaxlabs/x 0.44.4 → 0.45.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/.turbo/turbo-build.log +38 -38
  2. package/dist/{base-BORMP3mH.js → base-DRybODwJ.js} +7 -6
  3. package/dist/base-KIBsp6TI.cjs +1 -0
  4. package/dist/binary.cjs +1 -1
  5. package/dist/binary.js +1 -1
  6. package/dist/{bounds-BXbqBINt.cjs → bounds-C2TKFgVk.cjs} +1 -1
  7. package/dist/{bounds-BQqppNFf.js → bounds-DeUXrllt.js} +11 -11
  8. package/dist/bounds.cjs +1 -1
  9. package/dist/bounds.js +1 -1
  10. package/dist/{box-DRH5SOaY.js → box-BYuq-Gjx.js} +4 -4
  11. package/dist/{box-qgxWXNhm.cjs → box-Blu-4d1n.cjs} +1 -1
  12. package/dist/box.cjs +1 -1
  13. package/dist/box.js +1 -1
  14. package/dist/caseconv.cjs +1 -1
  15. package/dist/caseconv.js +1 -1
  16. package/dist/compare.cjs +1 -1
  17. package/dist/compare.js +1 -1
  18. package/dist/deep.cjs +1 -1
  19. package/dist/deep.js +211 -99
  20. package/dist/{dimensions-qY12pyfC.cjs → dimensions-Cg5Owbwn.cjs} +1 -1
  21. package/dist/{dimensions-RaRkesPq.js → dimensions-DC0uLPwn.js} +1 -1
  22. package/dist/dimensions.cjs +1 -1
  23. package/dist/dimensions.js +1 -1
  24. package/dist/{direction-DKdfJwj7.js → direction-C_b4tfRN.js} +1 -1
  25. package/dist/{direction-XCdrc4is.cjs → direction-DqQB9M37.cjs} +1 -1
  26. package/dist/direction.cjs +1 -1
  27. package/dist/direction.js +1 -1
  28. package/dist/external-CtHGFcox.cjs +1 -0
  29. package/dist/{external-BM_NS5yM.js → external-tyaEMW4S.js} +1 -1
  30. package/dist/get-CXkBfLu1.js +82 -0
  31. package/dist/get-OP63N4c3.cjs +1 -0
  32. package/dist/{index-B5l_quQn.js → index-BHXRDFYj.js} +1 -1
  33. package/dist/index-Bxlv0uf_.js +57 -0
  34. package/dist/{index-CBMHFqs4.js → index-C452Pas0.js} +25 -22
  35. package/dist/{index-BQe8OIgm.cjs → index-CwGPVvbl.cjs} +1 -1
  36. package/dist/index-uDxeM-cl.cjs +1 -0
  37. package/dist/index-xaxa1hoa.cjs +1 -0
  38. package/dist/index.cjs +3 -3
  39. package/dist/index.js +442 -380
  40. package/dist/location-BPoXwOni.cjs +1 -0
  41. package/dist/{location-CGLioInQ.js → location-CVxysrHI.js} +31 -30
  42. package/dist/location.cjs +1 -1
  43. package/dist/location.js +1 -1
  44. package/dist/{scale-76Azh2EE.cjs → scale-BHs716im.cjs} +1 -1
  45. package/dist/{scale-BhIvACdB.js → scale-DjxC6ep2.js} +4 -4
  46. package/dist/scale.cjs +1 -1
  47. package/dist/scale.js +1 -1
  48. package/dist/series-C6ZwNf8i.cjs +6 -0
  49. package/dist/{series-kgnLXSDr.js → series-W5Aafjeu.js} +268 -251
  50. package/dist/{spatial-QY891r0E.js → spatial-DnsaOypA.js} +1 -1
  51. package/dist/{spatial-BsGadoUr.cjs → spatial-DrxzaD5U.cjs} +1 -1
  52. package/dist/spatial.cjs +1 -1
  53. package/dist/spatial.js +8 -8
  54. package/dist/src/caseconv/caseconv.d.ts +9 -0
  55. package/dist/src/caseconv/caseconv.d.ts.map +1 -1
  56. package/dist/src/color/color.d.ts +7 -0
  57. package/dist/src/color/color.d.ts.map +1 -1
  58. package/dist/src/deep/copy.spec.d.ts +2 -0
  59. package/dist/src/deep/copy.spec.d.ts.map +1 -0
  60. package/dist/src/deep/external.d.ts +3 -1
  61. package/dist/src/deep/external.d.ts.map +1 -1
  62. package/dist/src/deep/get.bench.d.ts +2 -0
  63. package/dist/src/deep/get.bench.d.ts.map +1 -0
  64. package/dist/src/deep/get.d.ts +16 -0
  65. package/dist/src/deep/get.d.ts.map +1 -0
  66. package/dist/src/deep/get.spec.d.ts +2 -0
  67. package/dist/src/deep/get.spec.d.ts.map +1 -0
  68. package/dist/src/deep/partial.spec.d.ts +2 -0
  69. package/dist/src/deep/partial.spec.d.ts.map +1 -0
  70. package/dist/src/deep/path.d.ts +4 -92
  71. package/dist/src/deep/path.d.ts.map +1 -1
  72. package/dist/src/deep/remove.bench.d.ts +2 -0
  73. package/dist/src/deep/remove.bench.d.ts.map +1 -0
  74. package/dist/src/deep/remove.d.ts +2 -0
  75. package/dist/src/deep/remove.d.ts.map +1 -0
  76. package/dist/src/deep/remove.spec.d.ts +2 -0
  77. package/dist/src/deep/remove.spec.d.ts.map +1 -0
  78. package/dist/src/deep/set.bench.d.ts +2 -0
  79. package/dist/src/deep/set.bench.d.ts.map +1 -0
  80. package/dist/src/deep/set.d.ts +2 -0
  81. package/dist/src/deep/set.d.ts.map +1 -0
  82. package/dist/src/deep/set.spec.d.ts +2 -0
  83. package/dist/src/deep/set.spec.d.ts.map +1 -0
  84. package/dist/src/index.d.ts +1 -0
  85. package/dist/src/index.d.ts.map +1 -1
  86. package/dist/src/primitive/primitive.d.ts +4 -0
  87. package/dist/src/primitive/primitive.d.ts.map +1 -1
  88. package/dist/src/spatial/location/location.d.ts +2 -2
  89. package/dist/src/spatial/location/location.d.ts.map +1 -1
  90. package/dist/src/status/status.d.ts +2 -1
  91. package/dist/src/status/status.d.ts.map +1 -1
  92. package/dist/src/telem/series.d.ts +1 -0
  93. package/dist/src/telem/series.d.ts.map +1 -1
  94. package/dist/src/telem/telem.d.ts +15 -6
  95. package/dist/src/telem/telem.d.ts.map +1 -1
  96. package/dist/src/undefined.d.ts +2 -0
  97. package/dist/src/undefined.d.ts.map +1 -0
  98. package/dist/telem.cjs +1 -1
  99. package/dist/telem.js +1 -1
  100. package/dist/{xy-BKIJiLu_.cjs → xy-DWwtHmgn.cjs} +1 -1
  101. package/dist/{xy-CBuhMaIo.js → xy-DYPw8-8C.js} +1 -1
  102. package/dist/xy.cjs +1 -1
  103. package/dist/xy.js +1 -1
  104. package/dist/zod.cjs +1 -1
  105. package/dist/zod.js +1 -1
  106. package/package.json +4 -4
  107. package/src/caseconv/caseconv.spec.ts +30 -0
  108. package/src/caseconv/caseconv.ts +46 -0
  109. package/src/color/color.spec.ts +40 -0
  110. package/src/color/color.ts +72 -0
  111. package/src/deep/copy.spec.ts +148 -0
  112. package/src/deep/external.ts +3 -1
  113. package/src/deep/get.bench.ts +170 -0
  114. package/src/deep/get.spec.ts +196 -0
  115. package/src/deep/get.ts +79 -0
  116. package/src/deep/partial.spec.ts +194 -0
  117. package/src/deep/path.spec.ts +92 -183
  118. package/src/deep/path.ts +27 -198
  119. package/src/deep/remove.bench.ts +238 -0
  120. package/src/deep/remove.spec.ts +219 -0
  121. package/src/deep/remove.ts +102 -0
  122. package/src/deep/set.bench.ts +208 -0
  123. package/src/deep/set.spec.ts +369 -0
  124. package/src/deep/set.ts +91 -0
  125. package/src/index.ts +1 -0
  126. package/src/primitive/primitive.spec.ts +9 -0
  127. package/src/primitive/primitive.ts +9 -0
  128. package/src/spatial/location/location.ts +2 -0
  129. package/src/status/status.spec.ts +52 -0
  130. package/src/status/status.ts +19 -6
  131. package/src/telem/series.ts +2 -0
  132. package/src/telem/telem.spec.ts +302 -0
  133. package/src/telem/telem.ts +60 -18
  134. package/src/undefined.ts +14 -0
  135. package/tsconfig.tsbuildinfo +1 -1
  136. package/dist/base-BLNViP3D.cjs +0 -1
  137. package/dist/external-E3ErJeeM.cjs +0 -1
  138. package/dist/index-D2xcvEO5.js +0 -46
  139. package/dist/index-DOJlZHqJ.cjs +0 -1
  140. package/dist/index-DdhM_E4k.cjs +0 -1
  141. package/dist/location-DJ_K4SlP.cjs +0 -1
  142. package/dist/path-Blh4wJuA.js +0 -110
  143. package/dist/path-CPSfCjde.cjs +0 -1
  144. package/dist/series-tAhThbnz.cjs +0 -6
  145. package/dist/src/deep/delete.d.ts +0 -3
  146. package/dist/src/deep/delete.d.ts.map +0 -1
  147. package/dist/src/deep/delete.spec.d.ts +0 -2
  148. package/dist/src/deep/delete.spec.d.ts.map +0 -1
  149. package/src/deep/delete.spec.ts +0 -73
  150. package/src/deep/delete.ts +0 -27
package/dist/telem.js CHANGED
@@ -1,4 +1,4 @@
1
- import { c as s, D as i, M as t, R as r, S, d as m, b as p, a as T, T as d, g as y, f as l, i as n, e as D, t as o } from "./series-kgnLXSDr.js";
1
+ import { c as s, D as i, M as t, R as r, S, d as m, b as p, a as T, T as d, g as y, f as l, i as n, e as D, t as o } from "./series-W5Aafjeu.js";
2
2
  export {
3
3
  s as DataType,
4
4
  i as Density,
@@ -1 +1 @@
1
- "use strict";const O=require("zod"),i=require("./base-BLNViP3D.cjs"),z=O.z.union([O.z.number(),i.xy,i.numberCouple,i.dimensions,i.signedDimensions,i.clientXY]),o=(t,n)=>{if(typeof t=="string"){if(n===void 0)throw new Error("The y coordinate must be given.");return t==="x"?{x:n,y:0}:{x:0,y:n}}return typeof t=="number"?{x:t,y:n??t}:Array.isArray(t)?{x:t[0],y:t[1]}:"signedWidth"in t?{x:t.signedWidth,y:t.signedHeight}:"clientX"in t?{x:t.clientX,y:t.clientY}:"width"in t?{x:t.width,y:t.height}:{x:t.x,y:t.y}},x=Object.freeze({x:0,y:0}),P=Object.freeze({x:1,y:1}),q=Object.freeze({x:1/0,y:1/0}),v=Object.freeze({x:NaN,y:NaN}),g=(t,n,e=0)=>{const r=o(t),s=o(n);return e===0?r.x===s.x&&r.y===s.y:Math.abs(r.x-s.x)<=e&&Math.abs(r.y-s.y)<=e},I=t=>g(t,x),b=(t,n,e)=>{const r=o(t),s=o(n,e);return{x:r.x*s.x,y:r.y*s.y}},_=(t,n)=>{const e=o(t);return{x:e.x+n,y:e.y}},j=(t,n)=>{const e=o(t);return{x:e.x,y:e.y+n}},p=(t,n,e,...r)=>{if(typeof n=="string"){if(typeof e!="number")throw new Error("The value must be a number.");return n==="x"?_(t,e):j(t,e)}if(typeof n=="object"&&"x"in n&&typeof n.x=="string"){const s=o(e),c=o(t);return n.x==="left"?s.x=-s.x:n.x==="center"&&(s.x=0),n.y==="top"?s.y=-s.y:n.y==="center"&&(s.y=0),{x:c.x+s.x,y:c.y+s.y}}return[t,n,e??x,...r].reduce((s,c)=>{const y=o(c);return{x:s.x+y.x,y:s.y+y.y}},x)},Z=(t,n,e)=>{const r=o(t);return n==="x"?{x:e,y:r.y}:{x:r.x,y:e}},A=(t,n)=>{const e=o(t),r=o(n);return Math.sqrt((e.x-r.x)**2+(e.y-r.y)**2)},T=(t,n)=>{const e=o(t),r=o(n);return{x:r.x-e.x,y:r.y-e.y}},H=t=>{const n=o(t);return Number.isNaN(n.x)||Number.isNaN(n.y)},R=t=>{const n=o(t);return Number.isFinite(n.x)&&Number.isFinite(n.y)},S=t=>{const n=o(t);return[n.x,n.y]},W=t=>{const n=o(t);return{left:n.x,top:n.y}},w=(t,n=0)=>{const e=o(t);return{x:Number(e.x.toFixed(n)),y:Number(e.y.toFixed(n))}},d=(t,n)=>{const e=o(t),r=o(n);return{x:e.x-r.x,y:e.y-r.y}},N=t=>{const n=o(t),e=Math.hypot(n.x,n.y);return e===0?{x:0,y:0}:{x:-n.y/e,y:n.x/e}},E=t=>{const n=o(t),e=Math.hypot(n.x,n.y);return e===0?{x:0,y:0}:{x:n.x/e,y:n.y/e}},F=(...t)=>{const n=t.reduce((e,r)=>p(e,r),x);return b(n,1/t.length)},C=(t,n)=>{const e=[];for(let r=0;r<t.length;r++){const s=t[r];let c,y,h,u;if(r===0){const a=t[r+1],l=d(a,s);y=N(l),h=y,u=n}else if(r===t.length-1){const a=t[r-1],l=d(s,a);c=N(l),h=c,u=n}else{const a=t[r-1],l=t[r+1],f=d(s,a),m=d(l,s);c=N(f),y=N(m);const Y=Math.acos((f.x*m.x+f.y*m.y)/(Math.hypot(f.x,f.y)*Math.hypot(m.x,m.y))),M=Math.sin(Y/2);M===0?u=n:u=n/M,h=E(F(c,y))}e.push(b(h,u))}return e},D=t=>{const n=o(t);return{x:n.y,y:n.x}},X=t=>{const n=o(t);return{x:Math.round(n.x),y:Math.round(n.y)}},L=Object.freeze(Object.defineProperty({__proto__:null,INFINITY:q,NAN:v,ONE:P,ZERO:x,average:F,calculateMiters:C,clientXY:i.clientXY,construct:o,couple:S,crudeZ:z,css:W,distance:A,equals:g,isFinite:R,isNan:H,isZero:I,normal:N,normalize:E,round:X,scale:b,set:Z,sub:d,swap:D,translate:p,translateX:_,translateY:j,translation:T,truncate:w,xy:i.xy},Symbol.toStringTag,{value:"Module"}));exports.ONE=P;exports.ZERO=x;exports.construct=o;exports.crudeZ=z;exports.equals=g;exports.round=X;exports.translate=p;exports.truncate=w;exports.xy=L;
1
+ "use strict";const O=require("zod"),i=require("./base-KIBsp6TI.cjs"),z=O.z.union([O.z.number(),i.xy,i.numberCouple,i.dimensions,i.signedDimensions,i.clientXY]),o=(t,n)=>{if(typeof t=="string"){if(n===void 0)throw new Error("The y coordinate must be given.");return t==="x"?{x:n,y:0}:{x:0,y:n}}return typeof t=="number"?{x:t,y:n??t}:Array.isArray(t)?{x:t[0],y:t[1]}:"signedWidth"in t?{x:t.signedWidth,y:t.signedHeight}:"clientX"in t?{x:t.clientX,y:t.clientY}:"width"in t?{x:t.width,y:t.height}:{x:t.x,y:t.y}},x=Object.freeze({x:0,y:0}),P=Object.freeze({x:1,y:1}),q=Object.freeze({x:1/0,y:1/0}),v=Object.freeze({x:NaN,y:NaN}),g=(t,n,e=0)=>{const r=o(t),s=o(n);return e===0?r.x===s.x&&r.y===s.y:Math.abs(r.x-s.x)<=e&&Math.abs(r.y-s.y)<=e},I=t=>g(t,x),b=(t,n,e)=>{const r=o(t),s=o(n,e);return{x:r.x*s.x,y:r.y*s.y}},_=(t,n)=>{const e=o(t);return{x:e.x+n,y:e.y}},j=(t,n)=>{const e=o(t);return{x:e.x,y:e.y+n}},p=(t,n,e,...r)=>{if(typeof n=="string"){if(typeof e!="number")throw new Error("The value must be a number.");return n==="x"?_(t,e):j(t,e)}if(typeof n=="object"&&"x"in n&&typeof n.x=="string"){const s=o(e),c=o(t);return n.x==="left"?s.x=-s.x:n.x==="center"&&(s.x=0),n.y==="top"?s.y=-s.y:n.y==="center"&&(s.y=0),{x:c.x+s.x,y:c.y+s.y}}return[t,n,e??x,...r].reduce((s,c)=>{const y=o(c);return{x:s.x+y.x,y:s.y+y.y}},x)},Z=(t,n,e)=>{const r=o(t);return n==="x"?{x:e,y:r.y}:{x:r.x,y:e}},A=(t,n)=>{const e=o(t),r=o(n);return Math.sqrt((e.x-r.x)**2+(e.y-r.y)**2)},T=(t,n)=>{const e=o(t),r=o(n);return{x:r.x-e.x,y:r.y-e.y}},H=t=>{const n=o(t);return Number.isNaN(n.x)||Number.isNaN(n.y)},R=t=>{const n=o(t);return Number.isFinite(n.x)&&Number.isFinite(n.y)},S=t=>{const n=o(t);return[n.x,n.y]},W=t=>{const n=o(t);return{left:n.x,top:n.y}},w=(t,n=0)=>{const e=o(t);return{x:Number(e.x.toFixed(n)),y:Number(e.y.toFixed(n))}},d=(t,n)=>{const e=o(t),r=o(n);return{x:e.x-r.x,y:e.y-r.y}},N=t=>{const n=o(t),e=Math.hypot(n.x,n.y);return e===0?{x:0,y:0}:{x:-n.y/e,y:n.x/e}},E=t=>{const n=o(t),e=Math.hypot(n.x,n.y);return e===0?{x:0,y:0}:{x:n.x/e,y:n.y/e}},F=(...t)=>{const n=t.reduce((e,r)=>p(e,r),x);return b(n,1/t.length)},C=(t,n)=>{const e=[];for(let r=0;r<t.length;r++){const s=t[r];let c,y,h,u;if(r===0){const a=t[r+1],l=d(a,s);y=N(l),h=y,u=n}else if(r===t.length-1){const a=t[r-1],l=d(s,a);c=N(l),h=c,u=n}else{const a=t[r-1],l=t[r+1],f=d(s,a),m=d(l,s);c=N(f),y=N(m);const Y=Math.acos((f.x*m.x+f.y*m.y)/(Math.hypot(f.x,f.y)*Math.hypot(m.x,m.y))),M=Math.sin(Y/2);M===0?u=n:u=n/M,h=E(F(c,y))}e.push(b(h,u))}return e},D=t=>{const n=o(t);return{x:n.y,y:n.x}},X=t=>{const n=o(t);return{x:Math.round(n.x),y:Math.round(n.y)}},L=Object.freeze(Object.defineProperty({__proto__:null,INFINITY:q,NAN:v,ONE:P,ZERO:x,average:F,calculateMiters:C,clientXY:i.clientXY,construct:o,couple:S,crudeZ:z,css:W,distance:A,equals:g,isFinite:R,isNan:H,isZero:I,normal:N,normalize:E,round:X,scale:b,set:Z,sub:d,swap:D,translate:p,translateX:_,translateY:j,translation:T,truncate:w,xy:i.xy},Symbol.toStringTag,{value:"Module"}));exports.ONE=P;exports.ZERO=x;exports.construct=o;exports.crudeZ=z;exports.equals=g;exports.round=X;exports.translate=p;exports.truncate=w;exports.xy=L;
@@ -1,5 +1,5 @@
1
1
  import { z as b } from "zod";
2
- import { x as p, n as v, d as I, s as A, i as M } from "./base-BORMP3mH.js";
2
+ import { x as p, n as v, d as I, s as A, j as M } from "./base-DRybODwJ.js";
3
3
  const E = b.union([
4
4
  b.number(),
5
5
  p,
package/dist/xy.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./xy-BKIJiLu_.cjs");exports.xy=e.xy;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./xy-DWwtHmgn.cjs");exports.xy=e.xy;
package/dist/xy.js CHANGED
@@ -1,4 +1,4 @@
1
- import { x as r } from "./xy-CBuhMaIo.js";
1
+ import { x as r } from "./xy-DYPw8-8C.js";
2
2
  export {
3
3
  r as xy
4
4
  };
package/dist/zod.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./external-E3ErJeeM.cjs");exports.zod=e.external;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./external-CtHGFcox.cjs");exports.zod=e.external;
package/dist/zod.js CHANGED
@@ -1,4 +1,4 @@
1
- import { e as r } from "./external-BM_NS5yM.js";
1
+ import { e as r } from "./external-tyaEMW4S.js";
2
2
  export {
3
3
  r as zod
4
4
  };
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@synnaxlabs/x",
3
- "version": "0.44.4",
3
+ "version": "0.45.1",
4
4
  "type": "module",
5
5
  "description": "Common Utilities for Synnax Labs",
6
- "repository": "https://github.com/synnaxlabs/synnax/tree/main/x/go",
6
+ "repository": "https://github.com/synnaxlabs/synnax/tree/main/x/ts",
7
7
  "keywords": [
8
8
  "synnax",
9
9
  "utilities",
@@ -27,9 +27,9 @@
27
27
  "typescript": "^5.8.3",
28
28
  "vite": "^7.0.4",
29
29
  "vitest": "^3.2.4",
30
+ "@synnaxlabs/vite-plugin": "^0.43.0",
30
31
  "@synnaxlabs/tsconfig": "^0.43.0",
31
- "eslint-config-synnaxlabs": "^0.43.0",
32
- "@synnaxlabs/vite-plugin": "^0.43.0"
32
+ "eslint-config-synnaxlabs": "^0.43.0"
33
33
  },
34
34
  "main": "./dist/index.cjs",
35
35
  "module": "./dist/index.js",
@@ -127,4 +127,34 @@ describe("caseconv", () => {
127
127
  });
128
128
  });
129
129
  });
130
+ describe("toProperNoun", () => {
131
+ const SPECS: [string, string][] = [
132
+ ["fooBar", "Foo Bar"],
133
+ ["foo_bar", "Foo Bar"],
134
+ ["foo-bar", "Foo Bar"],
135
+ ["FooBar", "Foo Bar"],
136
+ ["foo_bar_baz", "Foo Bar Baz"],
137
+ ["fooBarBaz", "Foo Bar Baz"],
138
+ ["foo-bar-baz", "Foo Bar Baz"],
139
+ ["XMLParser", "XML Parser"],
140
+ ["parseXMLDocument", "Parse XML Document"],
141
+ ["IODevice", "IO Device"],
142
+ ["temperature_sensor", "Temperature Sensor"],
143
+ ["pressure-gauge", "Pressure Gauge"],
144
+ ["flowMeter", "Flow Meter"],
145
+ ["my_custom_symbol", "My Custom Symbol"],
146
+ ["valve-actuator-v2", "Valve Actuator V2"],
147
+ ["PIDController", "PID Controller"],
148
+ ["", ""],
149
+ ["a", "A"],
150
+ ["ABC", "ABC"],
151
+ ["test123value", "Test123value"],
152
+ ["test_123_value", "Test 123 Value"],
153
+ ];
154
+ SPECS.forEach(([input, expected]) => {
155
+ it(`should convert ${input} to ${expected}`, () => {
156
+ expect(caseconv.toProperNoun(input)).toBe(expected);
157
+ });
158
+ });
159
+ });
130
160
  });
@@ -165,3 +165,49 @@ const toKebabStr = (str: string): string =>
165
165
  * @returns The converted string in kebab-case
166
166
  */
167
167
  export const toKebab = createConverter(toKebabStr);
168
+
169
+ /**
170
+ * Converts a string to proper noun format.
171
+ * Handles snake_case, kebab-case, camelCase, and PascalCase.
172
+ * Capitalizes the first letter of each word.
173
+ *
174
+ * @param str - The string to convert
175
+ * @returns The converted string in proper noun format
176
+ */
177
+ const toProperNounStr = (str: string): string => {
178
+ if (str.length === 0) return str;
179
+
180
+ // Replace underscores and hyphens with spaces
181
+ let result = str.replace(/[_-]/g, " ");
182
+
183
+ // Insert spaces before capital letters (for camelCase/PascalCase)
184
+ // but not at the start or when there are consecutive capitals
185
+ result = result.replace(
186
+ /([a-z0-9])([A-Z])/g,
187
+ (_, p1: string, p2: string) => `${p1} ${p2}`
188
+ );
189
+
190
+ // Handle consecutive capitals (e.g., "XMLParser" -> "XML Parser")
191
+ result = result.replace(
192
+ /([A-Z]+)([A-Z][a-z])/g,
193
+ (_, p1: string, p2: string) => `${p1} ${p2}`
194
+ );
195
+
196
+ // Clean up multiple spaces
197
+ result = result.replace(/\s+/g, " ").trim();
198
+
199
+ // Capitalize first letter of each word (proper noun format)
200
+ result = result.replace(/\b\w/g, (char) => char.toUpperCase());
201
+
202
+ return result;
203
+ };
204
+
205
+ /**
206
+ * Converts a string to proper noun format.
207
+ * Handles snake_case, kebab-case, camelCase, and PascalCase.
208
+ * Each word is capitalized.
209
+ *
210
+ * @param str - The string to convert
211
+ * @returns The converted string in proper noun format
212
+ */
213
+ export const toProperNoun = createConverter(toProperNounStr);
@@ -757,6 +757,46 @@ describe("color.Color", () => {
757
757
  });
758
758
  });
759
759
 
760
+ describe("fromCSS", () => {
761
+ test("parses hex colors", () => {
762
+ expect(color.fromCSS("#ff0000")).toEqual([255, 0, 0, 1]);
763
+ expect(color.fromCSS("#00ff00")).toEqual([0, 255, 0, 1]);
764
+ expect(color.fromCSS("#0000ff")).toEqual([0, 0, 255, 1]);
765
+ expect(color.fromCSS("#f00")).toEqual([255, 0, 0, 1]);
766
+ expect(color.fromCSS("#0f0")).toEqual([0, 255, 0, 1]);
767
+ expect(color.fromCSS("#00f")).toEqual([0, 0, 255, 1]);
768
+ });
769
+
770
+ test("parses rgb/rgba colors", () => {
771
+ expect(color.fromCSS("rgb(255, 0, 0)")).toEqual([255, 0, 0, 1]);
772
+ expect(color.fromCSS("rgb(0, 255, 0)")).toEqual([0, 255, 0, 1]);
773
+ expect(color.fromCSS("rgba(0, 0, 255, 0.5)")).toEqual([0, 0, 255, 0.5]);
774
+ expect(color.fromCSS("rgba(128, 128, 128, 1)")).toEqual([128, 128, 128, 1]);
775
+ });
776
+
777
+ test("parses named colors", () => {
778
+ expect(color.fromCSS("red")).toEqual([255, 0, 0, 1]);
779
+ expect(color.fromCSS("green")).toEqual([0, 128, 0, 1]);
780
+ expect(color.fromCSS("blue")).toEqual([0, 0, 255, 1]);
781
+ expect(color.fromCSS("black")).toEqual([0, 0, 0, 1]);
782
+ expect(color.fromCSS("white")).toEqual([255, 255, 255, 1]);
783
+ });
784
+
785
+ test("handles case insensitive input", () => {
786
+ expect(color.fromCSS("RED")).toEqual([255, 0, 0, 1]);
787
+ expect(color.fromCSS("Green")).toEqual([0, 128, 0, 1]);
788
+ expect(color.fromCSS("BLUE")).toEqual([0, 0, 255, 1]);
789
+ });
790
+
791
+ test("returns undefined for invalid input", () => {
792
+ expect(color.fromCSS("")).toBeUndefined();
793
+ expect(color.fromCSS("none")).toBeUndefined();
794
+ expect(color.fromCSS("transparent")).toBeUndefined();
795
+ expect(color.fromCSS("invalid")).toBeUndefined();
796
+ expect(color.fromCSS("#gggggg")).toBeUndefined();
797
+ });
798
+ });
799
+
760
800
  describe("isZero", () => {
761
801
  test("returns true for zero color", () => {
762
802
  expect(color.isZero([0, 0, 0, 0])).toBe(true);
@@ -282,6 +282,78 @@ const rgbaToHex = (n: number): string => Math.floor(n).toString(16).padStart(2,
282
282
  const hexToRgba = (s: string, n: number): number => parseInt(s.slice(n, n + 2), 16);
283
283
  const stripHash = (hex: string): string => (hex.startsWith("#") ? hex.slice(1) : hex);
284
284
 
285
+ const NAMED: Record<string, string> = {
286
+ black: "#000000",
287
+ white: "#ffffff",
288
+ red: "#ff0000",
289
+ green: "#008000",
290
+ blue: "#0000ff",
291
+ yellow: "#ffff00",
292
+ cyan: "#00ffff",
293
+ magenta: "#ff00ff",
294
+ silver: "#c0c0c0",
295
+ gray: "#808080",
296
+ grey: "#808080",
297
+ maroon: "#800000",
298
+ olive: "#808000",
299
+ lime: "#00ff00",
300
+ aqua: "#00ffff",
301
+ teal: "#008080",
302
+ navy: "#000080",
303
+ fuchsia: "#ff00ff",
304
+ purple: "#800080",
305
+ orange: "#ffa500",
306
+ brown: "#a52a2a",
307
+ tan: "#d2b48c",
308
+ gold: "#ffd700",
309
+ indigo: "#4b0082",
310
+ violet: "#ee82ee",
311
+ pink: "#ffc0cb",
312
+ coral: "#ff7f50",
313
+ salmon: "#fa8072",
314
+ khaki: "#f0e68c",
315
+ crimson: "#dc143c",
316
+ transparent: "transparent",
317
+ };
318
+
319
+ /**
320
+ * Parses a CSS color string into a Color.
321
+ * Supports hex colors, rgb/rgba functions, and named colors.
322
+ * @param cssColor - The CSS color string to parse
323
+ * @returns The parsed color or undefined if invalid
324
+ */
325
+ export const fromCSS = (cssColor: string): Color | undefined => {
326
+ if (!cssColor) return undefined;
327
+ const trimmed = cssColor.trim().toLowerCase();
328
+ if (trimmed === "transparent" || trimmed === "none") return undefined;
329
+ if (trimmed.startsWith("#")) {
330
+ if (trimmed.length === 4) {
331
+ const r = trimmed[1];
332
+ const g = trimmed[2];
333
+ const b = trimmed[3];
334
+ const expanded = `#${r}${r}${g}${g}${b}${b}`;
335
+ if (hexZ.safeParse(expanded).success) return fromHex(expanded);
336
+ }
337
+ if (
338
+ (trimmed.length === 7 || trimmed.length === 9) &&
339
+ hexZ.safeParse(trimmed).success
340
+ )
341
+ return fromHex(trimmed);
342
+ return undefined;
343
+ }
344
+ if (trimmed.startsWith("rgb")) {
345
+ const match = trimmed.match(
346
+ /rgba?\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*([\d.]+))?\s*\)/,
347
+ );
348
+ if (match) {
349
+ const [, r, g, b, a] = match;
350
+ return [parseInt(r), parseInt(g), parseInt(b), a ? parseFloat(a) : 1];
351
+ }
352
+ }
353
+ if (NAMED[trimmed]) return fromHex(NAMED[trimmed]);
354
+ return undefined;
355
+ };
356
+
285
357
  /** @returns parse a color from an HSLA tuple. */
286
358
  export const fromHSLA = (hsla: HSLA): RGBA => {
287
359
  hsla = hslaZ.parse(hsla);
@@ -0,0 +1,148 @@
1
+ // Copyright 2025 Synnax Labs, Inc.
2
+ //
3
+ // Use of this software is governed by the Business Source License included in the file
4
+ // licenses/BSL.txt.
5
+ //
6
+ // As of the Change Date specified in that file, in accordance with the Business Source
7
+ // License, use of this software will be governed by the Apache License, Version 2.0,
8
+ // included in the file licenses/APL.txt.
9
+
10
+ import { describe, expect, it } from "vitest";
11
+
12
+ import { deep } from "@/deep";
13
+
14
+ describe("copy", () => {
15
+ it("should deep copy a simple object", () => {
16
+ const obj = { a: 1, b: 2 };
17
+ const copied = deep.copy(obj);
18
+ expect(copied).toEqual(obj);
19
+ expect(copied).not.toBe(obj);
20
+ });
21
+
22
+ it("should deep copy nested objects", () => {
23
+ const obj = { a: 1, b: { c: 2, d: { e: 3 } } };
24
+ const copied = deep.copy(obj);
25
+ expect(copied).toEqual(obj);
26
+ expect(copied).not.toBe(obj);
27
+ expect(copied.b).not.toBe(obj.b);
28
+ expect(copied.b.d).not.toBe(obj.b.d);
29
+ });
30
+
31
+ it("should deep copy arrays", () => {
32
+ const obj = { a: [1, 2, 3], b: [{ c: 4 }, { d: 5 }] };
33
+ const copied = deep.copy(obj);
34
+ expect(copied).toEqual(obj);
35
+ expect(copied.a).not.toBe(obj.a);
36
+ expect(copied.b).not.toBe(obj.b);
37
+ expect(copied.b[0]).not.toBe(obj.b[0]);
38
+ });
39
+
40
+ it("should handle null values", () => {
41
+ const obj = { a: null, b: { c: null } };
42
+ const copied = deep.copy(obj);
43
+ expect(copied).toEqual(obj);
44
+ expect(copied).not.toBe(obj);
45
+ });
46
+
47
+ it("should handle undefined values", () => {
48
+ const obj = { a: undefined, b: { c: undefined } };
49
+ const copied = deep.copy(obj);
50
+ expect(copied).toEqual(obj);
51
+ expect(copied).not.toBe(obj);
52
+ });
53
+
54
+ it("should handle Date objects", () => {
55
+ const date = new Date("2025-01-01");
56
+ const obj = { a: date, b: { c: date } };
57
+ const copied = deep.copy(obj);
58
+ expect(copied.a).toEqual(date);
59
+ expect(copied.a).not.toBe(date);
60
+ expect(copied.b.c).toEqual(date);
61
+ expect(copied.b.c).not.toBe(date);
62
+ });
63
+
64
+ it("should handle circular references with structuredClone", () => {
65
+ const obj: any = { a: 1 };
66
+ obj.circular = obj;
67
+
68
+ const copied = deep.copy(obj);
69
+
70
+ expect(copied.a).toBe(1);
71
+ expect(copied.circular).toBe(copied);
72
+ expect(copied).not.toBe(obj);
73
+ });
74
+
75
+ it("should handle Map objects", () => {
76
+ const map = new Map([
77
+ ["a", 1],
78
+ ["b", 2],
79
+ ]);
80
+ const obj = { map };
81
+ const copied = deep.copy(obj);
82
+ expect(copied.map).toEqual(map);
83
+ expect(copied.map).not.toBe(map);
84
+ });
85
+
86
+ it("should handle Set objects", () => {
87
+ const set = new Set([1, 2, 3]);
88
+ const obj = { set };
89
+ const copied = deep.copy(obj);
90
+ expect(copied.set).toEqual(set);
91
+ expect(copied.set).not.toBe(set);
92
+ });
93
+
94
+ it("should handle RegExp objects", () => {
95
+ const regex = /test/gi;
96
+ const obj = { regex };
97
+ const copied = deep.copy(obj);
98
+ expect(copied.regex).toEqual(regex);
99
+ expect(copied.regex).not.toBe(regex);
100
+ });
101
+
102
+ it("should handle mixed types", () => {
103
+ const obj = {
104
+ num: 42,
105
+ str: "test",
106
+ bool: true,
107
+ arr: [1, 2, { nested: "value" }],
108
+ obj: { a: 1, b: { c: 2 } },
109
+ date: new Date("2025-01-01"),
110
+ nil: null,
111
+ undef: undefined,
112
+ };
113
+ const copied = deep.copy(obj);
114
+ expect(copied).toEqual(obj);
115
+ expect(copied).not.toBe(obj);
116
+ expect(copied.arr).not.toBe(obj.arr);
117
+ expect(copied.obj).not.toBe(obj.obj);
118
+ expect(copied.date).not.toBe(obj.date);
119
+ });
120
+
121
+ it("should handle empty objects and arrays", () => {
122
+ const obj = { empty: {}, arr: [] };
123
+ const copied = deep.copy(obj);
124
+ expect(copied).toEqual(obj);
125
+ expect(copied.empty).not.toBe(obj.empty);
126
+ expect(copied.arr).not.toBe(obj.arr);
127
+ });
128
+
129
+ it("should preserve array length with sparse arrays", () => {
130
+ // eslint-disable-next-line no-sparse-arrays
131
+ const arr = [1, , , 4];
132
+ const copied = deep.copy(arr);
133
+ expect(copied).toEqual(arr);
134
+ expect(copied.length).toBe(4);
135
+ expect(copied[1]).toBeUndefined();
136
+ });
137
+
138
+ it("should handle Uint8Array and other typed arrays", () => {
139
+ const uint8 = new Uint8Array([1, 2, 3]);
140
+ const obj = { uint8 };
141
+ const copied = deep.copy(obj);
142
+ expect(copied.uint8).toBeDefined();
143
+ if (copied.uint8 instanceof Uint8Array) {
144
+ expect(Array.from(copied.uint8)).toEqual([1, 2, 3]);
145
+ expect(copied.uint8).not.toBe(uint8);
146
+ } else expect(Array.from(copied.uint8 as Uint8Array)).toEqual([1, 2, 3]);
147
+ });
148
+ });
@@ -8,9 +8,11 @@
8
8
  // included in the file licenses/APL.txt.
9
9
 
10
10
  export * from "@/deep/copy";
11
- export * from "@/deep/delete";
12
11
  export * from "@/deep/difference";
13
12
  export * from "@/deep/equal";
13
+ export * from "@/deep/get";
14
14
  export * from "@/deep/merge";
15
15
  export * from "@/deep/partial";
16
16
  export * from "@/deep/path";
17
+ export * from "@/deep/remove";
18
+ export * from "@/deep/set";
@@ -0,0 +1,170 @@
1
+ // Copyright 2025 Synnax Labs, Inc.
2
+ //
3
+ // Use of this software is governed by the Business Source License included in the file
4
+ // licenses/BSL.txt.
5
+ //
6
+ // As of the Change Date specified in that file, in accordance with the Business Source
7
+ // License, use of this software will be governed by the Apache License, Version 2.0,
8
+ // included in the file licenses/APL.txt.
9
+
10
+ import { bench, describe } from "vitest";
11
+
12
+ import { deep } from "@/deep";
13
+
14
+ describe("deep.get benchmarks", () => {
15
+ const shallowObject = {
16
+ a: 1,
17
+ b: 2,
18
+ c: 3,
19
+ d: 4,
20
+ e: 5,
21
+ };
22
+
23
+ const nestedObject = {
24
+ level1: {
25
+ level2: {
26
+ level3: {
27
+ level4: {
28
+ level5: {
29
+ value: 42,
30
+ },
31
+ },
32
+ },
33
+ },
34
+ },
35
+ };
36
+
37
+ const arrayObject = {
38
+ items: [
39
+ { key: "item1", value: 1 },
40
+ { key: "item2", value: 2 },
41
+ { key: "item3", value: 3 },
42
+ { key: "item4", value: 4 },
43
+ { key: "item5", value: 5 },
44
+ ],
45
+ };
46
+
47
+ const deeplyNestedArray = {
48
+ a: [
49
+ {
50
+ b: [
51
+ {
52
+ c: [{ value: 1 }, { value: 2 }, { value: 3 }],
53
+ },
54
+ ],
55
+ },
56
+ ],
57
+ };
58
+
59
+ const largeObject: any = {};
60
+ for (let i = 0; i < 1000; i++)
61
+ largeObject[`key${i}`] = {
62
+ nested: {
63
+ value: i,
64
+ data: {
65
+ id: i,
66
+ name: `name${i}`,
67
+ },
68
+ },
69
+ };
70
+
71
+ const veryDeepObject: any = {};
72
+ let current = veryDeepObject;
73
+ for (let i = 0; i < 100; i++) {
74
+ current.next = { level: i };
75
+ current = current.next;
76
+ }
77
+ current.value = "deep";
78
+
79
+ const keyedWithPeriods = {
80
+ channels: [
81
+ { key: "sensor.temperature.1", reading: 25.5 },
82
+ { key: "sensor.temperature.2", reading: 26.1 },
83
+ { key: "sensor.pressure.1", reading: 101.3 },
84
+ { key: "sensor.pressure.2", reading: 101.5 },
85
+ ],
86
+ };
87
+
88
+ bench("shallow property access", () => {
89
+ deep.get(shallowObject, "c");
90
+ });
91
+
92
+ bench("nested property access (5 levels)", () => {
93
+ deep.get(nestedObject, "level1.level2.level3.level4.level5.value");
94
+ });
95
+
96
+ bench("array index access", () => {
97
+ deep.get(arrayObject, "items.2");
98
+ });
99
+
100
+ bench("keyed array access", () => {
101
+ deep.get(arrayObject, "items.item3.value");
102
+ });
103
+
104
+ bench("mixed array and object access", () => {
105
+ deep.get(deeplyNestedArray, "a.0.b.0.c.1.value");
106
+ });
107
+
108
+ bench("large object property access", () => {
109
+ deep.get(largeObject, "key500.nested.data.name");
110
+ });
111
+
112
+ bench("very deep path (100 levels)", () => {
113
+ const path = `${new Array(99).fill("next").join(".")}.value`;
114
+ deep.get(veryDeepObject, path);
115
+ });
116
+
117
+ bench("optional path that doesn't exist", () => {
118
+ deep.get(nestedObject, "level1.nonexistent.path", { optional: true });
119
+ });
120
+
121
+ bench("keyed array with periods in keys", () => {
122
+ deep.get(keyedWithPeriods, "channels.sensor.temperature.1.reading");
123
+ });
124
+
125
+ bench("has() on existing path", () => {
126
+ deep.has(nestedObject, "level1.level2.level3");
127
+ });
128
+
129
+ bench("has() on non-existing path", () => {
130
+ deep.has(nestedObject, "level1.level2.nonexistent");
131
+ });
132
+
133
+ bench("has() on keyed array", () => {
134
+ deep.has(arrayObject, "items.item2");
135
+ });
136
+
137
+ const pathsWith100Keys: any = {};
138
+ let temp = pathsWith100Keys;
139
+ for (let i = 0; i < 100; i++) {
140
+ temp[`key_${i}`] = {};
141
+ temp = temp[`key_${i}`];
142
+ }
143
+ temp.final = "value";
144
+
145
+ bench("100 unique keys traversal", () => {
146
+ const path = Array.from({ length: 100 }, (_, i) => `key_${i}`).join(".");
147
+ deep.get(pathsWith100Keys, `${path}.final`);
148
+ });
149
+
150
+ bench("repeated get operations (cache test)", () => {
151
+ for (let i = 0; i < 10; i++)
152
+ deep.get(nestedObject, "level1.level2.level3.level4.level5.value");
153
+ });
154
+
155
+ bench("get with custom getter function", () => {
156
+ const obj = {
157
+ data: {
158
+ value: () => ({ result: 42 }),
159
+ },
160
+ };
161
+ deep.get(obj, "data.value().result", {
162
+ optional: false,
163
+ getter: (obj, key) => {
164
+ if (key === "value()")
165
+ return (obj as { value: () => { result: number } }).value();
166
+ return obj[key];
167
+ },
168
+ });
169
+ });
170
+ });