feat: Implement stock price component and api

This commit is contained in:
bracesproul
2025-03-05 18:02:46 -08:00
parent 10da745533
commit 06b865c1c2
8 changed files with 1116 additions and 937 deletions

391
pnpm-lock.yaml generated
View File

@@ -114,6 +114,9 @@ importers:
react-syntax-highlighter:
specifier: ^15.5.0
version: 15.6.1(react@19.0.0)
recharts:
specifier: ^2.15.1
version: 2.15.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
rehype-katex:
specifier: ^7.0.1
version: 7.0.1
@@ -166,6 +169,9 @@ importers:
autoprefixer:
specifier: ^10.4.20
version: 10.4.20(postcss@8.5.3)
dotenv:
specifier: ^16.4.7
version: 16.4.7
eslint:
specifier: ^9.19.0
version: 9.21.0(jiti@2.4.2)
@@ -1850,6 +1856,60 @@ packages:
integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==,
}
"@types/d3-array@3.2.1":
resolution:
{
integrity: sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==,
}
"@types/d3-color@3.1.3":
resolution:
{
integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==,
}
"@types/d3-ease@3.0.2":
resolution:
{
integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==,
}
"@types/d3-interpolate@3.0.4":
resolution:
{
integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==,
}
"@types/d3-path@3.1.1":
resolution:
{
integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==,
}
"@types/d3-scale@4.0.9":
resolution:
{
integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==,
}
"@types/d3-shape@3.1.7":
resolution:
{
integrity: sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==,
}
"@types/d3-time@3.0.4":
resolution:
{
integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==,
}
"@types/d3-timer@3.0.2":
resolution:
{
integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==,
}
"@types/debug@4.1.12":
resolution:
{
@@ -2499,6 +2559,83 @@ packages:
integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==,
}
d3-array@3.2.4:
resolution:
{
integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==,
}
engines: { node: ">=12" }
d3-color@3.1.0:
resolution:
{
integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==,
}
engines: { node: ">=12" }
d3-ease@3.0.1:
resolution:
{
integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==,
}
engines: { node: ">=12" }
d3-format@3.1.0:
resolution:
{
integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==,
}
engines: { node: ">=12" }
d3-interpolate@3.0.1:
resolution:
{
integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==,
}
engines: { node: ">=12" }
d3-path@3.1.0:
resolution:
{
integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==,
}
engines: { node: ">=12" }
d3-scale@4.0.2:
resolution:
{
integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==,
}
engines: { node: ">=12" }
d3-shape@3.2.0:
resolution:
{
integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==,
}
engines: { node: ">=12" }
d3-time-format@4.1.0:
resolution:
{
integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==,
}
engines: { node: ">=12" }
d3-time@3.1.0:
resolution:
{
integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==,
}
engines: { node: ">=12" }
d3-timer@3.0.1:
resolution:
{
integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==,
}
engines: { node: ">=12" }
date-fns@4.1.0:
resolution:
{
@@ -2524,6 +2661,12 @@ packages:
}
engines: { node: ">=0.10.0" }
decimal.js-light@2.5.1:
resolution:
{
integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==,
}
decode-named-character-reference@1.0.2:
resolution:
{
@@ -2602,6 +2745,12 @@ packages:
integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==,
}
dom-helpers@5.2.1:
resolution:
{
integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==,
}
dotenv@16.4.7:
resolution:
{
@@ -2898,6 +3047,13 @@ packages:
integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==,
}
fast-equals@5.2.2:
resolution:
{
integrity: sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw==,
}
engines: { node: ">=6.0.0" }
fast-glob@3.3.3:
resolution:
{
@@ -3354,6 +3510,13 @@ packages:
integrity: sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==,
}
internmap@2.0.3:
resolution:
{
integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==,
}
engines: { node: ">=12" }
is-alphabetical@1.0.4:
resolution:
{
@@ -3743,6 +3906,12 @@ packages:
integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==,
}
lodash@4.17.21:
resolution:
{
integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==,
}
logform@2.7.0:
resolution:
{
@@ -3756,6 +3925,13 @@ packages:
integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==,
}
loose-envify@1.4.0:
resolution:
{
integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==,
}
hasBin: true
lowlight@1.20.0:
resolution:
{
@@ -4215,6 +4391,13 @@ packages:
}
engines: { node: ">=18" }
object-assign@4.1.1:
resolution:
{
integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==,
}
engines: { node: ">=0.10.0" }
once@1.4.0:
resolution:
{
@@ -4491,6 +4674,12 @@ packages:
}
engines: { node: ">=6" }
prop-types@15.8.1:
resolution:
{
integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==,
}
property-information@5.6.0:
resolution:
{
@@ -4530,6 +4719,18 @@ packages:
peerDependencies:
react: ^19.0.0
react-is@16.13.1:
resolution:
{
integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==,
}
react-is@18.3.1:
resolution:
{
integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==,
}
react-markdown@10.0.1:
resolution:
{
@@ -4600,6 +4801,15 @@ packages:
peerDependencies:
react: ">=16.8"
react-smooth@4.0.4:
resolution:
{
integrity: sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==,
}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
react-style-singleton@2.2.3:
resolution:
{
@@ -4630,6 +4840,15 @@ packages:
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
react-transition-group@4.4.5:
resolution:
{
integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==,
}
peerDependencies:
react: ">=16.6.0"
react-dom: ">=16.6.0"
react@19.0.0:
resolution:
{
@@ -4651,6 +4870,22 @@ packages:
}
engines: { node: ">= 14.18.0" }
recharts-scale@0.4.5:
resolution:
{
integrity: sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==,
}
recharts@2.15.1:
resolution:
{
integrity: sha512-v8PUTUlyiDe56qUj82w/EDVuzEFXwEHp9/xOowGAZwfLjB9uAy3GllQVIYMWF6nU+qibx85WF75zD7AjqoT54Q==,
}
engines: { node: ">=14" }
peerDependencies:
react: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
refractor@3.6.0:
resolution:
{
@@ -5003,6 +5238,12 @@ packages:
integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==,
}
tiny-invariant@1.3.3:
resolution:
{
integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==,
}
to-regex-range@5.0.1:
resolution:
{
@@ -5303,6 +5544,12 @@ packages:
integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==,
}
victory-vendor@36.9.2:
resolution:
{
integrity: sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==,
}
vite@6.2.0:
resolution:
{
@@ -6512,6 +6759,30 @@ snapshots:
dependencies:
"@babel/types": 7.26.9
"@types/d3-array@3.2.1": {}
"@types/d3-color@3.1.3": {}
"@types/d3-ease@3.0.2": {}
"@types/d3-interpolate@3.0.4":
dependencies:
"@types/d3-color": 3.1.3
"@types/d3-path@3.1.1": {}
"@types/d3-scale@4.0.9":
dependencies:
"@types/d3-time": 3.0.4
"@types/d3-shape@3.1.7":
dependencies:
"@types/d3-path": 3.1.1
"@types/d3-time@3.0.4": {}
"@types/d3-timer@3.0.2": {}
"@types/debug@4.1.12":
dependencies:
"@types/ms": 2.1.0
@@ -6880,6 +7151,44 @@ snapshots:
csstype@3.1.3: {}
d3-array@3.2.4:
dependencies:
internmap: 2.0.3
d3-color@3.1.0: {}
d3-ease@3.0.1: {}
d3-format@3.1.0: {}
d3-interpolate@3.0.1:
dependencies:
d3-color: 3.1.0
d3-path@3.1.0: {}
d3-scale@4.0.2:
dependencies:
d3-array: 3.2.4
d3-format: 3.1.0
d3-interpolate: 3.0.1
d3-time: 3.1.0
d3-time-format: 4.1.0
d3-shape@3.2.0:
dependencies:
d3-path: 3.1.0
d3-time-format@4.1.0:
dependencies:
d3-time: 3.1.0
d3-time@3.1.0:
dependencies:
d3-array: 3.2.4
d3-timer@3.0.1: {}
date-fns@4.1.0: {}
debug@4.4.0:
@@ -6888,6 +7197,8 @@ snapshots:
decamelize@1.2.0: {}
decimal.js-light@2.5.1: {}
decode-named-character-reference@1.0.2:
dependencies:
character-entities: 2.0.2
@@ -6917,6 +7228,11 @@ snapshots:
dependencies:
dequal: 2.0.3
dom-helpers@5.2.1:
dependencies:
"@babel/runtime": 7.26.9
csstype: 3.1.3
dotenv@16.4.7: {}
dunder-proto@1.0.1:
@@ -7154,6 +7470,8 @@ snapshots:
fast-deep-equal@3.1.3: {}
fast-equals@5.2.2: {}
fast-glob@3.3.3:
dependencies:
"@nodelib/fs.stat": 2.0.5
@@ -7444,6 +7762,8 @@ snapshots:
inline-style-parser@0.2.4: {}
internmap@2.0.3: {}
is-alphabetical@1.0.4: {}
is-alphabetical@2.0.1: {}
@@ -7623,6 +7943,8 @@ snapshots:
lodash.merge@4.6.2: {}
lodash@4.17.21: {}
logform@2.7.0:
dependencies:
"@colors/colors": 1.6.0
@@ -7634,6 +7956,10 @@ snapshots:
longest-streak@3.1.0: {}
loose-envify@1.4.0:
dependencies:
js-tokens: 4.0.0
lowlight@1.20.0:
dependencies:
fault: 1.0.4
@@ -8083,6 +8409,8 @@ snapshots:
path-key: 4.0.0
unicorn-magic: 0.3.0
object-assign@4.1.1: {}
once@1.4.0:
dependencies:
wrappy: 1.0.2
@@ -8257,6 +8585,12 @@ snapshots:
prismjs@1.29.0: {}
prop-types@15.8.1:
dependencies:
loose-envify: 1.4.0
object-assign: 4.1.1
react-is: 16.13.1
property-information@5.6.0:
dependencies:
xtend: 4.0.2
@@ -8277,6 +8611,10 @@ snapshots:
react: 19.0.0
scheduler: 0.25.0
react-is@16.13.1: {}
react-is@18.3.1: {}
react-markdown@10.0.1(@types/react@19.0.10)(react@19.0.0):
dependencies:
"@types/hast": 3.0.4
@@ -8346,6 +8684,14 @@ snapshots:
"@remix-run/router": 1.23.0
react: 19.0.0
react-smooth@4.0.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
dependencies:
fast-equals: 5.2.2
prop-types: 15.8.1
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
react-transition-group: 4.4.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
react-style-singleton@2.2.3(@types/react@19.0.10)(react@19.0.0):
dependencies:
get-nonce: 1.0.1
@@ -8373,6 +8719,15 @@ snapshots:
transitivePeerDependencies:
- "@types/react"
react-transition-group@4.4.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
dependencies:
"@babel/runtime": 7.26.9
dom-helpers: 5.2.1
loose-envify: 1.4.0
prop-types: 15.8.1
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
react@19.0.0: {}
readable-stream@3.6.2:
@@ -8383,6 +8738,23 @@ snapshots:
readdirp@4.1.2: {}
recharts-scale@0.4.5:
dependencies:
decimal.js-light: 2.5.1
recharts@2.15.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
dependencies:
clsx: 2.1.1
eventemitter3: 4.0.7
lodash: 4.17.21
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
react-is: 18.3.1
react-smooth: 4.0.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
recharts-scale: 0.4.5
tiny-invariant: 1.3.3
victory-vendor: 36.9.2
refractor@3.6.0:
dependencies:
hastscript: 6.0.0
@@ -8607,6 +8979,8 @@ snapshots:
text-hex@1.0.0: {}
tiny-invariant@1.3.3: {}
to-regex-range@5.0.1:
dependencies:
is-number: 7.0.0
@@ -8778,6 +9152,23 @@ snapshots:
"@types/unist": 3.0.3
vfile-message: 4.0.2
victory-vendor@36.9.2:
dependencies:
"@types/d3-array": 3.2.1
"@types/d3-ease": 3.0.2
"@types/d3-interpolate": 3.0.4
"@types/d3-scale": 4.0.9
"@types/d3-shape": 3.1.7
"@types/d3-time": 3.0.4
"@types/d3-timer": 3.0.2
d3-array: 3.2.4
d3-ease: 3.0.1
d3-interpolate: 3.0.1
d3-scale: 4.0.2
d3-shape: 3.2.0
d3-time: 3.1.0
d3-timer: 3.0.1
vite@6.2.0(@types/node@22.13.5)(jiti@2.4.2)(lightningcss@1.29.1)(tsx@4.19.3)(yaml@2.7.0):
dependencies:
esbuild: 0.25.0