chore: knip fixes
chore: knip fixes
This commit is contained in:
59
bun.lock
59
bun.lock
@@ -56,6 +56,7 @@
|
||||
"@opentelemetry/semantic-conventions": "^1.34.0",
|
||||
"@posthog/ai": "^7.4.2",
|
||||
"@puzzmo/revenue-cat-webhook-types": "^1.1.0",
|
||||
"@react-email/components": "^1.0.6",
|
||||
"@sentry/bun": "catalog:",
|
||||
"@supabase/supabase-js": "^2.46.2",
|
||||
"@types/qs": "^6.14.0",
|
||||
@@ -166,6 +167,7 @@
|
||||
"@sentry/vite-plugin": "^4.6.0",
|
||||
"@squircle/core": "^1.0.6",
|
||||
"@squircle/paint-polyfill": "^1.0.6",
|
||||
"@squircle/tailwindcss": "^1.0.6",
|
||||
"@tailwindcss/vite": "^4.0.13",
|
||||
"@tanstack/react-form": "^1.23.8",
|
||||
"@tanstack/react-query": "^5.85.6",
|
||||
@@ -203,6 +205,9 @@
|
||||
"svix-react": "^1.13.3",
|
||||
"swr": "^2.3.3",
|
||||
"tailwind-merge": "^3.0.2",
|
||||
"tailwind-scrollbar-hide": "^4.0.0",
|
||||
"tailwindcss": "^4.0.13",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"use-stick-to-bottom": "^1.1.1",
|
||||
"zod": "^3.25.23",
|
||||
"zustand": "^5.0.8",
|
||||
@@ -1026,7 +1031,47 @@
|
||||
|
||||
"@radix-ui/rect": ["@radix-ui/rect@1.1.1", "", {}, "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw=="],
|
||||
|
||||
"@react-email/render": ["@react-email/render@1.1.2", "", { "dependencies": { "html-to-text": "^9.0.5", "prettier": "^3.5.3", "react-promise-suspense": "^0.3.4" }, "peerDependencies": { "react": "^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-RnRehYN3v9gVlNMehHPHhyp2RQo7+pSkHDtXPvg3s0GbzM9SQMW4Qrf8GRNvtpLC4gsI+Wt0VatNRUFqjvevbw=="],
|
||||
"@react-email/body": ["@react-email/body@0.2.1", "", { "peerDependencies": { "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-ljDiQiJDu/Fq//vSIIP0z5Nuvt4+DX1RqGasstChDGJB/14ogd4VdNS9aacoede/ZjGy3o3Qb+cxyS+XgM6SwQ=="],
|
||||
|
||||
"@react-email/button": ["@react-email/button@0.2.1", "", { "peerDependencies": { "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-qXyj7RZLE7POy9BMKSoqQ00tOXThjOZSUnI2Yu9i29IHngPlmrNayIWBoVKtElES7OWwypUcpiajwi1mUWx6/A=="],
|
||||
|
||||
"@react-email/code-block": ["@react-email/code-block@0.2.1", "", { "dependencies": { "prismjs": "^1.30.0" }, "peerDependencies": { "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-M3B7JpVH4ytgn83/ujRR1k1DQHvTeABiDM61OvAbjLRPhC/5KLHU5KkzIbbuGIrjWwxAbL1kSQzU8MhLEtSxyw=="],
|
||||
|
||||
"@react-email/code-inline": ["@react-email/code-inline@0.0.6", "", { "peerDependencies": { "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-jfhebvv3dVsp3OdPgKXnk8+e2pBiDVZejDOBFzBa/IblrAJ9cQDkN6rBD5IyEg8hTOxwbw3iaI/yZFmDmIguIA=="],
|
||||
|
||||
"@react-email/column": ["@react-email/column@0.0.14", "", { "peerDependencies": { "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-f+W+Bk2AjNO77zynE33rHuQhyqVICx4RYtGX9NKsGUg0wWjdGP0qAuIkhx9Rnmk4/hFMo1fUrtYNqca9fwJdHg=="],
|
||||
|
||||
"@react-email/components": ["@react-email/components@1.0.6", "", { "dependencies": { "@react-email/body": "0.2.1", "@react-email/button": "0.2.1", "@react-email/code-block": "0.2.1", "@react-email/code-inline": "0.0.6", "@react-email/column": "0.0.14", "@react-email/container": "0.0.16", "@react-email/font": "0.0.10", "@react-email/head": "0.0.13", "@react-email/heading": "0.0.16", "@react-email/hr": "0.0.12", "@react-email/html": "0.0.12", "@react-email/img": "0.0.12", "@react-email/link": "0.0.13", "@react-email/markdown": "0.0.18", "@react-email/preview": "0.0.14", "@react-email/render": "2.0.4", "@react-email/row": "0.0.13", "@react-email/section": "0.0.17", "@react-email/tailwind": "2.0.3", "@react-email/text": "0.1.6" }, "peerDependencies": { "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-3GwOeq+5yyiAcwSf7TnHi/HWKn22lXbwxQmkkAviSwZLlhsRVxvmWqRxvUVfQk/HclDUG+62+sGz9qjfb2Uxjw=="],
|
||||
|
||||
"@react-email/container": ["@react-email/container@0.0.16", "", { "peerDependencies": { "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-QWBB56RkkU0AJ9h+qy33gfT5iuZknPC7Un/IjZv9B0QmMIK+WWacc0cH6y2SV5Cv/b99hU94fjEMOOO4enpkbQ=="],
|
||||
|
||||
"@react-email/font": ["@react-email/font@0.0.10", "", { "peerDependencies": { "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-0urVSgCmQIfx5r7Xc586miBnQUVnGp3OTYUm8m5pwtQRdTRO5XrTtEfNJ3JhYhSOruV0nD8fd+dXtKXobum6tA=="],
|
||||
|
||||
"@react-email/head": ["@react-email/head@0.0.13", "", { "peerDependencies": { "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-AJg6le/08Gz4tm+6MtKXqtNNyKHzmooOCdmtqmWxD7FxoAdU1eVcizhtQ0gcnVaY6ethEyE/hnEzQxt1zu5Kog=="],
|
||||
|
||||
"@react-email/heading": ["@react-email/heading@0.0.16", "", { "peerDependencies": { "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-jmsKnQm1ykpBzw4hCYHwBkt5pW2jScXffPeEH5ZRF5tZeF5b1pvlFTO9han7C0pCkZYo1kEvWiRtx69yfCIwuw=="],
|
||||
|
||||
"@react-email/hr": ["@react-email/hr@0.0.12", "", { "peerDependencies": { "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-TwmOmBDibavUQpXBxpmZYi2Iks/yeZOzFYh+di9EltMSnEabH8dMZXrl+pxNXzCgZ2XE8HY7VmUL65Lenfu5PA=="],
|
||||
|
||||
"@react-email/html": ["@react-email/html@0.0.12", "", { "peerDependencies": { "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-KTShZesan+UsreU7PDUV90afrZwU5TLwYlALuCSU0OT+/U8lULNNbAUekg+tGwCnOfIKYtpDPKkAMRdYlqUznw=="],
|
||||
|
||||
"@react-email/img": ["@react-email/img@0.0.12", "", { "peerDependencies": { "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-sRCpEARNVTf3FQhZOC+JTvu5r6ubiYWkT0ucYXg8ctkyi4G8QG+jgYPiNUqVeTLA2STOfmPM/nrk1nb84y6CPQ=="],
|
||||
|
||||
"@react-email/link": ["@react-email/link@0.0.13", "", { "peerDependencies": { "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-lkWc/NjOcefRZMkQoSDDbuKBEBDES9aXnFEOuPH845wD3TxPwh+QTf0fStuzjoRLUZWpHnio4z7qGGRYusn/sw=="],
|
||||
|
||||
"@react-email/markdown": ["@react-email/markdown@0.0.18", "", { "dependencies": { "marked": "^15.0.12" }, "peerDependencies": { "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-gSuYK5fsMbGk87jDebqQ6fa2fKcWlkf2Dkva8kMONqLgGCq8/0d+ZQYMEJsdidIeBo3kmsnHZPrwdFB4HgjUXg=="],
|
||||
|
||||
"@react-email/preview": ["@react-email/preview@0.0.14", "", { "peerDependencies": { "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-aYK8q0IPkBXyMsbpMXgxazwHxYJxTrXrV95GFuu2HbEiIToMwSyUgb8HDFYwPqqfV03/jbwqlsXmFxsOd+VNaw=="],
|
||||
|
||||
"@react-email/render": ["@react-email/render@2.0.4", "", { "dependencies": { "html-to-text": "^9.0.5", "prettier": "^3.5.3" }, "peerDependencies": { "react": "^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-kht2oTFQ1SwrLpd882ahTvUtNa9s53CERHstiTbzhm6aR2Hbykp/mQ4tpPvsBGkKAEvKRlDEoooh60Uk6nHK1g=="],
|
||||
|
||||
"@react-email/row": ["@react-email/row@0.0.13", "", { "peerDependencies": { "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-bYnOac40vIKCId7IkwuLAAsa3fKfSfqCvv6epJKmPE0JBuu5qI4FHFCl9o9dVpIIS08s/ub+Y/txoMt0dYziGw=="],
|
||||
|
||||
"@react-email/section": ["@react-email/section@0.0.17", "", { "peerDependencies": { "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-qNl65ye3W0Rd5udhdORzTV9ezjb+GFqQQSae03NDzXtmJq6sqVXNWNiVolAjvJNypim+zGXmv6J9TcV5aNtE/w=="],
|
||||
|
||||
"@react-email/tailwind": ["@react-email/tailwind@2.0.3", "", { "dependencies": { "tailwindcss": "^4.1.18" }, "peerDependencies": { "@react-email/body": "0.2.1", "@react-email/button": "0.2.1", "@react-email/code-block": "0.2.1", "@react-email/code-inline": "0.0.6", "@react-email/container": "0.0.16", "@react-email/heading": "0.0.16", "@react-email/hr": "0.0.12", "@react-email/img": "0.0.12", "@react-email/link": "0.0.13", "@react-email/preview": "0.0.14", "@react-email/text": "0.1.6", "react": "^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@react-email/body", "@react-email/button", "@react-email/code-block", "@react-email/code-inline", "@react-email/container", "@react-email/heading", "@react-email/hr", "@react-email/img", "@react-email/link", "@react-email/preview"] }, "sha512-URXb/T2WS4RlNGM5QwekYnivuiVUcU87H0y5sqLl6/Oi3bMmgL0Bmw/W9GeJylC+876Vw+E6NkE0uRiUFIQwGg=="],
|
||||
|
||||
"@react-email/text": ["@react-email/text@0.1.6", "", { "peerDependencies": { "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-TYqkioRS45wTR5il3dYk/SbUjjEdhSwh9BtRNB99qNH1pXAwA45H7rAuxehiu8iJQJH0IyIr+6n62gBz9ezmsw=="],
|
||||
|
||||
"@reduxjs/toolkit": ["@reduxjs/toolkit@2.11.2", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@standard-schema/utils": "^0.3.0", "immer": "^11.0.0", "redux": "^5.0.1", "redux-thunk": "^3.1.0", "reselect": "^5.1.0" }, "peerDependencies": { "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" }, "optionalPeers": ["react", "react-redux"] }, "sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ=="],
|
||||
|
||||
@@ -1234,6 +1279,8 @@
|
||||
|
||||
"@squircle/paint-polyfill": ["@squircle/paint-polyfill@1.0.6", "", {}, "sha512-w3FotA1v53MG+R6eGM37wv1aVoW3jwymwGteGCGXEM10Gq7zYeCus5dTgcyW6fhc9piRqsFLyU8MIl+EbtM9RA=="],
|
||||
|
||||
"@squircle/tailwindcss": ["@squircle/tailwindcss@1.0.6", "", {}, "sha512-tmOWdpX7lzBAJzuvajn4NVBtC7mVORmzf/jKREYMw8iJ806Xq4FCUkrtbxkd9blzOx7TG2JwLF3avh5It/Z6TQ=="],
|
||||
|
||||
"@stablelib/base64": ["@stablelib/base64@1.0.1", "", {}, "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ=="],
|
||||
|
||||
"@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||
@@ -2662,6 +2709,8 @@
|
||||
|
||||
"prettier": ["prettier@3.8.1", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg=="],
|
||||
|
||||
"prismjs": ["prismjs@1.30.0", "", {}, "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw=="],
|
||||
|
||||
"process-warning": ["process-warning@5.0.0", "", {}, "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA=="],
|
||||
|
||||
"progress": ["progress@2.0.3", "", {}, "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA=="],
|
||||
@@ -2950,8 +2999,12 @@
|
||||
|
||||
"tailwind-merge": ["tailwind-merge@3.4.0", "", {}, "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g=="],
|
||||
|
||||
"tailwind-scrollbar-hide": ["tailwind-scrollbar-hide@4.0.0", "", { "peerDependencies": { "tailwindcss": ">=3.0.0 || >= 4.0.0 || >= 4.0.0-beta.8 || >= 4.0.0-alpha.20" } }, "sha512-gobtvVcThB2Dxhy0EeYSS1RKQJ5baDFkamkhwBvzvevwX6L4XQfpZ3me9s25Ss1ecFVT5jPYJ50n+7xTBJG9WQ=="],
|
||||
|
||||
"tailwindcss": ["tailwindcss@4.1.18", "", {}, "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw=="],
|
||||
|
||||
"tailwindcss-animate": ["tailwindcss-animate@1.0.7", "", { "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders" } }, "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA=="],
|
||||
|
||||
"tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="],
|
||||
|
||||
"tar-fs": ["tar-fs@3.1.1", "", { "dependencies": { "pump": "^3.0.0", "tar-stream": "^3.1.5" }, "optionalDependencies": { "bare-fs": "^4.0.1", "bare-path": "^3.0.0" } }, "sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg=="],
|
||||
@@ -3654,6 +3707,8 @@
|
||||
|
||||
"@radix-ui/react-tooltip/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="],
|
||||
|
||||
"@react-email/markdown/marked": ["marked@15.0.12", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA=="],
|
||||
|
||||
"@reduxjs/toolkit/immer": ["immer@11.1.3", "", {}, "sha512-6jQTc5z0KJFtr1UgFpIL3N9XSC3saRaI9PwWtzM2pSqkNGtiNkYY2OSwkOGDK2XcTRcLb1pi/aNkKZz0nxVH4Q=="],
|
||||
|
||||
"@sentry-internal/browser-utils/@sentry/core": ["@sentry/core@10.37.0", "", {}, "sha512-hkRz7S4gkKLgPf+p3XgVjVm7tAfvcEPZxeACCC6jmoeKhGkzN44nXwLiqqshJ25RMcSrhfFvJa/FlBg6zupz7g=="],
|
||||
@@ -3914,6 +3969,8 @@
|
||||
|
||||
"react-router/cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="],
|
||||
|
||||
"resend/@react-email/render": ["@react-email/render@1.1.2", "", { "dependencies": { "html-to-text": "^9.0.5", "prettier": "^3.5.3", "react-promise-suspense": "^0.3.4" }, "peerDependencies": { "react": "^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-RnRehYN3v9gVlNMehHPHhyp2RQo7+pSkHDtXPvg3s0GbzM9SQMW4Qrf8GRNvtpLC4gsI+Wt0VatNRUFqjvevbw=="],
|
||||
|
||||
"router/path-to-regexp": ["path-to-regexp@8.3.0", "", {}, "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA=="],
|
||||
|
||||
"send/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
||||
|
||||
13
knip.json
13
knip.json
@@ -11,8 +11,10 @@
|
||||
"src/_luaScriptsV2/**",
|
||||
"src/utils/importUtils/**",
|
||||
"src/utils/scriptUtils/**",
|
||||
"src/utils/workerUtils/**"
|
||||
]
|
||||
"src/utils/workerUtils/**",
|
||||
"src/db/**"
|
||||
],
|
||||
"ignoreDependencies": ["@react-email/components"]
|
||||
},
|
||||
"shared": {
|
||||
"entry": ["index.ts"],
|
||||
@@ -24,7 +26,12 @@
|
||||
"entry": ["src/main.tsx"],
|
||||
"project": ["src/**/*.{ts,tsx}"],
|
||||
"ignore": ["src/components/ai-elements/**"],
|
||||
"ignoreDependencies": ["tailwindcss"]
|
||||
"ignoreDependencies": [
|
||||
"tailwindcss",
|
||||
"tailwind-scrollbar-hide",
|
||||
"tailwindcss-animate",
|
||||
"@squircle/tailwindcss"
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"entry": ["**/*.{ts,js}"],
|
||||
|
||||
@@ -56,6 +56,7 @@
|
||||
"@opentelemetry/semantic-conventions": "^1.34.0",
|
||||
"@posthog/ai": "^7.4.2",
|
||||
"@puzzmo/revenue-cat-webhook-types": "^1.1.0",
|
||||
"@react-email/components": "^1.0.6",
|
||||
"@sentry/bun": "catalog:",
|
||||
"@supabase/supabase-js": "^2.46.2",
|
||||
"@types/qs": "^6.14.0",
|
||||
|
||||
44
server/src/db/initializeDatabaseFunctions.ts
Normal file
44
server/src/db/initializeDatabaseFunctions.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { dirname, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { sql } from "drizzle-orm";
|
||||
import { db } from "./initDrizzle.js";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
/**
|
||||
* Initialize all database functions (stored procedures)
|
||||
* Loads SQL files in dependency order: helpers first, then main functions
|
||||
*/
|
||||
export const initializeDatabaseFunctions = async () => {
|
||||
try {
|
||||
console.log("Initializing database functions...");
|
||||
|
||||
const sqlPath = join(__dirname, "../internal/balances/utils/sql");
|
||||
|
||||
// Load SQL files in dependency order:
|
||||
// 1. Helper functions (used by main functions)
|
||||
// 2. Main functions (depend on helpers)
|
||||
const sqlFiles = [
|
||||
// Helper functions
|
||||
"deductFromRollovers.sql",
|
||||
"deductFromMainBalance.sql",
|
||||
"getTotalBalance.sql",
|
||||
"deductFromAdditionalBalance.sql",
|
||||
"performDeduction.sql",
|
||||
"syncBalances.sql",
|
||||
"syncBalancesV2.sql",
|
||||
];
|
||||
|
||||
for (const file of sqlFiles) {
|
||||
const sqlContent = readFileSync(join(sqlPath, file), "utf-8");
|
||||
await db.execute(sql.raw(sqlContent));
|
||||
console.log(` ✓ Loaded ${file}`);
|
||||
}
|
||||
|
||||
console.log("Database functions initialized successfully");
|
||||
} catch (error) {
|
||||
console.error("Failed to initialize database functions:", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
65
server/src/db/validateDbSchema.ts
Normal file
65
server/src/db/validateDbSchema.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import * as schema from "@autumn/shared";
|
||||
import { is } from "drizzle-orm";
|
||||
import { PgTable } from "drizzle-orm/pg-core";
|
||||
import { logger } from "../external/logtail/logtailUtils";
|
||||
import type { DrizzleCli } from "./initDrizzle";
|
||||
|
||||
const SKIP_TABLES = ["migrationErrors"];
|
||||
|
||||
export const validateDbSchema = async ({ db }: { db: DrizzleCli }) => {
|
||||
// Dynamically get all tables from schema (exclude relations)
|
||||
|
||||
const tableEntries = Object.entries(schema)
|
||||
.filter(([name, table]) => {
|
||||
// Filter out relations and non-table exports
|
||||
if (name.includes("Relations")) return false;
|
||||
// Skip migrationErrors table (known issue)
|
||||
if (SKIP_TABLES.includes(name)) return false;
|
||||
return is(table, PgTable);
|
||||
})
|
||||
.map(([name, table]) => ({ name, table: table as PgTable }));
|
||||
|
||||
// Validate all tables by selecting all columns to ensure schema matches
|
||||
// If schema mismatches, Drizzle will throw an error
|
||||
const start = Date.now();
|
||||
const results = await Promise.allSettled(
|
||||
tableEntries.map(({ name, table }) =>
|
||||
db
|
||||
.select()
|
||||
.from(table)
|
||||
.limit(1)
|
||||
.then(() => ({ name, success: true as const }))
|
||||
.catch((err: Error) => ({
|
||||
name,
|
||||
success: false as const,
|
||||
error: err.message,
|
||||
})),
|
||||
),
|
||||
);
|
||||
const elapsed = Date.now() - start;
|
||||
|
||||
// Check for any failures
|
||||
const failures = results
|
||||
.map((r) => (r.status === "fulfilled" ? r.value : null))
|
||||
.filter(
|
||||
(v): v is { name: string; success: false; error: string } =>
|
||||
v !== null && !v.success,
|
||||
);
|
||||
|
||||
if (failures.length > 0) {
|
||||
const failureDetails = failures
|
||||
.map((f) => `Table '${f.name}': ${f.error}`)
|
||||
.join("; ");
|
||||
logger.error(
|
||||
`Health check failed - DB schema validation error: ${failureDetails}`,
|
||||
);
|
||||
throw new Error(
|
||||
`Health check failed - DB schema validation error: ${failureDetails}`,
|
||||
);
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`Health check passed - DB schema validated for ${tableEntries.length} tables in ${elapsed}ms`,
|
||||
);
|
||||
return true;
|
||||
};
|
||||
182
server/src/db/validateSqlFunctions.ts
Normal file
182
server/src/db/validateSqlFunctions.ts
Normal file
@@ -0,0 +1,182 @@
|
||||
import { createHash } from "node:crypto";
|
||||
import { readdirSync, readFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { sql } from "drizzle-orm";
|
||||
import { logger } from "../external/logtail/logtailUtils";
|
||||
import type { DrizzleCli } from "./initDrizzle";
|
||||
|
||||
type SqlFunction = {
|
||||
name: string;
|
||||
sourceFile: string;
|
||||
content: string;
|
||||
contentHash: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Dynamically discover SQL functions from the deductRpc folder
|
||||
*/
|
||||
const discoverSqlFunctions = (): SqlFunction[] => {
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const deductRpcPath = join(
|
||||
__filename,
|
||||
"../../internal/balances/track/trackUtils/deductRpc",
|
||||
);
|
||||
|
||||
const sqlFiles = readdirSync(deductRpcPath).filter((file) =>
|
||||
file.endsWith(".sql"),
|
||||
);
|
||||
|
||||
return sqlFiles.map((file) => {
|
||||
const filePath = join(deductRpcPath, file);
|
||||
const content = readFileSync(filePath, "utf-8");
|
||||
|
||||
// Extract function name from CREATE FUNCTION statement
|
||||
const functionNameMatch = content.match(
|
||||
/CREATE\s+(?:OR\s+REPLACE\s+)?FUNCTION\s+(\w+)/i,
|
||||
);
|
||||
const functionName = functionNameMatch?.[1];
|
||||
|
||||
if (!functionName) {
|
||||
throw new Error(`Could not extract function name from ${file}`);
|
||||
}
|
||||
|
||||
// Create hash of normalized content (ignore whitespace differences)
|
||||
const normalizedContent = content
|
||||
.replace(/--.*$/gm, "") // Remove comments
|
||||
.replace(/\s+/g, " ") // Normalize whitespace
|
||||
.trim();
|
||||
|
||||
const contentHash = createHash("sha256")
|
||||
.update(normalizedContent)
|
||||
.digest("hex")
|
||||
.substring(0, 16);
|
||||
|
||||
return {
|
||||
name: functionName,
|
||||
sourceFile: file,
|
||||
content,
|
||||
contentHash,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
export const validateSqlFunctions = async ({
|
||||
db,
|
||||
validateContent = false,
|
||||
}: {
|
||||
db: DrizzleCli;
|
||||
validateContent?: boolean;
|
||||
}) => {
|
||||
const start = Date.now();
|
||||
|
||||
// Dynamically discover SQL functions from source files
|
||||
const requiredFunctions = discoverSqlFunctions();
|
||||
logger.info(
|
||||
`Discovered ${requiredFunctions.length} SQL functions from source files`,
|
||||
);
|
||||
|
||||
// Query database for existing functions and their definitions
|
||||
const result = await db.execute<{
|
||||
function_name: string;
|
||||
definition: string;
|
||||
}>(sql`
|
||||
SELECT
|
||||
p.proname as function_name,
|
||||
pg_get_functiondef(p.oid) as definition
|
||||
FROM pg_proc p
|
||||
JOIN pg_namespace n ON p.pronamespace = n.oid
|
||||
WHERE n.nspname = 'public'
|
||||
AND p.prokind = 'f'
|
||||
ORDER BY p.proname;
|
||||
`);
|
||||
|
||||
const dbFunctions = new Map(
|
||||
result.map((row) => [row.function_name, row.definition]),
|
||||
);
|
||||
|
||||
// Check for missing functions
|
||||
const missingFunctions = requiredFunctions.filter(
|
||||
(fn) => !dbFunctions.has(fn.name),
|
||||
);
|
||||
|
||||
if (missingFunctions.length > 0) {
|
||||
const missingDetails = missingFunctions
|
||||
.map((fn) => `'${fn.name}' (from ${fn.sourceFile})`)
|
||||
.join(", ");
|
||||
|
||||
logger.error(
|
||||
`SQL function validation failed: Missing functions: ${missingDetails}`,
|
||||
);
|
||||
throw new Error(`Missing SQL functions: ${missingDetails}`);
|
||||
}
|
||||
|
||||
// Optionally validate function content
|
||||
const mismatchedFunctions: Array<{
|
||||
name: string;
|
||||
sourceFile: string;
|
||||
reason: string;
|
||||
}> = [];
|
||||
|
||||
if (validateContent) {
|
||||
for (const fn of requiredFunctions) {
|
||||
const dbDefinition = dbFunctions.get(fn.name);
|
||||
if (!dbDefinition) continue;
|
||||
|
||||
// Normalize both definitions for comparison
|
||||
const normalizeFunc = (str: string) =>
|
||||
str
|
||||
.replace(/--.*$/gm, "") // Remove comments
|
||||
.replace(/\s+/g, " ") // Normalize whitespace
|
||||
.toLowerCase()
|
||||
.trim();
|
||||
|
||||
const normalizedSource = normalizeFunc(fn.content);
|
||||
const normalizedDb = normalizeFunc(dbDefinition);
|
||||
|
||||
// Create hashes for comparison
|
||||
const sourceHash = createHash("sha256")
|
||||
.update(normalizedSource)
|
||||
.digest("hex")
|
||||
.substring(0, 16);
|
||||
|
||||
const dbHash = createHash("sha256")
|
||||
.update(normalizedDb)
|
||||
.digest("hex")
|
||||
.substring(0, 16);
|
||||
|
||||
if (sourceHash !== dbHash) {
|
||||
mismatchedFunctions.push({
|
||||
name: fn.name,
|
||||
sourceFile: fn.sourceFile,
|
||||
reason: `Source hash: ${sourceHash}, DB hash: ${dbHash}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (mismatchedFunctions.length > 0) {
|
||||
const mismatchDetails = mismatchedFunctions
|
||||
.map((fn) => `'${fn.name}' (${fn.sourceFile}): ${fn.reason}`)
|
||||
.join("; ");
|
||||
|
||||
logger.warn(`SQL function content mismatch detected: ${mismatchDetails}`);
|
||||
logger.warn(
|
||||
"Functions exist but their content differs from source files. Run migrations to update.",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const elapsed = Date.now() - start;
|
||||
|
||||
logger.info(
|
||||
`SQL function validation passed - ${requiredFunctions.length} functions verified in ${elapsed}ms${validateContent ? " (content validated)" : ""}`,
|
||||
);
|
||||
|
||||
if (mismatchedFunctions.length > 0) {
|
||||
logger.info(
|
||||
`⚠️ ${mismatchedFunctions.length} function(s) have content differences`,
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
@@ -17,7 +17,6 @@
|
||||
"author": "Recase Inc.",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"better-auth": "catalog:",
|
||||
"@ai-sdk/react": "^3.0.25",
|
||||
"@autumn/shared": "workspace:*",
|
||||
"@better-auth/dash": "catalog:",
|
||||
@@ -43,8 +42,8 @@
|
||||
"@sentry/vite-plugin": "^4.6.0",
|
||||
"@squircle/core": "^1.0.6",
|
||||
"@squircle/paint-polyfill": "^1.0.6",
|
||||
"@squircle/tailwindcss": "^1.0.6",
|
||||
"@tailwindcss/vite": "^4.0.13",
|
||||
"tailwindcss": "^4.0.13",
|
||||
"@tanstack/react-form": "^1.23.8",
|
||||
"@tanstack/react-query": "^5.85.6",
|
||||
"@tanstack/react-table": "^8.21.3",
|
||||
@@ -54,6 +53,7 @@
|
||||
"ai": "^6.0.5",
|
||||
"autumn-js": "^0.1.8",
|
||||
"axios": "^1.8.3",
|
||||
"better-auth": "catalog:",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "^1.1.1",
|
||||
@@ -80,6 +80,9 @@
|
||||
"svix-react": "^1.13.3",
|
||||
"swr": "^2.3.3",
|
||||
"tailwind-merge": "^3.0.2",
|
||||
"tailwind-scrollbar-hide": "^4.0.0",
|
||||
"tailwindcss": "^4.0.13",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"use-stick-to-bottom": "^1.1.1",
|
||||
"zod": "^3.25.23",
|
||||
"zustand": "^5.0.8"
|
||||
|
||||
Reference in New Issue
Block a user