wip
This commit is contained in:
348
bun.lock
348
bun.lock
@@ -275,8 +275,6 @@
|
||||
|
||||
"@ai-sdk/ui-utils": ["@ai-sdk/ui-utils@1.2.11", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "@ai-sdk/provider-utils": "2.2.8", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.23.8" } }, "sha512-3zcwCc8ezzFlwp3ZD15wAPjf2Au4s3vAbKsXQVyhxODHcmu0iyPO2Eua6D/vicq/AUm/BAo60r97O6HU+EI0+w=="],
|
||||
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
"@amplitude/analytics-browser": ["@amplitude/analytics-browser@2.27.0", "", { "dependencies": { "@amplitude/analytics-core": "^2.28.0", "@amplitude/plugin-autocapture-browser": "^1.15.3", "@amplitude/plugin-network-capture-browser": "^1.6.9", "@amplitude/plugin-page-view-tracking-browser": "^2.5.3", "@amplitude/plugin-web-vitals-browser": "^0.1.0-beta.31", "tslib": "^2.4.1" } }, "sha512-1LBCLmnr7aUpLtOp64lpr8GzN3vPKM0fwiM/7tWJ9XU9/GKA+k3CUSjI8OdERKrw2yVywujoAVQo4anGZXYIDA=="],
|
||||
|
||||
"@amplitude/analytics-client-common": ["@amplitude/analytics-client-common@2.4.8", "", { "dependencies": { "@amplitude/analytics-connector": "^1.4.8", "@amplitude/analytics-core": "^2.28.0", "@amplitude/analytics-types": "^2.10.0", "tslib": "^2.4.1" } }, "sha512-cSm9Q+qcLy65kV2MnD7WlKrFMDOkj14dHM2YQn5njUrSXQljyjtYCss+HVzgIlWPSDgA5v6dDeHwxzZl3VSggw=="],
|
||||
@@ -333,7 +331,6 @@
|
||||
|
||||
"@amplitude/unified": ["@amplitude/unified@1.0.0-beta.9", "", { "dependencies": { "@amplitude/analytics-browser": "^2.25.2", "@amplitude/engagement-browser": "^1.0.3", "@amplitude/plugin-experiment-browser": "^1.0.0-beta.0", "@amplitude/plugin-session-replay-browser": "^1.16.0" } }, "sha512-JS7Dq6GJ6amR0olsVu0ekypAUHpcect8pJNTKjWYtEwdUbfQP6giE41Bum8rLR85SOMBqbuMdDr3yO9oj2CKyw=="],
|
||||
|
||||
>>>>>>> staging
|
||||
"@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.32.1", "", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7" } }, "sha512-U9JwTrDvdQ9iWuABVsMLj8nJVwAyQz6QXvgLsVhryhCEPkLsbcP/MXxm+jYcAwLoV8ESbaTTjnD4kuAFa+Hyjg=="],
|
||||
|
||||
"@autumn/scripts": ["@autumn/scripts@workspace:scripts"],
|
||||
@@ -388,17 +385,11 @@
|
||||
|
||||
"@babel/types": ["@babel/types@7.28.4", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q=="],
|
||||
|
||||
<<<<<<< HEAD
|
||||
"@better-auth/core": ["@better-auth/core@1.3.27", "", { "dependencies": { "better-call": "1.0.19", "zod": "^4.1.5" } }, "sha512-3Sfdax6MQyronY+znx7bOsfQHI6m1SThvJWb0RDscFEAhfqLy95k1sl+/PgGyg0cwc2cUXoEiAOSqYdFYrg3vA=="],
|
||||
|
||||
"@better-auth/stripe": ["@better-auth/stripe@1.3.27", "", { "dependencies": { "defu": "^6.1.4", "zod": "^4.1.5" }, "peerDependencies": { "@better-auth/core": "1.3.27", "better-auth": "1.3.27", "stripe": "^18" } }, "sha512-YXmWMvX07lCDHheRB65jl80sSHTClFeJqN2vU6v5tI+0R4tzYBeWxYjvkS6cks1JJ8IjPUKTew0gy2Px3AlOCA=="],
|
||||
=======
|
||||
"@better-auth/core": ["@better-auth/core@1.3.28", "", { "dependencies": { "zod": "^4.1.5" }, "peerDependencies": { "@better-auth/utils": "0.3.0", "@better-fetch/fetch": "1.1.18", "better-call": "1.0.19", "better-sqlite3": "^12.4.1", "jose": "^6.1.0", "kysely": "^0.28.5", "nanostores": "^1.0.1" } }, "sha512-iZOGKlXaNEIEj0Q3z7+REE94I89YUJ0sel/1pvm1qqdHkm59G+ToTysHtyTcLYby3+UtAeJRKyFAY0nwJH0H7A=="],
|
||||
|
||||
"@better-auth/stripe": ["@better-auth/stripe@1.3.28", "", { "dependencies": { "defu": "^6.1.4", "zod": "^4.1.5" }, "peerDependencies": { "@better-auth/core": "1.3.28", "better-auth": "1.3.28", "stripe": "^18" } }, "sha512-FGvQnIcLoMzNrfvzIVmT80/bBYyUwIQwHrG5KXbmTi26r9YTE0pylDiYD7RHX6q7H3wOntUXUyGDJ4b19h1+ew=="],
|
||||
|
||||
"@better-auth/telemetry": ["@better-auth/telemetry@1.3.28", "", { "dependencies": { "@better-auth/core": "1.3.28", "@better-auth/utils": "0.3.0", "@better-fetch/fetch": "1.1.18" } }, "sha512-qZtV82IFuyQZc2c37VkiDgO/qfqPnJuWIyeC/iFK1AA5N8RSuC2+CVIH1sNDytPXUAthbYeOzcOCW2YEkgz1Ow=="],
|
||||
>>>>>>> staging
|
||||
|
||||
"@better-auth/utils": ["@better-auth/utils@0.3.0", "", {}, "sha512-W+Adw6ZA6mgvnSnhOki270rwJ42t4XzSK6YWGF//BbVXL6SwCLWfyzBc1lN2m/4RM28KubdBKQ4X5VMoLRNPQw=="],
|
||||
|
||||
@@ -424,15 +415,6 @@
|
||||
|
||||
"@browserbasehq/sdk": ["@browserbasehq/sdk@2.6.0", "", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7" } }, "sha512-83iXP5D7xMm8Wyn66TUaUrgoByCmAJuoMoZQI3sGg3JAiMlTfnCIMqyVBoNSaItaPIkaCnrsj6LiusmXV2X9YA=="],
|
||||
|
||||
<<<<<<< HEAD
|
||||
"@clerk/backend": ["@clerk/backend@2.18.0", "", { "dependencies": { "@clerk/shared": "^3.27.4", "@clerk/types": "^4.93.0", "cookie": "1.0.2", "standardwebhooks": "^1.0.0", "tslib": "2.8.1" } }, "sha512-Vt7yP0FbNDptWGAsNYrCK572k+0m/jFDwXaHor4164rekwUJXunQVo8DX8vqCOZn9LsKUZbs4odmG9uoRVePIQ=="],
|
||||
|
||||
"@clerk/express": ["@clerk/express@1.7.38", "", { "dependencies": { "@clerk/backend": "^2.18.0", "@clerk/shared": "^3.27.4", "@clerk/types": "^4.93.0", "tslib": "2.8.1" }, "peerDependencies": { "express": "^4.17.0 || ^5.0.0" } }, "sha512-+5SAeggm0847Ylvh6W/TGCqy/+ddhpzkYBdNNoPgjK13PuKkDodg7t5Wl8VR+Egs62y2Y9YEoTEQ6NE3MBd/sw=="],
|
||||
|
||||
"@clerk/shared": ["@clerk/shared@3.27.4", "", { "dependencies": { "@clerk/types": "^4.93.0", "dequal": "2.0.3", "glob-to-regexp": "0.4.1", "js-cookie": "3.0.5", "std-env": "^3.9.0", "swr": "2.3.4" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0 || ^19.0.0-0", "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-0" }, "optionalPeers": ["react", "react-dom"] }, "sha512-K+glWUasxr9CeMHfOcpU+wiRhGQfQm1i7TQcM6tZuKmUD+DltryuF7sPjR+pj2ABUGSXt3Chlv2Y1i+8zaLLRw=="],
|
||||
|
||||
"@clerk/types": ["@clerk/types@4.93.0", "", { "dependencies": { "csstype": "3.1.3" } }, "sha512-xrYAAF0OHCvZm5kPjCexff44ImhIC85LG7Z8OU3WEWvsGyeSnEjZuMTp5jZnns6i+hLQbxn5/cgINzC+V7VSpw=="],
|
||||
=======
|
||||
"@clerk/backend": ["@clerk/backend@2.18.3", "", { "dependencies": { "@clerk/shared": "^3.28.2", "@clerk/types": "^4.95.0", "cookie": "1.0.2", "standardwebhooks": "^1.0.0", "tslib": "2.8.1" } }, "sha512-fWMq/Tb2hgfUXLKJN8jr6pbpA5XLUwC4BjWz7lB5Y+YhXhBrO7GtfpZIS91L/aDhNb17X6IaE6XvS6tDJBCUUw=="],
|
||||
|
||||
"@clerk/express": ["@clerk/express@1.7.41", "", { "dependencies": { "@clerk/backend": "^2.18.3", "@clerk/shared": "^3.28.2", "@clerk/types": "^4.95.0", "tslib": "2.8.1" }, "peerDependencies": { "express": "^4.17.0 || ^5.0.0" } }, "sha512-SYKXi/Prjkxx15QGOjHlvjfwO05vUg6fBaxVdg53/vcLJNyyfER+JM9qRxxcmEqN5vkkpdqURANF3eZ8dkf85w=="],
|
||||
@@ -440,7 +422,6 @@
|
||||
"@clerk/shared": ["@clerk/shared@3.28.2", "", { "dependencies": { "@clerk/types": "^4.95.0", "dequal": "2.0.3", "glob-to-regexp": "0.4.1", "js-cookie": "3.0.5", "std-env": "^3.9.0", "swr": "2.3.4" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0 || ^19.0.0-0", "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-0" }, "optionalPeers": ["react", "react-dom"] }, "sha512-BfBCPaoPoLCiU0b0MhQUfCjs+bWRRLkdHw0vBffSjtsFLxp1b5IL5D8nKgDPIKIIv7DmCCmO15tr+GqG3CGpYQ=="],
|
||||
|
||||
"@clerk/types": ["@clerk/types@4.95.0", "", { "dependencies": { "csstype": "3.1.3" } }, "sha512-K1kI3BjvufG1mZBZJ5Q8Yu9wV6AFpjjITml5vhvP95xibJWOi3eYvlRCTKXDNKBFGvQfrTJbwn67jSG2VdyLKw=="],
|
||||
>>>>>>> staging
|
||||
|
||||
"@clickhouse/client": ["@clickhouse/client@1.12.1", "", { "dependencies": { "@clickhouse/client-common": "1.12.1" } }, "sha512-7ORY85rphRazqHzImNXMrh4vsaPrpetFoTWpZYueCO2bbO6PXYDXp/GQ4DgxnGIqbWB/Di1Ai+Xuwq2o7DJ36A=="],
|
||||
|
||||
@@ -466,59 +447,6 @@
|
||||
|
||||
"@esbuild-kit/esm-loader": ["@esbuild-kit/esm-loader@2.6.5", "", { "dependencies": { "@esbuild-kit/core-utils": "^3.3.2", "get-tsconfig": "^4.7.0" } }, "sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA=="],
|
||||
|
||||
<<<<<<< HEAD
|
||||
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.10", "", { "os": "aix", "cpu": "ppc64" }, "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw=="],
|
||||
|
||||
"@esbuild/android-arm": ["@esbuild/android-arm@0.25.10", "", { "os": "android", "cpu": "arm" }, "sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w=="],
|
||||
|
||||
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.10", "", { "os": "android", "cpu": "arm64" }, "sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg=="],
|
||||
|
||||
"@esbuild/android-x64": ["@esbuild/android-x64@0.25.10", "", { "os": "android", "cpu": "x64" }, "sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg=="],
|
||||
|
||||
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.10", "", { "os": "darwin", "cpu": "arm64" }, "sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA=="],
|
||||
|
||||
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.10", "", { "os": "darwin", "cpu": "x64" }, "sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg=="],
|
||||
|
||||
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.10", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg=="],
|
||||
|
||||
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.10", "", { "os": "freebsd", "cpu": "x64" }, "sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA=="],
|
||||
|
||||
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.10", "", { "os": "linux", "cpu": "arm" }, "sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg=="],
|
||||
|
||||
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ=="],
|
||||
|
||||
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.10", "", { "os": "linux", "cpu": "ia32" }, "sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ=="],
|
||||
|
||||
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.10", "", { "os": "linux", "cpu": "none" }, "sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg=="],
|
||||
|
||||
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.10", "", { "os": "linux", "cpu": "none" }, "sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA=="],
|
||||
|
||||
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.10", "", { "os": "linux", "cpu": "ppc64" }, "sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA=="],
|
||||
|
||||
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.10", "", { "os": "linux", "cpu": "none" }, "sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA=="],
|
||||
|
||||
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.10", "", { "os": "linux", "cpu": "s390x" }, "sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew=="],
|
||||
|
||||
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.10", "", { "os": "linux", "cpu": "x64" }, "sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA=="],
|
||||
|
||||
"@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.10", "", { "os": "none", "cpu": "arm64" }, "sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A=="],
|
||||
|
||||
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.10", "", { "os": "none", "cpu": "x64" }, "sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig=="],
|
||||
|
||||
"@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.10", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw=="],
|
||||
|
||||
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.10", "", { "os": "openbsd", "cpu": "x64" }, "sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw=="],
|
||||
|
||||
"@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.10", "", { "os": "none", "cpu": "arm64" }, "sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag=="],
|
||||
|
||||
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.10", "", { "os": "sunos", "cpu": "x64" }, "sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ=="],
|
||||
|
||||
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.10", "", { "os": "win32", "cpu": "arm64" }, "sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw=="],
|
||||
|
||||
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.10", "", { "os": "win32", "cpu": "ia32" }, "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw=="],
|
||||
|
||||
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.10", "", { "os": "win32", "cpu": "x64" }, "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw=="],
|
||||
=======
|
||||
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.11", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg=="],
|
||||
|
||||
"@esbuild/android-arm": ["@esbuild/android-arm@0.25.11", "", { "os": "android", "cpu": "arm" }, "sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg=="],
|
||||
@@ -570,7 +498,6 @@
|
||||
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.11", "", { "os": "win32", "cpu": "ia32" }, "sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA=="],
|
||||
|
||||
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.11", "", { "os": "win32", "cpu": "x64" }, "sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA=="],
|
||||
>>>>>>> staging
|
||||
|
||||
"@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g=="],
|
||||
|
||||
@@ -578,21 +505,13 @@
|
||||
|
||||
"@eslint/config-array": ["@eslint/config-array@0.21.1", "", { "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA=="],
|
||||
|
||||
<<<<<<< HEAD
|
||||
"@eslint/config-helpers": ["@eslint/config-helpers@0.4.0", "", { "dependencies": { "@eslint/core": "^0.16.0" } }, "sha512-WUFvV4WoIwW8Bv0KeKCIIEgdSiFOsulyN0xrMu+7z43q/hkOLXjvb5u7UC9jDxvRzcrbEmuZBX5yJZz1741jog=="],
|
||||
=======
|
||||
"@eslint/config-helpers": ["@eslint/config-helpers@0.4.1", "", { "dependencies": { "@eslint/core": "^0.16.0" } }, "sha512-csZAzkNhsgwb0I/UAV6/RGFTbiakPCf0ZrGmrIxQpYvGZ00PhTkSnyKNolphgIvmnJeGw6rcGVEXfTzUnFuEvw=="],
|
||||
>>>>>>> staging
|
||||
|
||||
"@eslint/core": ["@eslint/core@0.16.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q=="],
|
||||
|
||||
"@eslint/eslintrc": ["@eslint/eslintrc@3.3.1", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ=="],
|
||||
|
||||
<<<<<<< HEAD
|
||||
"@eslint/js": ["@eslint/js@9.37.0", "", {}, "sha512-jaS+NJ+hximswBG6pjNX0uEJZkrT0zwpVi3BA3vX22aFGjJjmgSTSmPpZCRKmoBL5VY/M6p0xsSJx7rk7sy5gg=="],
|
||||
=======
|
||||
"@eslint/js": ["@eslint/js@9.38.0", "", {}, "sha512-UZ1VpFvXf9J06YG9xQBdnzU+kthors6KjhMAl6f4gH4usHyh31rUf2DLGInT8RFYIReYXNSydgPY0V2LuWgl7A=="],
|
||||
>>>>>>> staging
|
||||
|
||||
"@eslint/object-schema": ["@eslint/object-schema@2.1.7", "", {}, "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA=="],
|
||||
|
||||
@@ -770,25 +689,6 @@
|
||||
|
||||
"@msgpackr-extract/msgpackr-extract-win32-x64": ["@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3", "", { "os": "win32", "cpu": "x64" }, "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ=="],
|
||||
|
||||
<<<<<<< HEAD
|
||||
"@next/env": ["@next/env@15.5.5", "", {}, "sha512-2Zhvss36s/yL+YSxD5ZL5dz5pI6ki1OLxYlh6O77VJ68sBnlUrl5YqhBgCy7FkdMsp9RBeGFwpuDCdpJOqdKeQ=="],
|
||||
|
||||
"@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.5.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-lYExGHuFIHeOxf40mRLWoA84iY2sLELB23BV5FIDHhdJkN1LpRTPc1MDOawgTo5ifbM5dvAwnGuHyNm60G1+jw=="],
|
||||
|
||||
"@next/swc-darwin-x64": ["@next/swc-darwin-x64@15.5.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-cacs/WQqa96IhqUm+7CY+z/0j9sW6X80KE07v3IAJuv+z0UNvJtKSlT/T1w1SpaQRa9l0wCYYZlRZUhUOvEVmg=="],
|
||||
|
||||
"@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@15.5.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-tLd90SvkRFik6LSfuYjcJEmwqcNEnVYVOyKTacSazya/SLlSwy/VYKsDE4GIzOBd+h3gW+FXqShc2XBavccHCg=="],
|
||||
|
||||
"@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@15.5.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-ekV76G2R/l3nkvylkfy9jBSYHeB4QcJ7LdDseT6INnn1p51bmDS1eGoSoq+RxfQ7B1wt+Qa0pIl5aqcx0GLpbw=="],
|
||||
|
||||
"@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@15.5.5", "", { "os": "linux", "cpu": "x64" }, "sha512-tI+sBu+3FmWtqlqD4xKJcj3KJtqbniLombKTE7/UWyyoHmOyAo3aZ7QcEHIOgInXOG1nt0rwh0KGmNbvSB0Djg=="],
|
||||
|
||||
"@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@15.5.5", "", { "os": "linux", "cpu": "x64" }, "sha512-kDRh+epN/ulroNJLr+toDjN+/JClY5L+OAWjOrrKCI0qcKvTw9GBx7CU/rdA2bgi4WpZN3l0rf/3+b8rduEwrQ=="],
|
||||
|
||||
"@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@15.5.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-GDgdNPFFqiKjTrmfw01sMMRWhVN5wOCmFzPloxa7ksDfX6TZt62tAK986f0ZYqWpvDFqeBCLAzmgTURvtQBdgw=="],
|
||||
|
||||
"@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.5.5", "", { "os": "win32", "cpu": "x64" }, "sha512-5kE3oRJxc7M8RmcTANP8RGoJkaYlwIiDD92gSwCjJY0+j8w8Sl1lvxgQ3bxfHY2KkHFai9tpy/Qx1saWV8eaJQ=="],
|
||||
=======
|
||||
"@next/env": ["@next/env@15.5.6", "", {}, "sha512-3qBGRW+sCGzgbpc5TS1a0p7eNxnOarGVQhZxfvTdnV0gFI61lX7QNtQ4V1TSREctXzYn5NetbUsLvyqwLFJM6Q=="],
|
||||
|
||||
"@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.5.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ES3nRz7N+L5Umz4KoGfZ4XX6gwHplwPhioVRc25+QNsDa7RtUF/z8wJcbuQ2Tffm5RZwuN2A063eapoJ1u4nPg=="],
|
||||
@@ -806,7 +706,6 @@
|
||||
"@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@15.5.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-ONOMrqWxdzXDJNh2n60H6gGyKed42Ieu6UTVPZteXpuKbLZTH4G4eBMsr5qWgOBA+s7F+uB4OJbZnrkEDnZ5Fg=="],
|
||||
|
||||
"@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.5.6", "", { "os": "win32", "cpu": "x64" }, "sha512-pxK4VIjFRx1MY92UycLOOw7dTdvccWsNETQ0kDHkBlcFH1GrTLUjSiHU1ohrznnux6TqRHgv5oflhfIWZwVROQ=="],
|
||||
>>>>>>> staging
|
||||
|
||||
"@noble/ciphers": ["@noble/ciphers@2.0.1", "", {}, "sha512-xHK3XHPUW8DTAobU+G0XT+/w+JLM7/8k1UFdB5xg/zTFPnFCobhftzw8wl4Lw2aq/Rvir5pxfZV5fEazmeCJ2g=="],
|
||||
|
||||
@@ -1001,11 +900,8 @@
|
||||
"@peculiar/x509": ["@peculiar/x509@1.14.0", "", { "dependencies": { "@peculiar/asn1-cms": "^2.5.0", "@peculiar/asn1-csr": "^2.5.0", "@peculiar/asn1-ecc": "^2.5.0", "@peculiar/asn1-pkcs9": "^2.5.0", "@peculiar/asn1-rsa": "^2.5.0", "@peculiar/asn1-schema": "^2.5.0", "@peculiar/asn1-x509": "^2.5.0", "pvtsutils": "^1.3.6", "reflect-metadata": "^0.2.2", "tslib": "^2.8.1", "tsyringe": "^4.10.0" } }, "sha512-Yc4PDxN3OrxUPiXgU63c+ZRXKGE8YKF2McTciYhUHFtHVB0KMnjeFSU0qpztGhsp4P0uKix4+J2xEpIEDu8oXg=="],
|
||||
|
||||
"@phosphor-icons/react": ["@phosphor-icons/react@2.1.10", "", { "peerDependencies": { "react": ">= 16.8", "react-dom": ">= 16.8" } }, "sha512-vt8Tvq8GLjheAZZYa+YG/pW7HDbov8El/MANW8pOAz4eGxrwhnbfrQZq0Cp4q8zBEu8NIhHdnr+r8thnfRSNYA=="],
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
|
||||
"@pinojs/redact": ["@pinojs/redact@0.4.0", "", {}, "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg=="],
|
||||
>>>>>>> staging
|
||||
|
||||
"@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="],
|
||||
|
||||
@@ -1161,55 +1057,6 @@
|
||||
|
||||
"@react-email/text": ["@react-email/text@0.1.5", "", { "peerDependencies": { "react": "^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-o5PNHFSE085VMXayxH+SJ1LSOtGsTv+RpNKnTiJDrJUwoBu77G3PlKOsZZQHCNyD28WsQpl9v2WcJLbQudqwPg=="],
|
||||
|
||||
<<<<<<< HEAD
|
||||
"@reduxjs/toolkit": ["@reduxjs/toolkit@2.9.0", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@standard-schema/utils": "^0.3.0", "immer": "^10.0.3", "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-fSfQlSRu9Z5yBkvsNhYF2rPS8cGXn/TZVrlwN1948QyZ8xMZ0JvP50S2acZNaf+o63u6aEeMjipFyksjIcWrog=="],
|
||||
|
||||
"@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.27", "", {}, "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="],
|
||||
|
||||
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.52.4", "", { "os": "android", "cpu": "arm" }, "sha512-BTm2qKNnWIQ5auf4deoetINJm2JzvihvGb9R6K/ETwKLql/Bb3Eg2H1FBp1gUb4YGbydMA3jcmQTR73q7J+GAA=="],
|
||||
|
||||
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.52.4", "", { "os": "android", "cpu": "arm64" }, "sha512-P9LDQiC5vpgGFgz7GSM6dKPCiqR3XYN1WwJKA4/BUVDjHpYsf3iBEmVz62uyq20NGYbiGPR5cNHI7T1HqxNs2w=="],
|
||||
|
||||
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.52.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-QRWSW+bVccAvZF6cbNZBJwAehmvG9NwfWHwMy4GbWi/BQIA/laTIktebT2ipVjNncqE6GLPxOok5hsECgAxGZg=="],
|
||||
|
||||
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.52.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-hZgP05pResAkRJxL1b+7yxCnXPGsXU0fG9Yfd6dUaoGk+FhdPKCJ5L1Sumyxn8kvw8Qi5PvQ8ulenUbRjzeCTw=="],
|
||||
|
||||
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.52.4", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-xmc30VshuBNUd58Xk4TKAEcRZHaXlV+tCxIXELiE9sQuK3kG8ZFgSPi57UBJt8/ogfhAF5Oz4ZSUBN77weM+mQ=="],
|
||||
|
||||
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.52.4", "", { "os": "freebsd", "cpu": "x64" }, "sha512-WdSLpZFjOEqNZGmHflxyifolwAiZmDQzuOzIq9L27ButpCVpD7KzTRtEG1I0wMPFyiyUdOO+4t8GvrnBLQSwpw=="],
|
||||
|
||||
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.52.4", "", { "os": "linux", "cpu": "arm" }, "sha512-xRiOu9Of1FZ4SxVbB0iEDXc4ddIcjCv2aj03dmW8UrZIW7aIQ9jVJdLBIhxBI+MaTnGAKyvMwPwQnoOEvP7FgQ=="],
|
||||
|
||||
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.52.4", "", { "os": "linux", "cpu": "arm" }, "sha512-FbhM2p9TJAmEIEhIgzR4soUcsW49e9veAQCziwbR+XWB2zqJ12b4i/+hel9yLiD8pLncDH4fKIPIbt5238341Q=="],
|
||||
|
||||
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.52.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-4n4gVwhPHR9q/g8lKCyz0yuaD0MvDf7dV4f9tHt0C73Mp8h38UCtSCSE6R9iBlTbXlmA8CjpsZoujhszefqueg=="],
|
||||
|
||||
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.52.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-u0n17nGA0nvi/11gcZKsjkLj1QIpAuPFQbR48Subo7SmZJnGxDpspyw2kbpuoQnyK+9pwf3pAoEXerJs/8Mi9g=="],
|
||||
|
||||
"@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.52.4", "", { "os": "linux", "cpu": "none" }, "sha512-0G2c2lpYtbTuXo8KEJkDkClE/+/2AFPdPAbmaHoE870foRFs4pBrDehilMcrSScrN/fB/1HTaWO4bqw+ewBzMQ=="],
|
||||
|
||||
"@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.52.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-teSACug1GyZHmPDv14VNbvZFX779UqWTsd7KtTM9JIZRDI5NUwYSIS30kzI8m06gOPB//jtpqlhmraQ68b5X2g=="],
|
||||
|
||||
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.52.4", "", { "os": "linux", "cpu": "none" }, "sha512-/MOEW3aHjjs1p4Pw1Xk4+3egRevx8Ji9N6HUIA1Ifh8Q+cg9dremvFCUbOX2Zebz80BwJIgCBUemjqhU5XI5Eg=="],
|
||||
|
||||
"@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.52.4", "", { "os": "linux", "cpu": "none" }, "sha512-1HHmsRyh845QDpEWzOFtMCph5Ts+9+yllCrREuBR/vg2RogAQGGBRC8lDPrPOMnrdOJ+mt1WLMOC2Kao/UwcvA=="],
|
||||
|
||||
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.52.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-seoeZp4L/6D1MUyjWkOMRU6/iLmCU2EjbMTyAG4oIOs1/I82Y5lTeaxW0KBfkUdHAWN7j25bpkt0rjnOgAcQcA=="],
|
||||
|
||||
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.52.4", "", { "os": "linux", "cpu": "x64" }, "sha512-Wi6AXf0k0L7E2gteNsNHUs7UMwCIhsCTs6+tqQ5GPwVRWMaflqGec4Sd8n6+FNFDw9vGcReqk2KzBDhCa1DLYg=="],
|
||||
|
||||
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.52.4", "", { "os": "linux", "cpu": "x64" }, "sha512-dtBZYjDmCQ9hW+WgEkaffvRRCKm767wWhxsFW3Lw86VXz/uJRuD438/XvbZT//B96Vs8oTA8Q4A0AfHbrxP9zw=="],
|
||||
|
||||
"@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.52.4", "", { "os": "none", "cpu": "arm64" }, "sha512-1ox+GqgRWqaB1RnyZXL8PD6E5f7YyRUJYnCqKpNzxzP0TkaUh112NDrR9Tt+C8rJ4x5G9Mk8PQR3o7Ku2RKqKA=="],
|
||||
|
||||
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.52.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-8GKr640PdFNXwzIE0IrkMWUNUomILLkfeHjXBi/nUvFlpZP+FA8BKGKpacjW6OUUHaNI6sUURxR2U2g78FOHWQ=="],
|
||||
|
||||
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.52.4", "", { "os": "win32", "cpu": "ia32" }, "sha512-AIy/jdJ7WtJ/F6EcfOb2GjR9UweO0n43jNObQMb6oGxkYTfLcnN7vYYpG+CN3lLxrQkzWnMOoNSHTW54pgbVxw=="],
|
||||
|
||||
"@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.52.4", "", { "os": "win32", "cpu": "x64" }, "sha512-UF9KfsH9yEam0UjTwAgdK0anlQ7c8/pWPU2yVjyWcF1I1thABt6WXE47cI71pGiZ8wGvxohBoLnxM04L/wj8mQ=="],
|
||||
|
||||
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.52.4", "", { "os": "win32", "cpu": "x64" }, "sha512-bf9PtUa0u8IXDVxzRToFQKsNCRz9qLYfR/MpECxl4mRoWYjAeFjgxj1XdZr2M/GNVpT05p+LgQOHopYDlUu6/w=="],
|
||||
=======
|
||||
"@reduxjs/toolkit": ["@reduxjs/toolkit@2.9.1", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@standard-schema/utils": "^0.3.0", "immer": "^10.0.3", "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-sETJ3qO72y7L7WiR5K54UFLT3jRzAtqeBPVO15xC3bGA6kDqCH8m/v7BKCPH4czydXzz/1lPEGLvew7GjOO3Qw=="],
|
||||
|
||||
"@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.27", "", {}, "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="],
|
||||
@@ -1261,7 +1108,6 @@
|
||||
"@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.52.5", "", { "os": "win32", "cpu": "x64" }, "sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ=="],
|
||||
|
||||
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.52.5", "", { "os": "win32", "cpu": "x64" }, "sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg=="],
|
||||
>>>>>>> staging
|
||||
|
||||
"@selderee/plugin-htmlparser2": ["@selderee/plugin-htmlparser2@0.11.0", "", { "dependencies": { "domhandler": "^5.0.3", "selderee": "^0.11.0" } }, "sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ=="],
|
||||
|
||||
@@ -1295,23 +1141,6 @@
|
||||
|
||||
"@standard-schema/utils": ["@standard-schema/utils@0.3.0", "", {}, "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g=="],
|
||||
|
||||
<<<<<<< HEAD
|
||||
"@supabase/auth-js": ["@supabase/auth-js@2.75.0", "", { "dependencies": { "@supabase/node-fetch": "2.6.15" } }, "sha512-J8TkeqCOMCV4KwGKVoxmEBuDdHRwoInML2vJilthOo7awVCro2SM+tOcpljORwuBQ1vHUtV62Leit+5wlxrNtw=="],
|
||||
|
||||
"@supabase/functions-js": ["@supabase/functions-js@2.75.0", "", { "dependencies": { "@supabase/node-fetch": "2.6.15" } }, "sha512-18yk07Moj/xtQ28zkqswxDavXC3vbOwt1hDuYM3/7xPnwwpKnsmPyZ7bQ5th4uqiJzQ135t74La9tuaxBR6e7w=="],
|
||||
|
||||
"@supabase/node-fetch": ["@supabase/node-fetch@2.6.15", "", { "dependencies": { "whatwg-url": "^5.0.0" } }, "sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ=="],
|
||||
|
||||
"@supabase/postgrest-js": ["@supabase/postgrest-js@2.75.0", "", { "dependencies": { "@supabase/node-fetch": "2.6.15" } }, "sha512-YfBz4W/z7eYCFyuvHhfjOTTzRrQIvsMG2bVwJAKEVVUqGdzqfvyidXssLBG0Fqlql1zJFgtsPpK1n4meHrI7tg=="],
|
||||
|
||||
"@supabase/realtime-js": ["@supabase/realtime-js@2.75.0", "", { "dependencies": { "@supabase/node-fetch": "2.6.15", "@types/phoenix": "^1.6.6", "@types/ws": "^8.18.1", "ws": "^8.18.2" } }, "sha512-B4Xxsf2NHd5cEnM6MGswOSPSsZKljkYXpvzKKmNxoUmNQOfB7D8HOa6NwHcUBSlxcjV+vIrYKcYXtavGJqeGrw=="],
|
||||
|
||||
"@supabase/ssr": ["@supabase/ssr@0.5.2", "", { "dependencies": { "@types/cookie": "^0.6.0", "cookie": "^0.7.0" }, "peerDependencies": { "@supabase/supabase-js": "^2.43.4" } }, "sha512-n3plRhr2Bs8Xun1o4S3k1CDv17iH5QY9YcoEvXX3bxV1/5XSasA0mNXYycFmADIdtdE6BG9MRjP5CGIs8qxC8A=="],
|
||||
|
||||
"@supabase/storage-js": ["@supabase/storage-js@2.75.0", "", { "dependencies": { "@supabase/node-fetch": "2.6.15" } }, "sha512-wpJMYdfFDckDiHQaTpK+Ib14N/O2o0AAWWhguKvmmMurB6Unx17GGmYp5rrrqCTf8S1qq4IfIxTXxS4hzrUySg=="],
|
||||
|
||||
"@supabase/supabase-js": ["@supabase/supabase-js@2.75.0", "", { "dependencies": { "@supabase/auth-js": "2.75.0", "@supabase/functions-js": "2.75.0", "@supabase/node-fetch": "2.6.15", "@supabase/postgrest-js": "2.75.0", "@supabase/realtime-js": "2.75.0", "@supabase/storage-js": "2.75.0" } }, "sha512-8UN/vATSgS2JFuJlMVr51L3eUDz+j1m7Ww63wlvHLKULzCDaVWYzvacCjBTLW/lX/vedI2LBI4Vg+01G9ufsJQ=="],
|
||||
=======
|
||||
"@supabase/auth-js": ["@supabase/auth-js@2.75.1", "", { "dependencies": { "@supabase/node-fetch": "2.6.15" } }, "sha512-zktlxtXstQuVys/egDpVsargD9hQtG20CMdtn+mMn7d2Ulkzy2tgUT5FUtpppvCJtd9CkhPHO/73rvi5W6Am5A=="],
|
||||
|
||||
"@supabase/functions-js": ["@supabase/functions-js@2.75.1", "", { "dependencies": { "@supabase/node-fetch": "2.6.15" } }, "sha512-xO+01SUcwVmmo67J7Htxq8FmhkYLFdWkxfR/taxBOI36wACEUNQZmroXGPl4PkpYxBO7TaDsRHYGxUpv9zTKkg=="],
|
||||
@@ -1327,7 +1156,6 @@
|
||||
"@supabase/storage-js": ["@supabase/storage-js@2.75.1", "", { "dependencies": { "@supabase/node-fetch": "2.6.15" } }, "sha512-WdGEhroflt5O398Yg3dpf1uKZZ6N3CGloY9iGsdT873uWbkQKoP0wG8mtx98dh0fhj6dAlzBqOAvnlV12cJfzA=="],
|
||||
|
||||
"@supabase/supabase-js": ["@supabase/supabase-js@2.75.1", "", { "dependencies": { "@supabase/auth-js": "2.75.1", "@supabase/functions-js": "2.75.1", "@supabase/node-fetch": "2.6.15", "@supabase/postgrest-js": "2.75.1", "@supabase/realtime-js": "2.75.1", "@supabase/storage-js": "2.75.1" } }, "sha512-GEPVBvjQimcMd9z5K1eTKTixTRb6oVbudoLQ9JKqTUJnR6GQdBU4OifFZean1AnHfsQwtri1fop2OWwsMv019w=="],
|
||||
>>>>>>> staging
|
||||
|
||||
"@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="],
|
||||
|
||||
@@ -1361,19 +1189,11 @@
|
||||
|
||||
"@tailwindcss/vite": ["@tailwindcss/vite@4.1.14", "", { "dependencies": { "@tailwindcss/node": "4.1.14", "@tailwindcss/oxide": "4.1.14", "tailwindcss": "4.1.14" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" } }, "sha512-BoFUoU0XqgCUS1UXWhmDJroKKhNXeDzD7/XwabjkDIAbMnc4ULn5e2FuEuBbhZ6ENZoSYzKlzvZ44Yr6EUDUSA=="],
|
||||
|
||||
<<<<<<< HEAD
|
||||
"@tanstack/query-core": ["@tanstack/query-core@5.90.3", "", {}, "sha512-HtPOnCwmx4dd35PfXU8jjkhwYrsHfuqgC8RCJIwWglmhIUIlzPP0ZcEkDAc+UtAWCiLm7T8rxeEfHZlz3hYMCA=="],
|
||||
|
||||
"@tanstack/query-devtools": ["@tanstack/query-devtools@5.90.1", "", {}, "sha512-GtINOPjPUH0OegJExZ70UahT9ykmAhmtNVcmtdnOZbxLwT7R5OmRztR5Ahe3/Cu7LArEmR6/588tAycuaWb1xQ=="],
|
||||
|
||||
"@tanstack/react-query": ["@tanstack/react-query@5.90.3", "", { "dependencies": { "@tanstack/query-core": "5.90.3" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-i/LRL6DtuhG6bjGzavIMIVuKKPWx2AnEBIsBfuMm3YoHne0a20nWmsatOCBcVSaT0/8/5YFjNkebHAPLVUSi0Q=="],
|
||||
=======
|
||||
"@tanstack/query-core": ["@tanstack/query-core@5.90.5", "", {}, "sha512-wLamYp7FaDq6ZnNehypKI5fNvxHPfTYylE0m/ZpuuzJfJqhR5Pxg9gvGBHZx4n7J+V5Rg5mZxHHTlv25Zt5u+w=="],
|
||||
|
||||
"@tanstack/query-devtools": ["@tanstack/query-devtools@5.90.1", "", {}, "sha512-GtINOPjPUH0OegJExZ70UahT9ykmAhmtNVcmtdnOZbxLwT7R5OmRztR5Ahe3/Cu7LArEmR6/588tAycuaWb1xQ=="],
|
||||
|
||||
"@tanstack/react-query": ["@tanstack/react-query@5.90.5", "", { "dependencies": { "@tanstack/query-core": "5.90.5" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-pN+8UWpxZkEJ/Rnnj2v2Sxpx1WFlaa9L6a4UO89p6tTQbeo+m0MS8oYDjbggrR8QcTyjKoYWKS3xJQGr3ExT8Q=="],
|
||||
>>>>>>> staging
|
||||
|
||||
"@tanstack/react-query-devtools": ["@tanstack/react-query-devtools@5.90.2", "", { "dependencies": { "@tanstack/query-devtools": "5.90.1" }, "peerDependencies": { "@tanstack/react-query": "^5.90.2", "react": "^18 || ^19" } }, "sha512-vAXJzZuBXtCQtrY3F/yUNJCV4obT/A/n81kb3+YqLbro5Z2+phdAbceO+deU3ywPw8B42oyJlp4FhO0SoivDFQ=="],
|
||||
|
||||
@@ -1423,11 +1243,8 @@
|
||||
|
||||
"@types/cors": ["@types/cors@2.8.19", "", { "dependencies": { "@types/node": "*" } }, "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg=="],
|
||||
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
"@types/css-font-loading-module": ["@types/css-font-loading-module@0.0.7", "", {}, "sha512-nl09VhutdjINdWyXxHWN/w9zlNCfr60JUqJbd24YXUuCwgeL0TpFSdElCwb6cxfB6ybE19Gjj4g0jsgkXxKv1Q=="],
|
||||
|
||||
>>>>>>> staging
|
||||
"@types/d3-array": ["@types/d3-array@3.2.2", "", {}, "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw=="],
|
||||
|
||||
"@types/d3-color": ["@types/d3-color@3.1.3", "", {}, "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="],
|
||||
@@ -1486,11 +1303,7 @@
|
||||
|
||||
"@types/mysql": ["@types/mysql@2.15.27", "", { "dependencies": { "@types/node": "*" } }, "sha512-YfWiV16IY0OeBfBCk8+hXKmdTKrKlwKN1MNKAPBu5JYxLwBEZl7QzeEpGnlZb3VMGJrrGmB84gXiH+ofs/TezA=="],
|
||||
|
||||
<<<<<<< HEAD
|
||||
"@types/node": ["@types/node@24.7.2", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-/NbVmcGTP+lj5oa4yiYxxeBjRivKQ5Ns1eSZeB99ExsEQ6rX5XYU1Zy/gGxY/ilqtD4Etx9mKyrPxZRetiahhA=="],
|
||||
=======
|
||||
"@types/node": ["@types/node@24.8.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-alv65KGRadQVfVcG69MuB4IzdYVpRwMG/mq8KWOaoOdyY617P5ivaDiMCGOFDWD2sAn5Q0mR3mRtUOgm99hL9Q=="],
|
||||
>>>>>>> staging
|
||||
|
||||
"@types/node-fetch": ["@types/node-fetch@2.6.13", "", { "dependencies": { "@types/node": "*", "form-data": "^4.0.4" } }, "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw=="],
|
||||
|
||||
@@ -1522,7 +1335,7 @@
|
||||
|
||||
"@types/semver": ["@types/semver@7.7.1", "", {}, "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA=="],
|
||||
|
||||
"@types/send": ["@types/send@1.2.0", "", { "dependencies": { "@types/node": "*" } }, "sha512-zBF6vZJn1IaMpg3xUF25VK3gd3l8zwE0ZLRX7dsQyQi+jp4E8mMDJNGDYnYse+bQhYwWERTxVwHpi3dMOq7RKQ=="],
|
||||
"@types/send": ["@types/send@0.17.5", "", { "dependencies": { "@types/mime": "^1", "@types/node": "*" } }, "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w=="],
|
||||
|
||||
"@types/serve-static": ["@types/serve-static@1.15.9", "", { "dependencies": { "@types/http-errors": "*", "@types/node": "*", "@types/send": "<1" } }, "sha512-dOTIuqpWLyl3BBXU3maNQsS4A3zuuoYRNIvYSxxhebPfXg2mzWQEPne/nlJ37yOse6uGgR386uTpdsx4D0QZWA=="],
|
||||
|
||||
@@ -1566,23 +1379,6 @@
|
||||
|
||||
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.46.1", "", { "dependencies": { "@typescript-eslint/types": "8.46.1", "eslint-visitor-keys": "^4.2.1" } }, "sha512-ptkmIf2iDkNUjdeu2bQqhFPV1m6qTnFFjg7PPDjxKWaMaP0Z6I9l30Jr3g5QqbZGdw8YdYvLp+XnqnWWZOg/NA=="],
|
||||
|
||||
<<<<<<< HEAD
|
||||
"@typescript/native-preview": ["@typescript/native-preview@7.0.0-dev.20251014.1", "", { "optionalDependencies": { "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20251014.1", "@typescript/native-preview-darwin-x64": "7.0.0-dev.20251014.1", "@typescript/native-preview-linux-arm": "7.0.0-dev.20251014.1", "@typescript/native-preview-linux-arm64": "7.0.0-dev.20251014.1", "@typescript/native-preview-linux-x64": "7.0.0-dev.20251014.1", "@typescript/native-preview-win32-arm64": "7.0.0-dev.20251014.1", "@typescript/native-preview-win32-x64": "7.0.0-dev.20251014.1" }, "bin": { "tsgo": "bin/tsgo.js" } }, "sha512-IqmX5CYCBqXbfL+HKlcQAMaDlfJ0Z8OhUxvADFV2TENnzSYI4CuhvKxwOB2wFSLXufVsgtAlf3Fjwn24KmMyPQ=="],
|
||||
|
||||
"@typescript/native-preview-darwin-arm64": ["@typescript/native-preview-darwin-arm64@7.0.0-dev.20251014.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-7rQoLlerWnwnvrM56hP4rdEbo4xDE4zr7cch+EzgENq/tbXYereGq1fmnR83UNglb1Eyy53OvJZ3O2csYBa2vg=="],
|
||||
|
||||
"@typescript/native-preview-darwin-x64": ["@typescript/native-preview-darwin-x64@7.0.0-dev.20251014.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-SF29o9NFRGDM23Jz0nVO4/yS78GQ81rtOemmCVNXuJotoY4bP3npGDyEmfkZQHZgDOXogs2OWy3t7NUJ235ANQ=="],
|
||||
|
||||
"@typescript/native-preview-linux-arm": ["@typescript/native-preview-linux-arm@7.0.0-dev.20251014.1", "", { "os": "linux", "cpu": "arm" }, "sha512-o5cu7h+BBAp6V4qxYY5RWuaYouN3j+MGFLrrUtvvNj4XKM+kbq5qwsgVRsmJZ1LfUvHmzyQs86vt9djAWedzjQ=="],
|
||||
|
||||
"@typescript/native-preview-linux-arm64": ["@typescript/native-preview-linux-arm64@7.0.0-dev.20251014.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-+YWbW/JF4uggEUBr+vflqI5i7bL4Z3XInCOyUO1qQEY7VmfDCsPEzIwGi37O1mixfxw9Qj8LQsptCkU+fqKwGw=="],
|
||||
|
||||
"@typescript/native-preview-linux-x64": ["@typescript/native-preview-linux-x64@7.0.0-dev.20251014.1", "", { "os": "linux", "cpu": "x64" }, "sha512-3LC4tgcgi6zWJWBUpBNXOGSY3yISJrQezSP/T+v+mQRApkdoIpTSHIyQAhgaagcs3MOQRaqiIPaLOVrdHXdU6A=="],
|
||||
|
||||
"@typescript/native-preview-win32-arm64": ["@typescript/native-preview-win32-arm64@7.0.0-dev.20251014.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-P0D4UEXwzFZh3pHexe2Ky1tW/HjY/HxTBTIajz2ViDCNPw7uDSEsXSB4H9TTiFJw8gVdTUFbsoAQp1MteTeORA=="],
|
||||
|
||||
"@typescript/native-preview-win32-x64": ["@typescript/native-preview-win32-x64@7.0.0-dev.20251014.1", "", { "os": "win32", "cpu": "x64" }, "sha512-fi53g2ihH7tkQLlz8hZGAb2V+3aNZpcxrZ530CQ4xcWwAqssEj0EaZJX0VLEtIQBar1ttGVK9Pz/wJU9sYyVzg=="],
|
||||
=======
|
||||
"@typescript/native-preview": ["@typescript/native-preview@7.0.0-dev.20251019.1", "", { "optionalDependencies": { "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20251019.1", "@typescript/native-preview-darwin-x64": "7.0.0-dev.20251019.1", "@typescript/native-preview-linux-arm": "7.0.0-dev.20251019.1", "@typescript/native-preview-linux-arm64": "7.0.0-dev.20251019.1", "@typescript/native-preview-linux-x64": "7.0.0-dev.20251019.1", "@typescript/native-preview-win32-arm64": "7.0.0-dev.20251019.1", "@typescript/native-preview-win32-x64": "7.0.0-dev.20251019.1" }, "bin": { "tsgo": "bin/tsgo.js" } }, "sha512-ytCPJouuNmJyGjZwSFg/v0Ugkn/52drU5HymW1p0l6dU+iHuTIaZSKfHFWETJxQVwyyYqNxxvC0QMxTDfwPlGQ=="],
|
||||
|
||||
"@typescript/native-preview-darwin-arm64": ["@typescript/native-preview-darwin-arm64@7.0.0-dev.20251019.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-GecLPUXgaptUiBrpuLhKwsxsckJ/rBA1e9pY2HdFx+mIWze1FTUiXu0It6EcFbQ2IZCMke1WuZZz18Bo4lftwA=="],
|
||||
@@ -1598,7 +1394,6 @@
|
||||
"@typescript/native-preview-win32-arm64": ["@typescript/native-preview-win32-arm64@7.0.0-dev.20251019.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-vJvkjZEN6GRH+Y3atQO5t8WGGjqgnHzwU0ZP+4oqYJl7G6sNiqRw23lHedZxZF6Iav+lE6SPMAhVbz97LlvkbQ=="],
|
||||
|
||||
"@typescript/native-preview-win32-x64": ["@typescript/native-preview-win32-x64@7.0.0-dev.20251019.1", "", { "os": "win32", "cpu": "x64" }, "sha512-GMYGYxRHIX/+hFn7SGj9LMh4CLm90ZByGH4BvgKvwJGEctkYtOB6wXJUvQMo5koel7pDY1Yy3uChiexuG11biQ=="],
|
||||
>>>>>>> staging
|
||||
|
||||
"@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="],
|
||||
|
||||
@@ -1686,11 +1481,7 @@
|
||||
|
||||
"bare-events": ["bare-events@2.8.0", "", { "peerDependencies": { "bare-abort-controller": "*" }, "optionalPeers": ["bare-abort-controller"] }, "sha512-AOhh6Bg5QmFIXdViHbMc2tLDsBIRxdkIaIddPslJF9Z5De3APBScuqGP2uThXnIpqFrgoxMNC6km7uXNIMLHXA=="],
|
||||
|
||||
<<<<<<< HEAD
|
||||
"bare-fs": ["bare-fs@4.4.10", "", { "dependencies": { "bare-events": "^2.5.4", "bare-path": "^3.0.0", "bare-stream": "^2.6.4", "bare-url": "^2.2.2", "fast-fifo": "^1.3.2" }, "peerDependencies": { "bare-buffer": "*" }, "optionalPeers": ["bare-buffer"] }, "sha512-arqVF+xX/rJHwrONZaSPhlzleT2gXwVs9rsAe1p1mIVwWZI2A76/raio+KwwxfWMO8oV9Wo90EaUkS2QwVmy4w=="],
|
||||
=======
|
||||
"bare-fs": ["bare-fs@4.4.11", "", { "dependencies": { "bare-events": "^2.5.4", "bare-path": "^3.0.0", "bare-stream": "^2.6.4", "bare-url": "^2.2.2", "fast-fifo": "^1.3.2" }, "peerDependencies": { "bare-buffer": "*" }, "optionalPeers": ["bare-buffer"] }, "sha512-Bejmm9zRMvMTRoHS+2adgmXw1ANZnCNx+B5dgZpGwlP1E3x6Yuxea8RToddHUbWtVV0iUMWqsgZr8+jcgUI2SA=="],
|
||||
>>>>>>> staging
|
||||
|
||||
"bare-os": ["bare-os@3.6.2", "", {}, "sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A=="],
|
||||
|
||||
@@ -1708,15 +1499,6 @@
|
||||
|
||||
"base64id": ["base64id@2.0.0", "", {}, "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog=="],
|
||||
|
||||
<<<<<<< HEAD
|
||||
"baseline-browser-mapping": ["baseline-browser-mapping@2.8.16", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-OMu3BGQ4E7P1ErFsIPpbJh0qvDudM/UuJeHgkAvfWe+0HFJCXh+t/l8L6fVLR55RI/UbKrVLnAXZSVwd9ysWYw=="],
|
||||
|
||||
"basic-ftp": ["basic-ftp@5.0.5", "", {}, "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg=="],
|
||||
|
||||
"better-auth": ["better-auth@1.3.27", "", { "dependencies": { "@better-auth/core": "1.3.27", "@better-auth/utils": "0.3.0", "@better-fetch/fetch": "1.1.18", "@noble/ciphers": "^2.0.0", "@noble/hashes": "^2.0.0", "@simplewebauthn/browser": "^13.1.2", "@simplewebauthn/server": "^13.1.2", "better-call": "1.0.19", "defu": "^6.1.4", "jose": "^6.1.0", "kysely": "^0.28.5", "nanostores": "^1.0.1", "zod": "^4.1.5" } }, "sha512-SwiGAJ7yU6dBhNg0NdV1h5M8T5sa7/AszZVc4vBfMDrLLmvUfbt9JoJ0uRUJUEdKRAAxTyl9yA+F3+GhtAD80w=="],
|
||||
|
||||
"better-call": ["better-call@1.0.19", "", { "dependencies": { "@better-auth/utils": "^0.3.0", "@better-fetch/fetch": "^1.1.4", "rou3": "^0.5.1", "set-cookie-parser": "^2.7.1", "uncrypto": "^0.1.3" } }, "sha512-sI3GcA1SCVa3H+CDHl8W8qzhlrckwXOTKhqq3OOPXjgn5aTOMIqGY34zLY/pHA6tRRMjTUC3lz5Mi7EbDA24Kw=="],
|
||||
=======
|
||||
"baseline-browser-mapping": ["baseline-browser-mapping@2.8.18", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-UYmTpOBwgPScZpS4A+YbapwWuBwasxvO/2IOHArSsAhL/+ZdmATBXTex3t+l2hXwLVYK382ibr/nKoY9GKe86w=="],
|
||||
|
||||
"basic-ftp": ["basic-ftp@5.0.5", "", {}, "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg=="],
|
||||
@@ -1726,7 +1508,6 @@
|
||||
"better-call": ["better-call@1.0.19", "", { "dependencies": { "@better-auth/utils": "^0.3.0", "@better-fetch/fetch": "^1.1.4", "rou3": "^0.5.1", "set-cookie-parser": "^2.7.1", "uncrypto": "^0.1.3" } }, "sha512-sI3GcA1SCVa3H+CDHl8W8qzhlrckwXOTKhqq3OOPXjgn5aTOMIqGY34zLY/pHA6tRRMjTUC3lz5Mi7EbDA24Kw=="],
|
||||
|
||||
"better-sqlite3": ["better-sqlite3@12.4.1", "", { "dependencies": { "bindings": "^1.5.0", "prebuild-install": "^7.1.1" } }, "sha512-3yVdyZhklTiNrtg+4WqHpJpFDd+WHTg2oM7UcR80GqL05AOV0xEJzc6qNvFYoEtE+hRp1n9MpN6/+4yhlGkDXQ=="],
|
||||
>>>>>>> staging
|
||||
|
||||
"bignumber.js": ["bignumber.js@9.3.1", "", {}, "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ=="],
|
||||
|
||||
@@ -1770,11 +1551,7 @@
|
||||
|
||||
"camelcase": ["camelcase@6.3.0", "", {}, "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA=="],
|
||||
|
||||
<<<<<<< HEAD
|
||||
"caniuse-lite": ["caniuse-lite@1.0.30001750", "", {}, "sha512-cuom0g5sdX6rw00qOoLNSFCJ9/mYIsuSOA+yzpDw8eopiFqcVwQvZHqov0vmEighRxX++cfC0Vg1G+1Iy/mSpQ=="],
|
||||
=======
|
||||
"caniuse-lite": ["caniuse-lite@1.0.30001751", "", {}, "sha512-A0QJhug0Ly64Ii3eIqHu5X51ebln3k4yTUkY1j8drqpWHVreg/VLijN48cZ1bYPiqOQuqpkIKnzr/Ul8V+p6Cw=="],
|
||||
>>>>>>> staging
|
||||
|
||||
"ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="],
|
||||
|
||||
@@ -1854,11 +1631,7 @@
|
||||
|
||||
"convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
|
||||
|
||||
<<<<<<< HEAD
|
||||
"convex": ["convex@1.27.5", "", { "dependencies": { "esbuild": "0.25.4", "prettier": "^3.0.0" }, "peerDependencies": { "@auth0/auth0-react": "^2.0.1", "@clerk/clerk-react": "^4.12.8 || ^5.0.0", "react": "^18.0.0 || ^19.0.0-0 || ^19.0.0" }, "optionalPeers": ["@auth0/auth0-react", "@clerk/clerk-react", "react"], "bin": { "convex": "bin/main.js" } }, "sha512-6YU/AVPnoNdAaJABKBI9c5IqRSKsow/c4yo/ntaOWtd8Dff2P2zaImA/ougICfPgTuTvjKRbgkxk6lJhODzb4g=="],
|
||||
=======
|
||||
"convex": ["convex@1.28.0", "", { "dependencies": { "esbuild": "0.25.4", "prettier": "^3.0.0" }, "peerDependencies": { "@auth0/auth0-react": "^2.0.1", "@clerk/clerk-react": "^4.12.8 || ^5.0.0", "react": "^18.0.0 || ^19.0.0-0 || ^19.0.0" }, "optionalPeers": ["@auth0/auth0-react", "@clerk/clerk-react", "react"], "bin": { "convex": "bin/main.js" } }, "sha512-40FgeJ/LxP9TxnkDDztU/A5gcGTdq1klcTT5mM0Ak+kSlQiDktMpjNX1TfkWLxXaE3lI4qvawKH95v2RiYgFxA=="],
|
||||
>>>>>>> staging
|
||||
|
||||
"cookie": ["cookie@0.7.1", "", {}, "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w=="],
|
||||
|
||||
@@ -1936,11 +1709,8 @@
|
||||
|
||||
"decode-uri-component": ["decode-uri-component@0.4.1", "", {}, "sha512-+8VxcR21HhTy8nOt6jf20w0c9CADrw1O8d+VZ/YzzCt4bJ3uBjw+D1q2osAB8RnpwwaeYBxy0HyKQxD5JBMuuQ=="],
|
||||
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
"decompress-response": ["decompress-response@6.0.0", "", { "dependencies": { "mimic-response": "^3.1.0" } }, "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ=="],
|
||||
|
||||
>>>>>>> staging
|
||||
"deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="],
|
||||
|
||||
"deep-extend": ["deep-extend@0.6.0", "", {}, "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="],
|
||||
@@ -2011,11 +1781,7 @@
|
||||
|
||||
"ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="],
|
||||
|
||||
<<<<<<< HEAD
|
||||
"electron-to-chromium": ["electron-to-chromium@1.5.235", "", {}, "sha512-i/7ntLFwOdoHY7sgjlTIDo4Sl8EdoTjWIaKinYOVfC6bOp71bmwenyZthWHcasxgHDNWbWxvG9M3Ia116zIaYQ=="],
|
||||
=======
|
||||
"electron-to-chromium": ["electron-to-chromium@1.5.237", "", {}, "sha512-icUt1NvfhGLar5lSWH3tHNzablaA5js3HVHacQimfP8ViEBOQv+L7DKEuHdbTZ0SKCO1ogTJTIL1Gwk9S6Qvcg=="],
|
||||
>>>>>>> staging
|
||||
|
||||
"emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||
|
||||
@@ -2043,11 +1809,7 @@
|
||||
|
||||
"es-toolkit": ["es-toolkit@1.40.0", "", {}, "sha512-8o6w0KFmU0CiIl0/Q/BCEOabF2IJaELM1T2PWj6e8KqzHv1gdx+7JtFnDwOx1kJH/isJ5NwlDG1nCr1HrRF94Q=="],
|
||||
|
||||
<<<<<<< HEAD
|
||||
"esbuild": ["esbuild@0.25.10", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.10", "@esbuild/android-arm": "0.25.10", "@esbuild/android-arm64": "0.25.10", "@esbuild/android-x64": "0.25.10", "@esbuild/darwin-arm64": "0.25.10", "@esbuild/darwin-x64": "0.25.10", "@esbuild/freebsd-arm64": "0.25.10", "@esbuild/freebsd-x64": "0.25.10", "@esbuild/linux-arm": "0.25.10", "@esbuild/linux-arm64": "0.25.10", "@esbuild/linux-ia32": "0.25.10", "@esbuild/linux-loong64": "0.25.10", "@esbuild/linux-mips64el": "0.25.10", "@esbuild/linux-ppc64": "0.25.10", "@esbuild/linux-riscv64": "0.25.10", "@esbuild/linux-s390x": "0.25.10", "@esbuild/linux-x64": "0.25.10", "@esbuild/netbsd-arm64": "0.25.10", "@esbuild/netbsd-x64": "0.25.10", "@esbuild/openbsd-arm64": "0.25.10", "@esbuild/openbsd-x64": "0.25.10", "@esbuild/openharmony-arm64": "0.25.10", "@esbuild/sunos-x64": "0.25.10", "@esbuild/win32-arm64": "0.25.10", "@esbuild/win32-ia32": "0.25.10", "@esbuild/win32-x64": "0.25.10" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ=="],
|
||||
=======
|
||||
"esbuild": ["esbuild@0.25.11", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.11", "@esbuild/android-arm": "0.25.11", "@esbuild/android-arm64": "0.25.11", "@esbuild/android-x64": "0.25.11", "@esbuild/darwin-arm64": "0.25.11", "@esbuild/darwin-x64": "0.25.11", "@esbuild/freebsd-arm64": "0.25.11", "@esbuild/freebsd-x64": "0.25.11", "@esbuild/linux-arm": "0.25.11", "@esbuild/linux-arm64": "0.25.11", "@esbuild/linux-ia32": "0.25.11", "@esbuild/linux-loong64": "0.25.11", "@esbuild/linux-mips64el": "0.25.11", "@esbuild/linux-ppc64": "0.25.11", "@esbuild/linux-riscv64": "0.25.11", "@esbuild/linux-s390x": "0.25.11", "@esbuild/linux-x64": "0.25.11", "@esbuild/netbsd-arm64": "0.25.11", "@esbuild/netbsd-x64": "0.25.11", "@esbuild/openbsd-arm64": "0.25.11", "@esbuild/openbsd-x64": "0.25.11", "@esbuild/openharmony-arm64": "0.25.11", "@esbuild/sunos-x64": "0.25.11", "@esbuild/win32-arm64": "0.25.11", "@esbuild/win32-ia32": "0.25.11", "@esbuild/win32-x64": "0.25.11" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q=="],
|
||||
>>>>>>> staging
|
||||
|
||||
"esbuild-register": ["esbuild-register@3.6.0", "", { "dependencies": { "debug": "^4.3.4" }, "peerDependencies": { "esbuild": ">=0.12 <1" } }, "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg=="],
|
||||
|
||||
@@ -2059,19 +1821,11 @@
|
||||
|
||||
"escodegen": ["escodegen@2.1.0", "", { "dependencies": { "esprima": "^4.0.1", "estraverse": "^5.2.0", "esutils": "^2.0.2" }, "optionalDependencies": { "source-map": "~0.6.1" }, "bin": { "esgenerate": "bin/esgenerate.js", "escodegen": "bin/escodegen.js" } }, "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w=="],
|
||||
|
||||
<<<<<<< HEAD
|
||||
"eslint": ["eslint@9.37.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", "@eslint/config-helpers": "^0.4.0", "@eslint/core": "^0.16.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.37.0", "@eslint/plugin-kit": "^0.4.0", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig=="],
|
||||
|
||||
"eslint-plugin-react-hooks": ["eslint-plugin-react-hooks@5.2.0", "", { "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg=="],
|
||||
|
||||
"eslint-plugin-react-refresh": ["eslint-plugin-react-refresh@0.4.23", "", { "peerDependencies": { "eslint": ">=8.40" } }, "sha512-G4j+rv0NmbIR45kni5xJOrYvCtyD3/7LjpVH8MPPcudXDcNu8gv+4ATTDXTtbRR8rTCM5HxECvCSsRmxKnWDsA=="],
|
||||
=======
|
||||
"eslint": ["eslint@9.38.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.1", "@eslint/config-helpers": "^0.4.1", "@eslint/core": "^0.16.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.38.0", "@eslint/plugin-kit": "^0.4.0", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw=="],
|
||||
|
||||
"eslint-plugin-react-hooks": ["eslint-plugin-react-hooks@5.2.0", "", { "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg=="],
|
||||
|
||||
"eslint-plugin-react-refresh": ["eslint-plugin-react-refresh@0.4.24", "", { "peerDependencies": { "eslint": ">=8.40" } }, "sha512-nLHIW7TEq3aLrEYWpVaJ1dRgFR+wLDPN8e8FpYAql/bMV2oBEfC37K0gLEGgv9fy66juNShSMV8OkTqzltcG/w=="],
|
||||
>>>>>>> staging
|
||||
|
||||
"eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="],
|
||||
|
||||
@@ -2101,11 +1855,8 @@
|
||||
|
||||
"events-universal": ["events-universal@1.0.1", "", { "dependencies": { "bare-events": "^2.7.0" } }, "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw=="],
|
||||
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
"expand-template": ["expand-template@2.0.3", "", {}, "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="],
|
||||
|
||||
>>>>>>> staging
|
||||
"express": ["express@4.21.2", "", { "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.19.0", "serve-static": "1.16.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" } }, "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA=="],
|
||||
|
||||
"express-rate-limit": ["express-rate-limit@7.5.1", "", { "peerDependencies": { "express": ">= 4.11" } }, "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw=="],
|
||||
@@ -2254,11 +2005,7 @@
|
||||
|
||||
"hoist-non-react-statics": ["hoist-non-react-statics@3.3.2", "", { "dependencies": { "react-is": "^16.7.0" } }, "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw=="],
|
||||
|
||||
<<<<<<< HEAD
|
||||
"hono": ["hono@4.9.12", "", {}, "sha512-SrTC0YxqPwnN7yKa8gg/giLyQ2pILCKoideIHbYbFQlWZjYt68D2A4Ae1hehO/aDQ6RmTcpqOV/O2yBtMzx/VQ=="],
|
||||
=======
|
||||
"hono": ["hono@4.10.1", "", {}, "sha512-rpGNOfacO4WEPClfkEt1yfl8cbu10uB1lNpiI33AKoiAHwOS8lV748JiLx4b5ozO/u4qLjIvfpFsPXdY5Qjkmg=="],
|
||||
>>>>>>> staging
|
||||
|
||||
"html-minifier-terser": ["html-minifier-terser@6.1.0", "", { "dependencies": { "camel-case": "^4.1.2", "clean-css": "^5.2.2", "commander": "^8.3.0", "he": "^1.2.0", "param-case": "^3.0.4", "relateurl": "^0.2.7", "terser": "^5.10.0" }, "bin": { "html-minifier-terser": "cli.js" } }, "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw=="],
|
||||
|
||||
@@ -2546,11 +2293,8 @@
|
||||
|
||||
"mitt": ["mitt@3.0.1", "", {}, "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="],
|
||||
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
"mkdirp-classic": ["mkdirp-classic@0.5.3", "", {}, "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="],
|
||||
|
||||
>>>>>>> staging
|
||||
"mocha": ["mocha@11.7.4", "", { "dependencies": { "browser-stdout": "^1.3.1", "chokidar": "^4.0.1", "debug": "^4.3.5", "diff": "^7.0.0", "escape-string-regexp": "^4.0.0", "find-up": "^5.0.0", "glob": "^10.4.5", "he": "^1.2.0", "is-path-inside": "^3.0.3", "js-yaml": "^4.1.0", "log-symbols": "^4.1.0", "minimatch": "^9.0.5", "ms": "^2.1.3", "picocolors": "^1.1.1", "serialize-javascript": "^6.0.2", "strip-json-comments": "^3.1.1", "supports-color": "^8.1.1", "workerpool": "^9.2.0", "yargs": "^17.7.2", "yargs-parser": "^21.1.1", "yargs-unparser": "^2.0.0" }, "bin": { "mocha": "bin/mocha.js", "_mocha": "bin/_mocha" } }, "sha512-1jYAaY8x0kAZ0XszLWu14pzsf4KV740Gld4HXkhNTXwcHx4AUEDkPzgEHg9CM5dVcW+zv036tjpsEbLraPJj4w=="],
|
||||
|
||||
"module-details-from-path": ["module-details-from-path@1.0.4", "", {}, "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w=="],
|
||||
@@ -2574,11 +2318,8 @@
|
||||
"nanoid": ["nanoid@5.1.6", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg=="],
|
||||
|
||||
"nanostores": ["nanostores@1.0.1", "", {}, "sha512-kNZ9xnoJYKg/AfxjrVL4SS0fKX++4awQReGqWnwTRHxeHGZ1FJFVgTqr/eMrNQdp0Tz7M7tG/TDaX8QfHDwVCw=="],
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
|
||||
"napi-build-utils": ["napi-build-utils@2.0.0", "", {}, "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA=="],
|
||||
>>>>>>> staging
|
||||
|
||||
"natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
|
||||
|
||||
@@ -2586,21 +2327,14 @@
|
||||
|
||||
"netmask": ["netmask@2.0.2", "", {}, "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg=="],
|
||||
|
||||
<<<<<<< HEAD
|
||||
"next": ["next@15.5.5", "", { "dependencies": { "@next/env": "15.5.5", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.5.5", "@next/swc-darwin-x64": "15.5.5", "@next/swc-linux-arm64-gnu": "15.5.5", "@next/swc-linux-arm64-musl": "15.5.5", "@next/swc-linux-x64-gnu": "15.5.5", "@next/swc-linux-x64-musl": "15.5.5", "@next/swc-win32-arm64-msvc": "15.5.5", "@next/swc-win32-x64-msvc": "15.5.5", "sharp": "^0.34.3" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-OQVdBPtpBfq7HxFN0kOVb7rXXOSIkt5lTzDJDGRBcOyVvNRIWFauMqi1gIHd1pszq1542vMOGY0HP4CaiALfkA=="],
|
||||
=======
|
||||
"next": ["next@15.5.6", "", { "dependencies": { "@next/env": "15.5.6", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.5.6", "@next/swc-darwin-x64": "15.5.6", "@next/swc-linux-arm64-gnu": "15.5.6", "@next/swc-linux-arm64-musl": "15.5.6", "@next/swc-linux-x64-gnu": "15.5.6", "@next/swc-linux-x64-musl": "15.5.6", "@next/swc-win32-arm64-msvc": "15.5.6", "@next/swc-win32-x64-msvc": "15.5.6", "sharp": "^0.34.3" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-zTxsnI3LQo3c9HSdSf91O1jMNsEzIXDShXd4wVdg9y5shwLqBXi4ZtUUJyB86KGVSJLZx0PFONvO54aheGX8QQ=="],
|
||||
>>>>>>> staging
|
||||
|
||||
"next-themes": ["next-themes@0.4.6", "", { "peerDependencies": { "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA=="],
|
||||
|
||||
"no-case": ["no-case@3.0.4", "", { "dependencies": { "lower-case": "^2.0.2", "tslib": "^2.0.3" } }, "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg=="],
|
||||
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
"node-abi": ["node-abi@3.78.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-E2wEyrgX/CqvicaQYU3Ze1PFGjc4QYPGsjUrlYkqAE0WjHEZwgOsGMPMzkMse4LjJbDmaEuDX3CM036j5K2DSQ=="],
|
||||
|
||||
>>>>>>> staging
|
||||
"node-abort-controller": ["node-abort-controller@3.1.1", "", {}, "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ=="],
|
||||
|
||||
"node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="],
|
||||
@@ -2609,11 +2343,7 @@
|
||||
|
||||
"node-gyp-build-optional-packages": ["node-gyp-build-optional-packages@5.2.2", "", { "dependencies": { "detect-libc": "^2.0.1" }, "bin": { "node-gyp-build-optional-packages": "bin.js", "node-gyp-build-optional-packages-optional": "optional.js", "node-gyp-build-optional-packages-test": "build-test.js" } }, "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw=="],
|
||||
|
||||
<<<<<<< HEAD
|
||||
"node-releases": ["node-releases@2.0.23", "", {}, "sha512-cCmFDMSm26S6tQSDpBCg/NR8NENrVPhAJSf+XbxBG4rPFaaonlEoE9wHQmun+cls499TQGSb7ZyPBRlzgKfpeg=="],
|
||||
=======
|
||||
"node-releases": ["node-releases@2.0.25", "", {}, "sha512-4auku8B/vw5psvTiiN9j1dAOsXvMoGqJuKJcR+dTdqiXEK20mMTk1UEo3HS16LeGQsVG6+qKTPM9u/qQ2LqATA=="],
|
||||
>>>>>>> staging
|
||||
|
||||
"nodemon": ["nodemon@3.1.10", "", { "dependencies": { "chokidar": "^3.5.2", "debug": "^4", "ignore-by-default": "^1.0.1", "minimatch": "^3.1.2", "pstree.remy": "^1.1.8", "semver": "^7.5.3", "simple-update-notifier": "^2.0.0", "supports-color": "^5.5.0", "touch": "^3.1.0", "undefsafe": "^2.0.5" }, "bin": { "nodemon": "bin/nodemon.js" } }, "sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw=="],
|
||||
|
||||
@@ -2621,11 +2351,7 @@
|
||||
|
||||
"nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="],
|
||||
|
||||
<<<<<<< HEAD
|
||||
"nuqs": ["nuqs@2.7.1", "", { "dependencies": { "@standard-schema/spec": "1.0.0" }, "peerDependencies": { "@remix-run/react": ">=2", "@tanstack/react-router": "^1", "next": ">=14.2.0", "react": ">=18.2.0 || ^19.0.0-0", "react-router": "^6 || ^7", "react-router-dom": "^6 || ^7" }, "optionalPeers": ["@remix-run/react", "@tanstack/react-router", "next", "react-router", "react-router-dom"] }, "sha512-3WDgrOZWat0QyOheyljTlXK4TGFh1JKSLvXMgusMDcTyMJXe1xL8+q3zuQ6ke1vyeGnpJwztlZl2aDkMW2eIUg=="],
|
||||
=======
|
||||
"nuqs": ["nuqs@2.7.2", "", { "dependencies": { "@standard-schema/spec": "1.0.0" }, "peerDependencies": { "@remix-run/react": ">=2", "@tanstack/react-router": "^1", "next": ">=14.2.0", "react": ">=18.2.0 || ^19.0.0-0", "react-router": "^6 || ^7", "react-router-dom": "^6 || ^7" }, "optionalPeers": ["@remix-run/react", "@tanstack/react-router", "next", "react-router", "react-router-dom"] }, "sha512-wOPJoz5om7jMJQick9zU1S/Q+joL+B2DZTZxfCleHEcUzjUnPoujGod4+nAmUWb+G9TwZnyv+mfNqlyfEi8Zag=="],
|
||||
>>>>>>> staging
|
||||
|
||||
"object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
|
||||
|
||||
@@ -2705,11 +2431,7 @@
|
||||
|
||||
"picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
|
||||
|
||||
<<<<<<< HEAD
|
||||
"pino": ["pino@9.13.1", "", { "dependencies": { "atomic-sleep": "^1.0.0", "on-exit-leak-free": "^2.1.0", "pino-abstract-transport": "^2.0.0", "pino-std-serializers": "^7.0.0", "process-warning": "^5.0.0", "quick-format-unescaped": "^4.0.3", "real-require": "^0.2.0", "safe-stable-stringify": "^2.3.1", "slow-redact": "^0.3.0", "sonic-boom": "^4.0.1", "thread-stream": "^3.0.0" }, "bin": { "pino": "bin.js" } }, "sha512-Szuj+ViDTjKPQYiKumGmEn3frdl+ZPSdosHyt9SnUevFosOkMY2b7ipxlEctNKPmMD/VibeBI+ZcZCJK+4DPuw=="],
|
||||
=======
|
||||
"pino": ["pino@9.14.0", "", { "dependencies": { "@pinojs/redact": "^0.4.0", "atomic-sleep": "^1.0.0", "on-exit-leak-free": "^2.1.0", "pino-abstract-transport": "^2.0.0", "pino-std-serializers": "^7.0.0", "process-warning": "^5.0.0", "quick-format-unescaped": "^4.0.3", "real-require": "^0.2.0", "safe-stable-stringify": "^2.3.1", "sonic-boom": "^4.0.1", "thread-stream": "^3.0.0" }, "bin": { "pino": "bin.js" } }, "sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w=="],
|
||||
>>>>>>> staging
|
||||
|
||||
"pino-abstract-transport": ["pino-abstract-transport@1.2.0", "", { "dependencies": { "readable-stream": "^4.0.0", "split2": "^4.0.0" } }, "sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q=="],
|
||||
|
||||
@@ -2731,20 +2453,13 @@
|
||||
|
||||
"postgres-interval": ["postgres-interval@1.2.0", "", { "dependencies": { "xtend": "^4.0.0" } }, "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ=="],
|
||||
|
||||
<<<<<<< HEAD
|
||||
"posthog-js": ["posthog-js@1.275.3", "", { "dependencies": { "@posthog/core": "1.3.0", "core-js": "^3.38.1", "fflate": "^0.4.8", "preact": "^10.19.3", "web-vitals": "^4.2.4" }, "peerDependencies": { "@rrweb/types": "2.0.0-alpha.17", "rrweb-snapshot": "2.0.0-alpha.17" }, "optionalPeers": ["@rrweb/types", "rrweb-snapshot"] }, "sha512-LitwVprl0Q8p0fN4O4ThvlOuO6r+TBzLfkGbSyI5tR/YhlWzX3yFf4KKHPXJgxge99sxKa0fuVKNzcspYsDzcg=="],
|
||||
=======
|
||||
"posthog-js": ["posthog-js@1.276.0", "", { "dependencies": { "@posthog/core": "1.3.0", "core-js": "^3.38.1", "fflate": "^0.4.8", "preact": "^10.19.3", "web-vitals": "^4.2.4" }, "peerDependencies": { "@rrweb/types": "2.0.0-alpha.17", "rrweb-snapshot": "2.0.0-alpha.17" }, "optionalPeers": ["@rrweb/types", "rrweb-snapshot"] }, "sha512-FYZE1037LrAoKKeUU0pUL7u8WwNK2BVeg5TFApwquVPUdj9h7u5Z077A313hPN19Ar+7Y+VHxqYqdHc4VNsVgw=="],
|
||||
>>>>>>> staging
|
||||
|
||||
"posthog-node": ["posthog-node@4.18.0", "", { "dependencies": { "axios": "^1.8.2" } }, "sha512-XROs1h+DNatgKh/AlIlCtDxWzwrKdYDb2mOs58n4yN8BkGN9ewqeQwG5ApS4/IzwCb7HPttUkOVulkYatd2PIw=="],
|
||||
|
||||
"preact": ["preact@10.27.2", "", {}, "sha512-5SYSgFKSyhCbk6SrXyMpqjb5+MQBgfvEKE/OC+PujcY34sOpqtr+0AZQtPYx5IA6VxynQ7rUPCtKzyovpj9Bpg=="],
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
|
||||
"prebuild-install": ["prebuild-install@7.1.3", "", { "dependencies": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", "github-from-package": "0.0.0", "minimist": "^1.2.3", "mkdirp-classic": "^0.5.3", "napi-build-utils": "^2.0.0", "node-abi": "^3.3.0", "pump": "^3.0.0", "rc": "^1.2.7", "simple-get": "^4.0.0", "tar-fs": "^2.0.0", "tunnel-agent": "^0.6.0" }, "bin": { "prebuild-install": "bin.js" } }, "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug=="],
|
||||
>>>>>>> staging
|
||||
|
||||
"prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
|
||||
|
||||
@@ -2780,11 +2495,7 @@
|
||||
|
||||
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
||||
|
||||
<<<<<<< HEAD
|
||||
"puppeteer-core": ["puppeteer-core@24.24.1", "", { "dependencies": { "@puppeteer/browsers": "2.10.12", "chromium-bidi": "9.1.0", "debug": "^4.4.3", "devtools-protocol": "0.0.1508733", "typed-query-selector": "^2.12.0", "webdriver-bidi-protocol": "0.3.7", "ws": "^8.18.3" } }, "sha512-4R9/hCjmyUBbQqjrCa+y4Pzgl3LneLfqB+Whh2JujA5Wzg+prnO60GxDPjAJmM+uirYxDx/8jIm0hGu8yDTyiA=="],
|
||||
=======
|
||||
"puppeteer-core": ["puppeteer-core@24.25.0", "", { "dependencies": { "@puppeteer/browsers": "2.10.12", "chromium-bidi": "9.1.0", "debug": "^4.4.3", "devtools-protocol": "0.0.1508733", "typed-query-selector": "^2.12.0", "webdriver-bidi-protocol": "0.3.7", "ws": "^8.18.3" } }, "sha512-8Xs6q3Ut+C8y7sAaqjIhzv1QykGWG4gc2mEZ2mYE7siZFuRp4xQVehOf8uQKSQAkeL7jXUs3mknEeiqnRqUKvQ=="],
|
||||
>>>>>>> staging
|
||||
|
||||
"pvtsutils": ["pvtsutils@1.3.6", "", { "dependencies": { "tslib": "^2.8.1" } }, "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg=="],
|
||||
|
||||
@@ -2856,11 +2567,7 @@
|
||||
|
||||
"recaseai": ["recaseai@0.0.37", "", { "dependencies": { "@anthropic-ai/sdk": "^0.32.1", "@supabase/supabase-js": "^2.47.2", "@types/cors": "^2.8.17", "@types/express": "^5.0.0", "@types/node": "^22.10.1", "async-listen": "^3.0.1", "axios": "^1.7.9", "commander": "^12.1.0", "cors": "^2.8.5", "dotenv": "^16.4.7", "express": "^4.21.2", "figures": "^6.1.0", "inquirer": "^12.1.0", "ksuid": "^3.0.0", "nanoid": "^5.0.9", "openai": "^4.76.0", "ora": "^8.1.1", "picocolors": "^1.1.1", "pino": "^9.5.0", "tsx": "^4.19.2", "typescript": "^5.7.2" }, "bin": { "recase": "dist/cli.js" } }, "sha512-cKVMWTGBnGtm8K+uD2vfMXOzxdHj1U3++vvTePpOoZABI/SY/jjDpW997vM5xeDnzz7Vk/qqVkLQUQ/ZMDXpsQ=="],
|
||||
|
||||
<<<<<<< HEAD
|
||||
"recharts": ["recharts@3.2.1", "", { "dependencies": { "@reduxjs/toolkit": "1.x.x || 2.x.x", "clsx": "^2.1.1", "decimal.js-light": "^2.5.1", "es-toolkit": "^1.39.3", "eventemitter3": "^5.0.1", "immer": "^10.1.1", "react-redux": "8.x.x || 9.x.x", "reselect": "5.1.1", "tiny-invariant": "^1.3.3", "use-sync-external-store": "^1.2.2", "victory-vendor": "^37.0.2" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-0JKwHRiFZdmLq/6nmilxEZl3pqb4T+aKkOkOi/ZISRZwfBhVMgInxzlYU9D4KnCH3KINScLy68m/OvMXoYGZUw=="],
|
||||
=======
|
||||
"recharts": ["recharts@3.3.0", "", { "dependencies": { "@reduxjs/toolkit": "1.x.x || 2.x.x", "clsx": "^2.1.1", "decimal.js-light": "^2.5.1", "es-toolkit": "^1.39.3", "eventemitter3": "^5.0.1", "immer": "^10.1.1", "react-redux": "8.x.x || 9.x.x", "reselect": "5.1.1", "tiny-invariant": "^1.3.3", "use-sync-external-store": "^1.2.2", "victory-vendor": "^37.0.2" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Vi0qmTB0iz1+/Cz9o5B7irVyUjX2ynvEgImbgMt/3sKRREcUM07QiYjS1QpAVrkmVlXqy5gykq4nGWMz9AS4Rg=="],
|
||||
>>>>>>> staging
|
||||
|
||||
"redis-errors": ["redis-errors@1.2.0", "", {}, "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w=="],
|
||||
|
||||
@@ -2894,11 +2601,7 @@
|
||||
|
||||
"reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
|
||||
|
||||
<<<<<<< HEAD
|
||||
"rollup": ["rollup@4.52.4", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.52.4", "@rollup/rollup-android-arm64": "4.52.4", "@rollup/rollup-darwin-arm64": "4.52.4", "@rollup/rollup-darwin-x64": "4.52.4", "@rollup/rollup-freebsd-arm64": "4.52.4", "@rollup/rollup-freebsd-x64": "4.52.4", "@rollup/rollup-linux-arm-gnueabihf": "4.52.4", "@rollup/rollup-linux-arm-musleabihf": "4.52.4", "@rollup/rollup-linux-arm64-gnu": "4.52.4", "@rollup/rollup-linux-arm64-musl": "4.52.4", "@rollup/rollup-linux-loong64-gnu": "4.52.4", "@rollup/rollup-linux-ppc64-gnu": "4.52.4", "@rollup/rollup-linux-riscv64-gnu": "4.52.4", "@rollup/rollup-linux-riscv64-musl": "4.52.4", "@rollup/rollup-linux-s390x-gnu": "4.52.4", "@rollup/rollup-linux-x64-gnu": "4.52.4", "@rollup/rollup-linux-x64-musl": "4.52.4", "@rollup/rollup-openharmony-arm64": "4.52.4", "@rollup/rollup-win32-arm64-msvc": "4.52.4", "@rollup/rollup-win32-ia32-msvc": "4.52.4", "@rollup/rollup-win32-x64-gnu": "4.52.4", "@rollup/rollup-win32-x64-msvc": "4.52.4", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ=="],
|
||||
=======
|
||||
"rollup": ["rollup@4.52.5", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.52.5", "@rollup/rollup-android-arm64": "4.52.5", "@rollup/rollup-darwin-arm64": "4.52.5", "@rollup/rollup-darwin-x64": "4.52.5", "@rollup/rollup-freebsd-arm64": "4.52.5", "@rollup/rollup-freebsd-x64": "4.52.5", "@rollup/rollup-linux-arm-gnueabihf": "4.52.5", "@rollup/rollup-linux-arm-musleabihf": "4.52.5", "@rollup/rollup-linux-arm64-gnu": "4.52.5", "@rollup/rollup-linux-arm64-musl": "4.52.5", "@rollup/rollup-linux-loong64-gnu": "4.52.5", "@rollup/rollup-linux-ppc64-gnu": "4.52.5", "@rollup/rollup-linux-riscv64-gnu": "4.52.5", "@rollup/rollup-linux-riscv64-musl": "4.52.5", "@rollup/rollup-linux-s390x-gnu": "4.52.5", "@rollup/rollup-linux-x64-gnu": "4.52.5", "@rollup/rollup-linux-x64-musl": "4.52.5", "@rollup/rollup-openharmony-arm64": "4.52.5", "@rollup/rollup-win32-arm64-msvc": "4.52.5", "@rollup/rollup-win32-ia32-msvc": "4.52.5", "@rollup/rollup-win32-x64-gnu": "4.52.5", "@rollup/rollup-win32-x64-msvc": "4.52.5", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw=="],
|
||||
>>>>>>> staging
|
||||
|
||||
"rou3": ["rou3@0.6.3", "", {}, "sha512-1HSG1ENTj7Kkm5muMnXuzzfdDOf7CFnbSYFA+H3Fp/rB9lOCxCPgy1jlZxTKyFoC5jJay8Mmc+VbPLYRjzYLrA=="],
|
||||
|
||||
@@ -2956,19 +2659,14 @@
|
||||
|
||||
"signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
|
||||
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
"simple-concat": ["simple-concat@1.0.1", "", {}, "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="],
|
||||
|
||||
"simple-get": ["simple-get@4.0.1", "", { "dependencies": { "decompress-response": "^6.0.0", "once": "^1.3.1", "simple-concat": "^1.0.0" } }, "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA=="],
|
||||
|
||||
>>>>>>> staging
|
||||
"simple-update-notifier": ["simple-update-notifier@2.0.0", "", { "dependencies": { "semver": "^7.5.3" } }, "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w=="],
|
||||
|
||||
"slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
|
||||
|
||||
"slow-redact": ["slow-redact@0.3.2", "", {}, "sha512-MseHyi2+E/hBRqdOi5COy6wZ7j7DxXRz9NkseavNYSvvWC06D8a5cidVZX3tcG5eCW3NIyVU4zT63hw0Q486jw=="],
|
||||
|
||||
"smart-buffer": ["smart-buffer@4.2.0", "", {}, "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg=="],
|
||||
|
||||
"socket.io": ["socket.io@4.8.1", "", { "dependencies": { "accepts": "~1.3.4", "base64id": "~2.0.0", "cors": "~2.8.5", "debug": "~4.3.2", "engine.io": "~6.6.0", "socket.io-adapter": "~2.5.2", "socket.io-parser": "~4.2.4" } }, "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg=="],
|
||||
@@ -3027,11 +2725,7 @@
|
||||
|
||||
"strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="],
|
||||
|
||||
<<<<<<< HEAD
|
||||
"stripe": ["stripe@18.5.0", "", { "dependencies": { "qs": "^6.11.0" }, "peerDependencies": { "@types/node": ">=12.x.x" }, "optionalPeers": ["@types/node"] }, "sha512-Hp+wFiEQtCB0LlNgcFh5uVyKznpDjzyUZ+CNVEf+I3fhlYvh7rZruIg+jOwzJRCpy0ZTPMjlzm7J2/M2N6d+DA=="],
|
||||
=======
|
||||
"stripe": ["stripe@18.4.0-beta.2", "", { "dependencies": { "qs": "^6.11.0" }, "peerDependencies": { "@types/node": ">=12.x.x" }, "optionalPeers": ["@types/node"] }, "sha512-4MCaxGkwZcCpMpgiE+Wb4hWwKTlnZgUf4pTlvIolxdrYAA5gb6MnIEJJAowrcrSnl41FvjQP0xV4QvdN2Fq8Zw=="],
|
||||
>>>>>>> staging
|
||||
|
||||
"styled-jsx": ["styled-jsx@5.1.6", "", { "dependencies": { "client-only": "0.0.1" }, "peerDependencies": { "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" } }, "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA=="],
|
||||
|
||||
@@ -3112,11 +2806,8 @@
|
||||
"tsx": ["tsx@4.20.6", "", { "dependencies": { "esbuild": "~0.25.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg=="],
|
||||
|
||||
"tsyringe": ["tsyringe@4.10.0", "", { "dependencies": { "tslib": "^1.9.3" } }, "sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw=="],
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
|
||||
"tunnel-agent": ["tunnel-agent@0.6.0", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="],
|
||||
>>>>>>> staging
|
||||
|
||||
"type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="],
|
||||
|
||||
@@ -3180,11 +2871,7 @@
|
||||
|
||||
"victory-vendor": ["victory-vendor@37.3.6", "", { "dependencies": { "@types/d3-array": "^3.0.3", "@types/d3-ease": "^3.0.0", "@types/d3-interpolate": "^3.0.1", "@types/d3-scale": "^4.0.2", "@types/d3-shape": "^3.1.0", "@types/d3-time": "^3.0.0", "@types/d3-timer": "^3.0.0", "d3-array": "^3.1.6", "d3-ease": "^3.0.1", "d3-interpolate": "^3.0.1", "d3-scale": "^4.0.2", "d3-shape": "^3.1.0", "d3-time": "^3.0.0", "d3-timer": "^3.0.1" } }, "sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ=="],
|
||||
|
||||
<<<<<<< HEAD
|
||||
"vite": ["vite@6.3.7", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-mQYaKepA0NGMBsz8Xktt3tJUG5ELE2iT7IJ+ssXI6nxVdE2sFc/d/6w/JByqMLvWg8hNKHpPgzjgOkrhpKFnrA=="],
|
||||
=======
|
||||
"vite": ["vite@6.4.0", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-oLnWs9Hak/LOlKjeSpOwD6JMks8BeICEdYMJBf6P4Lac/pO9tKiv/XhXnAM7nNfSkZahjlCZu9sS50zL8fSnsw=="],
|
||||
>>>>>>> staging
|
||||
"vite": ["vite@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="],
|
||||
|
||||
"vscode-oniguruma": ["vscode-oniguruma@2.0.1", "", {}, "sha512-poJU8iHIWnC3vgphJnrLZyI3YdqRlR27xzqDmpPXYzA93R4Gk8z7T6oqDzDoHjoikA2aS82crdXFkjELCdJsjQ=="],
|
||||
|
||||
@@ -3256,13 +2943,6 @@
|
||||
|
||||
"@ai-sdk/provider-utils/secure-json-parse": ["secure-json-parse@2.7.0", "", {}, "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw=="],
|
||||
|
||||
<<<<<<< HEAD
|
||||
"@anthropic-ai/sdk/@types/node": ["@types/node@18.19.130", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg=="],
|
||||
|
||||
"@autumn/server/@types/node": ["@types/node@22.18.10", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-anNG/V/Efn/YZY4pRzbACnKxNKoBng2VTFydVu8RRs5hQjikP8CQfaeAV59VFSCzKNp90mXiVXW2QzV56rwMrg=="],
|
||||
|
||||
"@autumn/vite/@types/node": ["@types/node@22.18.10", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-anNG/V/Efn/YZY4pRzbACnKxNKoBng2VTFydVu8RRs5hQjikP8CQfaeAV59VFSCzKNp90mXiVXW2QzV56rwMrg=="],
|
||||
=======
|
||||
"@amplitude/analytics-client-common/@amplitude/analytics-types": ["@amplitude/analytics-types@2.10.0", "", {}, "sha512-WP8eEbJh10MmFVnxkHjg92i5DBxBFsRvSZxjDQPXEGL8ZP+i7rSsleiH2K3VrwoKksYfZ/1eAqrZvevAmjSlig=="],
|
||||
|
||||
"@amplitude/analytics-remote-config/@amplitude/analytics-core": ["@amplitude/analytics-core@1.2.8", "", { "dependencies": { "@amplitude/analytics-types": "^1.4.0", "tslib": "^2.4.1" } }, "sha512-Krxpr5uvS3HmmjvpYqPfbMbs2kcZZu09L+6KwQnPiofWRzoXWIM217fRfy6aSD/QrAoPGbZjvtVitw9cB7Cx+A=="],
|
||||
@@ -3288,7 +2968,6 @@
|
||||
"@autumn/shared/stripe": ["stripe@18.5.0", "", { "dependencies": { "qs": "^6.11.0" }, "peerDependencies": { "@types/node": ">=12.x.x" }, "optionalPeers": ["@types/node"] }, "sha512-Hp+wFiEQtCB0LlNgcFh5uVyKznpDjzyUZ+CNVEf+I3fhlYvh7rZruIg+jOwzJRCpy0ZTPMjlzm7J2/M2N6d+DA=="],
|
||||
|
||||
"@autumn/vite/@types/node": ["@types/node@22.18.11", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-Gd33J2XIrXurb+eT2ktze3rJAfAp9ZNjlBdh4SVgyrKEOADwCbdUDaK7QgJno8Ue4kcajscsKqu6n8OBG3hhCQ=="],
|
||||
>>>>>>> staging
|
||||
|
||||
"@autumn/vite/date-fns": ["date-fns@3.6.0", "", {}, "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww=="],
|
||||
|
||||
@@ -3308,11 +2987,8 @@
|
||||
|
||||
"@better-auth/core/zod": ["zod@4.1.12", "", {}, "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ=="],
|
||||
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
"@better-auth/stripe/stripe": ["stripe@18.5.0", "", { "dependencies": { "qs": "^6.11.0" }, "peerDependencies": { "@types/node": ">=12.x.x" }, "optionalPeers": ["@types/node"] }, "sha512-Hp+wFiEQtCB0LlNgcFh5uVyKznpDjzyUZ+CNVEf+I3fhlYvh7rZruIg+jOwzJRCpy0ZTPMjlzm7J2/M2N6d+DA=="],
|
||||
|
||||
>>>>>>> staging
|
||||
"@better-auth/stripe/zod": ["zod@4.1.12", "", {}, "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ=="],
|
||||
|
||||
"@browserbasehq/sdk/@types/node": ["@types/node@18.19.130", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg=="],
|
||||
@@ -3624,11 +3300,6 @@
|
||||
"@opentelemetry/sdk-trace-node/@opentelemetry/core": ["@opentelemetry/core@2.1.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-RMEtHsxJs/GiHHxYT58IY57UXAQTuUnZVco6ymDEqTNlJKTimM4qPUPVe8InNFyBjhHBEAx4k3Q8LtNayBsbUQ=="],
|
||||
|
||||
"@opentelemetry/sql-common/@opentelemetry/core": ["@opentelemetry/core@2.1.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-RMEtHsxJs/GiHHxYT58IY57UXAQTuUnZVco6ymDEqTNlJKTimM4qPUPVe8InNFyBjhHBEAx4k3Q8LtNayBsbUQ=="],
|
||||
<<<<<<< HEAD
|
||||
|
||||
"@paralleldrive/cuid2/@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="],
|
||||
=======
|
||||
>>>>>>> staging
|
||||
|
||||
"@sentry/node/@opentelemetry/context-async-hooks": ["@opentelemetry/context-async-hooks@1.30.1", "", { "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA=="],
|
||||
|
||||
@@ -3694,11 +3365,6 @@
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
<<<<<<< HEAD
|
||||
"@types/serve-static/@types/send": ["@types/send@0.17.5", "", { "dependencies": { "@types/mime": "^1", "@types/node": "*" } }, "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w=="],
|
||||
|
||||
=======
|
||||
>>>>>>> staging
|
||||
"@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
|
||||
|
||||
"accepts/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
||||
@@ -3803,11 +3469,7 @@
|
||||
|
||||
"react-router/cookie": ["cookie@1.0.2", "", {}, "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA=="],
|
||||
|
||||
<<<<<<< HEAD
|
||||
"recaseai/@types/node": ["@types/node@22.18.10", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-anNG/V/Efn/YZY4pRzbACnKxNKoBng2VTFydVu8RRs5hQjikP8CQfaeAV59VFSCzKNp90mXiVXW2QzV56rwMrg=="],
|
||||
=======
|
||||
"recaseai/@types/node": ["@types/node@22.18.11", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-Gd33J2XIrXurb+eT2ktze3rJAfAp9ZNjlBdh4SVgyrKEOADwCbdUDaK7QgJno8Ue4kcajscsKqu6n8OBG3hhCQ=="],
|
||||
>>>>>>> staging
|
||||
|
||||
"recaseai/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="],
|
||||
|
||||
@@ -4246,13 +3908,10 @@
|
||||
"ora/log-symbols/is-unicode-supported": ["is-unicode-supported@1.3.0", "", {}, "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ=="],
|
||||
|
||||
"ora/string-width/emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="],
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
|
||||
"prebuild-install/tar-fs/chownr": ["chownr@1.1.4", "", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="],
|
||||
|
||||
"prebuild-install/tar-fs/tar-stream": ["tar-stream@2.2.0", "", { "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", "fs-constants": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.1.1" } }, "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ=="],
|
||||
>>>>>>> staging
|
||||
|
||||
"react-email/glob/jackspeak": ["jackspeak@4.1.1", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" } }, "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ=="],
|
||||
|
||||
@@ -4360,11 +4019,8 @@
|
||||
|
||||
"nodemon/chokidar/readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
"prebuild-install/tar-fs/tar-stream/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
|
||||
|
||||
>>>>>>> staging
|
||||
"react-email/glob/path-scurry/lru-cache": ["lru-cache@11.2.2", "", {}, "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg=="],
|
||||
|
||||
"renderkid/htmlparser2/domutils/dom-serializer": ["dom-serializer@1.4.1", "", { "dependencies": { "domelementtype": "^2.0.1", "domhandler": "^4.2.0", "entities": "^2.0.0" } }, "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag=="],
|
||||
|
||||
277
server/customer.json
Normal file
277
server/customer.json
Normal file
@@ -0,0 +1,277 @@
|
||||
{
|
||||
"id": "123",
|
||||
"created_at": 1761032316983,
|
||||
"name": "123",
|
||||
"email": "123@example.com",
|
||||
"fingerprint": null,
|
||||
"stripe_id": "cus_TH8QGlLFtlDpAj",
|
||||
"env": "sandbox",
|
||||
"metadata": {},
|
||||
"products": [
|
||||
{
|
||||
"id": "pro",
|
||||
"name": "Pro",
|
||||
"group": null,
|
||||
"status": "trialing",
|
||||
"canceled_at": null,
|
||||
"started_at": 1761122358550,
|
||||
"is_default": false,
|
||||
"is_add_on": false,
|
||||
"version": 5,
|
||||
"current_period_start": 1761032316000,
|
||||
"current_period_end": 1762073321000,
|
||||
"items": [
|
||||
{
|
||||
"type": "price",
|
||||
"feature_id": null,
|
||||
"feature": null,
|
||||
"interval": "month",
|
||||
"interval_count": 1,
|
||||
"price": 20
|
||||
},
|
||||
{
|
||||
"type": "priced_feature",
|
||||
"feature_id": "messages",
|
||||
"feature_type": "single_use",
|
||||
"feature": {
|
||||
"id": "messages",
|
||||
"name": "messages",
|
||||
"type": "single_use",
|
||||
"display": {
|
||||
"singular": "message",
|
||||
"plural": "messages"
|
||||
},
|
||||
"archived": false
|
||||
},
|
||||
"included_usage": 200,
|
||||
"interval": "month",
|
||||
"interval_count": 1,
|
||||
"price": 0.5,
|
||||
"usage_model": "pay_per_use",
|
||||
"billing_units": 1,
|
||||
"reset_usage_when_enabled": true,
|
||||
"entity_feature_id": null
|
||||
},
|
||||
{
|
||||
"type": "priced_feature",
|
||||
"feature_id": "websites",
|
||||
"feature_type": "continuous_use",
|
||||
"feature": {
|
||||
"id": "websites",
|
||||
"name": "Websites",
|
||||
"type": "continuous_use",
|
||||
"display": {
|
||||
"singular": "website",
|
||||
"plural": "websites"
|
||||
},
|
||||
"archived": false
|
||||
},
|
||||
"included_usage": 3,
|
||||
"interval": "month",
|
||||
"interval_count": 1,
|
||||
"price": 20,
|
||||
"usage_model": "pay_per_use",
|
||||
"billing_units": 1,
|
||||
"reset_usage_when_enabled": false,
|
||||
"entity_feature_id": null
|
||||
},
|
||||
{
|
||||
"type": "feature",
|
||||
"feature_id": "discord_notifications",
|
||||
"feature_type": "static",
|
||||
"feature": {
|
||||
"id": "discord_notifications",
|
||||
"name": "Discord notifications",
|
||||
"type": "boolean",
|
||||
"display": {
|
||||
"singular": "Discord notification",
|
||||
"plural": "Discord notifications"
|
||||
},
|
||||
"archived": false
|
||||
},
|
||||
"entity_feature_id": null
|
||||
},
|
||||
{
|
||||
"type": "feature",
|
||||
"feature_id": "messages",
|
||||
"feature_type": "single_use",
|
||||
"feature": {
|
||||
"id": "messages",
|
||||
"name": "messages",
|
||||
"type": "single_use",
|
||||
"display": {
|
||||
"singular": "message",
|
||||
"plural": "messages"
|
||||
},
|
||||
"archived": false
|
||||
},
|
||||
"included_usage": 500,
|
||||
"interval": null,
|
||||
"interval_count": 1,
|
||||
"reset_usage_when_enabled": true,
|
||||
"entity_feature_id": null
|
||||
},
|
||||
{
|
||||
"type": "feature",
|
||||
"feature_id": "projects",
|
||||
"feature_type": "continuous_use",
|
||||
"feature": {
|
||||
"id": "projects",
|
||||
"name": "Projects",
|
||||
"type": "continuous_use",
|
||||
"display": {
|
||||
"singular": "project",
|
||||
"plural": "projects"
|
||||
},
|
||||
"archived": false
|
||||
},
|
||||
"included_usage": 12,
|
||||
"interval": null,
|
||||
"interval_count": 1,
|
||||
"reset_usage_when_enabled": false,
|
||||
"entity_feature_id": null
|
||||
},
|
||||
{
|
||||
"type": "feature",
|
||||
"feature_id": "swag",
|
||||
"feature_type": "single_use",
|
||||
"feature": {
|
||||
"id": "swag",
|
||||
"name": "Swag",
|
||||
"type": "single_use",
|
||||
"display": {
|
||||
"singular": "swag",
|
||||
"plural": "swags"
|
||||
},
|
||||
"archived": false
|
||||
},
|
||||
"included_usage": "inf",
|
||||
"interval": null,
|
||||
"interval_count": 1,
|
||||
"reset_usage_when_enabled": true,
|
||||
"entity_feature_id": null
|
||||
}
|
||||
],
|
||||
"quantity": 1
|
||||
}
|
||||
],
|
||||
"features": {
|
||||
"discord_notifications": {
|
||||
"id": "discord_notifications",
|
||||
"type": "static",
|
||||
"name": "Discord notifications",
|
||||
"unlimited": false,
|
||||
"balance": 0,
|
||||
"usage": 0,
|
||||
"included_usage": 0,
|
||||
"next_reset_at": null,
|
||||
"overage_allowed": false
|
||||
},
|
||||
"swag": {
|
||||
"id": "swag",
|
||||
"type": "single_use",
|
||||
"name": "Swag",
|
||||
"unlimited": true,
|
||||
"balance": 0,
|
||||
"usage": 0,
|
||||
"included_usage": 0,
|
||||
"next_reset_at": null,
|
||||
"overage_allowed": false
|
||||
},
|
||||
"messages": {
|
||||
"id": "messages",
|
||||
"type": "single_use",
|
||||
"name": "messages",
|
||||
"interval": "multiple",
|
||||
"interval_count": null,
|
||||
"unlimited": false,
|
||||
"balance": 550,
|
||||
"usage": 150,
|
||||
"included_usage": 700,
|
||||
"next_reset_at": 1764665321000,
|
||||
"overage_allowed": false,
|
||||
"breakdown": [
|
||||
{
|
||||
"interval": "lifetime",
|
||||
"interval_count": 1,
|
||||
"balance": 350,
|
||||
"usage": 150,
|
||||
"included_usage": 500,
|
||||
"next_reset_at": null
|
||||
},
|
||||
{
|
||||
"interval": "month",
|
||||
"interval_count": 1,
|
||||
"balance": 200,
|
||||
"usage": 0,
|
||||
"included_usage": 200,
|
||||
"next_reset_at": 1764665321000,
|
||||
"usage_limit": 500
|
||||
}
|
||||
],
|
||||
"usage_limit": 1000
|
||||
},
|
||||
"projects": {
|
||||
"id": "projects",
|
||||
"type": "continuous_use",
|
||||
"name": "Projects",
|
||||
"interval": "lifetime",
|
||||
"interval_count": 1,
|
||||
"unlimited": false,
|
||||
"balance": 12,
|
||||
"usage": 0,
|
||||
"included_usage": 12,
|
||||
"next_reset_at": null,
|
||||
"overage_allowed": false
|
||||
},
|
||||
"websites": {
|
||||
"id": "websites",
|
||||
"type": "continuous_use",
|
||||
"name": "Websites",
|
||||
"interval": "lifetime",
|
||||
"interval_count": 1,
|
||||
"unlimited": false,
|
||||
"balance": 3,
|
||||
"usage": 0,
|
||||
"included_usage": 3,
|
||||
"next_reset_at": null,
|
||||
"overage_allowed": false,
|
||||
"usage_limit": 10
|
||||
}
|
||||
},
|
||||
"invoices": [
|
||||
{
|
||||
"product_ids": [
|
||||
"pro"
|
||||
],
|
||||
"stripe_id": "in_1SKi259mx3u0jkgOr86nPA3R",
|
||||
"status": "paid",
|
||||
"total": 0,
|
||||
"currency": "usd",
|
||||
"created_at": 1761467921000,
|
||||
"hosted_invoice_url": "http://localhost:8080/invoices/hosted_invoice_url/inv_34Nmt7zkME1IpjKXCBj0Mr6EBOx"
|
||||
},
|
||||
{
|
||||
"product_ids": [
|
||||
"pro"
|
||||
],
|
||||
"stripe_id": "in_1SKi1F9mx3u0jkgO38q4Vf2B",
|
||||
"status": "paid",
|
||||
"total": 0,
|
||||
"currency": "usd",
|
||||
"created_at": 1761467921000,
|
||||
"hosted_invoice_url": "http://localhost:8080/invoices/hosted_invoice_url/inv_34Nmmg52fFTqhf1rJhzKgCjEvzj"
|
||||
},
|
||||
{
|
||||
"product_ids": [
|
||||
"pro"
|
||||
],
|
||||
"stripe_id": "in_1SKa9U9mx3u0jkgOpZa0MZFx",
|
||||
"status": "paid",
|
||||
"total": 0,
|
||||
"currency": "usd",
|
||||
"created_at": 1761032316000,
|
||||
"hosted_invoice_url": "http://localhost:8080/invoices/hosted_invoice_url/inv_34MnUDUGNVi0hdc21Rg3gb8h24d"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,14 +1,8 @@
|
||||
// @ts-nocheck
|
||||
{
|
||||
id: "team_plan",
|
||||
// other fields,
|
||||
licenses: [
|
||||
{ plan_id: "dev_seat" },
|
||||
{
|
||||
plan_id: "pro_seat", price: 50 // to override price
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
// Old customer object
|
||||
|
||||
|
||||
// Global namesapce:
|
||||
//
|
||||
|
||||
59
server/src/errors/formatZodError.ts
Normal file
59
server/src/errors/formatZodError.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import type { ZodError, ZodIssue } from "zod/v4";
|
||||
|
||||
/**
|
||||
* Formats Zod validation errors into user-friendly messages
|
||||
*/
|
||||
export function formatZodError(error: ZodError): string {
|
||||
const formatMessage = (issue: ZodIssue): string => {
|
||||
const path = issue.path.length ? issue.path.join(".") : "input";
|
||||
|
||||
// Clean up common Zod error messages
|
||||
let message = issue.message;
|
||||
|
||||
// Handle common patterns and make them more user-friendly
|
||||
if (
|
||||
message.includes("Too small") &&
|
||||
message.includes("expected string to have >=1 characters")
|
||||
) {
|
||||
message = "cannot be empty";
|
||||
} else if (message.includes("Invalid option: expected one of")) {
|
||||
// Clean up enum error messages
|
||||
// Example: 'Invalid option: expected one of "a"|"b"|"c"' -> 'must be one of: a, b, c'
|
||||
const match = message.match(/expected one of (.+)/);
|
||||
if (match) {
|
||||
const options = match[1]
|
||||
.split("|")
|
||||
.map((opt) => opt.replace(/['"]/g, "").trim())
|
||||
.join(", ");
|
||||
message = `must be one of: ${options}`;
|
||||
}
|
||||
} else if (message.includes("Invalid string: must match pattern")) {
|
||||
// Extract the pattern and make it more readable
|
||||
if (message.includes("/^[a-zA-Z0-9_-]+$/")) {
|
||||
message =
|
||||
"must contain only letters, numbers, underscores, and hyphens";
|
||||
} else {
|
||||
message = "has invalid format";
|
||||
}
|
||||
} else if (message.includes("Invalid input: expected string, received")) {
|
||||
const receivedType = message.split("received ")[1];
|
||||
message = `must be a string (received ${receivedType})`;
|
||||
} else if (message.includes("Invalid input: expected number, received")) {
|
||||
const receivedType = message.split("received ")[1];
|
||||
message = `must be a number (received ${receivedType})`;
|
||||
} else if (message.includes("Invalid input: expected boolean, received")) {
|
||||
const receivedType = message.split("received ")[1];
|
||||
message = `must be a boolean (received ${receivedType})`;
|
||||
}
|
||||
|
||||
return `${path}: ${message}`;
|
||||
};
|
||||
|
||||
const formattedIssues = error.issues.map(formatMessage);
|
||||
|
||||
// If there are multiple issues, format them nicely
|
||||
if (formattedIssues.length === 1) {
|
||||
return formattedIssues[0];
|
||||
}
|
||||
return `[Validation Errors] ${formattedIssues.join("; ")}`;
|
||||
}
|
||||
@@ -140,7 +140,7 @@ export const priceToStripeItem = ({
|
||||
};
|
||||
}
|
||||
|
||||
if (apiVersion === ApiVersion.Beta && !isCheckout) {
|
||||
if (apiVersion === ApiVersion.V1_Beta && !isCheckout) {
|
||||
return {
|
||||
lineItem: {
|
||||
// lineItem: getEmptyPriceItem({ price, org }),
|
||||
|
||||
@@ -75,7 +75,7 @@ export const handleCheckoutSub = async ({
|
||||
if (
|
||||
arrearPrice &&
|
||||
(attachParams.internalEntityId ||
|
||||
attachParams.apiVersion === ApiVersion.Beta)
|
||||
attachParams.apiVersion === ApiVersion.V1_Beta)
|
||||
) {
|
||||
itemsUpdate.push({
|
||||
id: item.id,
|
||||
|
||||
@@ -38,7 +38,7 @@ export const handleRemainingSets = async ({
|
||||
|
||||
if (
|
||||
attachParams.internalEntityId ||
|
||||
attachParams.apiVersion === ApiVersion.Beta
|
||||
attachParams.apiVersion === ApiVersion.V1_Beta
|
||||
) {
|
||||
const replaceIndex = remainingItems.findIndex(
|
||||
(item) => item.price === config.stripe_price_id,
|
||||
|
||||
@@ -17,11 +17,11 @@ import { RolloverService } from "@/internal/customers/cusProducts/cusEnts/cusRol
|
||||
import { getRolloverUpdates } from "@/internal/customers/cusProducts/cusEnts/cusRollovers/rolloverUtils.js";
|
||||
import { getResetBalancesUpdate } from "@/internal/customers/cusProducts/cusEnts/groupByUtils.js";
|
||||
import { getCusPriceUsage } from "@/internal/customers/cusProducts/cusPrices/cusPriceUtils.js";
|
||||
import { getAllFullCustomers } from "@/utils/scriptUtils/getAll/getAllAutumnCustomers.js";
|
||||
import { submitUsageToStripe } from "../../stripeMeterUtils.js";
|
||||
import { getInvoiceItemForUsage } from "../../stripePriceUtils.js";
|
||||
import { subToPeriodStartEnd } from "../../stripeSubUtils/convertSubUtils.js";
|
||||
import { findStripeItemForPrice } from "../../stripeSubUtils/stripeSubItemUtils.js";
|
||||
import { getAllFullCustomers } from "@/utils/scriptUtils/getAll/getAllAutumnCustomers.js";
|
||||
|
||||
export const handleUsagePrices = async ({
|
||||
db,
|
||||
@@ -86,7 +86,7 @@ export const handleUsagePrices = async ({
|
||||
|
||||
const isNewUsageMethod =
|
||||
activeProduct.internal_entity_id ||
|
||||
activeProduct.api_semver === ApiVersion.Beta;
|
||||
activeProduct.api_semver === ApiVersion.V1_Beta;
|
||||
|
||||
if (isNewUsageMethod) {
|
||||
const invoiceItem = getInvoiceItemForUsage({
|
||||
|
||||
@@ -53,7 +53,7 @@ export const handleCusProductDeleted = async ({
|
||||
stripeId: fullCus.processor?.id,
|
||||
});
|
||||
|
||||
const isV4Usage = cusProduct.api_semver === ApiVersion.Beta;
|
||||
const isV4Usage = cusProduct.api_semver === ApiVersion.V1_Beta;
|
||||
|
||||
// refer to handleUpgradeFlow.ts, when cancel immediately through API / dashboard, this happens...?
|
||||
const isAutumnCancel =
|
||||
|
||||
@@ -47,6 +47,7 @@ export const apiVersionMiddleware = async (c: Context<HonoEnv>, next: Next) => {
|
||||
|
||||
finalVersion = new ApiVersionClass(parsedVersion);
|
||||
}
|
||||
|
||||
// 2. Calculate from org creation date
|
||||
else if (org?.created_at) {
|
||||
finalVersion = createdAtToVersion({
|
||||
|
||||
@@ -68,6 +68,9 @@ export const baseMiddleware = async (c: Context<HonoEnv>, next: Next) => {
|
||||
userId: undefined,
|
||||
authType: AuthType.Unknown,
|
||||
env: AppEnv.Sandbox, // maybe use app_env headers
|
||||
|
||||
// Query params
|
||||
expand: [],
|
||||
});
|
||||
|
||||
childLogger.info(`${method} ${path}`);
|
||||
|
||||
@@ -3,8 +3,9 @@ import type { Context } from "hono";
|
||||
import type { ContentfulStatusCode } from "hono/utils/http-status";
|
||||
import Stripe from "stripe";
|
||||
import { ZodError } from "zod/v4";
|
||||
import { formatZodError } from "@/errors/formatZodError.js";
|
||||
import type { HonoEnv } from "@/honoUtils/HonoEnv.js";
|
||||
import RecaseError, { formatZodError } from "@/utils/errorUtils.js";
|
||||
import RecaseError from "@/utils/errorUtils.js";
|
||||
import { handleErrorSkip } from "./errorSkipMiddleware.js";
|
||||
|
||||
/**
|
||||
|
||||
@@ -8,8 +8,9 @@ import type { Context } from "hono";
|
||||
import type { ContentfulStatusCode } from "hono/utils/http-status";
|
||||
import Stripe from "stripe";
|
||||
import { ZodError } from "zod/v4";
|
||||
import { formatZodError } from "@/errors/formatZodError.js";
|
||||
import type { HonoEnv } from "@/honoUtils/HonoEnv.js";
|
||||
import RecaseError, { formatZodError } from "@/utils/errorUtils.js";
|
||||
import RecaseError from "@/utils/errorUtils.js";
|
||||
import { matchRoute } from "./middlewareUtils.js";
|
||||
|
||||
// ============================================================================
|
||||
|
||||
21
server/src/honoMiddlewares/expandMiddleware.ts
Normal file
21
server/src/honoMiddlewares/expandMiddleware.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import type { MiddlewareHandler } from "hono";
|
||||
import type { HonoEnv } from "@/honoUtils/HonoEnv.js";
|
||||
|
||||
/**
|
||||
* Extracts expand from validated query and sets it in context.
|
||||
* Must run AFTER versionedValidator so expand has been transformed.
|
||||
*/
|
||||
export const expandMiddleware = (): MiddlewareHandler<HonoEnv> => {
|
||||
return async (c, next) => {
|
||||
const validatedQuery = c.req.valid("query");
|
||||
const expand = validatedQuery?.expand || [];
|
||||
|
||||
const ctx = c.get("ctx");
|
||||
c.set("ctx", {
|
||||
...ctx,
|
||||
expand: Array.isArray(expand) ? expand : [expand],
|
||||
});
|
||||
|
||||
await next();
|
||||
};
|
||||
};
|
||||
@@ -5,6 +5,7 @@ import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import type { HonoEnv } from "@/honoUtils/HonoEnv.js";
|
||||
import { validator } from "./validatorMiddleware.js";
|
||||
import { versionedValidator } from "./versionedValidator.js";
|
||||
import { expandMiddleware } from "./expandMiddleware.js";
|
||||
|
||||
/**
|
||||
* Extended context type that includes validated input
|
||||
@@ -128,6 +129,11 @@ export function createRoute<
|
||||
middlewares.push(validator("param", opts.params));
|
||||
}
|
||||
|
||||
// Add expand middleware after query validation
|
||||
if (opts.query || opts.versionedQuery) {
|
||||
middlewares.push(expandMiddleware());
|
||||
}
|
||||
|
||||
const wrappedHandler = async (
|
||||
c: ValidatedContext<HonoEnv, Body, Query, Params>,
|
||||
) => {
|
||||
|
||||
@@ -55,28 +55,39 @@ export const versionedValidator = ({
|
||||
const versionKey = userVersion.value as ApiVersion;
|
||||
const schema = schemas[versionKey] ?? schemas.latest;
|
||||
|
||||
// Validate with version-specific schema using zValidator
|
||||
const validatorMiddleware = zValidator(target, schema, (result, _c) => {
|
||||
let validatedData: Record<string, unknown>;
|
||||
|
||||
// For query validation, use the parsed query from queryMiddleware
|
||||
if (target === "query") {
|
||||
const parsedQuery = c.req.query();
|
||||
const result = schema.safeParse(parsedQuery);
|
||||
if (!result.success) {
|
||||
// Validation errors reference fields from user's version ✅
|
||||
throw result.error;
|
||||
}
|
||||
});
|
||||
validatedData = result.data as Record<string, unknown>;
|
||||
c.req.addValidatedData(target, result.data);
|
||||
} else {
|
||||
// For other targets, use zValidator
|
||||
const validatorMiddleware = zValidator(target, schema, (result, _c) => {
|
||||
if (!result.success) {
|
||||
// Validation errors reference fields from user's version ✅
|
||||
throw result.error;
|
||||
}
|
||||
});
|
||||
|
||||
// Run validation
|
||||
await validatorMiddleware(c, async () => {});
|
||||
// Run validation
|
||||
await validatorMiddleware(c, async () => {});
|
||||
|
||||
// Get validated data - type assertion needed due to Hono's dynamic validation target typing
|
||||
type ValidData = Record<string, unknown>;
|
||||
// biome-ignore lint/suspicious/noExplicitAny: Hono's typing requires dynamic target access
|
||||
const validatedData = (c.req as any).valid(target) as ValidData;
|
||||
// Get validated data - type assertion needed due to Hono's dynamic validation target typing
|
||||
validatedData = (c.req as any).valid(target) as Record<string, unknown>;
|
||||
}
|
||||
|
||||
// If user is on older version, transform to latest
|
||||
if (!userVersion.eq(new ApiVersionClass(LATEST_VERSION))) {
|
||||
const transformed = applyRequestVersionChanges({
|
||||
input: validatedData,
|
||||
targetVersion: userVersion,
|
||||
currentVersion: new ApiVersionClass(LATEST_VERSION),
|
||||
fromVersion: userVersion,
|
||||
toVersion: new ApiVersionClass(LATEST_VERSION),
|
||||
resource,
|
||||
});
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ export type RequestContext = {
|
||||
// Objects
|
||||
db: DrizzleCli;
|
||||
logger: Logger;
|
||||
clickhouseClient: ClickHouseClient;
|
||||
clickhouseClient?: ClickHouseClient;
|
||||
|
||||
// Info
|
||||
id: string;
|
||||
@@ -27,6 +27,9 @@ export type RequestContext = {
|
||||
authType: AuthType;
|
||||
apiVersion: ApiVersionClass;
|
||||
timestamp: number;
|
||||
|
||||
// Query params
|
||||
expand: string[];
|
||||
};
|
||||
|
||||
export type AutumnContext = RequestContext;
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import type { ExtendedRequest } from "@/utils/models/Request.js";
|
||||
import { ActionType, AppEnv, Organization } from "@autumn/shared";
|
||||
import { addTaskToQueue } from "@/queue/queueUtils.js";
|
||||
import { JobName } from "@/queue/JobName.js";
|
||||
import { constructAction, parseReqForAction } from "../actionUtils.js";
|
||||
import { ActionType, type AppEnv, type Organization } from "@autumn/shared";
|
||||
import type { AutumnContext } from "@/honoUtils/HonoEnv.js";
|
||||
import { CusService } from "@/internal/customers/CusService.js";
|
||||
import { JobName } from "@/queue/JobName.js";
|
||||
import { addTaskToQueue } from "@/queue/queueUtils.js";
|
||||
import type { ExtendedRequest } from "@/utils/models/Request.js";
|
||||
import type { HandleCustomerCreatedData } from "@/utils/workerUtils/jobTypes/HandleCustomerCreatedData.js";
|
||||
import { ActionService } from "../ActionService.js";
|
||||
import { constructAction, parseReqForAction } from "../actionUtils.js";
|
||||
|
||||
export const addCustomerCreatedTask = async ({
|
||||
req,
|
||||
@@ -23,31 +24,30 @@ export const addCustomerCreatedTask = async ({
|
||||
payload: {
|
||||
req: req ? parseReqForAction(req) : undefined,
|
||||
internalCustomerId,
|
||||
org,
|
||||
orgId: org.id,
|
||||
env,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const handleCustomerCreated = async ({
|
||||
db,
|
||||
logger,
|
||||
ctx,
|
||||
data,
|
||||
}: {
|
||||
db: DrizzleCli;
|
||||
logger: any;
|
||||
data: any;
|
||||
ctx: AutumnContext;
|
||||
data: HandleCustomerCreatedData;
|
||||
}) => {
|
||||
const { req, internalCustomerId, org, env } = data;
|
||||
const { req, internalCustomerId } = data;
|
||||
const { db, org, env } = ctx;
|
||||
|
||||
let customer = await CusService.getFull({
|
||||
const customer = await CusService.getFull({
|
||||
db,
|
||||
idOrInternalId: internalCustomerId,
|
||||
orgId: org.id,
|
||||
env,
|
||||
});
|
||||
|
||||
let action = constructAction({
|
||||
const action = constructAction({
|
||||
org,
|
||||
env,
|
||||
customer,
|
||||
|
||||
@@ -2,15 +2,14 @@ import {
|
||||
ActionType,
|
||||
type AppEnv,
|
||||
type AuthType,
|
||||
createdAtToVersion,
|
||||
cusProductToProduct,
|
||||
type FullCusProduct,
|
||||
type FullProduct,
|
||||
notNullish,
|
||||
type Organization,
|
||||
} from "@autumn/shared";
|
||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import { sendSvixEvent } from "@/external/svix/svixHelpers.js";
|
||||
import type { AutumnContext } from "@/honoUtils/HonoEnv.js";
|
||||
import { ActionService } from "@/internal/analytics/ActionService.js";
|
||||
import {
|
||||
constructAction,
|
||||
@@ -19,7 +18,7 @@ import {
|
||||
import { getSingleEntityResponse } from "@/internal/api/entities/getEntityUtils.js";
|
||||
import { CusService } from "@/internal/customers/CusService.js";
|
||||
import { RELEVANT_STATUSES } from "@/internal/customers/cusProducts/CusProductService.js";
|
||||
import { getCustomerDetails } from "@/internal/customers/cusUtils/getCustomerDetails.js";
|
||||
import { getApiCustomer } from "@/internal/customers/cusUtils/apiCusUtils/getApiCustomer.js";
|
||||
import { FeatureService } from "@/internal/features/FeatureService.js";
|
||||
import { getProductResponse } from "@/internal/products/productUtils/productResponseUtils/getProductResponse.js";
|
||||
import { JobName } from "@/queue/JobName.js";
|
||||
@@ -66,8 +65,10 @@ export const addProductsUpdatedWebhookTask = async ({
|
||||
payload: {
|
||||
req: req ? parseReqForAction(req) : undefined,
|
||||
internalCustomerId,
|
||||
org,
|
||||
orgId: org.id,
|
||||
env,
|
||||
// org,
|
||||
// env,
|
||||
customerId,
|
||||
cusProduct,
|
||||
scheduledCusProduct,
|
||||
@@ -90,18 +91,16 @@ export const addProductsUpdatedWebhookTask = async ({
|
||||
};
|
||||
|
||||
export const handleProductsUpdated = async ({
|
||||
db,
|
||||
logger,
|
||||
ctx,
|
||||
data,
|
||||
}: {
|
||||
db: DrizzleCli;
|
||||
logger: any;
|
||||
ctx: AutumnContext;
|
||||
data: {
|
||||
req: Partial<ExtendedRequest>;
|
||||
actionDetails: ActionDetails;
|
||||
internalCustomerId: string;
|
||||
org: Organization;
|
||||
env: AppEnv;
|
||||
// org: Organization;
|
||||
// env: AppEnv;
|
||||
customerId: string;
|
||||
product: FullProduct;
|
||||
scenario: string;
|
||||
@@ -110,15 +109,10 @@ export const handleProductsUpdated = async ({
|
||||
deletedCusProduct?: FullCusProduct;
|
||||
};
|
||||
}) => {
|
||||
const {
|
||||
req,
|
||||
org,
|
||||
env,
|
||||
scenario,
|
||||
cusProduct,
|
||||
scheduledCusProduct,
|
||||
deletedCusProduct,
|
||||
} = data;
|
||||
const { req, scenario, cusProduct, scheduledCusProduct, deletedCusProduct } =
|
||||
data;
|
||||
|
||||
const { db, org, env, logger } = ctx;
|
||||
|
||||
// Product:
|
||||
const product = cusProduct.product;
|
||||
@@ -126,8 +120,8 @@ export const handleProductsUpdated = async ({
|
||||
const customer = await CusService.getFull({
|
||||
db,
|
||||
idOrInternalId: data.customerId || data.internalCustomerId,
|
||||
orgId: data.org.id,
|
||||
env: data.env,
|
||||
orgId: org.id,
|
||||
env: env,
|
||||
inStatuses: RELEVANT_STATUSES,
|
||||
entityId: cusProduct.internal_entity_id || undefined,
|
||||
});
|
||||
@@ -138,20 +132,10 @@ export const handleProductsUpdated = async ({
|
||||
env,
|
||||
});
|
||||
|
||||
const apiVersion = createdAtToVersion({
|
||||
createdAt: org.created_at || Date.now(),
|
||||
});
|
||||
|
||||
const cusDetails = await getCustomerDetails({
|
||||
db,
|
||||
customer: customer,
|
||||
org,
|
||||
env,
|
||||
features,
|
||||
logger,
|
||||
cusProducts: customer.customer_products,
|
||||
const apiCustomer = await getApiCustomer({
|
||||
ctx,
|
||||
fullCus: customer,
|
||||
expand: [],
|
||||
apiVersion,
|
||||
});
|
||||
|
||||
const productRes = await getProductResponse({
|
||||
@@ -199,12 +183,10 @@ export const handleProductsUpdated = async ({
|
||||
let entityRes = null;
|
||||
if (notNullish(customer?.entity)) {
|
||||
entityRes = await getSingleEntityResponse({
|
||||
ctx,
|
||||
entityId: customer.entity!.id,
|
||||
org,
|
||||
env,
|
||||
fullCus: customer,
|
||||
entity: customer.entity!,
|
||||
features,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -215,7 +197,7 @@ export const handleProductsUpdated = async ({
|
||||
eventType: "customer.products.updated",
|
||||
data: {
|
||||
scenario,
|
||||
customer: cusDetails,
|
||||
customer: apiCustomer,
|
||||
entity: entityRes,
|
||||
updated_product: productRes,
|
||||
},
|
||||
|
||||
@@ -1,54 +1,47 @@
|
||||
import { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import type { Job, Queue } from "bullmq";
|
||||
import type { AutumnContext } from "@/honoUtils/HonoEnv.js";
|
||||
import { JobName } from "@/queue/JobName.js";
|
||||
import { getLock, releaseLock } from "@/queue/lockUtils.js";
|
||||
import { Queue } from "bullmq";
|
||||
import { Job } from "bullmq";
|
||||
import { handleProductsUpdated } from "./handlers/handleProductsUpdated.js";
|
||||
import { handleCustomerCreated } from "./handlers/handleCustomerCreated.js";
|
||||
import { handleProductsUpdated } from "./handlers/handleProductsUpdated.js";
|
||||
|
||||
export const runActionHandlerTask = async ({
|
||||
queue,
|
||||
job,
|
||||
logger,
|
||||
db,
|
||||
useBackup,
|
||||
ctx,
|
||||
}: {
|
||||
queue: Queue;
|
||||
job: Job;
|
||||
logger: any;
|
||||
db: DrizzleCli;
|
||||
useBackup: boolean;
|
||||
ctx: AutumnContext;
|
||||
}) => {
|
||||
let payload = job.data;
|
||||
let internalCustomerId = payload.internalCustomerId;
|
||||
let lockKey = `action:${internalCustomerId}`;
|
||||
const payload = job.data;
|
||||
const internalCustomerId = payload.internalCustomerId;
|
||||
const lockKey = `action:${internalCustomerId}`;
|
||||
|
||||
const { logger } = ctx;
|
||||
|
||||
try {
|
||||
let lock = await getLock({ queue, job, lockKey, useBackup });
|
||||
const lock = await getLock({ queue, job, lockKey, useBackup });
|
||||
if (!lock) return;
|
||||
|
||||
switch (job.name) {
|
||||
case JobName.HandleProductsUpdated:
|
||||
await handleProductsUpdated({
|
||||
db,
|
||||
logger,
|
||||
ctx,
|
||||
data: payload,
|
||||
});
|
||||
break;
|
||||
case JobName.HandleCustomerCreated:
|
||||
await handleCustomerCreated({
|
||||
db,
|
||||
logger,
|
||||
ctx,
|
||||
data: payload,
|
||||
});
|
||||
break;
|
||||
}
|
||||
} catch (error: any) {
|
||||
logger.error("Error processing action handler job:", {
|
||||
// jobName: job.name,
|
||||
// payload,
|
||||
message: error.message,
|
||||
});
|
||||
logger.error(`Error processing action handler job: ${error.message}`);
|
||||
} finally {
|
||||
await releaseLock({ lockKey, useBackup });
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import { handleGetOrg } from "../orgs/handlers/handleGetOrg.js";
|
||||
import { platformRouter } from "../platform/platformLegacy/platformRouter.js";
|
||||
import { productBetaRouter, productRouter } from "../products/productRouter.js";
|
||||
import { componentRouter } from "./components/componentRouter.js";
|
||||
import { entityRouter } from "./entities/entityRouter.js";
|
||||
import { expressEntityRouter } from "./entities/entityRouter.js";
|
||||
import { checkRouter } from "./entitled/checkRouter.js";
|
||||
import { eventsRouter } from "./events/eventRouter.js";
|
||||
import { usageRouter } from "./events/usageRouter.js";
|
||||
@@ -44,7 +44,7 @@ apiRouter.use("/features", featureRouter);
|
||||
apiRouter.use("/internal_features", internalFeatureRouter);
|
||||
|
||||
apiRouter.use("/usage", usageRouter);
|
||||
apiRouter.use("/entities", entityRouter);
|
||||
apiRouter.use("/entities", expressEntityRouter);
|
||||
apiRouter.use("/migrations", migrationRouter);
|
||||
|
||||
// REWARDS
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
type ApiVersionClass,
|
||||
type AppEnv,
|
||||
AuthType,
|
||||
CusExpand,
|
||||
type CusProductStatus,
|
||||
type Feature,
|
||||
@@ -10,7 +11,8 @@ import {
|
||||
import type { NodeClickHouseClient } from "@clickhouse/client/dist/client.js";
|
||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import { RELEVANT_STATUSES } from "@/internal/customers/cusProducts/CusProductService.js";
|
||||
import { getCustomerDetails } from "../../customers/cusUtils/getCustomerDetails.js";
|
||||
import { getApiCustomer } from "@/internal/customers/cusUtils/apiCusUtils/getApiCustomer.js";
|
||||
import { generateId } from "@/utils/genUtils.js";
|
||||
import { getPaginatedFullCusQuery } from "../../customers/getFullCusQuery.js";
|
||||
|
||||
export class CusBatchService {
|
||||
@@ -95,20 +97,24 @@ export class CusBatchService {
|
||||
const customer = normalizedCustomer as FullCustomer;
|
||||
const cusProducts = customer.customer_products || [];
|
||||
|
||||
const customerDetails = await getCustomerDetails({
|
||||
db,
|
||||
customer,
|
||||
features,
|
||||
org,
|
||||
env,
|
||||
params: {},
|
||||
logger: console,
|
||||
cusProducts,
|
||||
const apiCustomer = await getApiCustomer({
|
||||
ctx: {
|
||||
db,
|
||||
org,
|
||||
env,
|
||||
features,
|
||||
logger,
|
||||
apiVersion,
|
||||
id: generateId("local_req"),
|
||||
isPublic: false,
|
||||
authType: AuthType.Unknown,
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
fullCus: customer,
|
||||
expand: expand,
|
||||
apiVersion: apiVersion,
|
||||
});
|
||||
|
||||
finals.push(customerDetails);
|
||||
finals.push(apiCustomer);
|
||||
} catch (error) {
|
||||
console.error(`Failed to process customer ${result.id}:`, error);
|
||||
}
|
||||
@@ -124,7 +130,7 @@ export class CusBatchService {
|
||||
const normalizeTimestamp = (value: any): number => {
|
||||
if (typeof value === "string") {
|
||||
const parsed = parseInt(value, 10);
|
||||
return isNaN(parsed) ? Date.now() : parsed;
|
||||
return Number.isNaN(parsed) ? Date.now() : parsed;
|
||||
}
|
||||
return typeof value === "number" ? value : Date.now();
|
||||
};
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
import { Router } from "express";
|
||||
|
||||
import { routeHandler } from "@/utils/routerUtils.js";
|
||||
import { CusService } from "@/internal/customers/CusService.js";
|
||||
import { handleGetEntity } from "./handlers/handleGetEntity.js";
|
||||
import { routeHandler } from "@/utils/routerUtils.js";
|
||||
import { handlePostEntityRequest } from "../../entities/handlers/handleCreateEntity/handleCreateEntity.js";
|
||||
import { handleDeleteEntity } from "./handlers/handleDeleteEntity.js";
|
||||
import { handleGetEntity } from "./handlers/handleGetEntity.js";
|
||||
|
||||
export const entityRouter: Router = Router({ mergeParams: true });
|
||||
export const expressEntityRouter: Router = Router({ mergeParams: true });
|
||||
|
||||
// List entityes
|
||||
entityRouter.get("", (req: any, res: any) =>
|
||||
expressEntityRouter.get("", (req: any, res: any) =>
|
||||
routeHandler({
|
||||
req,
|
||||
res,
|
||||
action: "listEntities",
|
||||
handler: async (req, res) => {
|
||||
const customerId = String(req.params.customer_id);
|
||||
let { orgId, env } = req;
|
||||
const { orgId, env } = req;
|
||||
|
||||
let customer = await CusService.getFull({
|
||||
const customer = await CusService.getFull({
|
||||
db: req.db,
|
||||
idOrInternalId: customerId,
|
||||
orgId,
|
||||
@@ -34,10 +33,14 @@ entityRouter.get("", (req: any, res: any) =>
|
||||
);
|
||||
|
||||
// 1. Create entity
|
||||
entityRouter.post("", handlePostEntityRequest);
|
||||
expressEntityRouter.post("", handlePostEntityRequest);
|
||||
|
||||
// 2. Delete entity
|
||||
entityRouter.delete("/:entity_id", handleDeleteEntity);
|
||||
expressEntityRouter.delete("/:entity_id", handleDeleteEntity);
|
||||
|
||||
// 3. Get entity
|
||||
entityRouter.get("/:entity_id", handleGetEntity);
|
||||
expressEntityRouter.get("/:entity_id", handleGetEntity);
|
||||
|
||||
// export const entityRouter = new Hono<HonoEnv>();
|
||||
|
||||
// entityRouter.get("/:entity_id", ...handleGetEntity);
|
||||
|
||||
@@ -1,44 +1,34 @@
|
||||
import {
|
||||
type ApiCusProduct,
|
||||
ApiVersion,
|
||||
ApiVersionClass,
|
||||
type AppEnv,
|
||||
type ApiEntity,
|
||||
type Entity,
|
||||
type EntityExpand,
|
||||
type EntityResponse,
|
||||
ErrCode,
|
||||
type Feature,
|
||||
type FullCusProduct,
|
||||
type FullCustomer,
|
||||
notNullish,
|
||||
type Organization,
|
||||
type Subscription,
|
||||
} from "@autumn/shared";
|
||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import { getCusWithCache } from "@/internal/customers/cusCache/getCusWithCache.js";
|
||||
import type { AutumnContext } from "@/honoUtils/HonoEnv.js";
|
||||
import { CusService } from "@/internal/customers/CusService.js";
|
||||
import { getApiCusPlans } from "@/internal/customers/cusUtils/apiCusUtils/getApiCusPlan/getApiCusPlans.js";
|
||||
import { getCusFeaturesResponse } from "@/internal/customers/cusUtils/cusFeatureResponseUtils/getCusFeaturesResponse.js";
|
||||
import { processFullCusProducts } from "@/internal/customers/cusUtils/cusProductResponseUtils/processFullCusProducts.js";
|
||||
import RecaseError from "@/utils/errorUtils.js";
|
||||
import { nullish } from "@/utils/genUtils.js";
|
||||
|
||||
export const getSingleEntityResponse = async ({
|
||||
ctx,
|
||||
entityId,
|
||||
org,
|
||||
env,
|
||||
fullCus,
|
||||
entity,
|
||||
features,
|
||||
withAutumnId = false,
|
||||
}: {
|
||||
ctx: AutumnContext;
|
||||
entityId: string;
|
||||
org: Organization;
|
||||
env: AppEnv;
|
||||
fullCus: FullCustomer;
|
||||
entity: Entity;
|
||||
features: Feature[];
|
||||
withAutumnId?: boolean;
|
||||
}) => {
|
||||
const apiVersion = new ApiVersionClass(ApiVersion.V1_2);
|
||||
const { org, env, apiVersion } = ctx;
|
||||
|
||||
if (!entity) {
|
||||
throw new RecaseError({
|
||||
@@ -70,16 +60,24 @@ export const getSingleEntityResponse = async ({
|
||||
),
|
||||
);
|
||||
|
||||
const { main, addOns } = await processFullCusProducts({
|
||||
fullCusProducts: entityCusProducts,
|
||||
entity,
|
||||
subs: entitySubs,
|
||||
org,
|
||||
apiVersion,
|
||||
features,
|
||||
});
|
||||
const fullEntity = structuredClone(fullCus);
|
||||
fullEntity.customer_products = entityCusProducts;
|
||||
fullEntity.subscriptions = entitySubs;
|
||||
|
||||
const products: ApiCusProduct[] = [...main, ...addOns];
|
||||
// const { main, addOns } = await processFullCusProducts({
|
||||
// fullCusProducts: entityCusProducts,
|
||||
// entity,
|
||||
// subs: entitySubs,
|
||||
// org,
|
||||
// apiVersion,
|
||||
// features,
|
||||
// });
|
||||
|
||||
// const products: ApiCusProduct[] = [...main, ...addOns];
|
||||
const { apiCusPlans } = await getApiCusPlans({
|
||||
ctx,
|
||||
fullCus: fullEntity,
|
||||
});
|
||||
|
||||
const cusFeatures = await getCusFeaturesResponse({
|
||||
cusProducts: entityCusProducts,
|
||||
@@ -96,47 +94,52 @@ export const getSingleEntityResponse = async ({
|
||||
// feature_id: entity.feature_id,
|
||||
customer_id: fullCus.id || fullCus.internal_id,
|
||||
env,
|
||||
products,
|
||||
plans: apiCusPlans,
|
||||
features: cusFeatures,
|
||||
};
|
||||
} satisfies ApiEntity;
|
||||
};
|
||||
|
||||
export const getEntityResponse = async ({
|
||||
db,
|
||||
ctx,
|
||||
entityIds,
|
||||
org,
|
||||
env,
|
||||
customerId,
|
||||
expand,
|
||||
entityId,
|
||||
withAutumnId = false,
|
||||
apiVersion,
|
||||
features,
|
||||
logger,
|
||||
skipCache = false,
|
||||
}: {
|
||||
db: DrizzleCli;
|
||||
ctx: AutumnContext;
|
||||
entityIds: string[];
|
||||
org: Organization;
|
||||
env: AppEnv;
|
||||
customerId: string;
|
||||
expand?: EntityExpand[];
|
||||
entityId?: string;
|
||||
withAutumnId?: boolean;
|
||||
apiVersion: number;
|
||||
features: Feature[];
|
||||
logger: any;
|
||||
skipCache?: boolean;
|
||||
}) => {
|
||||
const fullCus = await getCusWithCache({
|
||||
// const fullCus = await getCusWithCache({
|
||||
// db,
|
||||
// idOrInternalId: customerId,
|
||||
// org,
|
||||
// env,
|
||||
// expand,
|
||||
// entityId,
|
||||
// logger,
|
||||
// skipCache,
|
||||
// });
|
||||
|
||||
// don't use cache anymore?
|
||||
|
||||
const { org, env, features, db } = ctx;
|
||||
|
||||
const fullCus = await CusService.getFull({
|
||||
db,
|
||||
idOrInternalId: customerId,
|
||||
org,
|
||||
orgId: org.id,
|
||||
env,
|
||||
expand,
|
||||
entityId,
|
||||
logger,
|
||||
skipCache,
|
||||
expand,
|
||||
withSubs: true,
|
||||
withEntities: true,
|
||||
});
|
||||
|
||||
if (!fullCus) {
|
||||
@@ -147,7 +150,7 @@ export const getEntityResponse = async ({
|
||||
});
|
||||
}
|
||||
|
||||
const entityResponses: EntityResponse[] = [];
|
||||
const entityResponses: ApiEntity[] = [];
|
||||
|
||||
for (const entityId of entityIds) {
|
||||
const entity = fullCus.entities.find(
|
||||
@@ -163,12 +166,10 @@ export const getEntityResponse = async ({
|
||||
}
|
||||
|
||||
const entityResponse = await getSingleEntityResponse({
|
||||
ctx,
|
||||
entityId,
|
||||
org,
|
||||
env,
|
||||
fullCus,
|
||||
entity,
|
||||
features,
|
||||
withAutumnId,
|
||||
});
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { ApiEntitySchema, EntityExpand, LegacyVersion } from "@autumn/shared";
|
||||
import { ApiEntitySchema, EntityExpand } from "@autumn/shared";
|
||||
import { invoicesToResponse } from "@/internal/invoices/invoiceUtils.js";
|
||||
import { OrgService } from "@/internal/orgs/OrgService.js";
|
||||
import { routeHandler } from "@/utils/routerUtils.js";
|
||||
import { orgToVersion } from "@/utils/versionUtils/legacyVersionUtils.js";
|
||||
import { parseEntityExpand } from "../entityUtils.js";
|
||||
import { getEntityResponse } from "../getEntityUtils.js";
|
||||
|
||||
@@ -16,32 +14,16 @@ export const handleGetEntity = async (req: any, res: any) =>
|
||||
const customerId = req.params.customer_id as string;
|
||||
const expand = parseEntityExpand(req.query.expand);
|
||||
|
||||
const { env, db, logger, features } = req;
|
||||
const { logger } = req;
|
||||
|
||||
const org = await OrgService.getFromReq(req);
|
||||
const apiVersion = orgToVersion({
|
||||
org,
|
||||
reqApiVersion:
|
||||
req.apiVersion >= LegacyVersion.v1_1
|
||||
? req.apiVersion
|
||||
: LegacyVersion.v1_2,
|
||||
const { entities, invoices } = await getEntityResponse({
|
||||
ctx: req,
|
||||
entityIds: [entityId],
|
||||
customerId,
|
||||
expand,
|
||||
entityId,
|
||||
});
|
||||
|
||||
// const start = performance.now();
|
||||
const { entities, customer, fullEntities, invoices } =
|
||||
await getEntityResponse({
|
||||
db,
|
||||
entityIds: [entityId],
|
||||
org,
|
||||
env,
|
||||
customerId,
|
||||
expand,
|
||||
entityId,
|
||||
apiVersion,
|
||||
features,
|
||||
logger,
|
||||
});
|
||||
|
||||
const entity = entities[0];
|
||||
const withInvoices = expand.includes(EntityExpand.Invoices);
|
||||
|
||||
|
||||
@@ -196,20 +196,3 @@ checkRouter.post("", async (req: any, res: any) => {
|
||||
handleRequestError({ req, error, res, action: "Failed to GET entitled" });
|
||||
}
|
||||
});
|
||||
|
||||
// let features = [feature, ...creditSystems];
|
||||
// let balanceObj: any, featureToUse: any;
|
||||
// try {
|
||||
// balanceObj = balances.length > 0 ? balances[0] : null;
|
||||
|
||||
// featureToUse =
|
||||
// notNullish(balanceObj) && balanceObj.feature_id !== feature.id
|
||||
// ? features.find((f) => f.id === balanceObj.feature_id)
|
||||
// : creditSystems.length > 0
|
||||
// ? creditSystems[0]
|
||||
// : feature;
|
||||
// } catch (error) {
|
||||
// logger.error(`/check: failed to get balance & feature to use`, error);
|
||||
// }
|
||||
|
||||
// 3. If with preview, get preview
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
cusProductToProduct,
|
||||
type Feature,
|
||||
FeaturePreviewScenario,
|
||||
type FullCusProduct,
|
||||
@@ -6,7 +7,6 @@ import {
|
||||
type FullProduct,
|
||||
} from "@autumn/shared";
|
||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import { fullCusProductToProduct } from "@/internal/customers/cusProducts/cusProductUtils.js";
|
||||
import { ProductService } from "@/internal/products/ProductService.js";
|
||||
import { getProductResponse } from "@/internal/products/productUtils/productResponseUtils/getProductResponse.js";
|
||||
import {
|
||||
@@ -43,7 +43,7 @@ export const getCheckPreview = async ({
|
||||
);
|
||||
|
||||
const cusOwnedProducts = mainCusProds.map((cp: FullCusProduct) =>
|
||||
fullCusProductToProduct(cp),
|
||||
cusProductToProduct({ cusProduct: cp }),
|
||||
);
|
||||
|
||||
sortProductsByPrice({ products: cusOwnedProducts });
|
||||
|
||||
0
server/src/internal/api/entitled/handleCheckV0.ts
Normal file
0
server/src/internal/api/entitled/handleCheckV0.ts
Normal file
@@ -1,37 +1,34 @@
|
||||
import { AttachParams } from "@/internal/customers/cusProducts/AttachParams.js";
|
||||
|
||||
import {
|
||||
AttachReplaceable,
|
||||
type AttachReplaceable,
|
||||
BillingType,
|
||||
FullCusProduct,
|
||||
FullCustomerEntitlement,
|
||||
FullEntitlement,
|
||||
type FullCusProduct,
|
||||
type FullCustomerEntitlement,
|
||||
type FullEntitlement,
|
||||
getFeatureInvoiceDescription,
|
||||
intervalsSame,
|
||||
PreviewLineItem,
|
||||
Price,
|
||||
type PreviewLineItem,
|
||||
type Price,
|
||||
} from "@autumn/shared";
|
||||
import Stripe from "stripe";
|
||||
import { attachParamsToProduct } from "../convertAttachParams.js";
|
||||
import {
|
||||
getBillingType,
|
||||
getPriceEntitlement,
|
||||
getPriceForOverage,
|
||||
} from "@/internal/products/prices/priceUtils.js";
|
||||
import { Decimal } from "decimal.js";
|
||||
import type Stripe from "stripe";
|
||||
import type { AttachParams } from "@/internal/customers/cusProducts/AttachParams.js";
|
||||
import { findCusEnt } from "@/internal/customers/cusProducts/cusEnts/cusEntUtils/findCusEntUtils.js";
|
||||
import {
|
||||
getExistingUsageFromCusProducts,
|
||||
getRelatedCusPrice,
|
||||
} from "@/internal/customers/cusProducts/cusEnts/cusEntUtils.js";
|
||||
import { subToAutumnInterval } from "@/external/stripe/utils.js";
|
||||
import { Decimal } from "decimal.js";
|
||||
import { getCurContUseItems } from "@/internal/invoices/previewItemUtils/getCurContUseItems.js";
|
||||
import { intervalsAreSame } from "../getAttachConfig.js";
|
||||
import { newPriceToInvoiceDescription } from "@/internal/invoices/invoiceFormatUtils.js";
|
||||
import { getCurContUseItems } from "@/internal/invoices/previewItemUtils/getCurContUseItems.js";
|
||||
import { getDefaultPriceStr } from "@/internal/invoices/previewItemUtils/getItemsForNewProduct.js";
|
||||
import { priceToUsageModel } from "@/internal/products/prices/priceUtils/convertPrice.js";
|
||||
import { priceToContUseItem } from "./priceToContUseItem.js";
|
||||
import { shouldProrate } from "@/internal/products/prices/priceUtils/prorationConfigUtils.js";
|
||||
import {
|
||||
getBillingType,
|
||||
getPriceEntitlement,
|
||||
getPriceForOverage,
|
||||
} from "@/internal/products/prices/priceUtils.js";
|
||||
import { attachParamsToProduct } from "../convertAttachParams.js";
|
||||
import { intervalsAreSame } from "../getAttachConfig.js";
|
||||
import { priceToContUseItem } from "./priceToContUseItem.js";
|
||||
|
||||
export const getContUseNewItems = async ({
|
||||
price,
|
||||
@@ -56,14 +53,14 @@ export const getContUseNewItems = async ({
|
||||
internalEntityId: attachParams.internalEntityId,
|
||||
});
|
||||
|
||||
let description = newPriceToInvoiceDescription({
|
||||
const description = newPriceToInvoiceDescription({
|
||||
org,
|
||||
price,
|
||||
product: newProduct,
|
||||
quantity: usage,
|
||||
});
|
||||
|
||||
if (usage == 0) {
|
||||
if (usage === 0) {
|
||||
return {
|
||||
price_id: price.id,
|
||||
price: getDefaultPriceStr({ org, price, ent, features }),
|
||||
@@ -80,8 +77,8 @@ export const getContUseNewItems = async ({
|
||||
prevCusEnt &&
|
||||
!shouldProrate(price.proration_config?.on_decrease)
|
||||
) {
|
||||
let isDowngrade = ent.allowance! > prevCusEnt.entitlement.allowance!;
|
||||
let prevBalance = prevCusEnt.balance!;
|
||||
const isDowngrade = ent.allowance! > prevCusEnt.entitlement.allowance!;
|
||||
const prevBalance = prevCusEnt.balance!;
|
||||
|
||||
if (isDowngrade && prevBalance < 0) {
|
||||
overage = new Decimal(prevBalance).abs().toNumber();
|
||||
@@ -89,8 +86,8 @@ export const getContUseNewItems = async ({
|
||||
}
|
||||
}
|
||||
|
||||
let amount = getPriceForOverage(price, overage);
|
||||
let description = getFeatureInvoiceDescription({
|
||||
const amount = getPriceForOverage(price, overage);
|
||||
const description = getFeatureInvoiceDescription({
|
||||
feature: ent.feature,
|
||||
usage: usage,
|
||||
prodName: newProduct.name,
|
||||
@@ -130,24 +127,24 @@ export const getContUseInvoiceItems = async ({
|
||||
})
|
||||
: [];
|
||||
|
||||
let newEnts = product.entitlements;
|
||||
let oldItems: PreviewLineItem[] = [];
|
||||
let newItems: PreviewLineItem[] = [];
|
||||
let replaceables: AttachReplaceable[] = [];
|
||||
const newEnts = product.entitlements;
|
||||
const oldItems: PreviewLineItem[] = [];
|
||||
const newItems: PreviewLineItem[] = [];
|
||||
const replaceables: AttachReplaceable[] = [];
|
||||
|
||||
for (const price of product.prices) {
|
||||
let billingType = getBillingType(price.config);
|
||||
const billingType = getBillingType(price.config);
|
||||
if (billingType !== BillingType.InArrearProrated) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let ent = getPriceEntitlement(price, newEnts);
|
||||
let prevCusEnt = findCusEnt({
|
||||
const ent = getPriceEntitlement(price, newEnts);
|
||||
const prevCusEnt = findCusEnt({
|
||||
cusEnts,
|
||||
feature: ent.feature,
|
||||
});
|
||||
|
||||
let prevCusPrice = prevCusEnt
|
||||
const prevCusPrice = prevCusEnt
|
||||
? getRelatedCusPrice(prevCusEnt, cusPrices)!
|
||||
: undefined;
|
||||
|
||||
@@ -176,7 +173,7 @@ export const getContUseInvoiceItems = async ({
|
||||
(item) => item.price_id === prevCusPrice?.price.id,
|
||||
);
|
||||
|
||||
let {
|
||||
const {
|
||||
oldItem,
|
||||
newItems: newItems_,
|
||||
replaceables: replaceables_,
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
import { AttachParams } from "@/internal/customers/cusProducts/AttachParams.js";
|
||||
import {
|
||||
FullCustomerEntitlement,
|
||||
FullEntitlement,
|
||||
PreviewLineItem,
|
||||
Price,
|
||||
cusProductsToCusPrices,
|
||||
type FullCustomerEntitlement,
|
||||
type FullEntitlement,
|
||||
type PreviewLineItem,
|
||||
type Price,
|
||||
} from "@autumn/shared";
|
||||
import Stripe from "stripe";
|
||||
import { attachParamsToProduct } from "../convertAttachParams.js";
|
||||
import { Decimal } from "decimal.js";
|
||||
import type Stripe from "stripe";
|
||||
import { subToPeriodStartEnd } from "@/external/stripe/stripeSubUtils/convertSubUtils.js";
|
||||
import { findStripeItemForPrice } from "@/external/stripe/stripeSubUtils/stripeSubItemUtils.js";
|
||||
import type { AttachParams } from "@/internal/customers/cusProducts/AttachParams.js";
|
||||
import {
|
||||
getExistingUsageFromCusProducts,
|
||||
getRelatedCusPrice,
|
||||
} from "@/internal/customers/cusProducts/cusEnts/cusEntUtils.js";
|
||||
import { getContUseDowngradeItems } from "./getContUseDowngradeItems.js";
|
||||
import { shouldProrate } from "@/internal/products/prices/priceUtils/prorationConfigUtils.js";
|
||||
import { getContUseUpgradeItems } from "./getContUseUpgradeItems.js";
|
||||
import { priceToInvoiceItem } from "@/internal/products/prices/priceUtils/priceToInvoiceItem.js";
|
||||
import { Decimal } from "decimal.js";
|
||||
import { shouldProrate } from "@/internal/products/prices/priceUtils/prorationConfigUtils.js";
|
||||
import { notNullish } from "@/utils/genUtils.js";
|
||||
import { cusProductsToCusPrices } from "@autumn/shared";
|
||||
import { findStripeItemForPrice } from "@/external/stripe/stripeSubUtils/stripeSubItemUtils.js";
|
||||
import { subToPeriodStartEnd } from "@/external/stripe/stripeSubUtils/convertSubUtils.js";
|
||||
import { attachParamsToProduct } from "../convertAttachParams.js";
|
||||
import { getContUseDowngradeItems } from "./getContUseDowngradeItems.js";
|
||||
import { getContUseUpgradeItems } from "./getContUseUpgradeItems.js";
|
||||
|
||||
export const priceToContUseItem = async ({
|
||||
price,
|
||||
@@ -74,7 +74,7 @@ export const priceToContUseItem = async ({
|
||||
: shouldProrate(price.proration_config?.on_increase);
|
||||
|
||||
// 1. Get current usage
|
||||
let curUsage = getExistingUsageFromCusProducts({
|
||||
const curUsage = getExistingUsageFromCusProducts({
|
||||
entitlement: ent,
|
||||
cusProducts,
|
||||
entities,
|
||||
@@ -132,8 +132,8 @@ export const priceToContUseItem = async ({
|
||||
|
||||
// Clean up items
|
||||
// 1. If old item and new item same, remove both
|
||||
let oldAmount = res.oldItem?.amount!;
|
||||
let newAmount = res.newItem?.amount!;
|
||||
const oldAmount = res.oldItem?.amount ?? 0;
|
||||
const newAmount = res.newItem?.amount ?? 0;
|
||||
|
||||
if (new Decimal(oldAmount).add(newAmount).eq(0)) {
|
||||
return {
|
||||
|
||||
@@ -1,37 +1,4 @@
|
||||
import {
|
||||
getStripeSchedules,
|
||||
getStripeSubs,
|
||||
} from "@/external/stripe/stripeSubUtils.js";
|
||||
import {
|
||||
AppEnv,
|
||||
AttachScenario,
|
||||
FullCusProduct,
|
||||
intervalsSame,
|
||||
Organization,
|
||||
Product,
|
||||
} from "@autumn/shared";
|
||||
import Stripe from "stripe";
|
||||
import { fullCusProductToProduct } from "../cusProducts/cusProductUtils.js";
|
||||
import {
|
||||
ACTIVE_STATUSES,
|
||||
CusProductService,
|
||||
} from "../cusProducts/CusProductService.js";
|
||||
|
||||
import { getExistingCusProducts } from "../cusProducts/cusProductUtils/getExistingCusProducts.js";
|
||||
import { isFreeProduct } from "@/internal/products/productUtils.js";
|
||||
|
||||
import { getFilteredScheduleItems } from "./scheduleUtils/getFilteredScheduleItems.js";
|
||||
import { updateScheduledSubWithNewItems } from "./scheduleUtils/updateScheduleWithNewItems.js";
|
||||
import {
|
||||
addCurMainProductToSchedule,
|
||||
getOtherCusProductsOnSub,
|
||||
} from "./scheduleUtils/cancelScheduledFreeProduct.js";
|
||||
import { addProductsUpdatedWebhookTask } from "@/internal/analytics/handlers/handleProductsUpdated.js";
|
||||
import { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import { ExtendedRequest } from "@/utils/models/Request.js";
|
||||
import { getStripeSubItems } from "@/external/stripe/stripeSubUtils/getStripeSubItems.js";
|
||||
import { notNullish } from "@/utils/genUtils.js";
|
||||
import { subToAutumnInterval } from "@/external/stripe/utils.js";
|
||||
import type { FullCusProduct } from "@autumn/shared";
|
||||
|
||||
export const getPricesForCusProduct = ({
|
||||
cusProduct,
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { cusProductToProduct, type FullCusProduct } from "@autumn/shared";
|
||||
import { getPricesForCusProduct } from "../scheduleUtils.js";
|
||||
|
||||
import { FullCusProduct } from "@autumn/shared";
|
||||
import { ScheduleObj } from "./ScheduleObj.js";
|
||||
import { fullCusProductToProduct } from "../../cusProducts/cusProductUtils.js";
|
||||
import type { ScheduleObj } from "./ScheduleObj.js";
|
||||
export const getFilteredScheduleItems = ({
|
||||
scheduleObj,
|
||||
cusProducts,
|
||||
@@ -11,7 +9,7 @@ export const getFilteredScheduleItems = ({
|
||||
cusProducts: (FullCusProduct | undefined)[];
|
||||
}) => {
|
||||
const { schedule, prices } = scheduleObj;
|
||||
let scheduleItems = schedule.phases[0].items;
|
||||
const scheduleItems = schedule.phases[0].items;
|
||||
|
||||
let curPrices: any[] = [];
|
||||
for (const cusProduct of cusProducts) {
|
||||
@@ -20,14 +18,14 @@ export const getFilteredScheduleItems = ({
|
||||
}
|
||||
}
|
||||
|
||||
let products = cusProducts
|
||||
const products = cusProducts
|
||||
.filter((cp): cp is FullCusProduct => !!cp)
|
||||
.map((cp: FullCusProduct) => fullCusProductToProduct(cp));
|
||||
.map((cp: FullCusProduct) => cusProductToProduct({ cusProduct: cp }));
|
||||
|
||||
return scheduleItems.filter((scheduleItem: any) => {
|
||||
let stripePrice = prices.find((price) => price.id === scheduleItem.price);
|
||||
const stripePrice = prices.find((price) => price.id === scheduleItem.price);
|
||||
|
||||
let inCurProduct =
|
||||
const inCurProduct =
|
||||
curPrices.some(
|
||||
(price) =>
|
||||
price.config?.stripe_price_id === scheduleItem.price ||
|
||||
|
||||
@@ -223,10 +223,10 @@ export const getUnlimitedAndUsageAllowed = ({
|
||||
);
|
||||
|
||||
const usageAllowed = cusEnts.some(
|
||||
(ent) =>
|
||||
ent.internal_feature_id === internalFeatureId &&
|
||||
ent.usage_allowed &&
|
||||
(includeUsageLimit ? nullish(ent.entitlement.usage_limit) : true),
|
||||
(ce) =>
|
||||
ce.internal_feature_id === internalFeatureId &&
|
||||
ce.usage_allowed &&
|
||||
(includeUsageLimit ? nullish(ce.entitlement.usage_limit) : true),
|
||||
);
|
||||
|
||||
return { unlimited, usageAllowed };
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import {
|
||||
ACTIVE_STATUSES,
|
||||
ApiCusProductSchema,
|
||||
ApiCusPlanSchema,
|
||||
ApiVersion,
|
||||
type ApiVersionClass,
|
||||
type AppEnv,
|
||||
AttachScenario,
|
||||
CusProductStatus,
|
||||
cusProductToPrices,
|
||||
type Entity,
|
||||
type FixedPriceConfig,
|
||||
type FullCusProduct,
|
||||
@@ -20,7 +18,6 @@ import {
|
||||
} from "@autumn/shared";
|
||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import { createStripeCli } from "@/external/connect/createStripeCli.js";
|
||||
import { getStripeSubs } from "@/external/stripe/stripeSubUtils.js";
|
||||
import { addProductsUpdatedWebhookTask } from "@/internal/analytics/handlers/handleProductsUpdated.js";
|
||||
import { ProductService } from "@/internal/products/ProductService.js";
|
||||
import {
|
||||
@@ -28,7 +25,7 @@ import {
|
||||
getUsageTier,
|
||||
} from "@/internal/products/prices/priceUtils.js";
|
||||
import { isDefaultTrialFullProduct } from "@/internal/products/productUtils/classifyProduct.js";
|
||||
import { isFreeProduct, isOneOff } from "@/internal/products/productUtils.js";
|
||||
import { isFreeProduct } from "@/internal/products/productUtils.js";
|
||||
import { notNullish, nullish } from "@/utils/genUtils.js";
|
||||
import type { ExtendedRequest } from "@/utils/models/Request.js";
|
||||
import { handleAddProduct } from "../attach/attachFunctions/addProductFlow/handleAddProduct.js";
|
||||
@@ -38,80 +35,6 @@ import { CusProductService, RELEVANT_STATUSES } from "./CusProductService.js";
|
||||
import { getRelatedCusEnt } from "./cusPrices/cusPriceUtils.js";
|
||||
import { getExistingCusProducts } from "./cusProductUtils/getExistingCusProducts.js";
|
||||
|
||||
// 1. Cancel cusProductSubscriptions
|
||||
// CAN DELETE
|
||||
export const cancelCusProductSubscriptions = async ({
|
||||
cusProduct,
|
||||
org,
|
||||
env,
|
||||
excludeIds,
|
||||
expireImmediately = true,
|
||||
logger,
|
||||
prorate = true,
|
||||
}: {
|
||||
cusProduct: FullCusProduct;
|
||||
org: Organization;
|
||||
env: AppEnv;
|
||||
excludeIds?: string[];
|
||||
expireImmediately?: boolean;
|
||||
logger: any;
|
||||
prorate?: boolean;
|
||||
}) => {
|
||||
// 1. Cancel all subscriptions
|
||||
const stripeCli = createStripeCli({
|
||||
org: org,
|
||||
env: env,
|
||||
});
|
||||
|
||||
let latestSubEnd: number | undefined;
|
||||
if (cusProduct.subscription_ids && cusProduct.subscription_ids.length > 0) {
|
||||
const stripeSubs = await getStripeSubs({
|
||||
stripeCli,
|
||||
subIds: cusProduct.subscription_ids,
|
||||
});
|
||||
|
||||
latestSubEnd = stripeSubs?.[0]?.items.data[0].current_period_end;
|
||||
}
|
||||
|
||||
const cancelStripeSub = async (subId: string) => {
|
||||
if (excludeIds && excludeIds.includes(subId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (expireImmediately) {
|
||||
await stripeCli.subscriptions.cancel(subId, {
|
||||
prorate: prorate,
|
||||
});
|
||||
} else {
|
||||
await stripeCli.subscriptions.update(subId, {
|
||||
cancel_at: latestSubEnd || undefined,
|
||||
cancel_at_period_end: latestSubEnd ? undefined : true,
|
||||
});
|
||||
}
|
||||
|
||||
logger.info(`Cancelled stripe subscription ${subId}, org: ${org.slug}`);
|
||||
} catch (error: any) {
|
||||
if (error.code !== "resource_missing") {
|
||||
console.log(
|
||||
`Error canceling stripe subscription ${error.code}: ${error.message}`,
|
||||
);
|
||||
} // else subscription probably already cancelled
|
||||
}
|
||||
};
|
||||
|
||||
if (cusProduct.subscription_ids && cusProduct.subscription_ids.length > 0) {
|
||||
const batchCancel = [];
|
||||
for (const subId of cusProduct.subscription_ids) {
|
||||
batchCancel.push(cancelStripeSub(subId));
|
||||
}
|
||||
await Promise.all(batchCancel);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
export const getDefaultProduct = async ({
|
||||
req,
|
||||
productGroup,
|
||||
@@ -160,22 +83,6 @@ export const activateDefaultProduct = async ({
|
||||
p.group === productGroup && !isDefaultTrialFullProduct({ product: p }),
|
||||
);
|
||||
|
||||
// const defaultableProducts = {
|
||||
// free: defaultProducts.filter(
|
||||
// (p) => p.group === productGroup && isFreeProduct(p.prices),
|
||||
// ),
|
||||
// paid: defaultProducts.filter(
|
||||
// (p) =>
|
||||
// p.group === productGroup && isDefaultTrialFullProduct({ product: p }),
|
||||
// ),
|
||||
// };
|
||||
|
||||
// if (defaultableProducts.paid.length > 0) {
|
||||
// defaultProd = defaultableProducts.paid[0];
|
||||
// } else if (defaultableProducts.free.length > 0) {
|
||||
// defaultProd = defaultableProducts.free[0];
|
||||
// }
|
||||
|
||||
if (!defaultProd) return false;
|
||||
|
||||
if (curCusProduct?.internal_product_id === defaultProd.internal_id) {
|
||||
@@ -221,96 +128,6 @@ export const activateDefaultProduct = async ({
|
||||
});
|
||||
|
||||
return true;
|
||||
|
||||
// if (!isDefaultTrial) {
|
||||
// const existingDefaultProduct = fullCus.customer_products.find(
|
||||
// (cp) =>
|
||||
// cp.product.internal_id === defaultProd?.internal_id &&
|
||||
// ACTIVE_STATUSES.includes(cp.status),
|
||||
// );
|
||||
|
||||
// if (existingDefaultProduct) {
|
||||
// logger.info(
|
||||
// `Default product ${defaultProd?.name} already exists for customer`,
|
||||
// );
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// await handleAddProduct({
|
||||
// req,
|
||||
// attachParams: newCusToAttachParams({
|
||||
// req,
|
||||
// newCus: fullCus,
|
||||
// products: [defaultProd],
|
||||
// stripeCli,
|
||||
// }),
|
||||
// });
|
||||
|
||||
// return true;
|
||||
// } else if (isDefaultTrial && defaultableProducts.free.length > 0) {
|
||||
// defaultProd = defaultableProducts.free[0];
|
||||
|
||||
// // Check if the free default product already exists to prevent duplicates
|
||||
// const existingFreeProduct = fullCus.customer_products.find(
|
||||
// (cp) =>
|
||||
// cp.product.internal_id === defaultProd!.internal_id &&
|
||||
// (cp.status === CusProductStatus.Active ||
|
||||
// cp.status === CusProductStatus.PastDue),
|
||||
// );
|
||||
|
||||
// if (existingFreeProduct) {
|
||||
// logger.info(
|
||||
// `Free default product ${defaultProd?.name} already exists for customer`,
|
||||
// );
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// await handleAddProduct({
|
||||
// req,
|
||||
// attachParams: newCusToAttachParams({
|
||||
// req,
|
||||
// newCus: fullCus,
|
||||
// products: [defaultProd],
|
||||
// stripeCli,
|
||||
// }),
|
||||
// config: getDefaultAttachConfig(),
|
||||
// });
|
||||
|
||||
// return true;
|
||||
// }
|
||||
};
|
||||
|
||||
export const expireAndActivate = async ({
|
||||
req,
|
||||
cusProduct,
|
||||
fullCus,
|
||||
}: {
|
||||
req: ExtendedRequest;
|
||||
cusProduct: FullCusProduct;
|
||||
fullCus: FullCustomer;
|
||||
}) => {
|
||||
const { db, org, env, logger } = req;
|
||||
// 1. Expire current product
|
||||
await CusProductService.update({
|
||||
db,
|
||||
cusProductId: cusProduct.id,
|
||||
updates: { status: CusProductStatus.Expired, ended_at: Date.now() },
|
||||
});
|
||||
|
||||
// Check if it's one time product
|
||||
const prices = cusProductToPrices({ cusProduct });
|
||||
const product = cusProduct.product;
|
||||
const isOneOffOrAddOn = product.is_add_on || isOneOff(prices);
|
||||
|
||||
if (isOneOffOrAddOn || notNullish(cusProduct.internal_entity_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
await activateDefaultProduct({
|
||||
req,
|
||||
productGroup: cusProduct.product.group,
|
||||
fullCus,
|
||||
});
|
||||
};
|
||||
|
||||
export const activateFutureProduct = async ({
|
||||
@@ -356,45 +173,8 @@ export const activateFutureProduct = async ({
|
||||
});
|
||||
|
||||
return futureProduct;
|
||||
|
||||
// if (subIsPrematurelyCanceled(subscription)) {
|
||||
// console.log(
|
||||
// " 🔔 Subscription prematurely canceled, deleting scheduled products"
|
||||
// );
|
||||
|
||||
// await deleteScheduledIds({
|
||||
// stripeCli,
|
||||
// scheduledIds: futureProduct.scheduled_ids || [],
|
||||
// });
|
||||
// await CusProductService.delete({
|
||||
// db,
|
||||
// cusProductId: futureProduct.id,
|
||||
// });
|
||||
// return false;
|
||||
// } else {
|
||||
// await CusProductService.update({
|
||||
// db,
|
||||
// cusProductId: futureProduct.id,
|
||||
// updates: { status: CusProductStatus.Active },
|
||||
// });
|
||||
|
||||
// await addProductsUpdatedWebhookTask({
|
||||
// req,
|
||||
// internalCustomerId: cusProduct.internal_customer_id,
|
||||
// org,
|
||||
// env,
|
||||
// customerId: null,
|
||||
// scenario: AttachScenario.New,
|
||||
// cusProduct: futureProduct,
|
||||
// logger,
|
||||
// });
|
||||
|
||||
// return true;
|
||||
// }
|
||||
};
|
||||
|
||||
// GET CUS ENTS FROM CUS PRODUCTS
|
||||
|
||||
export const processFullCusProduct = ({
|
||||
cusProduct,
|
||||
subs,
|
||||
@@ -512,7 +292,7 @@ export const processFullCusProduct = ({
|
||||
};
|
||||
}
|
||||
|
||||
return ApiCusProductSchema.parse({
|
||||
return ApiCusPlanSchema.parse({
|
||||
id: cusProduct.product.id,
|
||||
name: cusProduct.product.name,
|
||||
group: cusProduct.product.group || null,
|
||||
@@ -552,15 +332,6 @@ export const processFullCusProduct = ({
|
||||
}
|
||||
};
|
||||
|
||||
// GET CUSTOMER PRODUCT & ORG IN PARALLEL
|
||||
export const fullCusProductToProduct = (cusProduct: FullCusProduct) => {
|
||||
return {
|
||||
...cusProduct.product,
|
||||
prices: cusProduct.customer_prices.map((cp) => cp.price),
|
||||
entitlements: cusProduct.customer_entitlements.map((ce) => ce.entitlement),
|
||||
};
|
||||
};
|
||||
|
||||
export const searchCusProducts = ({
|
||||
productId,
|
||||
internalProductId,
|
||||
|
||||
@@ -9,7 +9,7 @@ import { CusSearchService } from "@/internal/customers/CusSearchService.js";
|
||||
import { OrgService } from "@/internal/orgs/OrgService.js";
|
||||
import RecaseError, { handleRequestError } from "@/utils/errorUtils.js";
|
||||
import { handleBatchCustomers } from "../api/batch/handlers/handleBatchCustomers.js";
|
||||
import { entityRouter } from "../api/entities/entityRouter.js";
|
||||
import { expressEntityRouter } from "../api/entities/entityRouter.js";
|
||||
import { toSuccessUrl } from "../orgs/orgUtils/convertOrgUtils.js";
|
||||
import { CusService } from "./CusService.js";
|
||||
import { handleAddCouponToCus } from "./handlers/handleAddCouponToCus.js";
|
||||
@@ -63,11 +63,6 @@ expressCusRouter.post(
|
||||
|
||||
expressCusRouter.post("/:customer_id/balances", handleUpdateBalances);
|
||||
|
||||
// cusRouter.post(
|
||||
// "/customer_products/:customer_product_id",
|
||||
// handleCusProductExpired
|
||||
// );
|
||||
|
||||
expressCusRouter.get(
|
||||
"/:customer_id/billing_portal",
|
||||
async (req: any, res: any) => {
|
||||
@@ -143,7 +138,7 @@ expressCusRouter.post(
|
||||
|
||||
expressCusRouter.post("/:customer_id/coupons/:coupon_id", handleAddCouponToCus);
|
||||
|
||||
expressCusRouter.use("/:customer_id/entities", entityRouter);
|
||||
expressCusRouter.use("/:customer_id/entities", expressEntityRouter);
|
||||
|
||||
expressCusRouter.post("/:customer_id/transfer", handleTransferProduct);
|
||||
|
||||
|
||||
@@ -1,33 +1,38 @@
|
||||
import {
|
||||
type ApiCusFeature,
|
||||
type ApiCusRollover,
|
||||
ApiFeatureType,
|
||||
type ApiFeature,
|
||||
cusEntToKey,
|
||||
type EntInterval,
|
||||
entIntvToResetIntv,
|
||||
type FullCusEntWithFullCusProduct,
|
||||
getRolloverFields,
|
||||
notNullish,
|
||||
ResetInterval,
|
||||
toIntervalCountResponse,
|
||||
} from "@autumn/shared";
|
||||
import { getCusFeatureType } from "@/internal/features/featureUtils.js";
|
||||
|
||||
export const cusEntsToInterval = ({
|
||||
cusEnts,
|
||||
}: {
|
||||
cusEnts: FullCusEntWithFullCusProduct[];
|
||||
}): {
|
||||
interval: EntInterval | "multiple" | null;
|
||||
interval_count: number | null;
|
||||
interval: ResetInterval | null;
|
||||
interval_count: number | undefined;
|
||||
} => {
|
||||
const cusEntKeys = cusEnts.map((cusEnt) => cusEntToKey({ cusEnt }));
|
||||
const uniqueCusEntKeys = [...new Set(cusEntKeys)];
|
||||
if (uniqueCusEntKeys.length === 1) {
|
||||
return {
|
||||
interval: cusEnts[0].entitlement.interval || null,
|
||||
interval_count: cusEnts[0].entitlement.interval_count,
|
||||
interval: entIntvToResetIntv({
|
||||
entInterval: cusEnts[0].entitlement.interval,
|
||||
}),
|
||||
interval_count: toIntervalCountResponse({
|
||||
intervalCount: cusEnts[0].entitlement.interval_count,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
return { interval: "multiple", interval_count: null };
|
||||
return { interval: null, interval_count: undefined };
|
||||
};
|
||||
|
||||
export const cusEntsToNextResetAt = ({
|
||||
@@ -76,39 +81,66 @@ export const cusEntsToRollovers = ({
|
||||
|
||||
export const getBooleanApiCusFeature = ({
|
||||
cusEnts,
|
||||
apiFeature,
|
||||
}: {
|
||||
cusEnts: FullCusEntWithFullCusProduct[];
|
||||
apiFeature?: ApiFeature;
|
||||
}): ApiCusFeature => {
|
||||
const feature = cusEnts[0].entitlement.feature;
|
||||
return {
|
||||
id: feature.id,
|
||||
type: ApiFeatureType.Static,
|
||||
name: feature.name,
|
||||
feature: apiFeature,
|
||||
feature_id: feature.id,
|
||||
|
||||
unlimited: false,
|
||||
|
||||
starting_balance: 0,
|
||||
balance: 0,
|
||||
usage: 0,
|
||||
included_usage: 0,
|
||||
next_reset_at: null,
|
||||
unlimited: false,
|
||||
overage_allowed: false,
|
||||
};
|
||||
|
||||
resets_at: null,
|
||||
reset_interval: ResetInterval.OneOff,
|
||||
reset_interval_count: undefined,
|
||||
|
||||
breakdown: undefined,
|
||||
rollovers: undefined,
|
||||
|
||||
// Old
|
||||
// id: feature.id,
|
||||
// type: ApiFeatureType.Static,
|
||||
// name: feature.name,
|
||||
// balance: 0,
|
||||
// usage: 0,
|
||||
// included_usage: 0,
|
||||
// next_reset_at: null,
|
||||
// unlimited: false,
|
||||
// overage_allowed: false,
|
||||
} as ApiCusFeature;
|
||||
};
|
||||
|
||||
export const getUnlimitedApiCusFeature = ({
|
||||
apiFeature,
|
||||
cusEnts,
|
||||
}: {
|
||||
apiFeature?: ApiFeature;
|
||||
cusEnts: FullCusEntWithFullCusProduct[];
|
||||
}): ApiCusFeature => {
|
||||
const feature = cusEnts[0].entitlement.feature;
|
||||
|
||||
return {
|
||||
id: feature.id,
|
||||
type: getCusFeatureType({ feature }),
|
||||
name: feature.name,
|
||||
feature: apiFeature,
|
||||
feature_id: feature.id,
|
||||
|
||||
unlimited: true,
|
||||
|
||||
starting_balance: 0,
|
||||
balance: 0,
|
||||
usage: 0,
|
||||
included_usage: 0,
|
||||
next_reset_at: null,
|
||||
unlimited: true,
|
||||
overage_allowed: false,
|
||||
|
||||
resets_at: null,
|
||||
reset_interval: ResetInterval.OneOff,
|
||||
reset_interval_count: undefined,
|
||||
|
||||
breakdown: undefined,
|
||||
rollovers: undefined,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -6,10 +6,11 @@ import {
|
||||
type ApiCusFeatureBreakdown,
|
||||
ApiCusFeatureBreakdownSchema,
|
||||
ApiCusFeatureSchema,
|
||||
CusExpand,
|
||||
cusEntToBalance,
|
||||
cusEntToIncludedUsage,
|
||||
cusEntToKey,
|
||||
cusEntToUsageLimit,
|
||||
cusEntToMaxPurchase,
|
||||
type Feature,
|
||||
FeatureType,
|
||||
getCusEntBalance,
|
||||
@@ -20,7 +21,6 @@ import {
|
||||
import { Decimal } from "decimal.js";
|
||||
import type { RequestContext } from "@/honoUtils/HonoEnv.js";
|
||||
import { getUnlimitedAndUsageAllowed } from "@/internal/customers/cusProducts/cusEnts/cusEntUtils.js";
|
||||
import { getCusFeatureType } from "@/internal/features/featureUtils.js";
|
||||
import {
|
||||
cusEntsToInterval,
|
||||
cusEntsToNextResetAt,
|
||||
@@ -60,11 +60,12 @@ const cusEntsToBreakdown = ({
|
||||
cusEnts,
|
||||
feature,
|
||||
});
|
||||
|
||||
breakdown.push(
|
||||
ApiCusFeatureBreakdownSchema.parse({
|
||||
...breakdownItem,
|
||||
interval,
|
||||
interval_count,
|
||||
reset_interval: interval,
|
||||
reset_interval_count: interval_count,
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -85,21 +86,27 @@ export const getApiCusFeature = ({
|
||||
}) => {
|
||||
const entityId = fullCus.entity?.id;
|
||||
|
||||
const apiFeature = ctx.expand.includes(CusExpand.Feature)
|
||||
? toApiFeature({ feature })
|
||||
: undefined;
|
||||
|
||||
// 1. If feature is boolean
|
||||
if (feature.type === FeatureType.Boolean) {
|
||||
return getBooleanApiCusFeature({
|
||||
cusEnts,
|
||||
apiFeature,
|
||||
});
|
||||
}
|
||||
|
||||
const { unlimited, usageAllowed } = getUnlimitedAndUsageAllowed({
|
||||
cusEnts: cusEnts,
|
||||
internalFeatureId: feature.internal_id,
|
||||
includeUsageLimit: false,
|
||||
});
|
||||
|
||||
// 2. If feature is unlimited
|
||||
if (unlimited) {
|
||||
return getUnlimitedApiCusFeature({ cusEnts: cusEnts });
|
||||
return getUnlimitedApiCusFeature({ apiFeature, cusEnts });
|
||||
}
|
||||
|
||||
const totalBalanceWithRollovers = sumValues(
|
||||
@@ -130,8 +137,8 @@ export const getApiCusFeature = ({
|
||||
|
||||
const nextResetAt = cusEntsToNextResetAt({ cusEnts });
|
||||
|
||||
const totalUsageLimit = sumValues(
|
||||
cusEnts.map((cusEnt) => cusEntToUsageLimit({ cusEnt })),
|
||||
const totalMaxPurchase = sumValues(
|
||||
cusEnts.map((cusEnt) => cusEntToMaxPurchase({ cusEnt })),
|
||||
);
|
||||
|
||||
const totalIncludedUsage = sumValues(
|
||||
@@ -154,30 +161,53 @@ export const getApiCusFeature = ({
|
||||
|
||||
const rollovers = cusEntsToRollovers({ cusEnts, entityId });
|
||||
|
||||
const apiFeature = toApiFeature({ feature });
|
||||
// const apiFeature = toApiFeature({ feature });
|
||||
|
||||
const { data: apiCusFeature, error } = ApiCusFeatureSchema.safeParse({
|
||||
id: feature.id,
|
||||
name: feature.name,
|
||||
type: getCusFeatureType({ feature }),
|
||||
balance: totalBalanceWithRollovers,
|
||||
usage: totalUsage,
|
||||
included_usage: totalIncludedUsage,
|
||||
usage_limit:
|
||||
totalUsageLimit === totalIncludedUsage ? undefined : totalUsageLimit,
|
||||
next_reset_at: nextResetAt,
|
||||
feature: ctx.expand.includes(CusExpand.Feature) ? apiFeature : undefined,
|
||||
|
||||
feature_id: feature.id,
|
||||
|
||||
unlimited: false,
|
||||
overage_allowed: usageAllowed,
|
||||
interval,
|
||||
interval_count,
|
||||
rollovers,
|
||||
credit_schema:
|
||||
apiFeature.credit_schema?.map((credit) => ({
|
||||
feature_id: credit.metered_feature_id,
|
||||
credit_amount: credit.credit_cost,
|
||||
})) || undefined,
|
||||
|
||||
starting_balance: totalIncludedUsageWithRollovers,
|
||||
balance: totalBalanceWithRollovers,
|
||||
|
||||
usage: totalUsage,
|
||||
|
||||
resets_at: nextResetAt,
|
||||
reset_interval: interval,
|
||||
reset_interval_count: interval_count !== 1 ? interval_count : undefined,
|
||||
|
||||
// Max purchase...
|
||||
max_purchase: totalMaxPurchase > 0 ? totalMaxPurchase : undefined,
|
||||
pay_per_use: usageAllowed || undefined,
|
||||
|
||||
breakdown: cusEntsToBreakdown({ ctx, fullCus, cusEnts }),
|
||||
rollovers,
|
||||
|
||||
// Old stuff...
|
||||
// id: feature.id,
|
||||
// name: feature.name,
|
||||
// type: getCusFeatureType({ feature }),
|
||||
// balance: totalBalanceWithRollovers,
|
||||
// usage: totalUsage,
|
||||
// included_usage: totalIncludedUsage,
|
||||
// usage_limit:
|
||||
// totalUsageLimit === totalIncludedUsage ? undefined : totalUsageLimit,
|
||||
// next_reset_at: nextResetAt,
|
||||
// unlimited: false,
|
||||
// overage_allowed: usageAllowed,
|
||||
// interval,
|
||||
// interval_count,
|
||||
// rollovers,
|
||||
// credit_schema:
|
||||
// apiFeature.credit_schema?.map((credit) => ({
|
||||
// feature_id: credit.metered_feature_id,
|
||||
// credit_amount: credit.credit_cost,
|
||||
// })) || undefined,
|
||||
|
||||
// breakdown: cusEntsToBreakdown({ ctx, fullCus, cusEnts }),
|
||||
});
|
||||
|
||||
if (error) throw error;
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
import {
|
||||
ApiCusPlanSchema,
|
||||
CusExpand,
|
||||
type CusProductLegacyData,
|
||||
CusProductStatus,
|
||||
cusProductToPlanStatus,
|
||||
cusProductToProduct,
|
||||
type Entity,
|
||||
type FullCusProduct,
|
||||
type FullCustomer,
|
||||
isTrialing,
|
||||
type Subscription,
|
||||
} from "@autumn/shared";
|
||||
import type { RequestContext } from "@/honoUtils/HonoEnv.js";
|
||||
import { getPlanResponse } from "@/internal/products/productUtils/productResponseUtils/getPlanResponse.js";
|
||||
|
||||
export const getApiCusPlan = async ({
|
||||
ctx,
|
||||
fullCus,
|
||||
cusProduct,
|
||||
entities,
|
||||
}: {
|
||||
ctx: RequestContext;
|
||||
fullCus: FullCustomer;
|
||||
cusProduct: FullCusProduct;
|
||||
entities?: Entity[];
|
||||
}) => {
|
||||
const trialing =
|
||||
cusProduct.trial_ends_at && cusProduct.trial_ends_at > Date.now();
|
||||
|
||||
const fullProduct = cusProductToProduct({ cusProduct });
|
||||
|
||||
const subId = cusProduct.subscription_ids?.[0];
|
||||
const autumnSub = fullCus.subscriptions?.find(
|
||||
(s) => s.id === subId || (s as Subscription).stripe_id === subId,
|
||||
);
|
||||
|
||||
let stripeSubData: {
|
||||
current_period_start: number | null;
|
||||
current_period_end: number | null;
|
||||
} = {
|
||||
current_period_start: null,
|
||||
current_period_end: null,
|
||||
};
|
||||
|
||||
if (autumnSub) {
|
||||
stripeSubData = {
|
||||
current_period_end: autumnSub?.current_period_end
|
||||
? autumnSub.current_period_end * 1000
|
||||
: null,
|
||||
current_period_start: autumnSub?.current_period_start
|
||||
? autumnSub.current_period_start * 1000
|
||||
: null,
|
||||
};
|
||||
}
|
||||
|
||||
if (!subId && trialing) {
|
||||
stripeSubData = {
|
||||
current_period_start: cusProduct.starts_at,
|
||||
current_period_end: cusProduct.trial_ends_at || null,
|
||||
};
|
||||
}
|
||||
|
||||
const status = cusProductToPlanStatus({ status: cusProduct.status });
|
||||
|
||||
// Check if we should expand the plan object
|
||||
const shouldExpandPlan = ctx.expand.includes(CusExpand.Plan);
|
||||
const apiPlan = shouldExpandPlan
|
||||
? await getPlanResponse({
|
||||
product: fullProduct,
|
||||
features: ctx.features,
|
||||
})
|
||||
: undefined;
|
||||
|
||||
const apiCusPlan = ApiCusPlanSchema.parse({
|
||||
plan: apiPlan,
|
||||
|
||||
plan_id: fullProduct.id,
|
||||
add_on: fullProduct.is_add_on,
|
||||
|
||||
status,
|
||||
past_due: cusProduct.status === CusProductStatus.PastDue,
|
||||
canceled_at: cusProduct.canceled_at || null,
|
||||
expires_at: cusProduct.ended_at || null,
|
||||
|
||||
trial_ends_at: isTrialing({ cusProduct }) ? cusProduct.trial_ends_at : null,
|
||||
started_at: cusProduct.starts_at,
|
||||
quantity: cusProduct.quantity,
|
||||
current_period_start: stripeSubData?.current_period_start || null,
|
||||
current_period_end: stripeSubData?.current_period_end || null,
|
||||
});
|
||||
|
||||
return {
|
||||
data: apiCusPlan,
|
||||
legacyData: {
|
||||
subscription_id: subId || undefined,
|
||||
options: cusProduct.options,
|
||||
features: ctx.features,
|
||||
} satisfies CusProductLegacyData,
|
||||
};
|
||||
};
|
||||
|
||||
// const apiCusProduct = ApiCusProductSchema.parse({
|
||||
// id: fullProduct.id,
|
||||
// name: fullProduct.name,
|
||||
// group: fullProduct.group || null,
|
||||
// status: trialing ? CusProductStatus.Trialing : cusProduct.status,
|
||||
// canceled_at: cusProduct.canceled_at || null,
|
||||
|
||||
// is_default: fullProduct.is_default || false,
|
||||
// is_add_on: fullProduct.is_add_on || false,
|
||||
// version: fullProduct.version,
|
||||
// quantity: cusProduct.quantity,
|
||||
|
||||
// started_at: cusProduct.starts_at,
|
||||
// entity_id: entity?.id || cusProduct.entity_id || undefined,
|
||||
|
||||
// ...stripeSubData,
|
||||
// items: v2Product.items,
|
||||
// });
|
||||
@@ -0,0 +1,79 @@
|
||||
import {
|
||||
ACTIVE_STATUSES,
|
||||
type ApiCusPlan,
|
||||
type CusProductLegacyData,
|
||||
type CusProductStatus,
|
||||
type FullCustomer,
|
||||
} from "@autumn/shared";
|
||||
import type { RequestContext } from "@/honoUtils/HonoEnv.js";
|
||||
import { getApiCusPlan } from "./getApiCusPlan.js";
|
||||
|
||||
const mergeCusPlanResponses = ({
|
||||
cusPlanResponses,
|
||||
}: {
|
||||
cusPlanResponses: ApiCusPlan[];
|
||||
}) => {
|
||||
const getPlanKey = (cp: ApiCusPlan) => {
|
||||
const status = ACTIVE_STATUSES.includes(cp.status as CusProductStatus)
|
||||
? "active"
|
||||
: cp.status;
|
||||
return `${cp.plan_id}:${status}`;
|
||||
};
|
||||
|
||||
const record: Record<string, any> = {};
|
||||
|
||||
for (const curr of cusPlanResponses) {
|
||||
const key = getPlanKey(curr);
|
||||
const latest = record[key];
|
||||
|
||||
const currStartedAt = curr.started_at;
|
||||
|
||||
record[key] = {
|
||||
...(latest || curr),
|
||||
canceled_at: curr.canceled_at
|
||||
? curr.canceled_at
|
||||
: latest?.canceled_at || null,
|
||||
started_at: latest?.started_at
|
||||
? Math.min(latest?.started_at, currStartedAt)
|
||||
: currStartedAt,
|
||||
quantity: (latest?.quantity || 0) + (curr?.quantity || 0),
|
||||
};
|
||||
}
|
||||
|
||||
return Object.values(record);
|
||||
};
|
||||
|
||||
export const getApiCusPlans = async ({
|
||||
ctx,
|
||||
fullCus,
|
||||
}: {
|
||||
ctx: RequestContext;
|
||||
fullCus: FullCustomer;
|
||||
}) => {
|
||||
// Process full cus products
|
||||
const apiCusPlans: ApiCusPlan[] = [];
|
||||
|
||||
const cusProducts = fullCus.customer_products;
|
||||
|
||||
const legacyData: Record<string, CusProductLegacyData> = {};
|
||||
for (const cusProduct of cusProducts) {
|
||||
const processed = await getApiCusPlan({
|
||||
cusProduct,
|
||||
ctx,
|
||||
fullCus,
|
||||
entities: fullCus.entities,
|
||||
});
|
||||
|
||||
apiCusPlans.push(processed.data);
|
||||
legacyData[processed.data.plan_id] = processed.legacyData;
|
||||
}
|
||||
|
||||
const merged = mergeCusPlanResponses({
|
||||
cusPlanResponses: apiCusPlans,
|
||||
});
|
||||
|
||||
return {
|
||||
apiCusPlans: merged,
|
||||
legacyData,
|
||||
};
|
||||
};
|
||||
@@ -1,236 +0,0 @@
|
||||
import {
|
||||
ApiCusProductSchema,
|
||||
type CusProductLegacyData,
|
||||
CusProductStatus,
|
||||
cusProductToProduct,
|
||||
type Entity,
|
||||
type FullCusProduct,
|
||||
type FullCustomer,
|
||||
type Subscription,
|
||||
} from "@autumn/shared";
|
||||
import type { RequestContext } from "@/honoUtils/HonoEnv.js";
|
||||
import { getProductResponse } from "@/internal/products/productUtils/productResponseUtils/getProductResponse.js";
|
||||
|
||||
// const prices = cusProduct.customer_prices.map((cp) => {
|
||||
// const price = cp.price;
|
||||
|
||||
// if (price.config?.type === PriceType.Fixed) {
|
||||
// const config = price.config as FixedPriceConfig;
|
||||
// return {
|
||||
// amount: config.amount,
|
||||
// interval: config.interval,
|
||||
// };
|
||||
// } else {
|
||||
// const config = price.config as UsagePriceConfig;
|
||||
// const priceOptions = getPriceOptions(price, cusProduct.options);
|
||||
// const usageTier = getUsageTier(price, priceOptions?.quantity!);
|
||||
// const cusEnt = getRelatedCusEnt({
|
||||
// cusPrice: cp,
|
||||
// cusEnts: cusProduct.customer_entitlements,
|
||||
// });
|
||||
|
||||
// const ent = cusEnt?.entitlement;
|
||||
|
||||
// const singleTier =
|
||||
// ent?.allowance === 0 && config.usage_tiers.length === 1;
|
||||
|
||||
// if (singleTier) {
|
||||
// return {
|
||||
// amount: usageTier.amount,
|
||||
// interval: config.interval,
|
||||
// quantity: priceOptions?.quantity,
|
||||
// };
|
||||
// } else {
|
||||
// // Add allowance to tiers
|
||||
// const allowance = ent?.allowance;
|
||||
// let tiers;
|
||||
|
||||
// if (notNullish(allowance) && allowance! > 0) {
|
||||
// tiers = [
|
||||
// {
|
||||
// to: allowance,
|
||||
// amount: 0,
|
||||
// },
|
||||
// ...config.usage_tiers.map((tier) => {
|
||||
// const isLastTier = tier.to === -1 || tier.to === TierInfinite;
|
||||
// return {
|
||||
// to: isLastTier ? tier.to : Number(tier.to) + allowance!,
|
||||
// amount: tier.amount,
|
||||
// };
|
||||
// }),
|
||||
// ];
|
||||
// } else {
|
||||
// tiers = config.usage_tiers.map((tier) => {
|
||||
// const isLastTier = tier.to === -1 || tier.to === TierInfinite;
|
||||
// return {
|
||||
// to: isLastTier ? tier.to : Number(tier.to) + allowance!,
|
||||
// amount: tier.amount,
|
||||
// };
|
||||
// });
|
||||
// }
|
||||
|
||||
// return {
|
||||
// tiers: tiers,
|
||||
// name: "",
|
||||
// quantity: priceOptions?.quantity,
|
||||
// };
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
|
||||
export const getApiCusProduct = async ({
|
||||
ctx,
|
||||
fullCus,
|
||||
cusProduct,
|
||||
entities,
|
||||
}: {
|
||||
ctx: RequestContext;
|
||||
fullCus: FullCustomer;
|
||||
cusProduct: FullCusProduct;
|
||||
entities?: Entity[];
|
||||
}) => {
|
||||
const { org } = ctx;
|
||||
const trialing =
|
||||
cusProduct.trial_ends_at && cusProduct.trial_ends_at > Date.now();
|
||||
|
||||
const fullProduct = cusProductToProduct({ cusProduct });
|
||||
const v2Product = await getProductResponse({
|
||||
product: fullProduct,
|
||||
features: ctx.features,
|
||||
fullCus,
|
||||
currency: org.default_currency || "usd",
|
||||
options: cusProduct.options,
|
||||
withDisplay: false,
|
||||
});
|
||||
|
||||
const entity = entities?.find(
|
||||
(e) => e.internal_id === cusProduct.internal_entity_id,
|
||||
);
|
||||
|
||||
const subId = cusProduct.subscription_ids?.[0];
|
||||
const autumnSub = fullCus.subscriptions?.find(
|
||||
(s) => s.id === subId || (s as Subscription).stripe_id === subId,
|
||||
);
|
||||
|
||||
let stripeSubData = {};
|
||||
if (autumnSub) {
|
||||
stripeSubData = {
|
||||
current_period_end: autumnSub?.current_period_end
|
||||
? autumnSub.current_period_end * 1000
|
||||
: null,
|
||||
current_period_start: autumnSub?.current_period_start
|
||||
? autumnSub.current_period_start * 1000
|
||||
: null,
|
||||
};
|
||||
}
|
||||
|
||||
if (!subId && trialing) {
|
||||
stripeSubData = {
|
||||
current_period_start: cusProduct.starts_at,
|
||||
current_period_end: cusProduct.trial_ends_at,
|
||||
};
|
||||
}
|
||||
|
||||
const apiCusProduct = ApiCusProductSchema.parse({
|
||||
id: fullProduct.id,
|
||||
name: fullProduct.name,
|
||||
group: fullProduct.group || null,
|
||||
status: trialing ? CusProductStatus.Trialing : cusProduct.status,
|
||||
canceled_at: cusProduct.canceled_at || null,
|
||||
|
||||
is_default: fullProduct.is_default || false,
|
||||
is_add_on: fullProduct.is_add_on || false,
|
||||
version: fullProduct.version,
|
||||
quantity: cusProduct.quantity,
|
||||
|
||||
started_at: cusProduct.starts_at,
|
||||
entity_id: entity?.id || cusProduct.entity_id || undefined,
|
||||
|
||||
...stripeSubData,
|
||||
items: v2Product.items,
|
||||
});
|
||||
|
||||
return {
|
||||
data: apiCusProduct,
|
||||
legacyData: {
|
||||
subscription_id: subId || undefined,
|
||||
} satisfies CusProductLegacyData,
|
||||
};
|
||||
|
||||
// if (subIds && subIds.length > 0 && apiVersion.gte(ApiVersion.V0_2)) {
|
||||
// const baseSub = subs?.find(
|
||||
// (s) => s.id === subIds[0] || (s as Subscription).stripe_id === subIds[0],
|
||||
// );
|
||||
// stripeSubData = {
|
||||
// current_period_end: baseSub?.current_period_end
|
||||
// ? baseSub.current_period_end * 1000
|
||||
// : null,
|
||||
// current_period_start: baseSub?.current_period_start
|
||||
// ? baseSub.current_period_start * 1000
|
||||
// : null,
|
||||
// };
|
||||
// }
|
||||
|
||||
// if (!subIds && trialing) {
|
||||
// stripeSubData = {
|
||||
// current_period_start: cusProduct.starts_at,
|
||||
// current_period_end: cusProduct.trial_ends_at,
|
||||
// };
|
||||
// }
|
||||
|
||||
// if (apiVersion.gte(ApiVersion.V1_1)) {
|
||||
// if ((!subIds || subIds.length === 0) && trialing) {
|
||||
// stripeSubData = {
|
||||
// current_period_start: cusProduct.starts_at,
|
||||
// current_period_end: cusProduct.trial_ends_at,
|
||||
// };
|
||||
// }
|
||||
|
||||
// const fullProduct = fullCusProductToProduct(cusProduct);
|
||||
// const v2Product = await getProductResponse({
|
||||
// product: fullProduct,
|
||||
// features,
|
||||
// withDisplay: false,
|
||||
// options: cusProduct.options,
|
||||
// });
|
||||
|
||||
// return ApiCusProductSchema.parse({
|
||||
// id: fullProduct.id,
|
||||
// name: fullProduct.name,
|
||||
// group: fullProduct.group || null,
|
||||
// status: trialing ? CusProductStatus.Trialing : cusProduct.status,
|
||||
// canceled_at: cusProduct.canceled_at || null,
|
||||
|
||||
// is_default: fullProduct.is_default || false,
|
||||
// is_add_on: fullProduct.is_add_on || false,
|
||||
// version: fullProduct.version,
|
||||
// quantity: cusProduct.quantity,
|
||||
|
||||
// started_at: cusProduct.starts_at,
|
||||
// entity_id: entity?.id || cusProduct.entity_id || undefined,
|
||||
|
||||
// ...stripeSubData,
|
||||
// items: v2Product.items,
|
||||
// });
|
||||
// } else {
|
||||
// const cusProductResponse = {
|
||||
// id: cusProduct.product.id,
|
||||
// name: cusProduct.product.name,
|
||||
// group: cusProduct.product.group,
|
||||
// status: trialing ? CusProductStatus.Trialing : cusProduct.status,
|
||||
// created_at: cusProduct.created_at,
|
||||
// canceled_at: cusProduct.canceled_at,
|
||||
// processor: {
|
||||
// type: cusProduct.processor?.type,
|
||||
// subscription_id: cusProduct.processor?.subscription_id || null,
|
||||
// },
|
||||
// subscription_ids: cusProduct.subscription_ids || [],
|
||||
// prices: prices,
|
||||
// starts_at: cusProduct.starts_at,
|
||||
|
||||
// ...stripeSubData,
|
||||
// };
|
||||
|
||||
// return cusProductResponse;
|
||||
// }
|
||||
};
|
||||
@@ -1,87 +0,0 @@
|
||||
import {
|
||||
ACTIVE_STATUSES,
|
||||
type ApiCusProduct,
|
||||
type CusProductLegacyData,
|
||||
type CusProductStatus,
|
||||
type FullCustomer,
|
||||
} from "@autumn/shared";
|
||||
import type { RequestContext } from "@/honoUtils/HonoEnv.js";
|
||||
import { getApiCusProduct } from "./getApiCusProduct.js";
|
||||
|
||||
const mergeCusProductResponses = ({
|
||||
cusProductResponses,
|
||||
}: {
|
||||
cusProductResponses: ApiCusProduct[];
|
||||
}) => {
|
||||
const getProductKey = (product: ApiCusProduct) => {
|
||||
const status = ACTIVE_STATUSES.includes(product.status as CusProductStatus)
|
||||
? "active"
|
||||
: product.status;
|
||||
return `${product.id}:${status}`;
|
||||
};
|
||||
|
||||
const record: Record<string, any> = {};
|
||||
|
||||
for (const curr of cusProductResponses) {
|
||||
const key = getProductKey(curr);
|
||||
const latest = record[key];
|
||||
|
||||
const currStartedAt = curr.started_at;
|
||||
|
||||
record[key] = {
|
||||
...(latest || curr),
|
||||
version: Math.max(latest?.version || 1, curr?.version || 1),
|
||||
canceled_at: curr.canceled_at
|
||||
? curr.canceled_at
|
||||
: latest?.canceled_at || null,
|
||||
started_at: latest?.started_at
|
||||
? Math.min(latest?.started_at, currStartedAt)
|
||||
: currStartedAt,
|
||||
quantity: (latest?.quantity || 0) + (curr?.quantity || 0),
|
||||
};
|
||||
}
|
||||
|
||||
return Object.values(record);
|
||||
};
|
||||
|
||||
export const getApiCusProducts = async ({
|
||||
ctx,
|
||||
fullCus,
|
||||
}: {
|
||||
ctx: RequestContext;
|
||||
fullCus: FullCustomer;
|
||||
}) => {
|
||||
// Process full cus products
|
||||
const apiCusProducts: ApiCusProduct[] = [];
|
||||
|
||||
// const inStatuses = ctx.org.config.include_past_due
|
||||
// ? [CusProductStatus.Active, CusProductStatus.PastDue]
|
||||
// : [CusProductStatus.Active];
|
||||
|
||||
// const cusProducts = fullCus.customer_products.filter((cp) =>
|
||||
// inStatuses.includes(cp.status),
|
||||
// );
|
||||
const cusProducts = fullCus.customer_products;
|
||||
|
||||
const legacyData: Record<string, CusProductLegacyData> = {};
|
||||
for (const cusProduct of cusProducts) {
|
||||
const processed = await getApiCusProduct({
|
||||
cusProduct,
|
||||
ctx,
|
||||
fullCus,
|
||||
entities: fullCus.entities,
|
||||
});
|
||||
|
||||
apiCusProducts.push(processed.data);
|
||||
legacyData[processed.data.id] = processed.legacyData;
|
||||
}
|
||||
|
||||
const merged = mergeCusProductResponses({
|
||||
cusProductResponses: apiCusProducts,
|
||||
});
|
||||
|
||||
return {
|
||||
apiCusProducts: merged,
|
||||
legacyData,
|
||||
};
|
||||
};
|
||||
@@ -3,25 +3,22 @@ import {
|
||||
type ApiCustomer,
|
||||
ApiCustomerSchema,
|
||||
applyResponseVersionChanges,
|
||||
type CusExpand,
|
||||
type CustomerLegacyData,
|
||||
type FullCustomer,
|
||||
} from "@autumn/shared";
|
||||
import { z } from "zod/v4";
|
||||
import type { RequestContext } from "@/honoUtils/HonoEnv.js";
|
||||
import { getApiCusFeatures } from "./getApiCusFeature/getApiCusFeatures.js";
|
||||
import { getApiCusProducts } from "./getApiCusProduct/getApiCusProducts.js";
|
||||
import { getApiCusPlans } from "./getApiCusPlan/getApiCusPlans.js";
|
||||
import { getApiCustomerExpand } from "./getApiCustomerExpand.js";
|
||||
|
||||
export const getApiCustomer = async ({
|
||||
ctx,
|
||||
fullCus,
|
||||
expand,
|
||||
withAutumnId = false,
|
||||
}: {
|
||||
ctx: RequestContext;
|
||||
fullCus: FullCustomer;
|
||||
expand: CusExpand[];
|
||||
withAutumnId?: boolean;
|
||||
}) => {
|
||||
const apiCusFeatures = await getApiCusFeatures({
|
||||
@@ -29,8 +26,8 @@ export const getApiCustomer = async ({
|
||||
fullCus,
|
||||
});
|
||||
|
||||
const { apiCusProducts, legacyData: cusProductLegacyData } =
|
||||
await getApiCusProducts({
|
||||
const { apiCusPlans, legacyData: cusProductLegacyData } =
|
||||
await getApiCusPlans({
|
||||
ctx,
|
||||
fullCus,
|
||||
});
|
||||
@@ -38,7 +35,6 @@ export const getApiCustomer = async ({
|
||||
const apiCusExpand = await getApiCustomerExpand({
|
||||
ctx,
|
||||
fullCus,
|
||||
expand,
|
||||
});
|
||||
|
||||
const apiCustomer = ApiCustomerSchema.extend({
|
||||
@@ -56,16 +52,16 @@ export const getApiCustomer = async ({
|
||||
env: fullCus.env,
|
||||
metadata: fullCus.metadata,
|
||||
|
||||
products: apiCusProducts,
|
||||
plans: apiCusPlans,
|
||||
|
||||
features: apiCusFeatures,
|
||||
|
||||
...apiCusExpand,
|
||||
});
|
||||
|
||||
return applyResponseVersionChanges<ApiCustomer, CustomerLegacyData>({
|
||||
input: apiCustomer,
|
||||
legacyData: {
|
||||
cusProductLegacyData,
|
||||
},
|
||||
legacyData: { cusProductLegacyData },
|
||||
targetVersion: ctx.apiVersion,
|
||||
resource: AffectedResource.Customer,
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {
|
||||
ApiBaseEntitySchema,
|
||||
type ApiCustomerExpand,
|
||||
type ApiCusExpand,
|
||||
CusExpand,
|
||||
type FullCusProduct,
|
||||
type FullCustomer,
|
||||
@@ -15,13 +15,12 @@ import { getCusUpcomingInvoice } from "../cusResponseUtils/getCusUpcomingInvoice
|
||||
export const getApiCustomerExpand = async ({
|
||||
ctx,
|
||||
fullCus,
|
||||
expand,
|
||||
}: {
|
||||
ctx: AutumnContext;
|
||||
fullCus: FullCustomer;
|
||||
expand: CusExpand[];
|
||||
}): Promise<ApiCustomerExpand> => {
|
||||
const { org, env, db, logger } = ctx;
|
||||
}): Promise<ApiCusExpand> => {
|
||||
const { org, env, db, logger, expand } = ctx;
|
||||
console.log("Expand: ", expand);
|
||||
const getCusTrialsUsed = () => {
|
||||
if (expand.includes(CusExpand.TrialsUsed)) {
|
||||
return fullCus.trials_used;
|
||||
@@ -43,6 +42,8 @@ export const getApiCustomerExpand = async ({
|
||||
})
|
||||
: undefined;
|
||||
|
||||
const cusExpand = expand as CusExpand[];
|
||||
|
||||
const [rewards, upcomingInvoice, referrals, paymentMethod] =
|
||||
await Promise.all([
|
||||
getCusRewards({
|
||||
@@ -52,25 +53,25 @@ export const getApiCustomerExpand = async ({
|
||||
subIds: fullCus.customer_products.flatMap(
|
||||
(cp: FullCusProduct) => cp.subscription_ids || [],
|
||||
),
|
||||
expand,
|
||||
expand: cusExpand,
|
||||
}),
|
||||
getCusUpcomingInvoice({
|
||||
db,
|
||||
org,
|
||||
env,
|
||||
fullCus,
|
||||
expand,
|
||||
expand: cusExpand,
|
||||
}),
|
||||
getCusReferrals({
|
||||
db,
|
||||
fullCus,
|
||||
expand,
|
||||
expand: cusExpand,
|
||||
}),
|
||||
getCusPaymentMethodRes({
|
||||
org,
|
||||
env,
|
||||
fullCus,
|
||||
expand,
|
||||
expand: cusExpand,
|
||||
}),
|
||||
]);
|
||||
|
||||
|
||||
@@ -1,191 +0,0 @@
|
||||
import {
|
||||
ApiCusProductSchema,
|
||||
ApiVersion,
|
||||
type ApiVersionClass,
|
||||
CusProductStatus,
|
||||
type Entity,
|
||||
type Feature,
|
||||
type FixedPriceConfig,
|
||||
type FullCusProduct,
|
||||
type Organization,
|
||||
PriceType,
|
||||
type Subscription,
|
||||
TierInfinite,
|
||||
type UsagePriceConfig,
|
||||
} from "@autumn/shared";
|
||||
import {
|
||||
getPriceOptions,
|
||||
getUsageTier,
|
||||
} from "@/internal/products/prices/priceUtils.js";
|
||||
import { getProductResponse } from "@/internal/products/productUtils/productResponseUtils/getProductResponse.js";
|
||||
import { notNullish } from "@/utils/genUtils.js";
|
||||
import { getRelatedCusEnt } from "../../cusProducts/cusPrices/cusPriceUtils.js";
|
||||
import { fullCusProductToProduct } from "../../cusProducts/cusProductUtils.js";
|
||||
|
||||
export const getCusProductResponse = async ({
|
||||
cusProduct,
|
||||
subs,
|
||||
org,
|
||||
apiVersion,
|
||||
features,
|
||||
entity,
|
||||
}: {
|
||||
cusProduct: FullCusProduct;
|
||||
org: Organization;
|
||||
subs?: Subscription[];
|
||||
apiVersion: ApiVersionClass;
|
||||
features: Feature[];
|
||||
entity?: Entity;
|
||||
}) => {
|
||||
// Process prices
|
||||
|
||||
const prices = cusProduct.customer_prices.map((cp) => {
|
||||
const price = cp.price;
|
||||
|
||||
if (price.config?.type === PriceType.Fixed) {
|
||||
const config = price.config as FixedPriceConfig;
|
||||
return {
|
||||
amount: config.amount,
|
||||
interval: config.interval,
|
||||
};
|
||||
} else {
|
||||
const config = price.config as UsagePriceConfig;
|
||||
const priceOptions = getPriceOptions(price, cusProduct.options);
|
||||
const usageTier = getUsageTier(price, priceOptions?.quantity!);
|
||||
const cusEnt = getRelatedCusEnt({
|
||||
cusPrice: cp,
|
||||
cusEnts: cusProduct.customer_entitlements,
|
||||
});
|
||||
|
||||
const ent = cusEnt?.entitlement;
|
||||
|
||||
const singleTier =
|
||||
ent?.allowance === 0 && config.usage_tiers.length === 1;
|
||||
|
||||
if (singleTier) {
|
||||
return {
|
||||
amount: usageTier.amount,
|
||||
interval: config.interval,
|
||||
quantity: priceOptions?.quantity,
|
||||
};
|
||||
} else {
|
||||
// Add allowance to tiers
|
||||
const allowance = ent?.allowance;
|
||||
let tiers;
|
||||
|
||||
if (notNullish(allowance) && allowance! > 0) {
|
||||
tiers = [
|
||||
{
|
||||
to: allowance,
|
||||
amount: 0,
|
||||
},
|
||||
...config.usage_tiers.map((tier) => {
|
||||
const isLastTier = tier.to === -1 || tier.to === TierInfinite;
|
||||
return {
|
||||
to: isLastTier ? tier.to : Number(tier.to) + allowance!,
|
||||
amount: tier.amount,
|
||||
};
|
||||
}),
|
||||
];
|
||||
} else {
|
||||
tiers = config.usage_tiers.map((tier) => {
|
||||
const isLastTier = tier.to === -1 || tier.to === TierInfinite;
|
||||
return {
|
||||
to: isLastTier ? tier.to : Number(tier.to) + allowance!,
|
||||
amount: tier.amount,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
tiers: tiers,
|
||||
name: "",
|
||||
quantity: priceOptions?.quantity,
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const trialing =
|
||||
cusProduct.trial_ends_at && cusProduct.trial_ends_at > Date.now();
|
||||
|
||||
const subIds = cusProduct.subscription_ids;
|
||||
let stripeSubData = {};
|
||||
|
||||
if (subIds && subIds.length > 0 && apiVersion.gte(ApiVersion.V0_2)) {
|
||||
const baseSub = subs?.find(
|
||||
(s) => s.id === subIds[0] || (s as Subscription).stripe_id === subIds[0],
|
||||
);
|
||||
stripeSubData = {
|
||||
current_period_end: baseSub?.current_period_end
|
||||
? baseSub.current_period_end * 1000
|
||||
: null,
|
||||
current_period_start: baseSub?.current_period_start
|
||||
? baseSub.current_period_start * 1000
|
||||
: null,
|
||||
};
|
||||
}
|
||||
|
||||
if (!subIds && trialing) {
|
||||
stripeSubData = {
|
||||
current_period_start: cusProduct.starts_at,
|
||||
current_period_end: cusProduct.trial_ends_at,
|
||||
};
|
||||
}
|
||||
|
||||
if (apiVersion.gte(ApiVersion.V1_1)) {
|
||||
if ((!subIds || subIds.length === 0) && trialing) {
|
||||
stripeSubData = {
|
||||
current_period_start: cusProduct.starts_at,
|
||||
current_period_end: cusProduct.trial_ends_at,
|
||||
};
|
||||
}
|
||||
|
||||
const fullProduct = fullCusProductToProduct(cusProduct);
|
||||
const v2Product = await getProductResponse({
|
||||
product: fullProduct,
|
||||
features,
|
||||
withDisplay: false,
|
||||
options: cusProduct.options,
|
||||
});
|
||||
|
||||
return ApiCusProductSchema.parse({
|
||||
id: fullProduct.id,
|
||||
name: fullProduct.name,
|
||||
group: fullProduct.group || null,
|
||||
status: trialing ? CusProductStatus.Trialing : cusProduct.status,
|
||||
canceled_at: cusProduct.canceled_at || null,
|
||||
|
||||
is_default: fullProduct.is_default || false,
|
||||
is_add_on: fullProduct.is_add_on || false,
|
||||
version: fullProduct.version,
|
||||
quantity: cusProduct.quantity,
|
||||
|
||||
started_at: cusProduct.starts_at,
|
||||
entity_id: entity?.id || cusProduct.entity_id || undefined,
|
||||
|
||||
...stripeSubData,
|
||||
items: v2Product.items,
|
||||
});
|
||||
} else {
|
||||
const cusProductResponse = {
|
||||
id: cusProduct.product.id,
|
||||
name: cusProduct.product.name,
|
||||
group: cusProduct.product.group,
|
||||
status: trialing ? CusProductStatus.Trialing : cusProduct.status,
|
||||
created_at: cusProduct.created_at,
|
||||
canceled_at: cusProduct.canceled_at,
|
||||
processor: {
|
||||
type: cusProduct.processor?.type,
|
||||
subscription_id: cusProduct.processor?.subscription_id || null,
|
||||
},
|
||||
subscription_ids: cusProduct.subscription_ids || [],
|
||||
prices: prices,
|
||||
starts_at: cusProduct.starts_at,
|
||||
|
||||
...stripeSubData,
|
||||
};
|
||||
|
||||
return cusProductResponse;
|
||||
}
|
||||
};
|
||||
@@ -1,98 +0,0 @@
|
||||
import {
|
||||
ACTIVE_STATUSES,
|
||||
type ApiCusProduct,
|
||||
ApiVersion,
|
||||
type ApiVersionClass,
|
||||
type CusProductStatus,
|
||||
type Entity,
|
||||
type Feature,
|
||||
type Organization,
|
||||
} from "@autumn/shared";
|
||||
import { getCusProductResponse } from "./getCusProductResponse.js";
|
||||
|
||||
const mergeCusProductResponses = ({
|
||||
cusProductResponses,
|
||||
}: {
|
||||
cusProductResponses: ApiCusProduct[];
|
||||
}) => {
|
||||
const getProductKey = (product: ApiCusProduct) => {
|
||||
const status = ACTIVE_STATUSES.includes(product.status as CusProductStatus)
|
||||
? "active"
|
||||
: product.status;
|
||||
return `${product.id}:${status}`;
|
||||
};
|
||||
|
||||
const record: Record<string, any> = {};
|
||||
|
||||
for (const curr of cusProductResponses) {
|
||||
const key = getProductKey(curr);
|
||||
const latest = record[key];
|
||||
|
||||
const currStartedAt = curr.started_at;
|
||||
|
||||
record[key] = {
|
||||
...(latest || curr),
|
||||
version: Math.max(latest?.version || 1, curr?.version || 1),
|
||||
canceled_at: curr.canceled_at
|
||||
? curr.canceled_at
|
||||
: latest?.canceled_at || null,
|
||||
started_at: latest?.started_at
|
||||
? Math.min(latest?.started_at, currStartedAt)
|
||||
: currStartedAt,
|
||||
quantity: (latest?.quantity || 0) + (curr?.quantity || 0),
|
||||
};
|
||||
}
|
||||
|
||||
return Object.values(record);
|
||||
};
|
||||
|
||||
export const processFullCusProducts = async ({
|
||||
fullCusProducts,
|
||||
subs,
|
||||
org,
|
||||
entity,
|
||||
apiVersion,
|
||||
features,
|
||||
}: {
|
||||
fullCusProducts: any;
|
||||
subs: any;
|
||||
org: Organization;
|
||||
entity?: Entity;
|
||||
apiVersion: ApiVersionClass;
|
||||
features: Feature[];
|
||||
}) => {
|
||||
// Process full cus products
|
||||
let main = [];
|
||||
let addOns = [];
|
||||
for (const cusProduct of fullCusProducts) {
|
||||
const processed = await getCusProductResponse({
|
||||
cusProduct,
|
||||
subs,
|
||||
org,
|
||||
entity,
|
||||
apiVersion,
|
||||
features,
|
||||
});
|
||||
|
||||
const isAddOn = cusProduct.product.is_add_on;
|
||||
if (isAddOn) {
|
||||
addOns.push(processed);
|
||||
} else {
|
||||
main.push(processed);
|
||||
}
|
||||
}
|
||||
|
||||
if (apiVersion.gte(ApiVersion.V1_1)) {
|
||||
main = mergeCusProductResponses({
|
||||
cusProductResponses: main as ApiCusProduct[],
|
||||
});
|
||||
addOns = mergeCusProductResponses({
|
||||
cusProductResponses: addOns as ApiCusProduct[],
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
main: main,
|
||||
addOns: addOns,
|
||||
};
|
||||
};
|
||||
@@ -1,213 +0,0 @@
|
||||
import {
|
||||
ApiBaseEntitySchema,
|
||||
ApiCusFeatureV2Schema,
|
||||
ApiCustomerSchema,
|
||||
ApiCustomerV2Schema,
|
||||
ApiVersion,
|
||||
type ApiVersionClass,
|
||||
type AppEnv,
|
||||
CusExpand,
|
||||
CusProductStatus,
|
||||
CustomerResponseSchema,
|
||||
cusProductsToCusEnts,
|
||||
cusProductsToCusPrices,
|
||||
type Feature,
|
||||
FeatureType,
|
||||
type FullCusProduct,
|
||||
type FullCustomer,
|
||||
type Organization,
|
||||
type RewardResponse,
|
||||
} from "@autumn/shared";
|
||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import { invoicesToResponse } from "@/internal/invoices/invoiceUtils.js";
|
||||
import { featuresToObject } from "./cusFeatureResponseUtils/balancesToFeatureResponse.js";
|
||||
import { getCusBalances } from "./cusFeatureResponseUtils/getCusBalances.js";
|
||||
import { processFullCusProducts } from "./cusProductResponseUtils/processFullCusProducts.js";
|
||||
import { getCusPaymentMethodRes } from "./cusResponseUtils/getCusPaymentMethodRes.js";
|
||||
import { getCusReferrals } from "./cusResponseUtils/getCusReferrals.js";
|
||||
import { getCusRewards } from "./cusResponseUtils/getCusRewards.js";
|
||||
import { getCusUpcomingInvoice } from "./cusResponseUtils/getCusUpcomingInvoice.js";
|
||||
import { getCusInvoices } from "./cusUtils.js";
|
||||
|
||||
export const getCustomerDetails = async ({
|
||||
db,
|
||||
customer,
|
||||
features,
|
||||
org,
|
||||
env,
|
||||
params = {},
|
||||
logger,
|
||||
cusProducts,
|
||||
expand,
|
||||
apiVersion,
|
||||
}: {
|
||||
db: DrizzleCli;
|
||||
customer: FullCustomer;
|
||||
features: Feature[];
|
||||
org: Organization;
|
||||
env: AppEnv;
|
||||
params?: any;
|
||||
logger: any;
|
||||
cusProducts: FullCusProduct[];
|
||||
expand: CusExpand[];
|
||||
apiVersion: ApiVersionClass;
|
||||
}) => {
|
||||
const withRewards = expand.includes(CusExpand.Rewards);
|
||||
|
||||
const inStatuses = org.config.include_past_due
|
||||
? [CusProductStatus.Active, CusProductStatus.PastDue]
|
||||
: [CusProductStatus.Active];
|
||||
|
||||
const cusEnts = cusProductsToCusEnts({ cusProducts, inStatuses }) as any;
|
||||
|
||||
const balances = await getCusBalances({
|
||||
cusEntsWithCusProduct: cusEnts,
|
||||
cusPrices: cusProductsToCusPrices({ cusProducts, inStatuses }),
|
||||
org,
|
||||
apiVersion,
|
||||
});
|
||||
|
||||
const subIds = cusProducts.flatMap(
|
||||
(cp: FullCusProduct) => cp.subscription_ids || [],
|
||||
);
|
||||
|
||||
const subs = customer.subscriptions || [];
|
||||
const { main, addOns } = await processFullCusProducts({
|
||||
fullCusProducts: cusProducts,
|
||||
subs,
|
||||
org,
|
||||
apiVersion,
|
||||
features,
|
||||
});
|
||||
|
||||
if (apiVersion.gte(ApiVersion.V1_1)) {
|
||||
let entList: any = balances.map((b) => {
|
||||
const isBoolean =
|
||||
features.find((f: Feature) => f.id === b.feature_id)?.type ===
|
||||
FeatureType.Boolean;
|
||||
if (b.unlimited || isBoolean) {
|
||||
return b;
|
||||
}
|
||||
|
||||
return ApiCusFeatureV2Schema.parse({
|
||||
...b,
|
||||
usage: b.used,
|
||||
included_usage: b.allowance,
|
||||
});
|
||||
});
|
||||
|
||||
const products: any = [...main, ...addOns];
|
||||
|
||||
if (apiVersion.gte(ApiVersion.V1_2)) {
|
||||
entList = featuresToObject({
|
||||
features,
|
||||
entList,
|
||||
});
|
||||
}
|
||||
|
||||
const withInvoices = expand.includes(CusExpand.Invoices);
|
||||
|
||||
const rewards: RewardResponse | undefined = await getCusRewards({
|
||||
org,
|
||||
env,
|
||||
fullCus: customer,
|
||||
subIds,
|
||||
expand,
|
||||
});
|
||||
|
||||
const upcomingInvoice = await getCusUpcomingInvoice({
|
||||
db,
|
||||
org,
|
||||
env,
|
||||
fullCus: customer,
|
||||
expand,
|
||||
});
|
||||
|
||||
const referrals = await getCusReferrals({
|
||||
db,
|
||||
fullCus: customer,
|
||||
expand,
|
||||
});
|
||||
|
||||
const paymentMethod = await getCusPaymentMethodRes({
|
||||
org,
|
||||
env,
|
||||
fullCus: customer,
|
||||
expand,
|
||||
});
|
||||
|
||||
const apiCustomerSchema = apiVersion.gte(ApiVersion.V1_2)
|
||||
? ApiCustomerSchema
|
||||
: ApiCustomerV2Schema;
|
||||
|
||||
const cusResponse = {
|
||||
...apiCustomerSchema.parse({
|
||||
...customer,
|
||||
stripe_id: customer.processor?.id,
|
||||
features: entList,
|
||||
products,
|
||||
// invoices: withInvoices ? invoices : undefined,
|
||||
|
||||
invoices: withInvoices
|
||||
? invoicesToResponse({
|
||||
invoices: customer.invoices || [],
|
||||
logger,
|
||||
})
|
||||
: undefined,
|
||||
trials_used: expand.includes(CusExpand.TrialsUsed)
|
||||
? customer.trials_used
|
||||
: undefined,
|
||||
rewards: withRewards ? rewards : undefined,
|
||||
metadata: customer.metadata,
|
||||
entities: expand.includes(CusExpand.Entities)
|
||||
? customer.entities.map((e) =>
|
||||
ApiBaseEntitySchema.parse({
|
||||
id: e.id,
|
||||
name: e.name,
|
||||
customer_id: customer.id,
|
||||
feature_id: e.feature_id,
|
||||
created_at: e.created_at,
|
||||
env: customer.env,
|
||||
}),
|
||||
)
|
||||
: undefined,
|
||||
referrals,
|
||||
payment_method: paymentMethod,
|
||||
upcoming_invoice: upcomingInvoice,
|
||||
}),
|
||||
};
|
||||
|
||||
if (params?.with_autumn_id === "true") {
|
||||
return {
|
||||
...cusResponse,
|
||||
autumn_id: customer.internal_id,
|
||||
};
|
||||
} else {
|
||||
return cusResponse;
|
||||
}
|
||||
} else {
|
||||
// Probably don't need items...?
|
||||
// const withItems = org.config.api_version >= BREAK_API_VERSION;
|
||||
// const withItems = apiVersion.gte(ApiVersion.V0_2);
|
||||
|
||||
const processedInvoices = await getCusInvoices({
|
||||
db,
|
||||
internalCustomerId: customer.internal_id,
|
||||
invoices: customer.invoices,
|
||||
limit: 20,
|
||||
withItems: false,
|
||||
features,
|
||||
});
|
||||
|
||||
return {
|
||||
customer: CustomerResponseSchema.parse(customer),
|
||||
products: main,
|
||||
add_ons: addOns,
|
||||
entitlements: balances,
|
||||
invoices: processedInvoices,
|
||||
trials_used: expand.includes(CusExpand.TrialsUsed)
|
||||
? customer.trials_used
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -1,211 +0,0 @@
|
||||
import {
|
||||
CusProductStatus,
|
||||
cusProductToPrices,
|
||||
ErrCode,
|
||||
type FullCusProduct,
|
||||
type FullCustomer,
|
||||
} from "@autumn/shared";
|
||||
import { StatusCodes } from "http-status-codes";
|
||||
import {
|
||||
ACTIVE_STATUSES,
|
||||
CusProductService,
|
||||
} from "@/internal/customers/cusProducts/CusProductService.js";
|
||||
import { getExistingCusProducts } from "@/internal/customers/cusProducts/cusProductUtils/getExistingCusProducts.js";
|
||||
import {
|
||||
cancelCusProductSubscriptions,
|
||||
expireAndActivate,
|
||||
} from "@/internal/customers/cusProducts/cusProductUtils.js";
|
||||
import { isFreeProduct, isOneOff } from "@/internal/products/productUtils.js";
|
||||
import RecaseError, { handleRequestError } from "@/utils/errorUtils.js";
|
||||
import type { ExtendedRequest } from "@/utils/models/Request.js";
|
||||
import { CusService } from "../CusService.js";
|
||||
export const expireCusProduct = async ({
|
||||
req,
|
||||
cusProduct, // cus product to expire
|
||||
fullCus,
|
||||
expireImmediately = true,
|
||||
prorate,
|
||||
}: {
|
||||
req: ExtendedRequest;
|
||||
cusProduct: FullCusProduct;
|
||||
fullCus: FullCustomer;
|
||||
expireImmediately: boolean;
|
||||
prorate: boolean;
|
||||
}) => {
|
||||
const { db, org, env, logger } = req;
|
||||
logger.info("--------------------------------");
|
||||
logger.info(
|
||||
`🔔 Expiring cutomer product (${
|
||||
expireImmediately ? "immediately" : "end of cycle"
|
||||
})`,
|
||||
);
|
||||
logger.info(
|
||||
`Customer: ${fullCus.id || fullCus.internal_id} (${env}), Org: ${org.id}`,
|
||||
);
|
||||
logger.info(
|
||||
`Product: ${cusProduct.product.name}, Status: ${cusProduct.status}`,
|
||||
);
|
||||
|
||||
// if (cusProduct.status == CusProductStatus.Scheduled) {
|
||||
// await CusProductService.delete({
|
||||
// db,
|
||||
// cusProductId: cusProduct.id,
|
||||
// });
|
||||
// return;
|
||||
// }
|
||||
|
||||
// 1. If main product, can't expire if there's scheduled product
|
||||
const isMain = !cusProduct.product.is_add_on;
|
||||
const { curScheduledProduct: futureProduct } = getExistingCusProducts({
|
||||
product: cusProduct.product,
|
||||
cusProducts: fullCus.customer_products,
|
||||
internalEntityId: cusProduct.internal_entity_id,
|
||||
});
|
||||
|
||||
if (isMain) {
|
||||
if (
|
||||
cusProduct.canceled_at &&
|
||||
ACTIVE_STATUSES.includes(cusProduct.status) &&
|
||||
!expireImmediately
|
||||
) {
|
||||
throw new RecaseError({
|
||||
message: `Product ${cusProduct.product.name} is already about to cancel at the end of cycle.`,
|
||||
code: ErrCode.InvalidRequest,
|
||||
statusCode: StatusCodes.BAD_REQUEST,
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
futureProduct &&
|
||||
!isFreeProduct(cusProductToPrices({ cusProduct: futureProduct }))
|
||||
) {
|
||||
throw new RecaseError({
|
||||
message: `Please delete scheduled product ${futureProduct.product.name} first`,
|
||||
code: ErrCode.InvalidRequest,
|
||||
statusCode: StatusCodes.BAD_REQUEST,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 2. If expire at cycle end, just cancel subscriptions
|
||||
if (!expireImmediately) {
|
||||
// 1. Check if already canceled
|
||||
await cancelCusProductSubscriptions({
|
||||
cusProduct,
|
||||
org,
|
||||
env,
|
||||
expireImmediately,
|
||||
logger,
|
||||
});
|
||||
|
||||
if (isOneOff(cusProduct.customer_prices.map((p) => p.price))) {
|
||||
await CusProductService.update({
|
||||
db,
|
||||
cusProductId: cusProduct.id,
|
||||
updates: { status: CusProductStatus.Expired },
|
||||
});
|
||||
} else {
|
||||
await CusProductService.update({
|
||||
db,
|
||||
cusProductId: cusProduct.id,
|
||||
updates: { canceled_at: Date.now() },
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (cusProduct.product.is_add_on) {
|
||||
await cancelCusProductSubscriptions({
|
||||
cusProduct,
|
||||
org,
|
||||
env,
|
||||
logger,
|
||||
});
|
||||
|
||||
await CusProductService.update({
|
||||
db,
|
||||
cusProductId: cusProduct.id,
|
||||
updates: {
|
||||
status: CusProductStatus.Expired,
|
||||
ended_at: Date.now(),
|
||||
},
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove scheduled products first...
|
||||
if (futureProduct) {
|
||||
await CusProductService.delete({
|
||||
db,
|
||||
cusProductId: futureProduct.id,
|
||||
});
|
||||
}
|
||||
|
||||
logger.info(`Expiring current product: ${cusProduct.product.name}`);
|
||||
await expireAndActivate({
|
||||
req,
|
||||
cusProduct,
|
||||
fullCus,
|
||||
});
|
||||
|
||||
logger.info(`Cancelling stripe subscriptions`);
|
||||
await cancelCusProductSubscriptions({
|
||||
cusProduct,
|
||||
org,
|
||||
env,
|
||||
logger,
|
||||
prorate,
|
||||
});
|
||||
|
||||
return;
|
||||
};
|
||||
|
||||
export const handleCusProductExpired = async (req: any, res: any) => {
|
||||
try {
|
||||
const { db } = req;
|
||||
|
||||
const customerProductId = req.params.customer_product_id;
|
||||
const cusProduct = await CusProductService.get({
|
||||
db,
|
||||
id: customerProductId,
|
||||
orgId: req.orgId,
|
||||
env: req.env,
|
||||
withCustomer: true,
|
||||
});
|
||||
|
||||
if (!cusProduct) {
|
||||
throw new RecaseError({
|
||||
message: `Cus product not found: ${customerProductId}`,
|
||||
code: ErrCode.CusProductNotFound,
|
||||
statusCode: 404,
|
||||
});
|
||||
}
|
||||
|
||||
const fullCus = await CusService.getFull({
|
||||
db,
|
||||
idOrInternalId:
|
||||
cusProduct.customer!.id || cusProduct.customer!.internal_id,
|
||||
orgId: req.orgId,
|
||||
env: req.env,
|
||||
});
|
||||
|
||||
await expireCusProduct({
|
||||
req,
|
||||
cusProduct,
|
||||
fullCus,
|
||||
expireImmediately: true,
|
||||
prorate: true,
|
||||
});
|
||||
|
||||
res.status(200).json({ message: "Product expired" });
|
||||
} catch (error) {
|
||||
handleRequestError({
|
||||
req,
|
||||
error,
|
||||
res,
|
||||
action: "update customer product",
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -1,68 +0,0 @@
|
||||
import { CusExpand, ErrCode, LegacyVersion } from "@autumn/shared";
|
||||
import { StatusCodes } from "http-status-codes";
|
||||
import { routeHandler } from "@/utils/routerUtils.js";
|
||||
import { orgToVersion } from "@/utils/versionUtils/legacyVersionUtils.js";
|
||||
import { getCusWithCache } from "../cusCache/getCusWithCache.js";
|
||||
import { parseCusExpand } from "../cusUtils/cusUtils.js";
|
||||
import { getCustomerDetails } from "../cusUtils/getCustomerDetails.js";
|
||||
|
||||
export const handleGetCustomer = async (req: any, res: any) =>
|
||||
routeHandler({
|
||||
req,
|
||||
res,
|
||||
action: "get customer",
|
||||
handler: async () => {
|
||||
const customerId = req.params.customer_id;
|
||||
const { env, db, logger, org, features } = req;
|
||||
const { expand } = req.query;
|
||||
|
||||
const expandArray = parseCusExpand(expand);
|
||||
|
||||
const apiVersion = orgToVersion({
|
||||
org,
|
||||
reqApiVersion: req.apiVersion,
|
||||
});
|
||||
|
||||
const getInvoices = apiVersion < LegacyVersion.v1_1;
|
||||
if (getInvoices) expandArray.push(CusExpand.Invoices);
|
||||
|
||||
logger.info(`getting customer ${customerId} for org ${org.slug}`);
|
||||
const startTime = Date.now();
|
||||
const customer = await getCusWithCache({
|
||||
db,
|
||||
idOrInternalId: customerId,
|
||||
org,
|
||||
env,
|
||||
expand: expandArray,
|
||||
allowNotFound: true,
|
||||
logger,
|
||||
});
|
||||
|
||||
logger.info(`get customer took ${Date.now() - startTime}ms`);
|
||||
|
||||
if (!customer) {
|
||||
logger.warn(
|
||||
`GET /customers/${customerId}: not found | Org: ${org.slug}`,
|
||||
);
|
||||
res.status(StatusCodes.NOT_FOUND).json({
|
||||
message: `Customer ${customerId} not found`,
|
||||
code: ErrCode.CustomerNotFound,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const cusData = await getCustomerDetails({
|
||||
db,
|
||||
customer,
|
||||
org,
|
||||
env: req.env,
|
||||
logger,
|
||||
cusProducts: customer.customer_products,
|
||||
expand: expandArray,
|
||||
features,
|
||||
apiVersion: req.apiVersion,
|
||||
});
|
||||
|
||||
res.status(200).json(cusData);
|
||||
},
|
||||
});
|
||||
@@ -1,6 +1,9 @@
|
||||
import {
|
||||
AffectedResource,
|
||||
ApiVersion,
|
||||
backwardsChangeActive,
|
||||
CusExpand,
|
||||
type EntityExpand,
|
||||
GetCustomerQuerySchema,
|
||||
V0_2_InvoicesAlwaysExpanded,
|
||||
} from "@autumn/shared";
|
||||
@@ -9,12 +12,15 @@ import { getCusWithCache } from "../cusCache/getCusWithCache.js";
|
||||
import { getApiCustomer } from "../cusUtils/apiCusUtils/getApiCustomer.js";
|
||||
|
||||
export const handleGetCustomerV2 = createRoute({
|
||||
query: GetCustomerQuerySchema,
|
||||
versionedQuery: {
|
||||
latest: GetCustomerQuerySchema,
|
||||
[ApiVersion.V1_2]: GetCustomerQuerySchema,
|
||||
},
|
||||
resource: AffectedResource.Customer,
|
||||
handler: async (c) => {
|
||||
const ctx = c.get("ctx");
|
||||
const customerId = c.req.param("customer_id");
|
||||
const { env, db, logger, org } = ctx;
|
||||
const { expand = [] } = c.req.valid("query");
|
||||
const { env, db, logger, org, expand } = ctx;
|
||||
|
||||
// SIDE EFFECT
|
||||
if (
|
||||
@@ -31,7 +37,7 @@ export const handleGetCustomerV2 = createRoute({
|
||||
idOrInternalId: customerId,
|
||||
org,
|
||||
env,
|
||||
expand,
|
||||
expand: expand as (CusExpand | EntityExpand)[],
|
||||
logger,
|
||||
allowNotFound: false,
|
||||
});
|
||||
@@ -39,7 +45,6 @@ export const handleGetCustomerV2 = createRoute({
|
||||
const customer = await getApiCustomer({
|
||||
ctx,
|
||||
fullCus: fullCus,
|
||||
expand,
|
||||
});
|
||||
|
||||
return c.json(customer);
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
import { CusProductStatus, ErrCode } from "@autumn/shared";
|
||||
import RecaseError, { handleRequestError } from "@/utils/errorUtils.js";
|
||||
import { parseCusExpand } from "../cusUtils/cusUtils.js";
|
||||
import { getCustomerDetails } from "../cusUtils/getCustomerDetails.js";
|
||||
import { getOrCreateCustomer } from "../cusUtils/getOrCreateCustomer.js";
|
||||
|
||||
export const handlePostCustomerRequest = async (req: any, res: any) => {
|
||||
try {
|
||||
const logger = req.logger;
|
||||
const data = req.body;
|
||||
|
||||
const expand = parseCusExpand(req.query.expand);
|
||||
const { db, org, features } = req;
|
||||
|
||||
if (data.customer_id) {
|
||||
throw new RecaseError({
|
||||
message:
|
||||
"use the `id` field instead of `customer_id` to specify the new customer's ID",
|
||||
code: ErrCode.InvalidInputs,
|
||||
});
|
||||
}
|
||||
|
||||
if (data.id === undefined) {
|
||||
throw new RecaseError({
|
||||
message: "`id` field must be either a string or null",
|
||||
code: ErrCode.InvalidInputs,
|
||||
});
|
||||
}
|
||||
|
||||
if (!data.id && !data.email) {
|
||||
throw new RecaseError({
|
||||
message: "ID or email is required",
|
||||
code: ErrCode.InvalidRequest,
|
||||
});
|
||||
}
|
||||
|
||||
const customer = await getOrCreateCustomer({
|
||||
req,
|
||||
customerId: data.id,
|
||||
customerData: data,
|
||||
inStatuses: [
|
||||
CusProductStatus.Active,
|
||||
CusProductStatus.PastDue,
|
||||
CusProductStatus.Scheduled,
|
||||
],
|
||||
expand,
|
||||
|
||||
entityId: data.entity_id,
|
||||
entityData: data.entity_data,
|
||||
withCache: true,
|
||||
});
|
||||
|
||||
const cusDetails = await getCustomerDetails({
|
||||
db,
|
||||
customer,
|
||||
org,
|
||||
env: req.env,
|
||||
params: req.query,
|
||||
logger,
|
||||
cusProducts: customer.customer_products,
|
||||
expand,
|
||||
features,
|
||||
apiVersion: req.apiVersion,
|
||||
});
|
||||
|
||||
res.status(200).json(cusDetails);
|
||||
} catch (error: any) {
|
||||
if (
|
||||
error instanceof RecaseError &&
|
||||
error.code === ErrCode.DuplicateCustomerId
|
||||
) {
|
||||
req.logger.warn(
|
||||
`POST /customers: ${error.message} (org: ${req.org?.slug})`,
|
||||
);
|
||||
res.status(error.statusCode).json({
|
||||
message: error.message,
|
||||
code: error.code,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Error: ${error}`);
|
||||
handleRequestError({ req, error, res, action: "create customer" });
|
||||
}
|
||||
};
|
||||
@@ -1,6 +1,7 @@
|
||||
import { CreateCustomerSchema, ErrCode, ProcessorType } from "@autumn/shared";
|
||||
import { StatusCodes } from "http-status-codes";
|
||||
import { createStripeCli } from "@/external/connect/createStripeCli.js";
|
||||
import type { AutumnContext } from "@/honoUtils/HonoEnv.js";
|
||||
import { CusService } from "@/internal/customers/CusService.js";
|
||||
import { FeatureService } from "@/internal/features/FeatureService.js";
|
||||
import RecaseError from "@/utils/errorUtils.js";
|
||||
@@ -10,8 +11,8 @@ import type {
|
||||
ExtendedResponse,
|
||||
} from "@/utils/models/Request.js";
|
||||
import { routeHandler } from "@/utils/routerUtils.js";
|
||||
import { getApiCustomer } from "../cusUtils/apiCusUtils/getApiCustomer.js";
|
||||
import { parseCusExpand } from "../cusUtils/cusUtils.js";
|
||||
import { getCustomerDetails } from "../cusUtils/getCustomerDetails.js";
|
||||
|
||||
export const handleUpdateCustomer = async (req: any, res: any) =>
|
||||
routeHandler({
|
||||
@@ -134,16 +135,10 @@ export const handleUpdateCustomer = async (req: any, res: any) =>
|
||||
});
|
||||
|
||||
// res.status(200).json({ customer: updatedCustomer });
|
||||
const customerDetails = await getCustomerDetails({
|
||||
db,
|
||||
customer: finalCustomer,
|
||||
org,
|
||||
env: req.env,
|
||||
logger: req.logger,
|
||||
cusProducts: finalCustomer.customer_products,
|
||||
const customerDetails = await getApiCustomer({
|
||||
ctx: req as AutumnContext,
|
||||
fullCus: finalCustomer,
|
||||
expand: parseCusExpand(req.query.expand as string),
|
||||
features,
|
||||
apiVersion: req.apiVersion,
|
||||
});
|
||||
|
||||
res.status(200).json(customerDetails);
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
type Entity,
|
||||
LegacyVersion,
|
||||
} from "@autumn/shared";
|
||||
import type { AutumnContext } from "@/honoUtils/HonoEnv.js";
|
||||
import type { ExtendedRequest } from "@/utils/models/Request.js";
|
||||
import { routeHandler } from "@/utils/routerUtils.js";
|
||||
import { orgToVersion } from "@/utils/versionUtils/legacyVersionUtils.js";
|
||||
@@ -91,16 +92,10 @@ export const createEntities = async ({
|
||||
}
|
||||
|
||||
const { entities } = await getEntityResponse({
|
||||
db,
|
||||
ctx: req as AutumnContext,
|
||||
entityIds: newEntities.map((e: any) => e.id || e.internal_id),
|
||||
org,
|
||||
env,
|
||||
customerId: customer.id || customer.internal_id,
|
||||
withAutumnId,
|
||||
apiVersion: apiVersion!,
|
||||
features,
|
||||
logger,
|
||||
skipCache: true,
|
||||
});
|
||||
|
||||
return entities;
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { CreateFeatureSchema, ErrCode, FeatureType } from "@autumn/shared";
|
||||
import express, { type Router } from "express";
|
||||
import { formatZodError } from "@/errors/formatZodError.js";
|
||||
import { handleDeleteFeature } from "@/internal/features/handlers/handleDeleteFeature.js";
|
||||
import { handleUpdateFeature } from "@/internal/features/handlers/handleUpdateFeature.js";
|
||||
import RecaseError, { formatZodError } from "@/utils/errorUtils.js";
|
||||
import RecaseError from "@/utils/errorUtils.js";
|
||||
import { generateId } from "@/utils/genUtils.js";
|
||||
import { FeatureService } from "./FeatureService.js";
|
||||
import {
|
||||
|
||||
@@ -75,7 +75,7 @@ export const isV4Usage = ({
|
||||
|
||||
return (
|
||||
billingType === BillingType.UsageInArrear &&
|
||||
(cusProduct.api_semver === ApiVersion.Beta ||
|
||||
(cusProduct.api_semver === ApiVersion.V1_Beta ||
|
||||
notNullish(cusProduct.internal_entity_id))
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { BillingInterval, FullProduct } from "@autumn/shared";
|
||||
import { generateObject } from "ai";
|
||||
import { anthropic } from "@ai-sdk/anthropic";
|
||||
import { BillingInterval, type FullProduct } from "@autumn/shared";
|
||||
import { generateObject } from "ai";
|
||||
import type { Logger } from "pino";
|
||||
import { z } from "zod";
|
||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import { nullish } from "@/utils/genUtils.js";
|
||||
import { Logger } from "pino";
|
||||
import { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import { ProductService } from "../ProductService.js";
|
||||
|
||||
const prompt = `Detect whether a given product (called "product_to_detect") is an interval variant of a base product from the list of existing products (called "existing_products").
|
||||
@@ -35,7 +35,7 @@ export const detectBaseVariant = async ({
|
||||
logger.info(`Detecting base variant for ${curProduct.id}`);
|
||||
if (!process.env.ANTHROPIC_API_KEY) return;
|
||||
|
||||
let existingProducts = (await ProductService.listFull({
|
||||
const existingProducts = (await ProductService.listFull({
|
||||
db,
|
||||
orgId: curProduct.org_id,
|
||||
env: curProduct.env,
|
||||
@@ -43,8 +43,8 @@ export const detectBaseVariant = async ({
|
||||
})) as FullProduct[];
|
||||
|
||||
// if (product.base_variant_id == baseVariantId) {
|
||||
let curPrices = curProduct.prices;
|
||||
let intervals = curPrices.map((price) => price.config.interval);
|
||||
const curPrices = curProduct.prices;
|
||||
const intervals = curPrices.map((price) => price.config.interval);
|
||||
|
||||
// 1. Return null if add on
|
||||
if (curProduct.is_add_on) return null;
|
||||
@@ -58,17 +58,17 @@ export const detectBaseVariant = async ({
|
||||
|
||||
const filteredExistingProducts = existingProducts.filter(
|
||||
(p) =>
|
||||
p.id != curProduct.id &&
|
||||
p.id !== curProduct.id &&
|
||||
nullish(p.base_variant_id) &&
|
||||
!p.is_add_on &&
|
||||
p.prices.length > 0 &&
|
||||
p.prices.every(
|
||||
(price) => price.config.interval == BillingInterval.Month,
|
||||
(price) => price.config.interval === BillingInterval.Month,
|
||||
) &&
|
||||
p.group == curProduct.group,
|
||||
p.group === curProduct.group,
|
||||
);
|
||||
|
||||
if (filteredExistingProducts.length == 0) {
|
||||
if (filteredExistingProducts.length === 0) {
|
||||
logger.info(`No base product to search for`);
|
||||
return null;
|
||||
}
|
||||
@@ -95,13 +95,13 @@ export const detectBaseVariant = async ({
|
||||
</existing_products>
|
||||
`;
|
||||
|
||||
let { object } = await generateObject({
|
||||
const { object } = await generateObject({
|
||||
model: anthropic("claude-3-5-haiku-latest"),
|
||||
schema: z.object({ base_variant_id: z.string().nullable() }),
|
||||
prompt: `${prompt}\n\n${variables}`,
|
||||
});
|
||||
|
||||
let baseVariantId = object.base_variant_id;
|
||||
const baseVariantId = object.base_variant_id;
|
||||
|
||||
logger.info(
|
||||
`llm response for base variant of ${curProduct.id}: ${baseVariantId}`,
|
||||
|
||||
@@ -4,12 +4,12 @@ import {
|
||||
type ApiPlan,
|
||||
ApiPlanSchema,
|
||||
AttachScenario,
|
||||
type BillingInterval,
|
||||
type Feature,
|
||||
type FeatureOptions,
|
||||
type FullCustomer,
|
||||
type FullProduct,
|
||||
itemsToPlanFeatures,
|
||||
itemToBillingInterval,
|
||||
productV2ToBasePrice,
|
||||
productV2ToFeatureItems,
|
||||
} from "@autumn/shared";
|
||||
@@ -114,62 +114,13 @@ export const getPlanResponse = async ({
|
||||
attachScenario,
|
||||
});
|
||||
|
||||
console.log(
|
||||
`New feature:\n ${JSON.stringify(
|
||||
{
|
||||
// Basic fields
|
||||
id: product.id,
|
||||
name: product.name || "",
|
||||
description: product.description, // Products don't have descriptions
|
||||
group: product.group,
|
||||
version: product.version,
|
||||
|
||||
// Boolean flags
|
||||
add_on: product.is_add_on,
|
||||
default: product.is_default,
|
||||
|
||||
// Price field (required in Plan schema)
|
||||
price: basePrice
|
||||
? {
|
||||
amount: basePrice.amount,
|
||||
interval: basePrice.interval as unknown as BillingInterval,
|
||||
}
|
||||
: {
|
||||
amount: 0,
|
||||
interval: "month" as BillingInterval,
|
||||
},
|
||||
|
||||
// Features array
|
||||
features: planFeatures,
|
||||
|
||||
// Free trial
|
||||
free_trial: freeTrial,
|
||||
|
||||
// Metadata fields
|
||||
created_at: product.created_at,
|
||||
env: product.env,
|
||||
archived: product.archived,
|
||||
base_variant_id: product.base_variant_id,
|
||||
|
||||
// Customer context (optional)
|
||||
// Uncomment when ready to add customer context
|
||||
// customer_context: {
|
||||
// trial_available: notNullish(freeTrial),
|
||||
// scenario: attachScenario,
|
||||
// },
|
||||
},
|
||||
null,
|
||||
4,
|
||||
)}`,
|
||||
);
|
||||
|
||||
// 9. Build Plan response
|
||||
return ApiPlanSchema.parse({
|
||||
// Basic fields
|
||||
id: product.id,
|
||||
name: product.name || "",
|
||||
description: product.description, // Products don't have descriptions
|
||||
group: product.group,
|
||||
name: product.name || null,
|
||||
description: product.description || null, // Products don't have descriptions
|
||||
group: product.group || null,
|
||||
version: product.version,
|
||||
|
||||
// Boolean flags
|
||||
@@ -180,12 +131,11 @@ export const getPlanResponse = async ({
|
||||
price: basePrice
|
||||
? {
|
||||
amount: basePrice.amount,
|
||||
interval: basePrice.interval as BillingInterval,
|
||||
interval: itemToBillingInterval({ item: basePrice }),
|
||||
interval_count:
|
||||
basePrice.intervalCount !== 1 ? basePrice.intervalCount : undefined,
|
||||
}
|
||||
: {
|
||||
amount: 0,
|
||||
interval: "month" as BillingInterval,
|
||||
},
|
||||
: null,
|
||||
|
||||
// Features array
|
||||
features: planFeatures,
|
||||
|
||||
@@ -43,7 +43,7 @@ export const mapToProductItems = ({
|
||||
}
|
||||
|
||||
for (const item of items) {
|
||||
const feature = features.find((f) => f.id == item.feature_id);
|
||||
const feature = features.find((f) => f.id === item.feature_id);
|
||||
if (feature) {
|
||||
item.feature_type = getItemFeatureType({ item, features });
|
||||
}
|
||||
@@ -84,7 +84,7 @@ export const mapToProductV2 = ({
|
||||
|
||||
const productV2: ProductV2 = {
|
||||
internal_id: product.internal_id,
|
||||
|
||||
env: product.env,
|
||||
id: product.id,
|
||||
name: product.name,
|
||||
is_add_on: product.is_add_on,
|
||||
|
||||
@@ -12,6 +12,7 @@ import { runTriggerCheckoutReward } from "@/internal/rewards/triggerCheckoutRewa
|
||||
import { runUpdateBalanceTask } from "@/trigger/updateBalanceTask.js";
|
||||
import { runUpdateUsageTask } from "@/trigger/updateUsageTask.js";
|
||||
import { generateId } from "@/utils/genUtils.js";
|
||||
import { createWorkerAutumnContext } from "@/utils/workerUtils/createAutumnContext.js";
|
||||
import { JobName } from "./JobName.js";
|
||||
import { acquireLock, getRedisConnection, releaseLock } from "./lockUtils.js";
|
||||
import { QueueManager } from "./QueueManager.js";
|
||||
@@ -79,11 +80,18 @@ const initWorker = ({
|
||||
}
|
||||
|
||||
if (actionHandlers.includes(job.name as JobName)) {
|
||||
const ctx = await createWorkerAutumnContext({
|
||||
db,
|
||||
orgId: job.data.orgId,
|
||||
env: job.data.env,
|
||||
logger: workerLogger,
|
||||
workerId: id.toString(),
|
||||
});
|
||||
|
||||
await runActionHandlerTask({
|
||||
queue,
|
||||
job,
|
||||
logger: workerLogger,
|
||||
db,
|
||||
ctx,
|
||||
useBackup,
|
||||
});
|
||||
return;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
type AppEnv,
|
||||
AuthType,
|
||||
createdAtToVersion,
|
||||
type Feature,
|
||||
type FullCusEntWithFullCusProduct,
|
||||
@@ -10,10 +11,12 @@ import {
|
||||
} from "@autumn/shared";
|
||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import { sendSvixEvent } from "@/external/svix/svixHelpers.js";
|
||||
import type { AutumnContext } from "@/honoUtils/HonoEnv.js";
|
||||
import { getSingleEntityResponse } from "@/internal/api/entities/getEntityUtils.js";
|
||||
import { getV2CheckResponse } from "@/internal/api/entitled/checkUtils/getV2CheckResponse.js";
|
||||
import { getCustomerDetails } from "@/internal/customers/cusUtils/getCustomerDetails.js";
|
||||
import { getApiCustomer } from "@/internal/customers/cusUtils/apiCusUtils/getApiCustomer.js";
|
||||
import { toApiFeature } from "@/internal/features/utils/mapFeatureUtils.js";
|
||||
import { generateId } from "@/utils/genUtils.js";
|
||||
|
||||
export const mergeNewCusEntsIntoCusProducts = ({
|
||||
cusProducts,
|
||||
@@ -39,45 +42,27 @@ export const mergeNewCusEntsIntoCusProducts = ({
|
||||
};
|
||||
|
||||
export const sendSvixThresholdReachedEvent = async ({
|
||||
db,
|
||||
org,
|
||||
env,
|
||||
features,
|
||||
logger,
|
||||
ctx,
|
||||
feature,
|
||||
fullCus,
|
||||
thresholdType,
|
||||
}: {
|
||||
db: DrizzleCli;
|
||||
org: Organization;
|
||||
env: AppEnv;
|
||||
features: Feature[];
|
||||
logger: any;
|
||||
ctx: AutumnContext;
|
||||
feature: Feature;
|
||||
fullCus: FullCustomer;
|
||||
thresholdType: "limit_reached" | "allowance_used";
|
||||
}) => {
|
||||
const apiVersion = createdAtToVersion({
|
||||
createdAt: org.created_at || undefined,
|
||||
});
|
||||
const { org, env, logger } = ctx;
|
||||
|
||||
const cusDetails = await getCustomerDetails({
|
||||
db,
|
||||
customer: fullCus,
|
||||
org,
|
||||
env,
|
||||
features,
|
||||
logger,
|
||||
cusProducts: fullCus.customer_products,
|
||||
const cusDetails = await getApiCustomer({
|
||||
ctx,
|
||||
fullCus,
|
||||
expand: [],
|
||||
apiVersion,
|
||||
});
|
||||
|
||||
if (fullCus.entity) {
|
||||
await getSingleEntityResponse({
|
||||
org,
|
||||
env,
|
||||
features,
|
||||
ctx,
|
||||
fullCus,
|
||||
entity: fullCus.entity,
|
||||
entityId: fullCus.entity.id,
|
||||
@@ -100,31 +85,20 @@ export const sendSvixThresholdReachedEvent = async ({
|
||||
};
|
||||
|
||||
export const handleAllowanceUsed = async ({
|
||||
db,
|
||||
org,
|
||||
env,
|
||||
features,
|
||||
logger,
|
||||
ctx,
|
||||
cusEnts,
|
||||
newCusEnts,
|
||||
feature,
|
||||
fullCus,
|
||||
}: {
|
||||
db: DrizzleCli;
|
||||
org: Organization;
|
||||
env: AppEnv;
|
||||
ctx: AutumnContext;
|
||||
cusEnts: FullCusEntWithFullCusProduct[];
|
||||
newCusEnts: FullCusEntWithFullCusProduct[];
|
||||
feature: Feature;
|
||||
fullCus: FullCustomer;
|
||||
features: Feature[];
|
||||
logger: any;
|
||||
}) => {
|
||||
const apiVersion = createdAtToVersion({
|
||||
createdAt: org.created_at || undefined,
|
||||
});
|
||||
const { org, env, apiVersion } = ctx;
|
||||
|
||||
// Allowance used...
|
||||
// Make sure overage allowed is false
|
||||
const oldCusEnts = structuredClone(cusEnts);
|
||||
for (const cusEnt of oldCusEnts) {
|
||||
@@ -166,13 +140,9 @@ export const handleAllowanceUsed = async ({
|
||||
|
||||
if (prevCheckResponse.allowed === true && v2CheckResponse.allowed === false) {
|
||||
await sendSvixThresholdReachedEvent({
|
||||
db,
|
||||
org,
|
||||
env,
|
||||
features,
|
||||
logger,
|
||||
feature,
|
||||
ctx,
|
||||
fullCus,
|
||||
feature,
|
||||
thresholdType: "allowance_used",
|
||||
});
|
||||
}
|
||||
@@ -200,6 +170,21 @@ export const handleThresholdReached = async ({
|
||||
features: Feature[];
|
||||
logger: any;
|
||||
}) => {
|
||||
const ctx: AutumnContext = {
|
||||
db,
|
||||
org,
|
||||
env,
|
||||
features,
|
||||
logger,
|
||||
apiVersion: createdAtToVersion({
|
||||
createdAt: org.created_at || undefined,
|
||||
}),
|
||||
id: generateId("local_req"),
|
||||
isPublic: false,
|
||||
authType: AuthType.Unknown,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
|
||||
try {
|
||||
const apiVersion = createdAtToVersion({
|
||||
createdAt: org.created_at || undefined,
|
||||
@@ -236,26 +221,18 @@ export const handleThresholdReached = async ({
|
||||
prevCheckResponse.allowed === true &&
|
||||
v2CheckResponse.allowed === false
|
||||
) {
|
||||
const cusDetails = await getCustomerDetails({
|
||||
db,
|
||||
customer: fullCus,
|
||||
org,
|
||||
env,
|
||||
features,
|
||||
logger,
|
||||
cusProducts: newCusProducts,
|
||||
const cusDetails = await getApiCustomer({
|
||||
ctx,
|
||||
fullCus,
|
||||
expand: [],
|
||||
apiVersion,
|
||||
});
|
||||
|
||||
if (fullCus.entity) {
|
||||
await getSingleEntityResponse({
|
||||
org,
|
||||
env,
|
||||
features,
|
||||
entity: fullCus.entity,
|
||||
fullCus,
|
||||
ctx,
|
||||
entityId: fullCus.entity.id,
|
||||
fullCus,
|
||||
entity: fullCus.entity,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -276,11 +253,7 @@ export const handleThresholdReached = async ({
|
||||
return;
|
||||
}
|
||||
await handleAllowanceUsed({
|
||||
db,
|
||||
org,
|
||||
env,
|
||||
features,
|
||||
logger,
|
||||
ctx,
|
||||
cusEnts,
|
||||
newCusEnts,
|
||||
feature,
|
||||
|
||||
@@ -2,7 +2,8 @@ import { ErrCode } from "@autumn/shared";
|
||||
import chalk from "chalk";
|
||||
import { StatusCodes } from "http-status-codes";
|
||||
import Stripe from "stripe";
|
||||
import { ZodError, type ZodIssue } from "zod/v4";
|
||||
import { ZodError } from "zod/v4";
|
||||
import { formatZodError } from "../errors/formatZodError.js";
|
||||
|
||||
export const isPaymentDeclined = (error: any) => {
|
||||
return (
|
||||
@@ -46,51 +47,6 @@ export default class RecaseError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
export function formatZodError(error: ZodError): string {
|
||||
const formatMessage = (issue: ZodIssue): string => {
|
||||
const path = issue.path.length ? issue.path.join(".") : "input";
|
||||
|
||||
// Clean up common Zod error messages
|
||||
let message = issue.message;
|
||||
|
||||
// Handle common patterns and make them more user-friendly
|
||||
if (
|
||||
message.includes("Too small") &&
|
||||
message.includes("expected string to have >=1 characters")
|
||||
) {
|
||||
message = "cannot be empty";
|
||||
} else if (message.includes("Invalid string: must match pattern")) {
|
||||
// Extract the pattern and make it more readable
|
||||
if (message.includes("/^[a-zA-Z0-9_-]+$/")) {
|
||||
message =
|
||||
"must contain only letters, numbers, underscores, and hyphens";
|
||||
} else {
|
||||
message = "has invalid format";
|
||||
}
|
||||
} else if (message.includes("Invalid input: expected string, received")) {
|
||||
const receivedType = message.split("received ")[1];
|
||||
message = `must be a string (received ${receivedType})`;
|
||||
} else if (message.includes("Invalid input: expected number, received")) {
|
||||
const receivedType = message.split("received ")[1];
|
||||
message = `must be a number (received ${receivedType})`;
|
||||
} else if (message.includes("Invalid input: expected boolean, received")) {
|
||||
const receivedType = message.split("received ")[1];
|
||||
message = `must be a boolean (received ${receivedType})`;
|
||||
}
|
||||
|
||||
return `${path}: ${message}`;
|
||||
};
|
||||
|
||||
const formattedIssues = error.issues.map(formatMessage);
|
||||
|
||||
// If there are multiple issues, format them nicely
|
||||
if (formattedIssues.length === 1) {
|
||||
return formattedIssues[0];
|
||||
} else {
|
||||
return `[Validation Errors] ${formattedIssues.join("; ")}`;
|
||||
}
|
||||
}
|
||||
|
||||
export const handleRequestError = ({
|
||||
error,
|
||||
req,
|
||||
|
||||
@@ -3,11 +3,9 @@ import { StatusCodes } from "http-status-codes";
|
||||
import qs from "qs";
|
||||
import Stripe from "stripe";
|
||||
import { ZodAny, ZodError, ZodObject } from "zod";
|
||||
import { formatZodError } from "@/errors/formatZodError.js";
|
||||
import { withSpan as withSpanTracer } from "@/internal/analytics/tracer/spanUtils.js";
|
||||
import RecaseError, {
|
||||
formatZodError,
|
||||
handleRequestError,
|
||||
} from "./errorUtils.js";
|
||||
import RecaseError, { handleRequestError } from "./errorUtils.js";
|
||||
import type { ExtendedRequest } from "./models/Request.js";
|
||||
|
||||
/**
|
||||
|
||||
64
server/src/utils/workerUtils/createAutumnContext.ts
Normal file
64
server/src/utils/workerUtils/createAutumnContext.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import {
|
||||
type AppEnv,
|
||||
AuthType,
|
||||
createdAtToVersion,
|
||||
ErrCode,
|
||||
RecaseError,
|
||||
} from "@autumn/shared";
|
||||
|
||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import type { Logger } from "@/external/logtail/logtailUtils.js";
|
||||
import type { AutumnContext } from "@/honoUtils/HonoEnv.js";
|
||||
import { OrgService } from "@/internal/orgs/OrgService.js";
|
||||
|
||||
export const createWorkerAutumnContext = async ({
|
||||
db,
|
||||
orgId,
|
||||
env,
|
||||
logger,
|
||||
workerId,
|
||||
}: {
|
||||
db: DrizzleCli;
|
||||
orgId: string;
|
||||
env: AppEnv;
|
||||
logger: Logger;
|
||||
workerId: string;
|
||||
}): Promise<AutumnContext> => {
|
||||
// 1. Get org with features
|
||||
const data = await OrgService.getWithFeatures({
|
||||
db,
|
||||
orgId,
|
||||
env,
|
||||
});
|
||||
|
||||
if (!data) {
|
||||
logger.warn(`Org ${orgId} not found in DB`);
|
||||
throw new RecaseError({
|
||||
message: "Org not found",
|
||||
code: ErrCode.OrgNotFound,
|
||||
statusCode: 500,
|
||||
});
|
||||
}
|
||||
|
||||
const { org, features } = data;
|
||||
|
||||
const apiVersion = createdAtToVersion({
|
||||
createdAt: org.created_at || Date.now(),
|
||||
});
|
||||
|
||||
return {
|
||||
org,
|
||||
env,
|
||||
features,
|
||||
|
||||
db,
|
||||
logger,
|
||||
expand: [],
|
||||
|
||||
id: workerId,
|
||||
isPublic: false,
|
||||
authType: AuthType.Unknown,
|
||||
apiVersion,
|
||||
timestamp: Date.now(),
|
||||
} satisfies AutumnContext;
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { AppEnv } from "@autumn/shared";
|
||||
import type { ExtendedRequest } from "@/utils/models/Request.js";
|
||||
|
||||
export interface HandleCustomerCreatedData {
|
||||
req: Partial<ExtendedRequest>;
|
||||
orgId: string;
|
||||
env: AppEnv;
|
||||
internalCustomerId: string;
|
||||
}
|
||||
@@ -81,7 +81,7 @@ export const expectSubToBeCorrect = async ({
|
||||
existingUsage: 0,
|
||||
withEntity: true,
|
||||
isCheckout: false,
|
||||
apiVersion: ApiVersion.Beta,
|
||||
apiVersion: ApiVersion.V1_Beta,
|
||||
});
|
||||
|
||||
const lineItem: any = res?.lineItem;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { EntityDataSchema } from "@api/common/entityData.js";
|
||||
import { ApiProductSchema } from "@api/products/previousVersions/apiProduct.js";
|
||||
import { z } from "zod/v4";
|
||||
import { CustomerDataSchema } from "../common/customerData.js";
|
||||
import { CoreCusFeatureSchema } from "../customers/cusFeatures/apiCusFeature.js";
|
||||
import { CoreCusFeatureSchema } from "../customers/cusFeatures/previousVersions/apiCusFeatureV3.js";
|
||||
|
||||
// Check Feature Enums
|
||||
export const CheckFeatureScenarioSchema = z.enum([
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { ApiCusReferralSchema } from "@api/customers/components/apiCusReferral.js";
|
||||
import { ApiCusFeatureSchema } from "@api/customers/cusFeatures/apiCusFeature.js";
|
||||
import { ApiBaseEntitySchema } from "@api/entities/apiEntity.js";
|
||||
import { ApiCusRewardsSchema } from "@api/others/apiDiscount.js";
|
||||
import { ApiInvoiceSchema } from "@api/others/apiInvoice.js";
|
||||
import { AppEnv } from "@models/genModels/genEnums.js";
|
||||
import { z } from "zod/v4";
|
||||
import { ApiCusReferralSchema } from "./components/apiCusReferral.js";
|
||||
import { ApiCusUpcomingInvoiceSchema } from "./components/apiCusUpcomingInvoice.js";
|
||||
import { ApiCusProductSchema } from "./cusProducts/apiCusProduct.js";
|
||||
import { ApiCusFeatureSchema } from "./cusFeatures/apiCusFeature.js";
|
||||
import { ApiCusPlanSchema } from "./cusPlans/apiCusPlan.js";
|
||||
|
||||
export const ApiTrialsUsedSchema = z.object({
|
||||
product_id: z.string(),
|
||||
@@ -25,47 +25,20 @@ export const ApiCusExpandSchema = z.object({
|
||||
});
|
||||
|
||||
export const ApiCustomerSchema = z.object({
|
||||
// Internal fields
|
||||
id: z.string().nullable().meta({
|
||||
description: "Your internal ID for the customer",
|
||||
example: "cus_123",
|
||||
}),
|
||||
created_at: z.number().meta({
|
||||
description: "Timestamp of customer creation in milliseconds since epoch",
|
||||
example: 1717000000,
|
||||
}),
|
||||
name: z.string().nullable().meta({
|
||||
description: "Customer’s name",
|
||||
example: "John Doe",
|
||||
}),
|
||||
email: z.string().nullable().meta({
|
||||
description: "Customer’s email address",
|
||||
example: "john@doe.com",
|
||||
}),
|
||||
fingerprint: z.string().nullable().meta({
|
||||
description:
|
||||
"Unique identifier (eg. serial number) to detect duplicate customers and prevent key leaks",
|
||||
example: "fp_9184And92839123hda",
|
||||
}),
|
||||
stripe_id: z.string().nullable().default(null).meta({
|
||||
description: "Stripe customer ID",
|
||||
example: "cus_J8A5c31A8tlpwN",
|
||||
}),
|
||||
env: z.enum(AppEnv).meta({
|
||||
description: "Environment the customer is in",
|
||||
example: "production",
|
||||
}),
|
||||
metadata: z.record(z.any(), z.any()).default({}),
|
||||
products: z.array(ApiCusProductSchema).meta({
|
||||
description: "List of products the customer has access to",
|
||||
example: [],
|
||||
}),
|
||||
features: z.record(z.string(), ApiCusFeatureSchema).meta({
|
||||
description: "List of features the customer has access to",
|
||||
example: {},
|
||||
}),
|
||||
id: z.string(),
|
||||
name: z.string().nullable(),
|
||||
email: z.string().nullable(),
|
||||
created_at: z.number(),
|
||||
fingerprint: z.string().nullable(),
|
||||
stripe_id: z.string().nullable(),
|
||||
env: z.enum(AppEnv),
|
||||
metadata: z.record(z.any(), z.any()),
|
||||
|
||||
plans: z.array(ApiCusPlanSchema),
|
||||
|
||||
features: z.record(z.string(), ApiCusFeatureSchema),
|
||||
...ApiCusExpandSchema.shape,
|
||||
});
|
||||
|
||||
export type ApiCustomer = z.infer<typeof ApiCustomerSchema>;
|
||||
export type ApiCustomerExpand = z.infer<typeof ApiCusExpandSchema>;
|
||||
export type ApiCusExpand = z.infer<typeof ApiCusExpandSchema>;
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
ApiCusFeatureV1Schema,
|
||||
} from "../cusFeatures/previousVersions/apiCusFeatureV1.js";
|
||||
import type { ApiCusFeatureV2 } from "../cusFeatures/previousVersions/apiCusFeatureV2.js";
|
||||
import { transformCusProductV2ToV1 } from "../cusProducts/changes/V0_2_CusProductChange.js";
|
||||
import { transformCusProductV2ToV1 } from "../cusPlans/changes/V0.2_CusProductChange.js";
|
||||
import {
|
||||
type CustomerLegacyData,
|
||||
CustomerLegacyDataSchema,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { ApiCustomerSchema } from "@api/customers/apiCustomer.js";
|
||||
import { ApiFeatureType } from "@api/features/apiFeature.js";
|
||||
import { ApiVersion } from "@api/versionUtils/ApiVersion.js";
|
||||
import {
|
||||
@@ -10,6 +9,7 @@ import {
|
||||
type ApiCusFeatureV2,
|
||||
ApiCusFeatureV2Schema,
|
||||
} from "../cusFeatures/previousVersions/apiCusFeatureV2.js";
|
||||
import { ApiCustomerV3Schema } from "../previousVersions/apiCustomerV3.js";
|
||||
|
||||
/**
|
||||
* V1_2: Features changed from array to object AND merged multi-interval features
|
||||
@@ -24,10 +24,10 @@ import {
|
||||
*/
|
||||
|
||||
// V1_2+ customer schema (features as object with breakdown)
|
||||
const V1_2_CustomerSchema = ApiCustomerSchema;
|
||||
const V1_2_CustomerSchema = ApiCustomerV3Schema;
|
||||
|
||||
// V1_1 customer schema (features as array without breakdown)
|
||||
const V1_1_CustomerSchema = ApiCustomerSchema.extend({
|
||||
const V1_1_CustomerSchema = ApiCustomerV3Schema.extend({
|
||||
features: z.array(ApiCusFeatureV2Schema),
|
||||
});
|
||||
|
||||
|
||||
79
shared/api/customers/changes/V1.2_CustomerChange.ts
Normal file
79
shared/api/customers/changes/V1.2_CustomerChange.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import { ApiVersion } from "@api/versionUtils/ApiVersion.js";
|
||||
import {
|
||||
AffectedResource,
|
||||
defineVersionChange,
|
||||
} from "@api/versionUtils/versionChangeUtils/VersionChange.js";
|
||||
import type { z } from "zod/v4";
|
||||
import { ApiCustomerSchema } from "../apiCustomer.js";
|
||||
import { transformCusFeatureToV3 } from "../cusFeatures/changes/V1.2_CusFeatureChange.js";
|
||||
import type { ApiCusFeatureV3 } from "../cusFeatures/previousVersions/apiCusFeatureV3.js";
|
||||
import { transformCusPlanToProductV3 } from "../cusPlans/changes/V1.2_CusPlanChange.js";
|
||||
import type { ApiCusProductV3 } from "../cusPlans/previousVersions/apiCusProductV3.js";
|
||||
import { CustomerLegacyDataSchema } from "../customerLegacyData.js";
|
||||
import { ApiCustomerV3Schema } from "../previousVersions/apiCustomerV3.js";
|
||||
/**
|
||||
* V1_2_CustomerChange: Transforms customer response TO V1_2 format
|
||||
*
|
||||
* Applied when: targetVersion <= V1_2
|
||||
*
|
||||
* Breaking changes introduced in V2.0:
|
||||
*
|
||||
* 1. Products renamed to Plans:
|
||||
* - V2.0+: "products" field contains ApiCusPlan objects
|
||||
* - V1.2: "products" field contains ApiCusProductV3 objects
|
||||
*
|
||||
* 2. Simplified feature schema:
|
||||
* - V2.0+: Minimal fields with optional feature object
|
||||
* - V1.2: Verbose fields with all metadata
|
||||
*
|
||||
* 3. Features remain as record (no change from V1.1):
|
||||
* - Both V2.0 and V1.2: Record<string, ApiCusFeature>
|
||||
*
|
||||
* Input: ApiCustomer (V2.0+ format)
|
||||
* Output: ApiCustomerV3 (V1.2 format)
|
||||
*/
|
||||
|
||||
export const V1_2_CustomerChange = defineVersionChange({
|
||||
newVersion: ApiVersion.V2,
|
||||
oldVersion: ApiVersion.V1_Beta,
|
||||
description: [
|
||||
"Products renamed to plans in SDK",
|
||||
"Simplified feature and plan schemas",
|
||||
"Added optional nested objects for expanded data",
|
||||
],
|
||||
affectedResources: [AffectedResource.Customer],
|
||||
newSchema: ApiCustomerSchema,
|
||||
oldSchema: ApiCustomerV3Schema,
|
||||
legacyDataSchema: CustomerLegacyDataSchema,
|
||||
|
||||
// Response: V2.0 → V1.2
|
||||
transformResponse: ({ input, legacyData }) => {
|
||||
// Step 1: Transform plans V2.0 → V1.2 (products)
|
||||
const v3CusProducts: ApiCusProductV3[] = input.plans.map((plan) =>
|
||||
transformCusPlanToProductV3({
|
||||
input: plan,
|
||||
legacyData: legacyData?.cusProductLegacyData[plan.plan_id],
|
||||
}),
|
||||
);
|
||||
|
||||
// Step 2: Transform features V2.0 → V1.2
|
||||
const v3_features: Record<string, ApiCusFeatureV3> = {};
|
||||
for (const [featureId, feature] of Object.entries(input.features)) {
|
||||
v3_features[featureId] = transformCusFeatureToV3({ input: feature });
|
||||
}
|
||||
|
||||
// Step 3: Return V1.2 customer format
|
||||
return {
|
||||
id: input.id,
|
||||
name: input.name,
|
||||
email: input.email,
|
||||
created_at: input.created_at,
|
||||
fingerprint: input.fingerprint,
|
||||
stripe_id: input.stripe_id,
|
||||
env: input.env,
|
||||
metadata: input.metadata,
|
||||
products: v3CusProducts,
|
||||
features: v3_features,
|
||||
} satisfies z.infer<typeof ApiCustomerV3Schema>;
|
||||
},
|
||||
});
|
||||
@@ -1,138 +1,56 @@
|
||||
import { ApiFeatureType } from "@api/features/apiFeature.js";
|
||||
import { EntInterval } from "@models/productModels/entModels/entEnums.js";
|
||||
import { ApiFeatureSchema } from "@api/features/apiFeature.js";
|
||||
import { ResetInterval } from "@api/products/planEnums.js";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const ApiCusRolloverSchema = z.object({
|
||||
balance: z.number().meta({
|
||||
description: "The remaining balance amount that has rolled over",
|
||||
example: 100,
|
||||
}),
|
||||
expires_at: z.number().meta({
|
||||
description: "Timestamp when the rollover balance expires",
|
||||
example: 1759247877000,
|
||||
}),
|
||||
balance: z.number(),
|
||||
expires_at: z.number(),
|
||||
});
|
||||
|
||||
// Version 3 of cus feature response
|
||||
export const ApiCusFeatureBreakdownSchema = z.object({
|
||||
interval: z.enum(EntInterval).meta({
|
||||
description: "The billing interval for this feature breakdown",
|
||||
example: "month",
|
||||
}),
|
||||
interval_count: z.number().nullish().meta({
|
||||
description: "The number of intervals between resets",
|
||||
example: 1,
|
||||
}),
|
||||
balance: z.number().nullish().meta({
|
||||
description: "The remaining balance for this interval",
|
||||
example: 500,
|
||||
}),
|
||||
usage: z.number().nullish().meta({
|
||||
description: "The current usage amount",
|
||||
example: 250,
|
||||
}),
|
||||
included_usage: z.number().nullish().meta({
|
||||
description: "The amount of usage included in this interval",
|
||||
example: 1000,
|
||||
}),
|
||||
next_reset_at: z.number().nullish().meta({
|
||||
description: "Timestamp when the usage resets",
|
||||
example: 1759247877000,
|
||||
}),
|
||||
usage_limit: z.number().nullish().meta({
|
||||
description: "The maximum usage allowed",
|
||||
example: 1000,
|
||||
}),
|
||||
rollovers: z.array(ApiCusRolloverSchema).nullish().meta({
|
||||
description: "Array of rollover balances from previous periods",
|
||||
example: [{ balance: 100, expires_at: 1759247877000 }],
|
||||
}),
|
||||
reset: z
|
||||
.object({
|
||||
interval: z.enum(ResetInterval),
|
||||
interval_count: z.number().optional(),
|
||||
when_enabled: z.boolean().optional(),
|
||||
})
|
||||
.optional(),
|
||||
|
||||
starting_balance: z.number(),
|
||||
balance: z.number(),
|
||||
usage: z.number(),
|
||||
resets_at: z.number().nullable(),
|
||||
|
||||
max_purchase: z.number().optional(),
|
||||
});
|
||||
|
||||
export const CoreCusFeatureSchema = z.object({
|
||||
interval: z.enum(EntInterval).or(z.literal("multiple")).nullish().meta({
|
||||
description: "The billing interval or 'multiple' if the feature has multiple intervals",
|
||||
example: "month",
|
||||
}),
|
||||
interval_count: z.number().nullish().meta({
|
||||
description: "The number of intervals between resets",
|
||||
example: 1,
|
||||
}),
|
||||
unlimited: z.boolean().nullish().meta({
|
||||
description: "Whether the feature has unlimited usage",
|
||||
example: false,
|
||||
}),
|
||||
balance: z.number().nullish().meta({
|
||||
description: "The remaining balance for this feature",
|
||||
example: 500,
|
||||
}),
|
||||
usage: z.number().nullish().meta({
|
||||
description: "The current usage amount",
|
||||
example: 250,
|
||||
}),
|
||||
included_usage: z.number().nullish().meta({
|
||||
description: "The amount of usage included",
|
||||
example: 1000,
|
||||
}),
|
||||
next_reset_at: z.number().nullish().meta({
|
||||
description: "Timestamp when the usage resets",
|
||||
example: 1759247877000,
|
||||
}),
|
||||
overage_allowed: z.boolean().nullish().meta({
|
||||
description: "Whether overage usage beyond the limit is allowed",
|
||||
example: true,
|
||||
}),
|
||||
export const ApiCusFeatureSchema = z.object({
|
||||
feature_id: z.string(),
|
||||
feature: ApiFeatureSchema.optional(),
|
||||
|
||||
breakdown: z.array(ApiCusFeatureBreakdownSchema).nullish().meta({
|
||||
description: "Detailed breakdown by interval for features with multiple intervals",
|
||||
example: [{ interval: "month", interval_count: 1, balance: 500, usage: 250 }],
|
||||
}),
|
||||
credit_schema: z
|
||||
.array(
|
||||
z.object({
|
||||
feature_id: z.string().meta({
|
||||
description: "The ID of the feature that credits are applied to",
|
||||
example: "feature_123",
|
||||
}),
|
||||
credit_amount: z.number().meta({
|
||||
description: "The amount of credits applied per usage",
|
||||
example: 10,
|
||||
}),
|
||||
}),
|
||||
)
|
||||
.nullish()
|
||||
.meta({
|
||||
description: "Credit conversion schema for credit system features",
|
||||
example: [{ feature_id: "feature_123", credit_amount: 10 }],
|
||||
}),
|
||||
unlimited: z.boolean(),
|
||||
|
||||
usage_limit: z.number().nullish().meta({
|
||||
description: "The maximum usage allowed",
|
||||
example: 1000,
|
||||
}),
|
||||
rollovers: z.array(ApiCusRolloverSchema).nullish().meta({
|
||||
description: "Array of rollover balances from previous periods",
|
||||
example: [{ balance: 100, expires_at: 1759247877000 }],
|
||||
}),
|
||||
granted_balance: z.number(), // 0
|
||||
purchased_balance: z.number(), // 3
|
||||
current_balance: z.number(), // 1
|
||||
usage: z.number(), // 2
|
||||
|
||||
resets_at: z.number().nullable(),
|
||||
|
||||
max_purchase: z.number().optional(),
|
||||
pay_per_use: z.boolean().optional(),
|
||||
|
||||
reset: z
|
||||
.object({
|
||||
interval: z.enum(ResetInterval).nullable(),
|
||||
interval_count: z.number().optional(),
|
||||
})
|
||||
.optional(),
|
||||
|
||||
breakdown: z.array(ApiCusFeatureBreakdownSchema).nullish(),
|
||||
rollovers: z.array(ApiCusRolloverSchema).nullish(),
|
||||
});
|
||||
|
||||
export const ApiCusFeatureSchema = z
|
||||
.object({
|
||||
id: z.string().meta({
|
||||
description: "The unique identifier of the feature",
|
||||
example: "feature_123",
|
||||
}),
|
||||
type: z.enum(ApiFeatureType).meta({
|
||||
description: "The type of the feature",
|
||||
example: "single_use",
|
||||
}),
|
||||
name: z.string().nullish().meta({
|
||||
description: "The name of the feature",
|
||||
example: "API Calls",
|
||||
}),
|
||||
})
|
||||
.extend(CoreCusFeatureSchema.shape);
|
||||
|
||||
export type ApiCusFeature = z.infer<typeof ApiCusFeatureSchema>;
|
||||
export type ApiCusRollover = z.infer<typeof ApiCusRolloverSchema>;
|
||||
export type ApiCusFeatureBreakdown = z.infer<
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
import { ApiFeatureType } from "@api/features/apiFeature.js";
|
||||
import { ApiVersion } from "@api/versionUtils/ApiVersion.js";
|
||||
import {
|
||||
AffectedResource,
|
||||
defineVersionChange,
|
||||
} from "@api/versionUtils/versionChangeUtils/VersionChange.js";
|
||||
import { resetIntvToEntIntv } from "@utils/planFeatureUtils/planFeatureIntervals.js";
|
||||
import { nullish } from "@utils/utils.js";
|
||||
import { Decimal } from "decimal.js";
|
||||
import type { z } from "zod/v4";
|
||||
import { ApiCusFeatureSchema } from "../apiCusFeature.js";
|
||||
import {
|
||||
type ApiCusFeatureV3Breakdown,
|
||||
ApiCusFeatureV3Schema,
|
||||
} from "../previousVersions/apiCusFeatureV3.js";
|
||||
// overage_allowed: z.boolean().nullish().meta({
|
||||
// description: "Whether overage usage beyond the limit is allowed",
|
||||
// example: true,
|
||||
// }),
|
||||
|
||||
/**
|
||||
* Transform feature from V2.0 format to V1.2 format
|
||||
* Exported so it can be reused in other transformations (e.g., V1_2_CustomerChange)
|
||||
*/
|
||||
export function transformCusFeatureToV3({
|
||||
input,
|
||||
}: {
|
||||
input: z.infer<typeof ApiCusFeatureSchema>;
|
||||
}): z.infer<typeof ApiCusFeatureV3Schema> {
|
||||
const toUsageLimit = ({
|
||||
maxPurchase,
|
||||
startingBalance,
|
||||
}: {
|
||||
maxPurchase?: number;
|
||||
startingBalance: number;
|
||||
}) => {
|
||||
return maxPurchase && startingBalance
|
||||
? new Decimal(maxPurchase).add(startingBalance).toNumber()
|
||||
: undefined;
|
||||
};
|
||||
|
||||
const toV3Type = ({ type }: { type: ApiFeatureType }) => {
|
||||
if (type === ApiFeatureType.Boolean) {
|
||||
return ApiFeatureType.Static;
|
||||
} else return type;
|
||||
};
|
||||
|
||||
const itemInterval =
|
||||
input.reset_interval === null
|
||||
? "multiple"
|
||||
: resetIntvToEntIntv({ resetIntv: input.reset_interval });
|
||||
|
||||
const usageLimit = toUsageLimit({
|
||||
maxPurchase: input.max_purchase,
|
||||
startingBalance: input.starting_balance,
|
||||
});
|
||||
|
||||
const overageAllowed =
|
||||
Boolean(input.pay_per_use) && nullish(input.max_purchase);
|
||||
|
||||
let newBreakdown: ApiCusFeatureV3Breakdown[] | undefined;
|
||||
if (input.breakdown && input.breakdown.length > 0) {
|
||||
newBreakdown = input.breakdown.map((breakdown) => {
|
||||
const interval = resetIntvToEntIntv({
|
||||
resetIntv: breakdown.reset_interval,
|
||||
});
|
||||
|
||||
const usageLimit = toUsageLimit({
|
||||
maxPurchase: breakdown.max_purchase,
|
||||
startingBalance: breakdown.starting_balance,
|
||||
});
|
||||
|
||||
return {
|
||||
interval,
|
||||
interval_count: breakdown.reset_interval_count || 1,
|
||||
|
||||
balance: breakdown.balance,
|
||||
usage: breakdown.usage,
|
||||
included_usage: breakdown.starting_balance,
|
||||
next_reset_at: breakdown.resets_at,
|
||||
usage_limit: usageLimit,
|
||||
} satisfies ApiCusFeatureV3Breakdown;
|
||||
});
|
||||
}
|
||||
|
||||
const v3Type = toV3Type({
|
||||
type: input.feature?.type ?? ApiFeatureType.SingleUsage,
|
||||
});
|
||||
|
||||
const omitInterval = v3Type === ApiFeatureType.Static || input.unlimited;
|
||||
|
||||
return {
|
||||
id: input.feature_id,
|
||||
type: v3Type,
|
||||
|
||||
name: input.feature?.name ?? null,
|
||||
unlimited: input.unlimited,
|
||||
|
||||
included_usage: input.starting_balance,
|
||||
balance: input.balance,
|
||||
usage: input.usage,
|
||||
next_reset_at: input.resets_at,
|
||||
|
||||
interval: omitInterval ? undefined : itemInterval,
|
||||
interval_count: omitInterval
|
||||
? undefined
|
||||
: itemInterval === "multiple"
|
||||
? null
|
||||
: input.reset_interval_count || 1,
|
||||
|
||||
overage_allowed: overageAllowed,
|
||||
|
||||
credit_schema: input.feature?.credit_schema
|
||||
? input.feature.credit_schema.map((credit) => ({
|
||||
feature_id: credit.metered_feature_id,
|
||||
credit_amount: credit.credit_cost,
|
||||
}))
|
||||
: undefined,
|
||||
|
||||
breakdown: newBreakdown,
|
||||
usage_limit: usageLimit,
|
||||
rollovers: input.rollovers,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* V1_2_CusFeatureChange: Transforms customer feature response TO V1_2 format
|
||||
*
|
||||
* Applied when: targetVersion <= V1_2
|
||||
*
|
||||
* Breaking changes introduced in V2.0:
|
||||
*
|
||||
* 1. Simplified schema with minimal required fields
|
||||
* 2. Optional feature object for expanded data
|
||||
* 3. Removed verbose metadata and display fields
|
||||
*
|
||||
* Input: ApiCusFeature (V2.0+ format)
|
||||
* Output: ApiCusFeatureV3 (V1.2 format)
|
||||
*/
|
||||
export const V1_2_CusFeatureChange = defineVersionChange({
|
||||
newVersion: ApiVersion.V2,
|
||||
oldVersion: ApiVersion.V1_2,
|
||||
description: [
|
||||
"Simplified customer feature schema",
|
||||
"Added optional feature object",
|
||||
"Removed verbose fields",
|
||||
],
|
||||
affectedResources: [AffectedResource.Customer],
|
||||
newSchema: ApiCusFeatureSchema,
|
||||
oldSchema: ApiCusFeatureV3Schema,
|
||||
|
||||
transformResponse: transformCusFeatureToV3,
|
||||
});
|
||||
@@ -0,0 +1,155 @@
|
||||
import { ApiFeatureType } from "@api/features/apiFeature.js";
|
||||
import { EntInterval } from "@models/productModels/entModels/entEnums.js";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const ApiCusFeatureV3RolloverSchema = z.object({
|
||||
balance: z.number().meta({
|
||||
description: "The remaining balance amount that has rolled over",
|
||||
example: 100,
|
||||
}),
|
||||
expires_at: z.number().meta({
|
||||
description: "Timestamp when the rollover balance expires",
|
||||
example: 1759247877000,
|
||||
}),
|
||||
});
|
||||
|
||||
// Version 3 of cus feature response
|
||||
export const ApiCusFeatureV3BreakdownSchema = z.object({
|
||||
interval: z.enum(EntInterval).meta({
|
||||
description: "The billing interval for this feature breakdown",
|
||||
example: "month",
|
||||
}),
|
||||
interval_count: z.number().nullish().meta({
|
||||
description: "The number of intervals between resets",
|
||||
example: 1,
|
||||
}),
|
||||
balance: z.number().nullish().meta({
|
||||
description: "The remaining balance for this interval",
|
||||
example: 500,
|
||||
}),
|
||||
usage: z.number().nullish().meta({
|
||||
description: "The current usage amount",
|
||||
example: 250,
|
||||
}),
|
||||
included_usage: z.number().nullish().meta({
|
||||
description: "The amount of usage included in this interval",
|
||||
example: 1000,
|
||||
}),
|
||||
next_reset_at: z.number().nullish().meta({
|
||||
description: "Timestamp when the usage resets",
|
||||
example: 1759247877000,
|
||||
}),
|
||||
usage_limit: z.number().nullish().meta({
|
||||
description: "The maximum usage allowed",
|
||||
example: 1000,
|
||||
}),
|
||||
rollovers: z
|
||||
.array(ApiCusFeatureV3RolloverSchema)
|
||||
.nullish()
|
||||
.meta({
|
||||
description: "Array of rollover balances from previous periods",
|
||||
example: [{ balance: 100, expires_at: 1759247877000 }],
|
||||
}),
|
||||
});
|
||||
|
||||
export const CoreCusFeatureSchema = z.object({
|
||||
interval: z.enum(EntInterval).or(z.literal("multiple")).nullish().meta({
|
||||
description:
|
||||
"The billing interval or 'multiple' if the feature has multiple intervals",
|
||||
example: "month",
|
||||
}),
|
||||
interval_count: z.number().nullish().meta({
|
||||
description: "The number of intervals between resets",
|
||||
example: 1,
|
||||
}),
|
||||
unlimited: z.boolean().nullish().meta({
|
||||
description: "Whether the feature has unlimited usage",
|
||||
example: false,
|
||||
}),
|
||||
balance: z.number().nullish().meta({
|
||||
description: "The remaining balance for this feature",
|
||||
example: 500,
|
||||
}),
|
||||
usage: z.number().nullish().meta({
|
||||
description: "The current usage amount",
|
||||
example: 250,
|
||||
}),
|
||||
included_usage: z.number().nullish().meta({
|
||||
description: "The amount of usage included",
|
||||
example: 1000,
|
||||
}),
|
||||
next_reset_at: z.number().nullish().meta({
|
||||
description: "Timestamp when the usage resets",
|
||||
example: 1759247877000,
|
||||
}),
|
||||
overage_allowed: z.boolean().nullish().meta({
|
||||
description: "Whether overage usage beyond the limit is allowed",
|
||||
example: true,
|
||||
}),
|
||||
|
||||
breakdown: z
|
||||
.array(ApiCusFeatureV3BreakdownSchema)
|
||||
.nullish()
|
||||
.meta({
|
||||
description:
|
||||
"Detailed breakdown by interval for features with multiple intervals",
|
||||
example: [
|
||||
{ interval: "month", interval_count: 1, balance: 500, usage: 250 },
|
||||
],
|
||||
}),
|
||||
credit_schema: z
|
||||
.array(
|
||||
z.object({
|
||||
feature_id: z.string().meta({
|
||||
description: "The ID of the feature that credits are applied to",
|
||||
example: "feature_123",
|
||||
}),
|
||||
credit_amount: z.number().meta({
|
||||
description: "The amount of credits applied per usage",
|
||||
example: 10,
|
||||
}),
|
||||
}),
|
||||
)
|
||||
.nullish()
|
||||
.meta({
|
||||
description: "Credit conversion schema for credit system features",
|
||||
example: [{ feature_id: "feature_123", credit_amount: 10 }],
|
||||
}),
|
||||
|
||||
usage_limit: z.number().nullish().meta({
|
||||
description: "The maximum usage allowed",
|
||||
example: 1000,
|
||||
}),
|
||||
rollovers: z
|
||||
.array(ApiCusFeatureV3RolloverSchema)
|
||||
.nullish()
|
||||
.meta({
|
||||
description: "Array of rollover balances from previous periods",
|
||||
example: [{ balance: 100, expires_at: 1759247877000 }],
|
||||
}),
|
||||
});
|
||||
|
||||
export const ApiCusFeatureV3Schema = z
|
||||
.object({
|
||||
id: z.string().meta({
|
||||
description: "The unique identifier of the feature",
|
||||
example: "feature_123",
|
||||
}),
|
||||
type: z.enum(ApiFeatureType).meta({
|
||||
description: "The type of the feature",
|
||||
example: "single_use",
|
||||
}),
|
||||
name: z.string().nullish().meta({
|
||||
description: "The name of the feature",
|
||||
example: "API Calls",
|
||||
}),
|
||||
})
|
||||
.extend(CoreCusFeatureSchema.shape);
|
||||
|
||||
export type ApiCusFeatureV3 = z.infer<typeof ApiCusFeatureV3Schema>;
|
||||
export type ApiCusFeatureV3Rollover = z.infer<
|
||||
typeof ApiCusFeatureV3RolloverSchema
|
||||
>;
|
||||
export type ApiCusFeatureV3Breakdown = z.infer<
|
||||
typeof ApiCusFeatureV3BreakdownSchema
|
||||
>;
|
||||
23
shared/api/customers/cusPlans/apiCusPlan.ts
Normal file
23
shared/api/customers/cusPlans/apiCusPlan.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { ApiPlanSchema } from "@api/products/apiPlan.js";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const ApiCusPlanSchema = z.object({
|
||||
plan: ApiPlanSchema.optional(),
|
||||
|
||||
plan_id: z.string(),
|
||||
add_on: z.boolean(),
|
||||
|
||||
// Flags / timestamps
|
||||
status: z.enum(["active", "scheduled", "expired"]),
|
||||
past_due: z.boolean(),
|
||||
canceled_at: z.number().nullable(),
|
||||
expires_at: z.number().nullable(),
|
||||
trial_ends_at: z.number().nullable(),
|
||||
|
||||
started_at: z.number(),
|
||||
current_period_start: z.number().nullable(),
|
||||
current_period_end: z.number().nullable(),
|
||||
quantity: z.number(),
|
||||
});
|
||||
|
||||
export type ApiCusPlan = z.infer<typeof ApiCusPlanSchema>;
|
||||
@@ -10,8 +10,11 @@ import {
|
||||
isPriceItem,
|
||||
} from "@utils/productV2Utils/productItemUtils/getItemType.js";
|
||||
import { notNullish } from "@utils/utils.js";
|
||||
import { z } from "zod/v4";
|
||||
import type { CusProductLegacyData } from "../cusProductLegacyData.js";
|
||||
import type { z } from "zod/v4";
|
||||
import {
|
||||
type CusProductLegacyData,
|
||||
CusProductLegacyDataSchema,
|
||||
} from "../cusProductLegacyData.js";
|
||||
import { ApiCusProductV1Schema } from "../previousVersions/apiCusProductV1.js";
|
||||
import { ApiCusProductV2Schema } from "../previousVersions/apiCusProductV2.js";
|
||||
|
||||
@@ -132,10 +135,6 @@ export function transformCusProductV2ToV1({
|
||||
return v1CusProduct;
|
||||
}
|
||||
|
||||
const CusProductLegacyDataSchema = z.object({
|
||||
subscription_id: z.string().optional(),
|
||||
});
|
||||
|
||||
export const V0_2_CusProductChange = defineVersionChange({
|
||||
newVersion: ApiVersion.V1_1, // Breaking change introduced in V1_1
|
||||
oldVersion: ApiVersion.V0_2, // Applied when targetVersion <= V0_2
|
||||
112
shared/api/customers/cusPlans/changes/V1.2_CusPlanChange.ts
Normal file
112
shared/api/customers/cusPlans/changes/V1.2_CusPlanChange.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
import type { ApiProductItem } from "@api/models.js";
|
||||
import { ApiVersion } from "@api/versionUtils/ApiVersion.js";
|
||||
import {
|
||||
AffectedResource,
|
||||
defineVersionChange,
|
||||
} from "@api/versionUtils/versionChangeUtils/VersionChange.js";
|
||||
import { CusProductStatus } from "@models/cusProductModels/cusProductEnums.js";
|
||||
import { convertPlanToItems } from "@utils/planFeatureUtils/planToItems.js";
|
||||
import { getProductItemResponse } from "@utils/productV2Utils/productItemUtils/getProductItemRes.js";
|
||||
import type { z } from "zod/v4";
|
||||
import { type ApiCusPlan, ApiCusPlanSchema } from "../apiCusPlan.js";
|
||||
import {
|
||||
type CusProductLegacyData,
|
||||
CusProductLegacyDataSchema,
|
||||
} from "../cusProductLegacyData.js";
|
||||
import { ApiCusProductV3Schema } from "../previousVersions/apiCusProductV3.js";
|
||||
|
||||
/**
|
||||
* Transform plan from V2.0 format to V1.2 product format
|
||||
* Exported so it can be reused in other transformations (e.g., V1_2_CustomerChange)
|
||||
*/
|
||||
export function transformCusPlanToProductV3({
|
||||
input,
|
||||
legacyData,
|
||||
}: {
|
||||
input: z.infer<typeof ApiCusPlanSchema>;
|
||||
legacyData?: CusProductLegacyData;
|
||||
}): z.infer<typeof ApiCusProductV3Schema> {
|
||||
const cusPlanToCusProductV3Status = (plan: ApiCusPlan) => {
|
||||
if (plan.status === CusProductStatus.Active) {
|
||||
if (plan.past_due) {
|
||||
return "past_due";
|
||||
}
|
||||
// trialing
|
||||
else if (plan.trial_ends_at && plan.trial_ends_at < Date.now()) {
|
||||
return "trialing";
|
||||
}
|
||||
return "active";
|
||||
}
|
||||
return plan.status;
|
||||
};
|
||||
|
||||
let items: ApiProductItem[] | null = null;
|
||||
if (input.plan && legacyData) {
|
||||
const productItems = convertPlanToItems({ plan: input.plan });
|
||||
|
||||
const itemResponses = productItems.map((item) =>
|
||||
getProductItemResponse({
|
||||
item,
|
||||
features: legacyData.features,
|
||||
options: legacyData.options,
|
||||
}),
|
||||
);
|
||||
|
||||
items = itemResponses;
|
||||
}
|
||||
|
||||
return {
|
||||
id: input.plan_id,
|
||||
|
||||
// Plan properties...
|
||||
name: input.plan?.name ?? null,
|
||||
group: input.plan?.group ?? null,
|
||||
is_default: input.plan?.default ?? false,
|
||||
is_add_on: input.add_on,
|
||||
version: input.plan?.version ?? null,
|
||||
items: items, // Map from plan to product v2 items...
|
||||
|
||||
status: cusPlanToCusProductV3Status(input),
|
||||
|
||||
canceled_at: input.canceled_at ?? null,
|
||||
started_at: input.started_at,
|
||||
current_period_start: input.current_period_start,
|
||||
current_period_end: input.current_period_end,
|
||||
|
||||
entity_id: null,
|
||||
|
||||
quantity: input.quantity,
|
||||
} satisfies z.infer<typeof ApiCusProductV3Schema>;
|
||||
}
|
||||
|
||||
/**
|
||||
* V1_2_CusPlanChange: Transforms customer plan response TO V1_2 format
|
||||
*
|
||||
* Applied when: targetVersion <= V1_2
|
||||
*
|
||||
* Breaking changes introduced in V2.0:
|
||||
*
|
||||
* 1. Renamed "products" to "plans" in SDK
|
||||
* 2. Simplified status enum to only "active"
|
||||
* 3. Added trial_ends_at field
|
||||
* 4. Optional product object for expanded plan data
|
||||
*
|
||||
* Input: ApiCusPlan (V2.0+ format with "product" field)
|
||||
* Output: ApiCusProductV3 (V1.2 format with verbose fields)
|
||||
*/
|
||||
export const V1_2_CusPlanChange = defineVersionChange({
|
||||
newVersion: ApiVersion.V2,
|
||||
oldVersion: ApiVersion.V1_2,
|
||||
description: [
|
||||
"Renamed products to plans",
|
||||
"Simplified status enum",
|
||||
"Added trial_ends_at field",
|
||||
],
|
||||
affectedResources: [AffectedResource.Customer],
|
||||
newSchema: ApiCusPlanSchema,
|
||||
oldSchema: ApiCusProductV3Schema,
|
||||
|
||||
legacyDataSchema: CusProductLegacyDataSchema,
|
||||
|
||||
transformResponse: transformCusPlanToProductV3,
|
||||
});
|
||||
11
shared/api/customers/cusPlans/cusProductLegacyData.ts
Normal file
11
shared/api/customers/cusPlans/cusProductLegacyData.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { FeatureOptionsSchema } from "@models/cusProductModels/cusProductModels.js";
|
||||
import { FeatureSchema } from "@models/featureModels/featureModels.js";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const CusProductLegacyDataSchema = z.object({
|
||||
subscription_id: z.string().optional(),
|
||||
options: z.array(FeatureOptionsSchema),
|
||||
features: z.array(FeatureSchema),
|
||||
});
|
||||
|
||||
export type CusProductLegacyData = z.infer<typeof CusProductLegacyDataSchema>;
|
||||
@@ -0,0 +1,96 @@
|
||||
import { ApiProductItemSchema } from "@api/products/planFeature/previousVersions/apiProductItem.js";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const ApiCusProductV3Schema = z.object({
|
||||
id: z.string().meta({
|
||||
description: "The unique identifier for the product",
|
||||
example: "pro_plan",
|
||||
}),
|
||||
name: z.string().nullable().meta({
|
||||
description: "The name of the product",
|
||||
example: "Pro Plan",
|
||||
}),
|
||||
group: z.string().nullable().meta({
|
||||
description: "The group the product belongs to",
|
||||
example: "product_set_1",
|
||||
}),
|
||||
status: z
|
||||
.enum(["active", "expired", "scheduled", "trialing", "past_due"])
|
||||
.meta({
|
||||
description: "Current status of the product for this customer",
|
||||
example: "active",
|
||||
}),
|
||||
canceled_at: z.number().nullish().meta({
|
||||
description: "Timestamp when the product was canceled for the customer",
|
||||
example: 1717000000,
|
||||
}),
|
||||
started_at: z.number().meta({
|
||||
description: "Timestamp when the customer started this product",
|
||||
example: 1700000000000,
|
||||
}),
|
||||
is_default: z.boolean().meta({
|
||||
description: "Whether this product is the default for the customer",
|
||||
example: true,
|
||||
}),
|
||||
is_add_on: z.boolean().meta({
|
||||
description: "Whether the product is an add-on",
|
||||
example: false,
|
||||
}),
|
||||
version: z.number().nullish().meta({
|
||||
description: "Version of the product",
|
||||
example: 1,
|
||||
}),
|
||||
stripe_subscription_ids: z
|
||||
.array(z.string())
|
||||
.nullish()
|
||||
.meta({
|
||||
description:
|
||||
"List of Stripe subscription IDs associated with this product, if any",
|
||||
example: ["sub_1Nc0JzBAbcxyz", "sub_1Nc0xyBAnopq"],
|
||||
}),
|
||||
current_period_start: z.number().nullish().meta({
|
||||
description: "Start of the current billing period",
|
||||
example: 1717000000,
|
||||
}),
|
||||
current_period_end: z.number().nullish().meta({
|
||||
description: "End of the current billing period",
|
||||
example: 1719600000,
|
||||
}),
|
||||
entity_id: z.string().nullish().meta({
|
||||
description:
|
||||
"ID of the entity this customer product is attached to, if applicable",
|
||||
example: "entity_1234abcd",
|
||||
}),
|
||||
items: z
|
||||
.array(ApiProductItemSchema)
|
||||
.nullish()
|
||||
.meta({
|
||||
description: "Array of product items defining the features and pricing",
|
||||
example: [
|
||||
{
|
||||
feature_id: "<string>",
|
||||
feature_type: "single_use",
|
||||
included_usage: 123,
|
||||
interval: "month",
|
||||
usage_model: "prepaid",
|
||||
price: 123,
|
||||
billing_units: 1000,
|
||||
entity_feature_id: "<string>",
|
||||
reset_usage_when_enabled: true,
|
||||
tiers: [
|
||||
{
|
||||
to: 100,
|
||||
amount: 10,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
quantity: z.number().optional().meta({
|
||||
description:
|
||||
"The number of units of this product held by the customer, if applicable",
|
||||
example: 1,
|
||||
}),
|
||||
});
|
||||
|
||||
export type ApiCusProductV3 = z.infer<typeof ApiCusProductV3Schema>;
|
||||
@@ -1,97 +0,0 @@
|
||||
import { ApiProductItemSchema } from "@api/products/planFeature/previousVersions/apiProductItem.js";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const ApiCusProductSchema = z
|
||||
.object({
|
||||
id: z.string().meta({
|
||||
description: "The unique identifier for the product",
|
||||
example: "pro_plan",
|
||||
}),
|
||||
name: z.string().nullable().meta({
|
||||
description: "The name of the product",
|
||||
example: "Pro Plan",
|
||||
}),
|
||||
group: z.string().nullable().meta({
|
||||
description: "The group the product belongs to",
|
||||
example: "product_set_1",
|
||||
}),
|
||||
status: z
|
||||
.enum(["active", "expired", "scheduled", "trialing", "past_due"])
|
||||
.meta({
|
||||
description: "Current status of the product for this customer",
|
||||
example: "active",
|
||||
}),
|
||||
canceled_at: z.number().nullish().meta({
|
||||
description: "Timestamp when the product was canceled for the customer",
|
||||
example: 1717000000,
|
||||
}),
|
||||
started_at: z.number().meta({
|
||||
description: "Timestamp when the customer started this product",
|
||||
example: 1700000000000,
|
||||
}),
|
||||
is_default: z.boolean().meta({
|
||||
description: "Whether this product is the default for the customer",
|
||||
example: true,
|
||||
}),
|
||||
is_add_on: z.boolean().meta({
|
||||
description: "Whether the product is an add-on",
|
||||
example: false,
|
||||
}),
|
||||
version: z.number().nullish().meta({
|
||||
description: "Version of the product",
|
||||
example: 1,
|
||||
}),
|
||||
stripe_subscription_ids: z
|
||||
.array(z.string())
|
||||
.nullish()
|
||||
.meta({
|
||||
description:
|
||||
"List of Stripe subscription IDs associated with this product, if any",
|
||||
example: ["sub_1Nc0JzBAbcxyz", "sub_1Nc0xyBAnopq"],
|
||||
}),
|
||||
current_period_start: z.number().nullish().meta({
|
||||
description: "Start of the current billing period",
|
||||
example: 1717000000,
|
||||
}),
|
||||
current_period_end: z.number().nullish().meta({
|
||||
description: "End of the current billing period",
|
||||
example: 1719600000,
|
||||
}),
|
||||
entity_id: z.string().nullish().meta({
|
||||
description:
|
||||
"ID of the entity this customer product is attached to, if applicable",
|
||||
example: "entity_1234abcd",
|
||||
}),
|
||||
items: z
|
||||
.array(ApiProductItemSchema)
|
||||
.nullish()
|
||||
.meta({
|
||||
description: "Array of product items defining the features and pricing",
|
||||
example: [
|
||||
{
|
||||
feature_id: "<string>",
|
||||
feature_type: "single_use",
|
||||
included_usage: 123,
|
||||
interval: "month",
|
||||
usage_model: "prepaid",
|
||||
price: 123,
|
||||
billing_units: 1000,
|
||||
entity_feature_id: "<string>",
|
||||
reset_usage_when_enabled: true,
|
||||
tiers: [
|
||||
{
|
||||
to: 100,
|
||||
amount: 10,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
quantity: z.number().optional().meta({
|
||||
description:
|
||||
"The number of units of this product held by the customer, if applicable",
|
||||
example: 1,
|
||||
}),
|
||||
});
|
||||
|
||||
export type ApiCusProduct = z.infer<typeof ApiCusProductSchema>;
|
||||
@@ -1,7 +0,0 @@
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const CusProductLegacyDataSchema = z.object({
|
||||
subscription_id: z.string().optional(),
|
||||
});
|
||||
|
||||
export type CusProductLegacyData = z.infer<typeof CusProductLegacyDataSchema>;
|
||||
@@ -1,5 +1,5 @@
|
||||
import { z } from "zod/v4";
|
||||
import { CusProductLegacyDataSchema } from "./cusProducts/cusProductLegacyData.js";
|
||||
import { CusProductLegacyDataSchema } from "./cusPlans/cusProductLegacyData.js";
|
||||
|
||||
export const CustomerLegacyDataSchema = z.object({
|
||||
cusProductLegacyData: z.record(z.string(), CusProductLegacyDataSchema),
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { ApiCusFeatureV0Schema } from "@api/customers/cusFeatures/previousVersions/apiCusFeatureV0.js";
|
||||
import { ApiCusProductV0Schema } from "@api/customers/cusProducts/previousVersions/apiCusProductV0.js";
|
||||
import { ApiCusProductV0Schema } from "@api/customers/cusPlans/previousVersions/apiCusProductV0.js";
|
||||
import { ApiInvoiceSchema } from "@api/others/apiInvoice.js";
|
||||
import { AppEnv } from "@models/genModels/genEnums.js";
|
||||
import { z } from "zod/v4";
|
||||
import { ApiTrialsUsedSchema } from "../apiCustomer.js";
|
||||
import { ApiTrialsUsedSchema } from "./apiCustomerV3.js";
|
||||
|
||||
/**
|
||||
* ApiCustomerV0Schema - Customer response format for API V0_1
|
||||
|
||||
@@ -2,8 +2,8 @@ import { ApiCusFeatureV1Schema } from "@api/customers/cusFeatures/previousVersio
|
||||
import { ApiInvoiceSchema } from "@api/others/apiInvoice.js";
|
||||
import { AppEnv } from "@models/genModels/genEnums.js";
|
||||
import { z } from "zod/v4";
|
||||
import { ApiTrialsUsedSchema } from "../apiCustomer.js";
|
||||
import { ApiCusProductV1Schema } from "../cusProducts/previousVersions/apiCusProductV1.js";
|
||||
import { ApiCusProductV1Schema } from "../cusPlans/previousVersions/apiCusProductV1.js";
|
||||
import { ApiTrialsUsedSchema } from "./apiCustomerV3.js";
|
||||
|
||||
/**
|
||||
* ApiCustomerV1Schema - Customer response format for API V0_2/V1_0/V1_1 (pre V1_2)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ApiCusReferralSchema } from "@api/customers/components/apiCusReferral.js";
|
||||
import { ApiCusUpcomingInvoiceSchema } from "@api/customers/components/apiCusUpcomingInvoice.js";
|
||||
import { ApiCusFeatureV2Schema } from "@api/customers/cusFeatures/previousVersions/apiCusFeatureV2.js";
|
||||
import { ApiCusProductV2Schema } from "@api/customers/cusProducts/previousVersions/apiCusProductV2.js";
|
||||
import { ApiCusProductV2Schema } from "@api/customers/cusPlans/previousVersions/apiCusProductV2.js";
|
||||
import { ApiEntitySchema } from "@api/entities/apiEntity.js";
|
||||
import { ApiCusRewardsSchema } from "@api/models.js";
|
||||
import { ApiInvoiceSchema } from "@api/others/apiInvoice.js";
|
||||
|
||||
71
shared/api/customers/previousVersions/apiCustomerV3.ts
Normal file
71
shared/api/customers/previousVersions/apiCustomerV3.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { ApiCusReferralSchema } from "@api/customers/components/apiCusReferral.js";
|
||||
import { ApiBaseEntitySchema } from "@api/entities/apiEntity.js";
|
||||
import { ApiCusRewardsSchema } from "@api/others/apiDiscount.js";
|
||||
import { ApiInvoiceSchema } from "@api/others/apiInvoice.js";
|
||||
import { AppEnv } from "@models/genModels/genEnums.js";
|
||||
import { z } from "zod/v4";
|
||||
import { ApiCusUpcomingInvoiceSchema } from "../components/apiCusUpcomingInvoice.js";
|
||||
import { ApiCusFeatureV3Schema } from "../cusFeatures/previousVersions/apiCusFeatureV3.js";
|
||||
import { ApiCusProductV3Schema } from "../cusPlans/previousVersions/apiCusProductV3.js";
|
||||
|
||||
export const ApiTrialsUsedSchema = z.object({
|
||||
product_id: z.string(),
|
||||
customer_id: z.string(),
|
||||
fingerprint: z.string().nullish(),
|
||||
});
|
||||
|
||||
export const ApiCusExpandV3Schema = z.object({
|
||||
invoices: z.array(ApiInvoiceSchema).optional(),
|
||||
entities: z.array(ApiBaseEntitySchema).optional(),
|
||||
trials_used: z.array(ApiTrialsUsedSchema).optional(),
|
||||
rewards: ApiCusRewardsSchema.nullish(),
|
||||
referrals: z.array(ApiCusReferralSchema).optional(),
|
||||
upcoming_invoice: ApiCusUpcomingInvoiceSchema.nullish(),
|
||||
payment_method: z.any().nullish(),
|
||||
});
|
||||
|
||||
export const ApiCustomerV3Schema = z.object({
|
||||
// Internal fields
|
||||
id: z.string().nullable().meta({
|
||||
description: "Your internal ID for the customer",
|
||||
example: "cus_123",
|
||||
}),
|
||||
created_at: z.number().meta({
|
||||
description: "Timestamp of customer creation in milliseconds since epoch",
|
||||
example: 1717000000,
|
||||
}),
|
||||
name: z.string().nullable().meta({
|
||||
description: "Customer's name",
|
||||
example: "John Doe",
|
||||
}),
|
||||
email: z.string().nullable().meta({
|
||||
description: "Customer's email address",
|
||||
example: "john@doe.com",
|
||||
}),
|
||||
fingerprint: z.string().nullable().meta({
|
||||
description:
|
||||
"Unique identifier (eg. serial number) to detect duplicate customers and prevent key leaks",
|
||||
example: "fp_9184And92839123hda",
|
||||
}),
|
||||
stripe_id: z.string().nullable().default(null).meta({
|
||||
description: "Stripe customer ID",
|
||||
example: "cus_J8A5c31A8tlpwN",
|
||||
}),
|
||||
env: z.enum(AppEnv).meta({
|
||||
description: "Environment the customer is in",
|
||||
example: "production",
|
||||
}),
|
||||
metadata: z.record(z.any(), z.any()).default({}),
|
||||
products: z.array(ApiCusProductV3Schema).meta({
|
||||
description: "List of products the customer has access to",
|
||||
example: [],
|
||||
}),
|
||||
features: z.record(z.string(), ApiCusFeatureV3Schema).meta({
|
||||
description: "List of features the customer has access to",
|
||||
example: {},
|
||||
}),
|
||||
...ApiCusExpandV3Schema.shape,
|
||||
});
|
||||
|
||||
export type ApiCustomerV3 = z.infer<typeof ApiCustomerV3Schema>;
|
||||
export type ApiCustomerV3Expand = z.infer<typeof ApiCusExpandV3Schema>;
|
||||
@@ -0,0 +1,60 @@
|
||||
import { ApiVersion } from "@api/versionUtils/ApiVersion.js";
|
||||
import {
|
||||
AffectedResource,
|
||||
defineVersionChange,
|
||||
} from "@api/versionUtils/versionChangeUtils/VersionChange.js";
|
||||
import { CusExpand } from "@models/cusModels/cusExpand.js";
|
||||
import type { z } from "zod/v4";
|
||||
import { GetCustomerQuerySchema } from "../customerOpModels.js";
|
||||
|
||||
/**
|
||||
* V1_2_CustomerQueryChange: Transforms customer query TO latest format
|
||||
*
|
||||
* Applied when: sourceVersion <= V1_2
|
||||
*
|
||||
* Changes introduced in V2.0:
|
||||
*
|
||||
* 1. Added new expand options:
|
||||
* - "plans.plan" - expands the full plan object in each customer plan
|
||||
* - "features.feature" - expands the full feature object in each customer feature
|
||||
*
|
||||
* This transformation automatically adds these expand options when transforming
|
||||
* from V1.2 to V2.0, so that V1.2 clients get the full plan/feature objects
|
||||
* (matching their expected verbose format).
|
||||
*
|
||||
* Input: Query without plans.plan/features.feature expand
|
||||
* Output: Query with plans.plan and features.feature automatically expanded
|
||||
*/
|
||||
|
||||
export const V1_2_CustomerQueryChange = defineVersionChange({
|
||||
newVersion: ApiVersion.V2,
|
||||
oldVersion: ApiVersion.V1_Beta,
|
||||
description: [
|
||||
"Automatically expands plans.plan and features.feature for V1.2 clients",
|
||||
"Ensures V1.2 clients get full plan/feature objects",
|
||||
],
|
||||
affectedResources: [AffectedResource.Customer],
|
||||
newSchema: GetCustomerQuerySchema,
|
||||
oldSchema: GetCustomerQuerySchema,
|
||||
|
||||
// Only transform requests (incoming queries)
|
||||
affectsRequest: true,
|
||||
affectsResponse: false,
|
||||
|
||||
// Request: V1.2 → V2.0 (add expand options)
|
||||
transformRequest: ({
|
||||
input,
|
||||
}: {
|
||||
input: z.infer<typeof GetCustomerQuerySchema>;
|
||||
}) => {
|
||||
const existingExpand = input.expand || [];
|
||||
|
||||
// Add `plan` and `feature` to expand array
|
||||
const newExpand = [...existingExpand, CusExpand.Plan, CusExpand.Feature];
|
||||
|
||||
return {
|
||||
...input,
|
||||
expand: newExpand,
|
||||
} satisfies z.infer<typeof GetCustomerQuerySchema>;
|
||||
},
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ApiCusFeatureSchema } from "@api/customers/cusFeatures/apiCusFeature.js";
|
||||
import { ApiCusProductSchema } from "@api/customers/cusProducts/apiCusProduct.js";
|
||||
import { ApiCusFeatureV3Schema } from "@api/customers/cusFeatures/previousVersions/apiCusFeatureV3.js";
|
||||
import { ApiCusPlanSchema } from "@api/customers/cusPlans/apiCusPlan.js";
|
||||
import { ApiInvoiceSchema } from "@api/others/apiInvoice.js";
|
||||
import { AppEnv } from "@models/genModels/genEnums.js";
|
||||
import { z } from "zod/v4";
|
||||
@@ -29,11 +29,11 @@ export const ApiBaseEntitySchema = z.object({
|
||||
});
|
||||
|
||||
export const ApiEntitySchema = ApiBaseEntitySchema.extend({
|
||||
products: z.array(ApiCusProductSchema).optional().meta({
|
||||
description: "Products associated with this entity",
|
||||
plans: z.array(ApiCusPlanSchema).optional().meta({
|
||||
description: "Plans associated with this entity",
|
||||
example: [],
|
||||
}),
|
||||
features: z.record(z.string(), ApiCusFeatureSchema).optional().meta({
|
||||
features: z.record(z.string(), ApiCusFeatureV3Schema).optional().meta({
|
||||
description: "Features associated with this entity",
|
||||
example: {},
|
||||
}),
|
||||
@@ -44,4 +44,4 @@ export const ApiEntitySchema = ApiBaseEntitySchema.extend({
|
||||
}),
|
||||
});
|
||||
|
||||
export type EntityResponse = z.infer<typeof ApiEntitySchema>;
|
||||
export type ApiEntity = z.infer<typeof ApiEntitySchema>;
|
||||
|
||||
@@ -18,8 +18,8 @@ export * from "./customers/cusFeatures/apiCusFeature.js";
|
||||
export * from "./customers/cusFeatures/previousVersions/apiCusFeatureV0.js";
|
||||
export * from "./customers/cusFeatures/previousVersions/apiCusFeatureV1.js";
|
||||
export * from "./customers/cusFeatures/previousVersions/apiCusFeatureV2.js";
|
||||
export * from "./customers/cusProducts/apiCusProduct.js";
|
||||
export * from "./customers/cusProducts/cusProductLegacyData.js";
|
||||
export * from "./customers/cusPlans/apiCusPlan.js";
|
||||
export * from "./customers/cusPlans/cusProductLegacyData.js";
|
||||
export * from "./customers/customerLegacyData.js";
|
||||
export * from "./customers/customerOpModels.js";
|
||||
export * from "./customers/previousVersions/apiCustomerV2.js";
|
||||
|
||||
@@ -9,7 +9,7 @@ import { CustomerDataSchema } from "./common/customerData.js";
|
||||
import { EntityDataSchema } from "./common/entityData.js";
|
||||
import { coreOps } from "./core/coreOpenApi.js";
|
||||
import { ApiCusFeatureSchema } from "./customers/cusFeatures/apiCusFeature.js";
|
||||
import { ApiCusProductSchema } from "./customers/cusProducts/apiCusProduct.js";
|
||||
import { ApiCusPlanSchema } from "./customers/cusPlans/apiCusPlan.js";
|
||||
import {
|
||||
ApiCustomerWithMeta,
|
||||
customerOps,
|
||||
@@ -65,9 +65,9 @@ const document = createDocument({
|
||||
id: "Customer",
|
||||
description: "Customer object returned by the API",
|
||||
}),
|
||||
CustomerProduct: ApiCusProductSchema.meta({
|
||||
id: "CustomerProduct",
|
||||
description: "Customer product object returned by the API",
|
||||
CustomerPlan: ApiCusPlanSchema.meta({
|
||||
id: "CustomerPlan",
|
||||
description: "Customer plan object returned by the API",
|
||||
}),
|
||||
CustomerFeature: ApiCusFeatureSchema.meta({
|
||||
id: "CustomerFeature",
|
||||
@@ -79,7 +79,8 @@ const document = createDocument({
|
||||
}),
|
||||
ProductItem: ApiProductItemWithMeta.meta({
|
||||
id: "ProductItem",
|
||||
description: "Product item defining features and pricing within a product",
|
||||
description:
|
||||
"Product item defining features and pricing within a product",
|
||||
}),
|
||||
Feature: ApiFeatureWithMeta.meta({
|
||||
id: "Feature",
|
||||
|
||||
@@ -27,10 +27,15 @@ export const ApiPlanSchema = z.object({
|
||||
add_on: z.boolean(),
|
||||
default: z.boolean(),
|
||||
|
||||
price: z.object({
|
||||
amount: z.number(),
|
||||
interval: z.enum(BillingInterval),
|
||||
}),
|
||||
// Change
|
||||
price: z
|
||||
.object({
|
||||
amount: z.number(),
|
||||
interval: z.enum(BillingInterval),
|
||||
interval_count: z.number().optional(),
|
||||
// tiers: z.array(UsageTierSchema).optional(),
|
||||
})
|
||||
.nullable(),
|
||||
|
||||
features: z.array(ApiPlanFeatureSchema),
|
||||
free_trial: ApiFreeTrialV2Schema.nullable().optional(),
|
||||
|
||||
@@ -40,7 +40,7 @@ import {
|
||||
|
||||
export const V1_2_ProductChanges = defineVersionChange({
|
||||
newVersion: ApiVersion.V2, // Breaking change introduced in V2
|
||||
oldVersion: ApiVersion.V1_2, // Applied when targetVersion <= V1.2
|
||||
oldVersion: ApiVersion.V1_Beta, // Applied when targetVersion <= V1.2
|
||||
description: [
|
||||
"Product format changed from Plan to ProductV2 schema",
|
||||
"Features array renamed to items with different structure",
|
||||
|
||||
@@ -11,12 +11,11 @@ import { ResetInterval } from "../planEnums.js";
|
||||
export const ApiPlanFeatureSchema = z
|
||||
.object({
|
||||
feature_id: z.string(),
|
||||
granted: z.number(),
|
||||
granted_balance: z.number(),
|
||||
unlimited: z.boolean(),
|
||||
|
||||
reset_interval: z.enum(ResetInterval).optional(),
|
||||
reset_interval_count: z.number().optional(),
|
||||
reset_usage_on_enabled: z.boolean().optional(),
|
||||
reset_usage_when_enabled: z.boolean().optional(),
|
||||
|
||||
price: z
|
||||
.object({
|
||||
@@ -48,6 +47,13 @@ export const ApiPlanFeatureSchema = z
|
||||
expiry_duration_length: z.number().optional(),
|
||||
})
|
||||
.optional(),
|
||||
|
||||
display: z
|
||||
.object({
|
||||
primary_text: z.string(),
|
||||
secondary_text: z.string().optional(),
|
||||
})
|
||||
.optional(),
|
||||
})
|
||||
.check((ctx) => {
|
||||
const resetGroup =
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
export enum ApiVersion {
|
||||
V2 = "2.0.0",
|
||||
Beta = "beta",
|
||||
V1_Beta = "beta",
|
||||
V1_2 = "1.2.0",
|
||||
V1_1 = "1.1.0",
|
||||
V0_2 = "0.2.0",
|
||||
|
||||
@@ -52,7 +52,7 @@ export function legacyToSemVer({
|
||||
return ApiVersion.V1_2;
|
||||
case LegacyVersion.v1_4:
|
||||
case 1.4:
|
||||
return ApiVersion.Beta;
|
||||
return ApiVersion.V1_Beta;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@@ -76,7 +76,7 @@ export function semVerToLegacy({
|
||||
return LegacyVersion.v1_1;
|
||||
case ApiVersion.V1_2:
|
||||
return LegacyVersion.v1_2;
|
||||
case ApiVersion.Beta:
|
||||
case ApiVersion.V1_Beta:
|
||||
return LegacyVersion.v1_4;
|
||||
default:
|
||||
return null;
|
||||
@@ -86,7 +86,10 @@ export function semVerToLegacy({
|
||||
/**
|
||||
* Parse version string (CalVer, SemVer, or legacy)
|
||||
* Supports .clover suffix: "2025-04-17.clover"
|
||||
* Supports partial SemVer: "2" → "2.0.0", "2.0" → "2.0.0"
|
||||
* @example parseVersion({ versionStr: "2025-04-17" }) // ApiVersion.V1_1
|
||||
* @example parseVersion({ versionStr: "2" }) // ApiVersion.V2
|
||||
* @example parseVersion({ versionStr: "2.0" }) // ApiVersion.V2
|
||||
*/
|
||||
export function parseVersion({
|
||||
versionStr,
|
||||
@@ -98,7 +101,18 @@ export function parseVersion({
|
||||
return calVerToSemVer({ calver: versionStr });
|
||||
}
|
||||
|
||||
// SemVer (X.Y.Z)
|
||||
// SemVer (X.Y.Z) - normalize partial versions
|
||||
// Supports "2" → "2.0.0", "2.0" → "2.0.0", "2.0.0" → "2.0.0"
|
||||
if (/^\d+(\.\d+)*$/.test(versionStr)) {
|
||||
const parts = versionStr.split(".");
|
||||
const normalizedVersion = `${parts[0] || "0"}.${parts[1] || "0"}.${parts[2] || "0"}`;
|
||||
|
||||
if (Object.values(ApiVersion).includes(normalizedVersion as ApiVersion)) {
|
||||
return normalizedVersion as ApiVersion;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for exact match (for special versions like "beta")
|
||||
if (Object.values(ApiVersion).includes(versionStr as ApiVersion)) {
|
||||
return versionStr as ApiVersion;
|
||||
}
|
||||
@@ -143,8 +157,8 @@ export function createdAtToVersion({
|
||||
version = ApiVersion.V0_1;
|
||||
}
|
||||
|
||||
// return new ApiVersionClass(version);
|
||||
return new ApiVersionClass(ApiVersion.V2);
|
||||
return new ApiVersionClass(version);
|
||||
// return new ApiVersionClass(ApiVersion.V2);
|
||||
}
|
||||
|
||||
// Convert org creation date
|
||||
|
||||
@@ -9,7 +9,6 @@ import type {
|
||||
* Maps versions → change classes
|
||||
*/
|
||||
const changes = new Map<ApiVersion, VersionChangeConstructor[]>();
|
||||
const instances = new Map<string, VersionChange>();
|
||||
|
||||
export const VersionChangeRegistryClass = {
|
||||
register({
|
||||
@@ -24,17 +23,8 @@ export const VersionChangeRegistryClass = {
|
||||
|
||||
getChangesForVersion({ version }: { version: ApiVersion }): VersionChange[] {
|
||||
const changeClasses = changes.get(version) || [];
|
||||
return changeClasses.map((ChangeClass) => {
|
||||
const key = `${version}-${ChangeClass.name}`;
|
||||
if (!instances.has(key)) {
|
||||
instances.set(key, new ChangeClass());
|
||||
}
|
||||
const instance = instances.get(key);
|
||||
if (!instance) {
|
||||
throw new Error(`Failed to create instance for ${ChangeClass.name}`);
|
||||
}
|
||||
return instance;
|
||||
});
|
||||
// Simply instantiate each change class - no caching needed since they're stateless
|
||||
return changeClasses.map((ChangeClass) => new ChangeClass());
|
||||
},
|
||||
|
||||
getRegisteredVersions(): ApiVersion[] {
|
||||
@@ -54,7 +44,6 @@ export const VersionChangeRegistryClass = {
|
||||
|
||||
clear() {
|
||||
changes.clear();
|
||||
instances.clear();
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
import { type ApiVersion, LATEST_VERSION } from "../ApiVersion.js";
|
||||
import { ApiVersionClass } from "../ApiVersionClass.js";
|
||||
import { getVersionsBetween } from "../versionRegistryUtils.js";
|
||||
import type { AffectedResource } from "./VersionChange.js";
|
||||
import {
|
||||
getVersionsBetween,
|
||||
getVersionsForRequestTransform,
|
||||
} from "../versionRegistryUtils.js";
|
||||
import type {
|
||||
AffectedResource,
|
||||
VersionChangeConstructor,
|
||||
} from "./VersionChange.js";
|
||||
import { VersionChangeRegistryClass } from "./VersionChangeRegistryClass.js";
|
||||
|
||||
/**
|
||||
@@ -33,6 +39,7 @@ export function applyResponseVersionChanges<T = any, TLegacyData = any>({
|
||||
}): T {
|
||||
// Default currentVersion to latest if not provided
|
||||
const _currentVersion = currentVersion || new ApiVersionClass(LATEST_VERSION);
|
||||
|
||||
// If versions are equal, no transformation needed
|
||||
if (_currentVersion.eq(targetVersion)) {
|
||||
return input;
|
||||
@@ -58,6 +65,7 @@ export function applyResponseVersionChanges<T = any, TLegacyData = any>({
|
||||
|
||||
// Apply each version's changes
|
||||
let transformedData = input;
|
||||
|
||||
for (const version of versionsToApply) {
|
||||
const changes = VersionChangeRegistryClass.getChangesForVersion({
|
||||
version,
|
||||
@@ -88,7 +96,7 @@ export function applyResponseVersionChanges<T = any, TLegacyData = any>({
|
||||
? change.description.join("; ")
|
||||
: change.description;
|
||||
|
||||
// console.log(`Applying change [${change.oldVersion}]: ${description}`);
|
||||
console.log(`Applying change [${change.oldVersion}]: ${description}`);
|
||||
// Apply the response transformation (backward)
|
||||
transformedData = change.transformResponse({
|
||||
input: transformedData,
|
||||
@@ -127,34 +135,34 @@ export function applyResponseVersionChanges<T = any, TLegacyData = any>({
|
||||
export function applyRequestVersionChanges<T = any, TLegacyData = any>({
|
||||
input,
|
||||
legacyData,
|
||||
targetVersion,
|
||||
currentVersion,
|
||||
fromVersion,
|
||||
toVersion,
|
||||
resource,
|
||||
}: {
|
||||
input: T;
|
||||
legacyData?: TLegacyData;
|
||||
targetVersion: ApiVersionClass; // User's version (old)
|
||||
currentVersion: ApiVersionClass; // Latest version (new)
|
||||
fromVersion: ApiVersionClass; // User's version (old)
|
||||
toVersion: ApiVersionClass; // Latest version (new)
|
||||
resource: AffectedResource;
|
||||
}): T {
|
||||
// If versions are equal, no transformation needed
|
||||
if (targetVersion.eq(currentVersion)) {
|
||||
if (fromVersion.eq(toVersion)) {
|
||||
return input;
|
||||
}
|
||||
|
||||
// If target is newer than current, throw error
|
||||
if (targetVersion.gt(currentVersion)) {
|
||||
if (fromVersion.gt(toVersion)) {
|
||||
throw new Error(
|
||||
`Cannot transform forward from ${currentVersion} to ${targetVersion}. ` +
|
||||
`Cannot transform forward from ${fromVersion} to ${toVersion}. ` +
|
||||
"Current version should be >= target version.",
|
||||
);
|
||||
}
|
||||
|
||||
// Get all versions between target and current (exclusive of current, inclusive of target)
|
||||
const versionsToApply = getVersionsBetween({
|
||||
from: targetVersion.value,
|
||||
to: currentVersion.value,
|
||||
}).filter((v) => v !== currentVersion.value); // Exclude current itself
|
||||
const versionsToApply = getVersionsForRequestTransform({
|
||||
from: fromVersion.value,
|
||||
to: toVersion.value,
|
||||
});
|
||||
|
||||
// Don't reverse - we want to go forward (old → new)
|
||||
// versionsToApply is already in ascending order from getVersionsBetween
|
||||
@@ -213,7 +221,7 @@ export function backwardsChangeActive({
|
||||
versionChange,
|
||||
}: {
|
||||
apiVersion: ApiVersionClass;
|
||||
versionChange: new () => any;
|
||||
versionChange: VersionChangeConstructor;
|
||||
}): boolean {
|
||||
const change = new versionChange();
|
||||
return apiVersion.lte(change.oldVersion);
|
||||
@@ -254,22 +262,22 @@ export function applyResponseVersionChangesToArray<T = any, TLegacyData = any>({
|
||||
export function applyRequestVersionChangesToArray<T = any, TLegacyData = any>({
|
||||
inputArray,
|
||||
legacyData,
|
||||
targetVersion,
|
||||
currentVersion,
|
||||
fromVersion,
|
||||
toVersion,
|
||||
resource,
|
||||
}: {
|
||||
inputArray: T[];
|
||||
legacyData?: TLegacyData;
|
||||
targetVersion: ApiVersionClass;
|
||||
currentVersion: ApiVersionClass;
|
||||
fromVersion: ApiVersionClass;
|
||||
toVersion: ApiVersionClass;
|
||||
resource: AffectedResource;
|
||||
}): T[] {
|
||||
return inputArray.map((item) =>
|
||||
applyRequestVersionChanges({
|
||||
input: item,
|
||||
legacyData,
|
||||
targetVersion,
|
||||
currentVersion,
|
||||
fromVersion,
|
||||
toVersion,
|
||||
resource,
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -4,16 +4,20 @@ import { V0_1_CustomerChange } from "@api/customers/changes/V0.1_CustomerChange.
|
||||
import { V0_2_CustomerChange } from "@api/customers/changes/V0.2_CustomerChange.js";
|
||||
import { V0_2_InvoicesAlwaysExpanded } from "@api/customers/changes/V0.2_InvoicesAlwaysExpanded.js";
|
||||
import { V1_1_FeaturesArrayToObject } from "@api/customers/changes/V1.1_FeaturesArrayToObject.js";
|
||||
|
||||
// Import customer product changes
|
||||
|
||||
import { V1_2_CustomerChange } from "@api/customers/changes/V1.2_CustomerChange.js";
|
||||
import { V1_2_CustomerQueryChange } from "@api/customers/requestChanges/V1.2_CustomerQueryChange.js";
|
||||
// Import product changes
|
||||
import { V1_2_ProductChanges } from "@api/products/changes/V1.2_ProductChanges.js";
|
||||
|
||||
import { ApiVersion } from "../ApiVersion.js";
|
||||
import type { VersionChangeConstructor } from "./VersionChange.js";
|
||||
import { VersionChangeRegistryClass } from "./VersionChangeRegistryClass.js";
|
||||
|
||||
export const V2_CHANGES: VersionChangeConstructor[] = [
|
||||
V1_2_CustomerChange, // Transforms Customer TO V1.2 format from V2 format
|
||||
V1_2_CustomerQueryChange, // Transforms Customer Query TO V2.0 format (adds expand options)
|
||||
V1_2_ProductChanges, // Transforms Product TO V1.2 format from V2 Plan format
|
||||
];
|
||||
|
||||
@@ -42,7 +46,7 @@ export function registerAllVersionChanges() {
|
||||
changes: V2_CHANGES,
|
||||
});
|
||||
VersionChangeRegistryClass.register({
|
||||
version: ApiVersion.Beta,
|
||||
version: ApiVersion.V1_Beta,
|
||||
changes: V1_4_CHANGES,
|
||||
});
|
||||
VersionChangeRegistryClass.register({
|
||||
|
||||
@@ -20,8 +20,9 @@ export const VERSION_REGISTRY: Record<ApiVersion, VersionMetadata> = {
|
||||
releasedAt: new Date("2025-10-16").getTime(),
|
||||
description: "Plan features with max_purchase",
|
||||
},
|
||||
[ApiVersion.Beta]: {
|
||||
semver: ApiVersion.Beta,
|
||||
|
||||
[ApiVersion.V1_Beta]: {
|
||||
semver: ApiVersion.V1_Beta,
|
||||
calver: "beta",
|
||||
releasedAt: new Date("2025-06-01").getTime(),
|
||||
description: "Beta version with experimental features",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user