This commit is contained in:
John Yeo
2025-10-25 10:52:03 +01:00
parent f7ebe22c00
commit dcc26b44a9
107 changed files with 2134 additions and 2738 deletions

348
bun.lock
View File

@@ -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=="], "@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-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=="], "@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=="], "@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=="], "@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"], "@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=="], "@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/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/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=="], "@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=="], "@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=="], "@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/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=="], "@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/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=="], "@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=="], "@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=="], "@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/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=="], "@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-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=="], "@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=="], "@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=="], "@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=="], "@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/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=="], "@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=="], "@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=="], "@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=="], "@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/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=="], "@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-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=="], "@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=="], "@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=="], "@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=="], "@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=="], "@pinojs/redact": ["@pinojs/redact@0.4.0", "", {}, "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg=="],
>>>>>>> staging
"@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="], "@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=="], "@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=="], "@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=="], "@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-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=="], "@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=="], "@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=="], "@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/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=="], "@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/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=="], "@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=="], "@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=="], "@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-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/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=="], "@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=="], "@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=="], "@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=="], "@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-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=="], "@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=="], "@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=="], "@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=="], "@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/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=="], "@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=="], "@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": ["@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=="], "@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-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=="], "@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=="], "@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=="], "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=="], "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=="], "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=="], "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=="], "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=="], "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-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=="], "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=="], "bignumber.js": ["bignumber.js@9.3.1", "", {}, "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ=="],
@@ -1770,11 +1551,7 @@
"camelcase": ["camelcase@6.3.0", "", {}, "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA=="], "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=="], "caniuse-lite": ["caniuse-lite@1.0.30001751", "", {}, "sha512-A0QJhug0Ly64Ii3eIqHu5X51ebln3k4yTUkY1j8drqpWHVreg/VLijN48cZ1bYPiqOQuqpkIKnzr/Ul8V+p6Cw=="],
>>>>>>> staging
"ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], "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=="], "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=="], "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=="], "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=="], "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=="], "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-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="],
"deep-extend": ["deep-extend@0.6.0", "", {}, "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="], "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=="], "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=="], "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=="], "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=="], "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=="], "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=="], "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=="], "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": ["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-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=="], "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=="], "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=="], "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=="], "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": ["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=="], "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=="], "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=="], "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=="], "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=="], "mitt": ["mitt@3.0.1", "", {}, "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="],
<<<<<<< HEAD
=======
"mkdirp-classic": ["mkdirp-classic@0.5.3", "", {}, "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="], "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=="], "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=="], "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=="], "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=="], "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=="], "napi-build-utils": ["napi-build-utils@2.0.0", "", {}, "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA=="],
>>>>>>> staging
"natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
@@ -2586,21 +2327,14 @@
"netmask": ["netmask@2.0.2", "", {}, "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg=="], "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=="], "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=="], "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=="], "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=="], "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-abort-controller": ["node-abort-controller@3.1.1", "", {}, "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ=="],
"node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="], "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=="], "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=="], "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=="], "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=="], "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=="], "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=="], "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
@@ -2705,11 +2431,7 @@
"picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], "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=="], "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=="], "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=="], "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=="], "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=="], "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=="], "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=="], "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=="], "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=="], "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=="], "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=="], "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=="], "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=="], "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=="], "redis-errors": ["redis-errors@1.2.0", "", {}, "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w=="],
@@ -2894,11 +2601,7 @@
"reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], "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=="], "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=="], "rou3": ["rou3@0.6.3", "", {}, "sha512-1HSG1ENTj7Kkm5muMnXuzzfdDOf7CFnbSYFA+H3Fp/rB9lOCxCPgy1jlZxTKyFoC5jJay8Mmc+VbPLYRjzYLrA=="],
@@ -2956,19 +2659,14 @@
"signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], "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-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=="], "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=="], "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=="], "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=="], "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=="], "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=="], "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=="], "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=="], "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=="], "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=="], "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=="], "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=="], "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=="], "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.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=="],
"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
"vscode-oniguruma": ["vscode-oniguruma@2.0.1", "", {}, "sha512-poJU8iHIWnC3vgphJnrLZyI3YdqRlR27xzqDmpPXYzA93R4Gk8z7T6oqDzDoHjoikA2aS82crdXFkjELCdJsjQ=="], "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=="], "@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-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=="], "@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/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=="], "@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=="], "@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=="], "@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=="], "@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=="], "@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=="], "@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/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=="], "@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=="], "@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=="], "@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=="], "@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=="], "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=="], "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=="], "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=="], "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/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=="], "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/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=="], "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=="], "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=="], "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=="], "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=="], "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=="], "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
View 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"
}
]
}

View File

@@ -1,14 +1,8 @@
// @ts-nocheck // @ts-nocheck
{
id: "team_plan",
// other fields, // Old customer object
licenses: [
{ plan_id: "dev_seat" },
{
plan_id: "pro_seat", price: 50 // to override price
},
]
}
// Global namesapce: // Global namesapce:
// //

View 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("; ")}`;
}

View File

@@ -140,7 +140,7 @@ export const priceToStripeItem = ({
}; };
} }
if (apiVersion === ApiVersion.Beta && !isCheckout) { if (apiVersion === ApiVersion.V1_Beta && !isCheckout) {
return { return {
lineItem: { lineItem: {
// lineItem: getEmptyPriceItem({ price, org }), // lineItem: getEmptyPriceItem({ price, org }),

View File

@@ -75,7 +75,7 @@ export const handleCheckoutSub = async ({
if ( if (
arrearPrice && arrearPrice &&
(attachParams.internalEntityId || (attachParams.internalEntityId ||
attachParams.apiVersion === ApiVersion.Beta) attachParams.apiVersion === ApiVersion.V1_Beta)
) { ) {
itemsUpdate.push({ itemsUpdate.push({
id: item.id, id: item.id,

View File

@@ -38,7 +38,7 @@ export const handleRemainingSets = async ({
if ( if (
attachParams.internalEntityId || attachParams.internalEntityId ||
attachParams.apiVersion === ApiVersion.Beta attachParams.apiVersion === ApiVersion.V1_Beta
) { ) {
const replaceIndex = remainingItems.findIndex( const replaceIndex = remainingItems.findIndex(
(item) => item.price === config.stripe_price_id, (item) => item.price === config.stripe_price_id,

View File

@@ -17,11 +17,11 @@ import { RolloverService } from "@/internal/customers/cusProducts/cusEnts/cusRol
import { getRolloverUpdates } from "@/internal/customers/cusProducts/cusEnts/cusRollovers/rolloverUtils.js"; import { getRolloverUpdates } from "@/internal/customers/cusProducts/cusEnts/cusRollovers/rolloverUtils.js";
import { getResetBalancesUpdate } from "@/internal/customers/cusProducts/cusEnts/groupByUtils.js"; import { getResetBalancesUpdate } from "@/internal/customers/cusProducts/cusEnts/groupByUtils.js";
import { getCusPriceUsage } from "@/internal/customers/cusProducts/cusPrices/cusPriceUtils.js"; import { getCusPriceUsage } from "@/internal/customers/cusProducts/cusPrices/cusPriceUtils.js";
import { getAllFullCustomers } from "@/utils/scriptUtils/getAll/getAllAutumnCustomers.js";
import { submitUsageToStripe } from "../../stripeMeterUtils.js"; import { submitUsageToStripe } from "../../stripeMeterUtils.js";
import { getInvoiceItemForUsage } from "../../stripePriceUtils.js"; import { getInvoiceItemForUsage } from "../../stripePriceUtils.js";
import { subToPeriodStartEnd } from "../../stripeSubUtils/convertSubUtils.js"; import { subToPeriodStartEnd } from "../../stripeSubUtils/convertSubUtils.js";
import { findStripeItemForPrice } from "../../stripeSubUtils/stripeSubItemUtils.js"; import { findStripeItemForPrice } from "../../stripeSubUtils/stripeSubItemUtils.js";
import { getAllFullCustomers } from "@/utils/scriptUtils/getAll/getAllAutumnCustomers.js";
export const handleUsagePrices = async ({ export const handleUsagePrices = async ({
db, db,
@@ -86,7 +86,7 @@ export const handleUsagePrices = async ({
const isNewUsageMethod = const isNewUsageMethod =
activeProduct.internal_entity_id || activeProduct.internal_entity_id ||
activeProduct.api_semver === ApiVersion.Beta; activeProduct.api_semver === ApiVersion.V1_Beta;
if (isNewUsageMethod) { if (isNewUsageMethod) {
const invoiceItem = getInvoiceItemForUsage({ const invoiceItem = getInvoiceItemForUsage({

View File

@@ -53,7 +53,7 @@ export const handleCusProductDeleted = async ({
stripeId: fullCus.processor?.id, 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...? // refer to handleUpgradeFlow.ts, when cancel immediately through API / dashboard, this happens...?
const isAutumnCancel = const isAutumnCancel =

View File

@@ -47,6 +47,7 @@ export const apiVersionMiddleware = async (c: Context<HonoEnv>, next: Next) => {
finalVersion = new ApiVersionClass(parsedVersion); finalVersion = new ApiVersionClass(parsedVersion);
} }
// 2. Calculate from org creation date // 2. Calculate from org creation date
else if (org?.created_at) { else if (org?.created_at) {
finalVersion = createdAtToVersion({ finalVersion = createdAtToVersion({

View File

@@ -68,6 +68,9 @@ export const baseMiddleware = async (c: Context<HonoEnv>, next: Next) => {
userId: undefined, userId: undefined,
authType: AuthType.Unknown, authType: AuthType.Unknown,
env: AppEnv.Sandbox, // maybe use app_env headers env: AppEnv.Sandbox, // maybe use app_env headers
// Query params
expand: [],
}); });
childLogger.info(`${method} ${path}`); childLogger.info(`${method} ${path}`);

View File

@@ -3,8 +3,9 @@ import type { Context } from "hono";
import type { ContentfulStatusCode } from "hono/utils/http-status"; import type { ContentfulStatusCode } from "hono/utils/http-status";
import Stripe from "stripe"; import Stripe from "stripe";
import { ZodError } from "zod/v4"; import { ZodError } from "zod/v4";
import { formatZodError } from "@/errors/formatZodError.js";
import type { HonoEnv } from "@/honoUtils/HonoEnv.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"; import { handleErrorSkip } from "./errorSkipMiddleware.js";
/** /**

View File

@@ -8,8 +8,9 @@ import type { Context } from "hono";
import type { ContentfulStatusCode } from "hono/utils/http-status"; import type { ContentfulStatusCode } from "hono/utils/http-status";
import Stripe from "stripe"; import Stripe from "stripe";
import { ZodError } from "zod/v4"; import { ZodError } from "zod/v4";
import { formatZodError } from "@/errors/formatZodError.js";
import type { HonoEnv } from "@/honoUtils/HonoEnv.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"; import { matchRoute } from "./middlewareUtils.js";
// ============================================================================ // ============================================================================

View 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();
};
};

View File

@@ -5,6 +5,7 @@ import type { DrizzleCli } from "@/db/initDrizzle.js";
import type { HonoEnv } from "@/honoUtils/HonoEnv.js"; import type { HonoEnv } from "@/honoUtils/HonoEnv.js";
import { validator } from "./validatorMiddleware.js"; import { validator } from "./validatorMiddleware.js";
import { versionedValidator } from "./versionedValidator.js"; import { versionedValidator } from "./versionedValidator.js";
import { expandMiddleware } from "./expandMiddleware.js";
/** /**
* Extended context type that includes validated input * Extended context type that includes validated input
@@ -128,6 +129,11 @@ export function createRoute<
middlewares.push(validator("param", opts.params)); middlewares.push(validator("param", opts.params));
} }
// Add expand middleware after query validation
if (opts.query || opts.versionedQuery) {
middlewares.push(expandMiddleware());
}
const wrappedHandler = async ( const wrappedHandler = async (
c: ValidatedContext<HonoEnv, Body, Query, Params>, c: ValidatedContext<HonoEnv, Body, Query, Params>,
) => { ) => {

View File

@@ -55,28 +55,39 @@ export const versionedValidator = ({
const versionKey = userVersion.value as ApiVersion; const versionKey = userVersion.value as ApiVersion;
const schema = schemas[versionKey] ?? schemas.latest; const schema = schemas[versionKey] ?? schemas.latest;
// Validate with version-specific schema using zValidator let validatedData: Record<string, unknown>;
const validatorMiddleware = zValidator(target, schema, (result, _c) => {
// 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) { if (!result.success) {
// Validation errors reference fields from user's version ✅
throw result.error; 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 // Run validation
await validatorMiddleware(c, async () => {}); await validatorMiddleware(c, async () => {});
// Get validated data - type assertion needed due to Hono's dynamic validation target typing // Get validated data - type assertion needed due to Hono's dynamic validation target typing
type ValidData = Record<string, unknown>; validatedData = (c.req as any).valid(target) as 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;
// If user is on older version, transform to latest // If user is on older version, transform to latest
if (!userVersion.eq(new ApiVersionClass(LATEST_VERSION))) { if (!userVersion.eq(new ApiVersionClass(LATEST_VERSION))) {
const transformed = applyRequestVersionChanges({ const transformed = applyRequestVersionChanges({
input: validatedData, input: validatedData,
targetVersion: userVersion, fromVersion: userVersion,
currentVersion: new ApiVersionClass(LATEST_VERSION), toVersion: new ApiVersionClass(LATEST_VERSION),
resource, resource,
}); });

View File

@@ -19,7 +19,7 @@ export type RequestContext = {
// Objects // Objects
db: DrizzleCli; db: DrizzleCli;
logger: Logger; logger: Logger;
clickhouseClient: ClickHouseClient; clickhouseClient?: ClickHouseClient;
// Info // Info
id: string; id: string;
@@ -27,6 +27,9 @@ export type RequestContext = {
authType: AuthType; authType: AuthType;
apiVersion: ApiVersionClass; apiVersion: ApiVersionClass;
timestamp: number; timestamp: number;
// Query params
expand: string[];
}; };
export type AutumnContext = RequestContext; export type AutumnContext = RequestContext;

View File

@@ -1,11 +1,12 @@
import { DrizzleCli } from "@/db/initDrizzle.js"; import { ActionType, type AppEnv, type Organization } from "@autumn/shared";
import type { ExtendedRequest } from "@/utils/models/Request.js"; import type { AutumnContext } from "@/honoUtils/HonoEnv.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 { CusService } from "@/internal/customers/CusService.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 { ActionService } from "../ActionService.js";
import { constructAction, parseReqForAction } from "../actionUtils.js";
export const addCustomerCreatedTask = async ({ export const addCustomerCreatedTask = async ({
req, req,
@@ -23,31 +24,30 @@ export const addCustomerCreatedTask = async ({
payload: { payload: {
req: req ? parseReqForAction(req) : undefined, req: req ? parseReqForAction(req) : undefined,
internalCustomerId, internalCustomerId,
org, orgId: org.id,
env, env,
}, },
}); });
}; };
export const handleCustomerCreated = async ({ export const handleCustomerCreated = async ({
db, ctx,
logger,
data, data,
}: { }: {
db: DrizzleCli; ctx: AutumnContext;
logger: any; data: HandleCustomerCreatedData;
data: any;
}) => { }) => {
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, db,
idOrInternalId: internalCustomerId, idOrInternalId: internalCustomerId,
orgId: org.id, orgId: org.id,
env, env,
}); });
let action = constructAction({ const action = constructAction({
org, org,
env, env,
customer, customer,

View File

@@ -2,15 +2,14 @@ import {
ActionType, ActionType,
type AppEnv, type AppEnv,
type AuthType, type AuthType,
createdAtToVersion,
cusProductToProduct, cusProductToProduct,
type FullCusProduct, type FullCusProduct,
type FullProduct, type FullProduct,
notNullish, notNullish,
type Organization, type Organization,
} from "@autumn/shared"; } from "@autumn/shared";
import type { DrizzleCli } from "@/db/initDrizzle.js";
import { sendSvixEvent } from "@/external/svix/svixHelpers.js"; import { sendSvixEvent } from "@/external/svix/svixHelpers.js";
import type { AutumnContext } from "@/honoUtils/HonoEnv.js";
import { ActionService } from "@/internal/analytics/ActionService.js"; import { ActionService } from "@/internal/analytics/ActionService.js";
import { import {
constructAction, constructAction,
@@ -19,7 +18,7 @@ import {
import { getSingleEntityResponse } from "@/internal/api/entities/getEntityUtils.js"; import { getSingleEntityResponse } from "@/internal/api/entities/getEntityUtils.js";
import { CusService } from "@/internal/customers/CusService.js"; import { CusService } from "@/internal/customers/CusService.js";
import { RELEVANT_STATUSES } from "@/internal/customers/cusProducts/CusProductService.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 { FeatureService } from "@/internal/features/FeatureService.js";
import { getProductResponse } from "@/internal/products/productUtils/productResponseUtils/getProductResponse.js"; import { getProductResponse } from "@/internal/products/productUtils/productResponseUtils/getProductResponse.js";
import { JobName } from "@/queue/JobName.js"; import { JobName } from "@/queue/JobName.js";
@@ -66,8 +65,10 @@ export const addProductsUpdatedWebhookTask = async ({
payload: { payload: {
req: req ? parseReqForAction(req) : undefined, req: req ? parseReqForAction(req) : undefined,
internalCustomerId, internalCustomerId,
org, orgId: org.id,
env, env,
// org,
// env,
customerId, customerId,
cusProduct, cusProduct,
scheduledCusProduct, scheduledCusProduct,
@@ -90,18 +91,16 @@ export const addProductsUpdatedWebhookTask = async ({
}; };
export const handleProductsUpdated = async ({ export const handleProductsUpdated = async ({
db, ctx,
logger,
data, data,
}: { }: {
db: DrizzleCli; ctx: AutumnContext;
logger: any;
data: { data: {
req: Partial<ExtendedRequest>; req: Partial<ExtendedRequest>;
actionDetails: ActionDetails; actionDetails: ActionDetails;
internalCustomerId: string; internalCustomerId: string;
org: Organization; // org: Organization;
env: AppEnv; // env: AppEnv;
customerId: string; customerId: string;
product: FullProduct; product: FullProduct;
scenario: string; scenario: string;
@@ -110,15 +109,10 @@ export const handleProductsUpdated = async ({
deletedCusProduct?: FullCusProduct; deletedCusProduct?: FullCusProduct;
}; };
}) => { }) => {
const { const { req, scenario, cusProduct, scheduledCusProduct, deletedCusProduct } =
req, data;
org,
env, const { db, org, env, logger } = ctx;
scenario,
cusProduct,
scheduledCusProduct,
deletedCusProduct,
} = data;
// Product: // Product:
const product = cusProduct.product; const product = cusProduct.product;
@@ -126,8 +120,8 @@ export const handleProductsUpdated = async ({
const customer = await CusService.getFull({ const customer = await CusService.getFull({
db, db,
idOrInternalId: data.customerId || data.internalCustomerId, idOrInternalId: data.customerId || data.internalCustomerId,
orgId: data.org.id, orgId: org.id,
env: data.env, env: env,
inStatuses: RELEVANT_STATUSES, inStatuses: RELEVANT_STATUSES,
entityId: cusProduct.internal_entity_id || undefined, entityId: cusProduct.internal_entity_id || undefined,
}); });
@@ -138,20 +132,10 @@ export const handleProductsUpdated = async ({
env, env,
}); });
const apiVersion = createdAtToVersion({ const apiCustomer = await getApiCustomer({
createdAt: org.created_at || Date.now(), ctx,
}); fullCus: customer,
const cusDetails = await getCustomerDetails({
db,
customer: customer,
org,
env,
features,
logger,
cusProducts: customer.customer_products,
expand: [], expand: [],
apiVersion,
}); });
const productRes = await getProductResponse({ const productRes = await getProductResponse({
@@ -199,12 +183,10 @@ export const handleProductsUpdated = async ({
let entityRes = null; let entityRes = null;
if (notNullish(customer?.entity)) { if (notNullish(customer?.entity)) {
entityRes = await getSingleEntityResponse({ entityRes = await getSingleEntityResponse({
ctx,
entityId: customer.entity!.id, entityId: customer.entity!.id,
org,
env,
fullCus: customer, fullCus: customer,
entity: customer.entity!, entity: customer.entity!,
features,
}); });
} }
@@ -215,7 +197,7 @@ export const handleProductsUpdated = async ({
eventType: "customer.products.updated", eventType: "customer.products.updated",
data: { data: {
scenario, scenario,
customer: cusDetails, customer: apiCustomer,
entity: entityRes, entity: entityRes,
updated_product: productRes, updated_product: productRes,
}, },

View File

@@ -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 { JobName } from "@/queue/JobName.js";
import { getLock, releaseLock } from "@/queue/lockUtils.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 { handleCustomerCreated } from "./handlers/handleCustomerCreated.js";
import { handleProductsUpdated } from "./handlers/handleProductsUpdated.js";
export const runActionHandlerTask = async ({ export const runActionHandlerTask = async ({
queue, queue,
job, job,
logger,
db,
useBackup, useBackup,
ctx,
}: { }: {
queue: Queue; queue: Queue;
job: Job; job: Job;
logger: any;
db: DrizzleCli;
useBackup: boolean; useBackup: boolean;
ctx: AutumnContext;
}) => { }) => {
let payload = job.data; const payload = job.data;
let internalCustomerId = payload.internalCustomerId; const internalCustomerId = payload.internalCustomerId;
let lockKey = `action:${internalCustomerId}`; const lockKey = `action:${internalCustomerId}`;
const { logger } = ctx;
try { try {
let lock = await getLock({ queue, job, lockKey, useBackup }); const lock = await getLock({ queue, job, lockKey, useBackup });
if (!lock) return; if (!lock) return;
switch (job.name) { switch (job.name) {
case JobName.HandleProductsUpdated: case JobName.HandleProductsUpdated:
await handleProductsUpdated({ await handleProductsUpdated({
db, ctx,
logger,
data: payload, data: payload,
}); });
break; break;
case JobName.HandleCustomerCreated: case JobName.HandleCustomerCreated:
await handleCustomerCreated({ await handleCustomerCreated({
db, ctx,
logger,
data: payload, data: payload,
}); });
break; break;
} }
} catch (error: any) { } catch (error: any) {
logger.error("Error processing action handler job:", { logger.error(`Error processing action handler job: ${error.message}`);
// jobName: job.name,
// payload,
message: error.message,
});
} finally { } finally {
await releaseLock({ lockKey, useBackup }); await releaseLock({ lockKey, useBackup });
} }

View File

@@ -17,7 +17,7 @@ import { handleGetOrg } from "../orgs/handlers/handleGetOrg.js";
import { platformRouter } from "../platform/platformLegacy/platformRouter.js"; import { platformRouter } from "../platform/platformLegacy/platformRouter.js";
import { productBetaRouter, productRouter } from "../products/productRouter.js"; import { productBetaRouter, productRouter } from "../products/productRouter.js";
import { componentRouter } from "./components/componentRouter.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 { checkRouter } from "./entitled/checkRouter.js";
import { eventsRouter } from "./events/eventRouter.js"; import { eventsRouter } from "./events/eventRouter.js";
import { usageRouter } from "./events/usageRouter.js"; import { usageRouter } from "./events/usageRouter.js";
@@ -44,7 +44,7 @@ apiRouter.use("/features", featureRouter);
apiRouter.use("/internal_features", internalFeatureRouter); apiRouter.use("/internal_features", internalFeatureRouter);
apiRouter.use("/usage", usageRouter); apiRouter.use("/usage", usageRouter);
apiRouter.use("/entities", entityRouter); apiRouter.use("/entities", expressEntityRouter);
apiRouter.use("/migrations", migrationRouter); apiRouter.use("/migrations", migrationRouter);
// REWARDS // REWARDS

View File

@@ -1,6 +1,7 @@
import { import {
type ApiVersionClass, type ApiVersionClass,
type AppEnv, type AppEnv,
AuthType,
CusExpand, CusExpand,
type CusProductStatus, type CusProductStatus,
type Feature, type Feature,
@@ -10,7 +11,8 @@ import {
import type { NodeClickHouseClient } from "@clickhouse/client/dist/client.js"; import type { NodeClickHouseClient } from "@clickhouse/client/dist/client.js";
import type { DrizzleCli } from "@/db/initDrizzle.js"; import type { DrizzleCli } from "@/db/initDrizzle.js";
import { RELEVANT_STATUSES } from "@/internal/customers/cusProducts/CusProductService.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"; import { getPaginatedFullCusQuery } from "../../customers/getFullCusQuery.js";
export class CusBatchService { export class CusBatchService {
@@ -95,20 +97,24 @@ export class CusBatchService {
const customer = normalizedCustomer as FullCustomer; const customer = normalizedCustomer as FullCustomer;
const cusProducts = customer.customer_products || []; const cusProducts = customer.customer_products || [];
const customerDetails = await getCustomerDetails({ const apiCustomer = await getApiCustomer({
db, ctx: {
customer, db,
features, org,
org, env,
env, features,
params: {}, logger,
logger: console, apiVersion,
cusProducts, id: generateId("local_req"),
isPublic: false,
authType: AuthType.Unknown,
timestamp: Date.now(),
},
fullCus: customer,
expand: expand, expand: expand,
apiVersion: apiVersion,
}); });
finals.push(customerDetails); finals.push(apiCustomer);
} catch (error) { } catch (error) {
console.error(`Failed to process customer ${result.id}:`, error); console.error(`Failed to process customer ${result.id}:`, error);
} }
@@ -124,7 +130,7 @@ export class CusBatchService {
const normalizeTimestamp = (value: any): number => { const normalizeTimestamp = (value: any): number => {
if (typeof value === "string") { if (typeof value === "string") {
const parsed = parseInt(value, 10); 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(); return typeof value === "number" ? value : Date.now();
}; };

View File

@@ -1,24 +1,23 @@
import { Router } from "express"; import { Router } from "express";
import { routeHandler } from "@/utils/routerUtils.js";
import { CusService } from "@/internal/customers/CusService.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 { handlePostEntityRequest } from "../../entities/handlers/handleCreateEntity/handleCreateEntity.js";
import { handleDeleteEntity } from "./handlers/handleDeleteEntity.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 // List entityes
entityRouter.get("", (req: any, res: any) => expressEntityRouter.get("", (req: any, res: any) =>
routeHandler({ routeHandler({
req, req,
res, res,
action: "listEntities", action: "listEntities",
handler: async (req, res) => { handler: async (req, res) => {
const customerId = String(req.params.customer_id); 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, db: req.db,
idOrInternalId: customerId, idOrInternalId: customerId,
orgId, orgId,
@@ -34,10 +33,14 @@ entityRouter.get("", (req: any, res: any) =>
); );
// 1. Create entity // 1. Create entity
entityRouter.post("", handlePostEntityRequest); expressEntityRouter.post("", handlePostEntityRequest);
// 2. Delete entity // 2. Delete entity
entityRouter.delete("/:entity_id", handleDeleteEntity); expressEntityRouter.delete("/:entity_id", handleDeleteEntity);
// 3. Get entity // 3. Get entity
entityRouter.get("/:entity_id", handleGetEntity); expressEntityRouter.get("/:entity_id", handleGetEntity);
// export const entityRouter = new Hono<HonoEnv>();
// entityRouter.get("/:entity_id", ...handleGetEntity);

View File

@@ -1,44 +1,34 @@
import { import {
type ApiCusProduct, type ApiEntity,
ApiVersion,
ApiVersionClass,
type AppEnv,
type Entity, type Entity,
type EntityExpand, type EntityExpand,
type EntityResponse,
ErrCode, ErrCode,
type Feature,
type FullCusProduct, type FullCusProduct,
type FullCustomer, type FullCustomer,
notNullish, notNullish,
type Organization,
type Subscription, type Subscription,
} from "@autumn/shared"; } from "@autumn/shared";
import type { DrizzleCli } from "@/db/initDrizzle.js"; import type { AutumnContext } from "@/honoUtils/HonoEnv.js";
import { getCusWithCache } from "@/internal/customers/cusCache/getCusWithCache.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 { getCusFeaturesResponse } from "@/internal/customers/cusUtils/cusFeatureResponseUtils/getCusFeaturesResponse.js";
import { processFullCusProducts } from "@/internal/customers/cusUtils/cusProductResponseUtils/processFullCusProducts.js";
import RecaseError from "@/utils/errorUtils.js"; import RecaseError from "@/utils/errorUtils.js";
import { nullish } from "@/utils/genUtils.js"; import { nullish } from "@/utils/genUtils.js";
export const getSingleEntityResponse = async ({ export const getSingleEntityResponse = async ({
ctx,
entityId, entityId,
org,
env,
fullCus, fullCus,
entity, entity,
features,
withAutumnId = false, withAutumnId = false,
}: { }: {
ctx: AutumnContext;
entityId: string; entityId: string;
org: Organization;
env: AppEnv;
fullCus: FullCustomer; fullCus: FullCustomer;
entity: Entity; entity: Entity;
features: Feature[];
withAutumnId?: boolean; withAutumnId?: boolean;
}) => { }) => {
const apiVersion = new ApiVersionClass(ApiVersion.V1_2); const { org, env, apiVersion } = ctx;
if (!entity) { if (!entity) {
throw new RecaseError({ throw new RecaseError({
@@ -70,16 +60,24 @@ export const getSingleEntityResponse = async ({
), ),
); );
const { main, addOns } = await processFullCusProducts({ const fullEntity = structuredClone(fullCus);
fullCusProducts: entityCusProducts, fullEntity.customer_products = entityCusProducts;
entity, fullEntity.subscriptions = entitySubs;
subs: entitySubs,
org,
apiVersion,
features,
});
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({ const cusFeatures = await getCusFeaturesResponse({
cusProducts: entityCusProducts, cusProducts: entityCusProducts,
@@ -96,47 +94,52 @@ export const getSingleEntityResponse = async ({
// feature_id: entity.feature_id, // feature_id: entity.feature_id,
customer_id: fullCus.id || fullCus.internal_id, customer_id: fullCus.id || fullCus.internal_id,
env, env,
products, plans: apiCusPlans,
features: cusFeatures, features: cusFeatures,
}; } satisfies ApiEntity;
}; };
export const getEntityResponse = async ({ export const getEntityResponse = async ({
db, ctx,
entityIds, entityIds,
org,
env,
customerId, customerId,
expand, expand,
entityId, entityId,
withAutumnId = false, withAutumnId = false,
apiVersion,
features,
logger,
skipCache = false, skipCache = false,
}: { }: {
db: DrizzleCli; ctx: AutumnContext;
entityIds: string[]; entityIds: string[];
org: Organization;
env: AppEnv;
customerId: string; customerId: string;
expand?: EntityExpand[]; expand?: EntityExpand[];
entityId?: string; entityId?: string;
withAutumnId?: boolean; withAutumnId?: boolean;
apiVersion: number;
features: Feature[];
logger: any;
skipCache?: boolean; 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, db,
idOrInternalId: customerId, idOrInternalId: customerId,
org, orgId: org.id,
env, env,
expand,
entityId, entityId,
logger, expand,
skipCache, withSubs: true,
withEntities: true,
}); });
if (!fullCus) { if (!fullCus) {
@@ -147,7 +150,7 @@ export const getEntityResponse = async ({
}); });
} }
const entityResponses: EntityResponse[] = []; const entityResponses: ApiEntity[] = [];
for (const entityId of entityIds) { for (const entityId of entityIds) {
const entity = fullCus.entities.find( const entity = fullCus.entities.find(
@@ -163,12 +166,10 @@ export const getEntityResponse = async ({
} }
const entityResponse = await getSingleEntityResponse({ const entityResponse = await getSingleEntityResponse({
ctx,
entityId, entityId,
org,
env,
fullCus, fullCus,
entity, entity,
features,
withAutumnId, withAutumnId,
}); });

View File

@@ -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 { invoicesToResponse } from "@/internal/invoices/invoiceUtils.js";
import { OrgService } from "@/internal/orgs/OrgService.js";
import { routeHandler } from "@/utils/routerUtils.js"; import { routeHandler } from "@/utils/routerUtils.js";
import { orgToVersion } from "@/utils/versionUtils/legacyVersionUtils.js";
import { parseEntityExpand } from "../entityUtils.js"; import { parseEntityExpand } from "../entityUtils.js";
import { getEntityResponse } from "../getEntityUtils.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 customerId = req.params.customer_id as string;
const expand = parseEntityExpand(req.query.expand); const expand = parseEntityExpand(req.query.expand);
const { env, db, logger, features } = req; const { logger } = req;
const org = await OrgService.getFromReq(req); const { entities, invoices } = await getEntityResponse({
const apiVersion = orgToVersion({ ctx: req,
org, entityIds: [entityId],
reqApiVersion: customerId,
req.apiVersion >= LegacyVersion.v1_1 expand,
? req.apiVersion entityId,
: LegacyVersion.v1_2,
}); });
// 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 entity = entities[0];
const withInvoices = expand.includes(EntityExpand.Invoices); const withInvoices = expand.includes(EntityExpand.Invoices);

View File

@@ -196,20 +196,3 @@ checkRouter.post("", async (req: any, res: any) => {
handleRequestError({ req, error, res, action: "Failed to GET entitled" }); 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

View File

@@ -1,4 +1,5 @@
import { import {
cusProductToProduct,
type Feature, type Feature,
FeaturePreviewScenario, FeaturePreviewScenario,
type FullCusProduct, type FullCusProduct,
@@ -6,7 +7,6 @@ import {
type FullProduct, type FullProduct,
} from "@autumn/shared"; } from "@autumn/shared";
import type { DrizzleCli } from "@/db/initDrizzle.js"; import type { DrizzleCli } from "@/db/initDrizzle.js";
import { fullCusProductToProduct } from "@/internal/customers/cusProducts/cusProductUtils.js";
import { ProductService } from "@/internal/products/ProductService.js"; import { ProductService } from "@/internal/products/ProductService.js";
import { getProductResponse } from "@/internal/products/productUtils/productResponseUtils/getProductResponse.js"; import { getProductResponse } from "@/internal/products/productUtils/productResponseUtils/getProductResponse.js";
import { import {
@@ -43,7 +43,7 @@ export const getCheckPreview = async ({
); );
const cusOwnedProducts = mainCusProds.map((cp: FullCusProduct) => const cusOwnedProducts = mainCusProds.map((cp: FullCusProduct) =>
fullCusProductToProduct(cp), cusProductToProduct({ cusProduct: cp }),
); );
sortProductsByPrice({ products: cusOwnedProducts }); sortProductsByPrice({ products: cusOwnedProducts });

View File

@@ -1,37 +1,34 @@
import { AttachParams } from "@/internal/customers/cusProducts/AttachParams.js";
import { import {
AttachReplaceable, type AttachReplaceable,
BillingType, BillingType,
FullCusProduct, type FullCusProduct,
FullCustomerEntitlement, type FullCustomerEntitlement,
FullEntitlement, type FullEntitlement,
getFeatureInvoiceDescription, getFeatureInvoiceDescription,
intervalsSame, type PreviewLineItem,
PreviewLineItem, type Price,
Price,
} from "@autumn/shared"; } from "@autumn/shared";
import Stripe from "stripe"; import { Decimal } from "decimal.js";
import { attachParamsToProduct } from "../convertAttachParams.js"; import type Stripe from "stripe";
import { import type { AttachParams } from "@/internal/customers/cusProducts/AttachParams.js";
getBillingType,
getPriceEntitlement,
getPriceForOverage,
} from "@/internal/products/prices/priceUtils.js";
import { findCusEnt } from "@/internal/customers/cusProducts/cusEnts/cusEntUtils/findCusEntUtils.js"; import { findCusEnt } from "@/internal/customers/cusProducts/cusEnts/cusEntUtils/findCusEntUtils.js";
import { import {
getExistingUsageFromCusProducts, getExistingUsageFromCusProducts,
getRelatedCusPrice, getRelatedCusPrice,
} from "@/internal/customers/cusProducts/cusEnts/cusEntUtils.js"; } 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 { newPriceToInvoiceDescription } from "@/internal/invoices/invoiceFormatUtils.js";
import { getCurContUseItems } from "@/internal/invoices/previewItemUtils/getCurContUseItems.js";
import { getDefaultPriceStr } from "@/internal/invoices/previewItemUtils/getItemsForNewProduct.js"; import { getDefaultPriceStr } from "@/internal/invoices/previewItemUtils/getItemsForNewProduct.js";
import { priceToUsageModel } from "@/internal/products/prices/priceUtils/convertPrice.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 { 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 ({ export const getContUseNewItems = async ({
price, price,
@@ -56,14 +53,14 @@ export const getContUseNewItems = async ({
internalEntityId: attachParams.internalEntityId, internalEntityId: attachParams.internalEntityId,
}); });
let description = newPriceToInvoiceDescription({ const description = newPriceToInvoiceDescription({
org, org,
price, price,
product: newProduct, product: newProduct,
quantity: usage, quantity: usage,
}); });
if (usage == 0) { if (usage === 0) {
return { return {
price_id: price.id, price_id: price.id,
price: getDefaultPriceStr({ org, price, ent, features }), price: getDefaultPriceStr({ org, price, ent, features }),
@@ -80,8 +77,8 @@ export const getContUseNewItems = async ({
prevCusEnt && prevCusEnt &&
!shouldProrate(price.proration_config?.on_decrease) !shouldProrate(price.proration_config?.on_decrease)
) { ) {
let isDowngrade = ent.allowance! > prevCusEnt.entitlement.allowance!; const isDowngrade = ent.allowance! > prevCusEnt.entitlement.allowance!;
let prevBalance = prevCusEnt.balance!; const prevBalance = prevCusEnt.balance!;
if (isDowngrade && prevBalance < 0) { if (isDowngrade && prevBalance < 0) {
overage = new Decimal(prevBalance).abs().toNumber(); overage = new Decimal(prevBalance).abs().toNumber();
@@ -89,8 +86,8 @@ export const getContUseNewItems = async ({
} }
} }
let amount = getPriceForOverage(price, overage); const amount = getPriceForOverage(price, overage);
let description = getFeatureInvoiceDescription({ const description = getFeatureInvoiceDescription({
feature: ent.feature, feature: ent.feature,
usage: usage, usage: usage,
prodName: newProduct.name, prodName: newProduct.name,
@@ -130,24 +127,24 @@ export const getContUseInvoiceItems = async ({
}) })
: []; : [];
let newEnts = product.entitlements; const newEnts = product.entitlements;
let oldItems: PreviewLineItem[] = []; const oldItems: PreviewLineItem[] = [];
let newItems: PreviewLineItem[] = []; const newItems: PreviewLineItem[] = [];
let replaceables: AttachReplaceable[] = []; const replaceables: AttachReplaceable[] = [];
for (const price of product.prices) { for (const price of product.prices) {
let billingType = getBillingType(price.config); const billingType = getBillingType(price.config);
if (billingType !== BillingType.InArrearProrated) { if (billingType !== BillingType.InArrearProrated) {
continue; continue;
} }
let ent = getPriceEntitlement(price, newEnts); const ent = getPriceEntitlement(price, newEnts);
let prevCusEnt = findCusEnt({ const prevCusEnt = findCusEnt({
cusEnts, cusEnts,
feature: ent.feature, feature: ent.feature,
}); });
let prevCusPrice = prevCusEnt const prevCusPrice = prevCusEnt
? getRelatedCusPrice(prevCusEnt, cusPrices)! ? getRelatedCusPrice(prevCusEnt, cusPrices)!
: undefined; : undefined;
@@ -176,7 +173,7 @@ export const getContUseInvoiceItems = async ({
(item) => item.price_id === prevCusPrice?.price.id, (item) => item.price_id === prevCusPrice?.price.id,
); );
let { const {
oldItem, oldItem,
newItems: newItems_, newItems: newItems_,
replaceables: replaceables_, replaceables: replaceables_,

View File

@@ -1,25 +1,25 @@
import { AttachParams } from "@/internal/customers/cusProducts/AttachParams.js";
import { import {
FullCustomerEntitlement, cusProductsToCusPrices,
FullEntitlement, type FullCustomerEntitlement,
PreviewLineItem, type FullEntitlement,
Price, type PreviewLineItem,
type Price,
} from "@autumn/shared"; } from "@autumn/shared";
import Stripe from "stripe"; import { Decimal } from "decimal.js";
import { attachParamsToProduct } from "../convertAttachParams.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 { import {
getExistingUsageFromCusProducts, getExistingUsageFromCusProducts,
getRelatedCusPrice, getRelatedCusPrice,
} from "@/internal/customers/cusProducts/cusEnts/cusEntUtils.js"; } 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 { 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 { notNullish } from "@/utils/genUtils.js";
import { cusProductsToCusPrices } from "@autumn/shared"; import { attachParamsToProduct } from "../convertAttachParams.js";
import { findStripeItemForPrice } from "@/external/stripe/stripeSubUtils/stripeSubItemUtils.js"; import { getContUseDowngradeItems } from "./getContUseDowngradeItems.js";
import { subToPeriodStartEnd } from "@/external/stripe/stripeSubUtils/convertSubUtils.js"; import { getContUseUpgradeItems } from "./getContUseUpgradeItems.js";
export const priceToContUseItem = async ({ export const priceToContUseItem = async ({
price, price,
@@ -74,7 +74,7 @@ export const priceToContUseItem = async ({
: shouldProrate(price.proration_config?.on_increase); : shouldProrate(price.proration_config?.on_increase);
// 1. Get current usage // 1. Get current usage
let curUsage = getExistingUsageFromCusProducts({ const curUsage = getExistingUsageFromCusProducts({
entitlement: ent, entitlement: ent,
cusProducts, cusProducts,
entities, entities,
@@ -132,8 +132,8 @@ export const priceToContUseItem = async ({
// Clean up items // Clean up items
// 1. If old item and new item same, remove both // 1. If old item and new item same, remove both
let oldAmount = res.oldItem?.amount!; const oldAmount = res.oldItem?.amount ?? 0;
let newAmount = res.newItem?.amount!; const newAmount = res.newItem?.amount ?? 0;
if (new Decimal(oldAmount).add(newAmount).eq(0)) { if (new Decimal(oldAmount).add(newAmount).eq(0)) {
return { return {

View File

@@ -1,37 +1,4 @@
import { import type { FullCusProduct } from "@autumn/shared";
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";
export const getPricesForCusProduct = ({ export const getPricesForCusProduct = ({
cusProduct, cusProduct,

View File

@@ -1,8 +1,6 @@
import { cusProductToProduct, type FullCusProduct } from "@autumn/shared";
import { getPricesForCusProduct } from "../scheduleUtils.js"; import { getPricesForCusProduct } from "../scheduleUtils.js";
import type { ScheduleObj } from "./ScheduleObj.js";
import { FullCusProduct } from "@autumn/shared";
import { ScheduleObj } from "./ScheduleObj.js";
import { fullCusProductToProduct } from "../../cusProducts/cusProductUtils.js";
export const getFilteredScheduleItems = ({ export const getFilteredScheduleItems = ({
scheduleObj, scheduleObj,
cusProducts, cusProducts,
@@ -11,7 +9,7 @@ export const getFilteredScheduleItems = ({
cusProducts: (FullCusProduct | undefined)[]; cusProducts: (FullCusProduct | undefined)[];
}) => { }) => {
const { schedule, prices } = scheduleObj; const { schedule, prices } = scheduleObj;
let scheduleItems = schedule.phases[0].items; const scheduleItems = schedule.phases[0].items;
let curPrices: any[] = []; let curPrices: any[] = [];
for (const cusProduct of cusProducts) { for (const cusProduct of cusProducts) {
@@ -20,14 +18,14 @@ export const getFilteredScheduleItems = ({
} }
} }
let products = cusProducts const products = cusProducts
.filter((cp): cp is FullCusProduct => !!cp) .filter((cp): cp is FullCusProduct => !!cp)
.map((cp: FullCusProduct) => fullCusProductToProduct(cp)); .map((cp: FullCusProduct) => cusProductToProduct({ cusProduct: cp }));
return scheduleItems.filter((scheduleItem: any) => { 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( curPrices.some(
(price) => (price) =>
price.config?.stripe_price_id === scheduleItem.price || price.config?.stripe_price_id === scheduleItem.price ||

View File

@@ -223,10 +223,10 @@ export const getUnlimitedAndUsageAllowed = ({
); );
const usageAllowed = cusEnts.some( const usageAllowed = cusEnts.some(
(ent) => (ce) =>
ent.internal_feature_id === internalFeatureId && ce.internal_feature_id === internalFeatureId &&
ent.usage_allowed && ce.usage_allowed &&
(includeUsageLimit ? nullish(ent.entitlement.usage_limit) : true), (includeUsageLimit ? nullish(ce.entitlement.usage_limit) : true),
); );
return { unlimited, usageAllowed }; return { unlimited, usageAllowed };

View File

@@ -1,12 +1,10 @@
import { import {
ACTIVE_STATUSES, ACTIVE_STATUSES,
ApiCusProductSchema, ApiCusPlanSchema,
ApiVersion, ApiVersion,
type ApiVersionClass, type ApiVersionClass,
type AppEnv,
AttachScenario, AttachScenario,
CusProductStatus, CusProductStatus,
cusProductToPrices,
type Entity, type Entity,
type FixedPriceConfig, type FixedPriceConfig,
type FullCusProduct, type FullCusProduct,
@@ -20,7 +18,6 @@ import {
} from "@autumn/shared"; } from "@autumn/shared";
import type { DrizzleCli } from "@/db/initDrizzle.js"; import type { DrizzleCli } from "@/db/initDrizzle.js";
import { createStripeCli } from "@/external/connect/createStripeCli.js"; import { createStripeCli } from "@/external/connect/createStripeCli.js";
import { getStripeSubs } from "@/external/stripe/stripeSubUtils.js";
import { addProductsUpdatedWebhookTask } from "@/internal/analytics/handlers/handleProductsUpdated.js"; import { addProductsUpdatedWebhookTask } from "@/internal/analytics/handlers/handleProductsUpdated.js";
import { ProductService } from "@/internal/products/ProductService.js"; import { ProductService } from "@/internal/products/ProductService.js";
import { import {
@@ -28,7 +25,7 @@ import {
getUsageTier, getUsageTier,
} from "@/internal/products/prices/priceUtils.js"; } from "@/internal/products/prices/priceUtils.js";
import { isDefaultTrialFullProduct } from "@/internal/products/productUtils/classifyProduct.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 { notNullish, nullish } from "@/utils/genUtils.js";
import type { ExtendedRequest } from "@/utils/models/Request.js"; import type { ExtendedRequest } from "@/utils/models/Request.js";
import { handleAddProduct } from "../attach/attachFunctions/addProductFlow/handleAddProduct.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 { getRelatedCusEnt } from "./cusPrices/cusPriceUtils.js";
import { getExistingCusProducts } from "./cusProductUtils/getExistingCusProducts.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 ({ export const getDefaultProduct = async ({
req, req,
productGroup, productGroup,
@@ -160,22 +83,6 @@ export const activateDefaultProduct = async ({
p.group === productGroup && !isDefaultTrialFullProduct({ product: p }), 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 (!defaultProd) return false;
if (curCusProduct?.internal_product_id === defaultProd.internal_id) { if (curCusProduct?.internal_product_id === defaultProd.internal_id) {
@@ -221,96 +128,6 @@ export const activateDefaultProduct = async ({
}); });
return true; 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 ({ export const activateFutureProduct = async ({
@@ -356,45 +173,8 @@ export const activateFutureProduct = async ({
}); });
return futureProduct; 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 = ({ export const processFullCusProduct = ({
cusProduct, cusProduct,
subs, subs,
@@ -512,7 +292,7 @@ export const processFullCusProduct = ({
}; };
} }
return ApiCusProductSchema.parse({ return ApiCusPlanSchema.parse({
id: cusProduct.product.id, id: cusProduct.product.id,
name: cusProduct.product.name, name: cusProduct.product.name,
group: cusProduct.product.group || null, 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 = ({ export const searchCusProducts = ({
productId, productId,
internalProductId, internalProductId,

View File

@@ -9,7 +9,7 @@ import { CusSearchService } from "@/internal/customers/CusSearchService.js";
import { OrgService } from "@/internal/orgs/OrgService.js"; import { OrgService } from "@/internal/orgs/OrgService.js";
import RecaseError, { handleRequestError } from "@/utils/errorUtils.js"; import RecaseError, { handleRequestError } from "@/utils/errorUtils.js";
import { handleBatchCustomers } from "../api/batch/handlers/handleBatchCustomers.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 { toSuccessUrl } from "../orgs/orgUtils/convertOrgUtils.js";
import { CusService } from "./CusService.js"; import { CusService } from "./CusService.js";
import { handleAddCouponToCus } from "./handlers/handleAddCouponToCus.js"; import { handleAddCouponToCus } from "./handlers/handleAddCouponToCus.js";
@@ -63,11 +63,6 @@ expressCusRouter.post(
expressCusRouter.post("/:customer_id/balances", handleUpdateBalances); expressCusRouter.post("/:customer_id/balances", handleUpdateBalances);
// cusRouter.post(
// "/customer_products/:customer_product_id",
// handleCusProductExpired
// );
expressCusRouter.get( expressCusRouter.get(
"/:customer_id/billing_portal", "/:customer_id/billing_portal",
async (req: any, res: any) => { async (req: any, res: any) => {
@@ -143,7 +138,7 @@ expressCusRouter.post(
expressCusRouter.post("/:customer_id/coupons/:coupon_id", handleAddCouponToCus); 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); expressCusRouter.post("/:customer_id/transfer", handleTransferProduct);

View File

@@ -1,33 +1,38 @@
import { import {
type ApiCusFeature, type ApiCusFeature,
type ApiCusRollover, type ApiCusRollover,
ApiFeatureType, type ApiFeature,
cusEntToKey, cusEntToKey,
type EntInterval, entIntvToResetIntv,
type FullCusEntWithFullCusProduct, type FullCusEntWithFullCusProduct,
getRolloverFields, getRolloverFields,
notNullish, notNullish,
ResetInterval,
toIntervalCountResponse,
} from "@autumn/shared"; } from "@autumn/shared";
import { getCusFeatureType } from "@/internal/features/featureUtils.js";
export const cusEntsToInterval = ({ export const cusEntsToInterval = ({
cusEnts, cusEnts,
}: { }: {
cusEnts: FullCusEntWithFullCusProduct[]; cusEnts: FullCusEntWithFullCusProduct[];
}): { }): {
interval: EntInterval | "multiple" | null; interval: ResetInterval | null;
interval_count: number | null; interval_count: number | undefined;
} => { } => {
const cusEntKeys = cusEnts.map((cusEnt) => cusEntToKey({ cusEnt })); const cusEntKeys = cusEnts.map((cusEnt) => cusEntToKey({ cusEnt }));
const uniqueCusEntKeys = [...new Set(cusEntKeys)]; const uniqueCusEntKeys = [...new Set(cusEntKeys)];
if (uniqueCusEntKeys.length === 1) { if (uniqueCusEntKeys.length === 1) {
return { return {
interval: cusEnts[0].entitlement.interval || null, interval: entIntvToResetIntv({
interval_count: cusEnts[0].entitlement.interval_count, 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 = ({ export const cusEntsToNextResetAt = ({
@@ -76,39 +81,66 @@ export const cusEntsToRollovers = ({
export const getBooleanApiCusFeature = ({ export const getBooleanApiCusFeature = ({
cusEnts, cusEnts,
apiFeature,
}: { }: {
cusEnts: FullCusEntWithFullCusProduct[]; cusEnts: FullCusEntWithFullCusProduct[];
apiFeature?: ApiFeature;
}): ApiCusFeature => { }): ApiCusFeature => {
const feature = cusEnts[0].entitlement.feature; const feature = cusEnts[0].entitlement.feature;
return { return {
id: feature.id, feature: apiFeature,
type: ApiFeatureType.Static, feature_id: feature.id,
name: feature.name,
unlimited: false,
starting_balance: 0,
balance: 0, balance: 0,
usage: 0, usage: 0,
included_usage: 0,
next_reset_at: null, resets_at: null,
unlimited: false, reset_interval: ResetInterval.OneOff,
overage_allowed: false, 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 = ({ export const getUnlimitedApiCusFeature = ({
apiFeature,
cusEnts, cusEnts,
}: { }: {
apiFeature?: ApiFeature;
cusEnts: FullCusEntWithFullCusProduct[]; cusEnts: FullCusEntWithFullCusProduct[];
}): ApiCusFeature => { }): ApiCusFeature => {
const feature = cusEnts[0].entitlement.feature; const feature = cusEnts[0].entitlement.feature;
return { return {
id: feature.id, feature: apiFeature,
type: getCusFeatureType({ feature }), feature_id: feature.id,
name: feature.name,
unlimited: true,
starting_balance: 0,
balance: 0, balance: 0,
usage: 0, usage: 0,
included_usage: 0,
next_reset_at: null, resets_at: null,
unlimited: true, reset_interval: ResetInterval.OneOff,
overage_allowed: false, reset_interval_count: undefined,
breakdown: undefined,
rollovers: undefined,
}; };
}; };

View File

@@ -6,10 +6,11 @@ import {
type ApiCusFeatureBreakdown, type ApiCusFeatureBreakdown,
ApiCusFeatureBreakdownSchema, ApiCusFeatureBreakdownSchema,
ApiCusFeatureSchema, ApiCusFeatureSchema,
CusExpand,
cusEntToBalance, cusEntToBalance,
cusEntToIncludedUsage, cusEntToIncludedUsage,
cusEntToKey, cusEntToKey,
cusEntToUsageLimit, cusEntToMaxPurchase,
type Feature, type Feature,
FeatureType, FeatureType,
getCusEntBalance, getCusEntBalance,
@@ -20,7 +21,6 @@ import {
import { Decimal } from "decimal.js"; import { Decimal } from "decimal.js";
import type { RequestContext } from "@/honoUtils/HonoEnv.js"; import type { RequestContext } from "@/honoUtils/HonoEnv.js";
import { getUnlimitedAndUsageAllowed } from "@/internal/customers/cusProducts/cusEnts/cusEntUtils.js"; import { getUnlimitedAndUsageAllowed } from "@/internal/customers/cusProducts/cusEnts/cusEntUtils.js";
import { getCusFeatureType } from "@/internal/features/featureUtils.js";
import { import {
cusEntsToInterval, cusEntsToInterval,
cusEntsToNextResetAt, cusEntsToNextResetAt,
@@ -60,11 +60,12 @@ const cusEntsToBreakdown = ({
cusEnts, cusEnts,
feature, feature,
}); });
breakdown.push( breakdown.push(
ApiCusFeatureBreakdownSchema.parse({ ApiCusFeatureBreakdownSchema.parse({
...breakdownItem, ...breakdownItem,
interval, reset_interval: interval,
interval_count, reset_interval_count: interval_count,
}), }),
); );
} }
@@ -85,21 +86,27 @@ export const getApiCusFeature = ({
}) => { }) => {
const entityId = fullCus.entity?.id; const entityId = fullCus.entity?.id;
const apiFeature = ctx.expand.includes(CusExpand.Feature)
? toApiFeature({ feature })
: undefined;
// 1. If feature is boolean // 1. If feature is boolean
if (feature.type === FeatureType.Boolean) { if (feature.type === FeatureType.Boolean) {
return getBooleanApiCusFeature({ return getBooleanApiCusFeature({
cusEnts, cusEnts,
apiFeature,
}); });
} }
const { unlimited, usageAllowed } = getUnlimitedAndUsageAllowed({ const { unlimited, usageAllowed } = getUnlimitedAndUsageAllowed({
cusEnts: cusEnts, cusEnts: cusEnts,
internalFeatureId: feature.internal_id, internalFeatureId: feature.internal_id,
includeUsageLimit: false,
}); });
// 2. If feature is unlimited // 2. If feature is unlimited
if (unlimited) { if (unlimited) {
return getUnlimitedApiCusFeature({ cusEnts: cusEnts }); return getUnlimitedApiCusFeature({ apiFeature, cusEnts });
} }
const totalBalanceWithRollovers = sumValues( const totalBalanceWithRollovers = sumValues(
@@ -130,8 +137,8 @@ export const getApiCusFeature = ({
const nextResetAt = cusEntsToNextResetAt({ cusEnts }); const nextResetAt = cusEntsToNextResetAt({ cusEnts });
const totalUsageLimit = sumValues( const totalMaxPurchase = sumValues(
cusEnts.map((cusEnt) => cusEntToUsageLimit({ cusEnt })), cusEnts.map((cusEnt) => cusEntToMaxPurchase({ cusEnt })),
); );
const totalIncludedUsage = sumValues( const totalIncludedUsage = sumValues(
@@ -154,30 +161,53 @@ export const getApiCusFeature = ({
const rollovers = cusEntsToRollovers({ cusEnts, entityId }); const rollovers = cusEntsToRollovers({ cusEnts, entityId });
const apiFeature = toApiFeature({ feature }); // const apiFeature = toApiFeature({ feature });
const { data: apiCusFeature, error } = ApiCusFeatureSchema.safeParse({ const { data: apiCusFeature, error } = ApiCusFeatureSchema.safeParse({
id: feature.id, feature: ctx.expand.includes(CusExpand.Feature) ? apiFeature : undefined,
name: feature.name,
type: getCusFeatureType({ feature }), feature_id: feature.id,
balance: totalBalanceWithRollovers,
usage: totalUsage,
included_usage: totalIncludedUsage,
usage_limit:
totalUsageLimit === totalIncludedUsage ? undefined : totalUsageLimit,
next_reset_at: nextResetAt,
unlimited: false, unlimited: false,
overage_allowed: usageAllowed,
interval, starting_balance: totalIncludedUsageWithRollovers,
interval_count, balance: totalBalanceWithRollovers,
rollovers,
credit_schema: usage: totalUsage,
apiFeature.credit_schema?.map((credit) => ({
feature_id: credit.metered_feature_id, resets_at: nextResetAt,
credit_amount: credit.credit_cost, reset_interval: interval,
})) || undefined, 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 }), 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; if (error) throw error;

View File

@@ -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,
// });

View File

@@ -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,
};
};

View File

@@ -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;
// }
};

View File

@@ -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,
};
};

View File

@@ -3,25 +3,22 @@ import {
type ApiCustomer, type ApiCustomer,
ApiCustomerSchema, ApiCustomerSchema,
applyResponseVersionChanges, applyResponseVersionChanges,
type CusExpand,
type CustomerLegacyData, type CustomerLegacyData,
type FullCustomer, type FullCustomer,
} from "@autumn/shared"; } from "@autumn/shared";
import { z } from "zod/v4"; import { z } from "zod/v4";
import type { RequestContext } from "@/honoUtils/HonoEnv.js"; import type { RequestContext } from "@/honoUtils/HonoEnv.js";
import { getApiCusFeatures } from "./getApiCusFeature/getApiCusFeatures.js"; import { getApiCusFeatures } from "./getApiCusFeature/getApiCusFeatures.js";
import { getApiCusProducts } from "./getApiCusProduct/getApiCusProducts.js"; import { getApiCusPlans } from "./getApiCusPlan/getApiCusPlans.js";
import { getApiCustomerExpand } from "./getApiCustomerExpand.js"; import { getApiCustomerExpand } from "./getApiCustomerExpand.js";
export const getApiCustomer = async ({ export const getApiCustomer = async ({
ctx, ctx,
fullCus, fullCus,
expand,
withAutumnId = false, withAutumnId = false,
}: { }: {
ctx: RequestContext; ctx: RequestContext;
fullCus: FullCustomer; fullCus: FullCustomer;
expand: CusExpand[];
withAutumnId?: boolean; withAutumnId?: boolean;
}) => { }) => {
const apiCusFeatures = await getApiCusFeatures({ const apiCusFeatures = await getApiCusFeatures({
@@ -29,8 +26,8 @@ export const getApiCustomer = async ({
fullCus, fullCus,
}); });
const { apiCusProducts, legacyData: cusProductLegacyData } = const { apiCusPlans, legacyData: cusProductLegacyData } =
await getApiCusProducts({ await getApiCusPlans({
ctx, ctx,
fullCus, fullCus,
}); });
@@ -38,7 +35,6 @@ export const getApiCustomer = async ({
const apiCusExpand = await getApiCustomerExpand({ const apiCusExpand = await getApiCustomerExpand({
ctx, ctx,
fullCus, fullCus,
expand,
}); });
const apiCustomer = ApiCustomerSchema.extend({ const apiCustomer = ApiCustomerSchema.extend({
@@ -56,16 +52,16 @@ export const getApiCustomer = async ({
env: fullCus.env, env: fullCus.env,
metadata: fullCus.metadata, metadata: fullCus.metadata,
products: apiCusProducts, plans: apiCusPlans,
features: apiCusFeatures, features: apiCusFeatures,
...apiCusExpand, ...apiCusExpand,
}); });
return applyResponseVersionChanges<ApiCustomer, CustomerLegacyData>({ return applyResponseVersionChanges<ApiCustomer, CustomerLegacyData>({
input: apiCustomer, input: apiCustomer,
legacyData: { legacyData: { cusProductLegacyData },
cusProductLegacyData,
},
targetVersion: ctx.apiVersion, targetVersion: ctx.apiVersion,
resource: AffectedResource.Customer, resource: AffectedResource.Customer,
}); });

View File

@@ -1,6 +1,6 @@
import { import {
ApiBaseEntitySchema, ApiBaseEntitySchema,
type ApiCustomerExpand, type ApiCusExpand,
CusExpand, CusExpand,
type FullCusProduct, type FullCusProduct,
type FullCustomer, type FullCustomer,
@@ -15,13 +15,12 @@ import { getCusUpcomingInvoice } from "../cusResponseUtils/getCusUpcomingInvoice
export const getApiCustomerExpand = async ({ export const getApiCustomerExpand = async ({
ctx, ctx,
fullCus, fullCus,
expand,
}: { }: {
ctx: AutumnContext; ctx: AutumnContext;
fullCus: FullCustomer; fullCus: FullCustomer;
expand: CusExpand[]; }): Promise<ApiCusExpand> => {
}): Promise<ApiCustomerExpand> => { const { org, env, db, logger, expand } = ctx;
const { org, env, db, logger } = ctx; console.log("Expand: ", expand);
const getCusTrialsUsed = () => { const getCusTrialsUsed = () => {
if (expand.includes(CusExpand.TrialsUsed)) { if (expand.includes(CusExpand.TrialsUsed)) {
return fullCus.trials_used; return fullCus.trials_used;
@@ -43,6 +42,8 @@ export const getApiCustomerExpand = async ({
}) })
: undefined; : undefined;
const cusExpand = expand as CusExpand[];
const [rewards, upcomingInvoice, referrals, paymentMethod] = const [rewards, upcomingInvoice, referrals, paymentMethod] =
await Promise.all([ await Promise.all([
getCusRewards({ getCusRewards({
@@ -52,25 +53,25 @@ export const getApiCustomerExpand = async ({
subIds: fullCus.customer_products.flatMap( subIds: fullCus.customer_products.flatMap(
(cp: FullCusProduct) => cp.subscription_ids || [], (cp: FullCusProduct) => cp.subscription_ids || [],
), ),
expand, expand: cusExpand,
}), }),
getCusUpcomingInvoice({ getCusUpcomingInvoice({
db, db,
org, org,
env, env,
fullCus, fullCus,
expand, expand: cusExpand,
}), }),
getCusReferrals({ getCusReferrals({
db, db,
fullCus, fullCus,
expand, expand: cusExpand,
}), }),
getCusPaymentMethodRes({ getCusPaymentMethodRes({
org, org,
env, env,
fullCus, fullCus,
expand, expand: cusExpand,
}), }),
]); ]);

View File

@@ -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;
}
};

View File

@@ -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,
};
};

View File

@@ -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,
};
}
};

View File

@@ -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",
});
}
};

View File

@@ -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);
},
});

View File

@@ -1,6 +1,9 @@
import { import {
AffectedResource,
ApiVersion,
backwardsChangeActive, backwardsChangeActive,
CusExpand, CusExpand,
type EntityExpand,
GetCustomerQuerySchema, GetCustomerQuerySchema,
V0_2_InvoicesAlwaysExpanded, V0_2_InvoicesAlwaysExpanded,
} from "@autumn/shared"; } from "@autumn/shared";
@@ -9,12 +12,15 @@ import { getCusWithCache } from "../cusCache/getCusWithCache.js";
import { getApiCustomer } from "../cusUtils/apiCusUtils/getApiCustomer.js"; import { getApiCustomer } from "../cusUtils/apiCusUtils/getApiCustomer.js";
export const handleGetCustomerV2 = createRoute({ export const handleGetCustomerV2 = createRoute({
query: GetCustomerQuerySchema, versionedQuery: {
latest: GetCustomerQuerySchema,
[ApiVersion.V1_2]: GetCustomerQuerySchema,
},
resource: AffectedResource.Customer,
handler: async (c) => { handler: async (c) => {
const ctx = c.get("ctx"); const ctx = c.get("ctx");
const customerId = c.req.param("customer_id"); const customerId = c.req.param("customer_id");
const { env, db, logger, org } = ctx; const { env, db, logger, org, expand } = ctx;
const { expand = [] } = c.req.valid("query");
// SIDE EFFECT // SIDE EFFECT
if ( if (
@@ -31,7 +37,7 @@ export const handleGetCustomerV2 = createRoute({
idOrInternalId: customerId, idOrInternalId: customerId,
org, org,
env, env,
expand, expand: expand as (CusExpand | EntityExpand)[],
logger, logger,
allowNotFound: false, allowNotFound: false,
}); });
@@ -39,7 +45,6 @@ export const handleGetCustomerV2 = createRoute({
const customer = await getApiCustomer({ const customer = await getApiCustomer({
ctx, ctx,
fullCus: fullCus, fullCus: fullCus,
expand,
}); });
return c.json(customer); return c.json(customer);

View File

@@ -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" });
}
};

View File

@@ -1,6 +1,7 @@
import { CreateCustomerSchema, ErrCode, ProcessorType } from "@autumn/shared"; import { CreateCustomerSchema, ErrCode, ProcessorType } from "@autumn/shared";
import { StatusCodes } from "http-status-codes"; import { StatusCodes } from "http-status-codes";
import { createStripeCli } from "@/external/connect/createStripeCli.js"; import { createStripeCli } from "@/external/connect/createStripeCli.js";
import type { AutumnContext } from "@/honoUtils/HonoEnv.js";
import { CusService } from "@/internal/customers/CusService.js"; import { CusService } from "@/internal/customers/CusService.js";
import { FeatureService } from "@/internal/features/FeatureService.js"; import { FeatureService } from "@/internal/features/FeatureService.js";
import RecaseError from "@/utils/errorUtils.js"; import RecaseError from "@/utils/errorUtils.js";
@@ -10,8 +11,8 @@ import type {
ExtendedResponse, ExtendedResponse,
} from "@/utils/models/Request.js"; } from "@/utils/models/Request.js";
import { routeHandler } from "@/utils/routerUtils.js"; import { routeHandler } from "@/utils/routerUtils.js";
import { getApiCustomer } from "../cusUtils/apiCusUtils/getApiCustomer.js";
import { parseCusExpand } from "../cusUtils/cusUtils.js"; import { parseCusExpand } from "../cusUtils/cusUtils.js";
import { getCustomerDetails } from "../cusUtils/getCustomerDetails.js";
export const handleUpdateCustomer = async (req: any, res: any) => export const handleUpdateCustomer = async (req: any, res: any) =>
routeHandler({ routeHandler({
@@ -134,16 +135,10 @@ export const handleUpdateCustomer = async (req: any, res: any) =>
}); });
// res.status(200).json({ customer: updatedCustomer }); // res.status(200).json({ customer: updatedCustomer });
const customerDetails = await getCustomerDetails({ const customerDetails = await getApiCustomer({
db, ctx: req as AutumnContext,
customer: finalCustomer, fullCus: finalCustomer,
org,
env: req.env,
logger: req.logger,
cusProducts: finalCustomer.customer_products,
expand: parseCusExpand(req.query.expand as string), expand: parseCusExpand(req.query.expand as string),
features,
apiVersion: req.apiVersion,
}); });
res.status(200).json(customerDetails); res.status(200).json(customerDetails);

View File

@@ -4,6 +4,7 @@ import {
type Entity, type Entity,
LegacyVersion, LegacyVersion,
} from "@autumn/shared"; } from "@autumn/shared";
import type { AutumnContext } from "@/honoUtils/HonoEnv.js";
import type { ExtendedRequest } from "@/utils/models/Request.js"; import type { ExtendedRequest } from "@/utils/models/Request.js";
import { routeHandler } from "@/utils/routerUtils.js"; import { routeHandler } from "@/utils/routerUtils.js";
import { orgToVersion } from "@/utils/versionUtils/legacyVersionUtils.js"; import { orgToVersion } from "@/utils/versionUtils/legacyVersionUtils.js";
@@ -91,16 +92,10 @@ export const createEntities = async ({
} }
const { entities } = await getEntityResponse({ const { entities } = await getEntityResponse({
db, ctx: req as AutumnContext,
entityIds: newEntities.map((e: any) => e.id || e.internal_id), entityIds: newEntities.map((e: any) => e.id || e.internal_id),
org,
env,
customerId: customer.id || customer.internal_id, customerId: customer.id || customer.internal_id,
withAutumnId, withAutumnId,
apiVersion: apiVersion!,
features,
logger,
skipCache: true,
}); });
return entities; return entities;

View File

@@ -1,8 +1,9 @@
import { CreateFeatureSchema, ErrCode, FeatureType } from "@autumn/shared"; import { CreateFeatureSchema, ErrCode, FeatureType } from "@autumn/shared";
import express, { type Router } from "express"; import express, { type Router } from "express";
import { formatZodError } from "@/errors/formatZodError.js";
import { handleDeleteFeature } from "@/internal/features/handlers/handleDeleteFeature.js"; import { handleDeleteFeature } from "@/internal/features/handlers/handleDeleteFeature.js";
import { handleUpdateFeature } from "@/internal/features/handlers/handleUpdateFeature.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 { generateId } from "@/utils/genUtils.js";
import { FeatureService } from "./FeatureService.js"; import { FeatureService } from "./FeatureService.js";
import { import {

View File

@@ -75,7 +75,7 @@ export const isV4Usage = ({
return ( return (
billingType === BillingType.UsageInArrear && billingType === BillingType.UsageInArrear &&
(cusProduct.api_semver === ApiVersion.Beta || (cusProduct.api_semver === ApiVersion.V1_Beta ||
notNullish(cusProduct.internal_entity_id)) notNullish(cusProduct.internal_entity_id))
); );
}; };

View File

@@ -1,10 +1,10 @@
import { BillingInterval, FullProduct } from "@autumn/shared";
import { generateObject } from "ai";
import { anthropic } from "@ai-sdk/anthropic"; 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 { z } from "zod";
import type { DrizzleCli } from "@/db/initDrizzle.js";
import { nullish } from "@/utils/genUtils.js"; import { nullish } from "@/utils/genUtils.js";
import { Logger } from "pino";
import { DrizzleCli } from "@/db/initDrizzle.js";
import { ProductService } from "../ProductService.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"). 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}`); logger.info(`Detecting base variant for ${curProduct.id}`);
if (!process.env.ANTHROPIC_API_KEY) return; if (!process.env.ANTHROPIC_API_KEY) return;
let existingProducts = (await ProductService.listFull({ const existingProducts = (await ProductService.listFull({
db, db,
orgId: curProduct.org_id, orgId: curProduct.org_id,
env: curProduct.env, env: curProduct.env,
@@ -43,8 +43,8 @@ export const detectBaseVariant = async ({
})) as FullProduct[]; })) as FullProduct[];
// if (product.base_variant_id == baseVariantId) { // if (product.base_variant_id == baseVariantId) {
let curPrices = curProduct.prices; const curPrices = curProduct.prices;
let intervals = curPrices.map((price) => price.config.interval); const intervals = curPrices.map((price) => price.config.interval);
// 1. Return null if add on // 1. Return null if add on
if (curProduct.is_add_on) return null; if (curProduct.is_add_on) return null;
@@ -58,17 +58,17 @@ export const detectBaseVariant = async ({
const filteredExistingProducts = existingProducts.filter( const filteredExistingProducts = existingProducts.filter(
(p) => (p) =>
p.id != curProduct.id && p.id !== curProduct.id &&
nullish(p.base_variant_id) && nullish(p.base_variant_id) &&
!p.is_add_on && !p.is_add_on &&
p.prices.length > 0 && p.prices.length > 0 &&
p.prices.every( 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`); logger.info(`No base product to search for`);
return null; return null;
} }
@@ -95,13 +95,13 @@ export const detectBaseVariant = async ({
</existing_products> </existing_products>
`; `;
let { object } = await generateObject({ const { object } = await generateObject({
model: anthropic("claude-3-5-haiku-latest"), model: anthropic("claude-3-5-haiku-latest"),
schema: z.object({ base_variant_id: z.string().nullable() }), schema: z.object({ base_variant_id: z.string().nullable() }),
prompt: `${prompt}\n\n${variables}`, prompt: `${prompt}\n\n${variables}`,
}); });
let baseVariantId = object.base_variant_id; const baseVariantId = object.base_variant_id;
logger.info( logger.info(
`llm response for base variant of ${curProduct.id}: ${baseVariantId}`, `llm response for base variant of ${curProduct.id}: ${baseVariantId}`,

View File

@@ -4,12 +4,12 @@ import {
type ApiPlan, type ApiPlan,
ApiPlanSchema, ApiPlanSchema,
AttachScenario, AttachScenario,
type BillingInterval,
type Feature, type Feature,
type FeatureOptions, type FeatureOptions,
type FullCustomer, type FullCustomer,
type FullProduct, type FullProduct,
itemsToPlanFeatures, itemsToPlanFeatures,
itemToBillingInterval,
productV2ToBasePrice, productV2ToBasePrice,
productV2ToFeatureItems, productV2ToFeatureItems,
} from "@autumn/shared"; } from "@autumn/shared";
@@ -114,62 +114,13 @@ export const getPlanResponse = async ({
attachScenario, 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 // 9. Build Plan response
return ApiPlanSchema.parse({ return ApiPlanSchema.parse({
// Basic fields // Basic fields
id: product.id, id: product.id,
name: product.name || "", name: product.name || null,
description: product.description, // Products don't have descriptions description: product.description || null, // Products don't have descriptions
group: product.group, group: product.group || null,
version: product.version, version: product.version,
// Boolean flags // Boolean flags
@@ -180,12 +131,11 @@ export const getPlanResponse = async ({
price: basePrice price: basePrice
? { ? {
amount: basePrice.amount, amount: basePrice.amount,
interval: basePrice.interval as BillingInterval, interval: itemToBillingInterval({ item: basePrice }),
interval_count:
basePrice.intervalCount !== 1 ? basePrice.intervalCount : undefined,
} }
: { : null,
amount: 0,
interval: "month" as BillingInterval,
},
// Features array // Features array
features: planFeatures, features: planFeatures,

View File

@@ -43,7 +43,7 @@ export const mapToProductItems = ({
} }
for (const item of items) { 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) { if (feature) {
item.feature_type = getItemFeatureType({ item, features }); item.feature_type = getItemFeatureType({ item, features });
} }
@@ -84,7 +84,7 @@ export const mapToProductV2 = ({
const productV2: ProductV2 = { const productV2: ProductV2 = {
internal_id: product.internal_id, internal_id: product.internal_id,
env: product.env,
id: product.id, id: product.id,
name: product.name, name: product.name,
is_add_on: product.is_add_on, is_add_on: product.is_add_on,

View File

@@ -12,6 +12,7 @@ import { runTriggerCheckoutReward } from "@/internal/rewards/triggerCheckoutRewa
import { runUpdateBalanceTask } from "@/trigger/updateBalanceTask.js"; import { runUpdateBalanceTask } from "@/trigger/updateBalanceTask.js";
import { runUpdateUsageTask } from "@/trigger/updateUsageTask.js"; import { runUpdateUsageTask } from "@/trigger/updateUsageTask.js";
import { generateId } from "@/utils/genUtils.js"; import { generateId } from "@/utils/genUtils.js";
import { createWorkerAutumnContext } from "@/utils/workerUtils/createAutumnContext.js";
import { JobName } from "./JobName.js"; import { JobName } from "./JobName.js";
import { acquireLock, getRedisConnection, releaseLock } from "./lockUtils.js"; import { acquireLock, getRedisConnection, releaseLock } from "./lockUtils.js";
import { QueueManager } from "./QueueManager.js"; import { QueueManager } from "./QueueManager.js";
@@ -79,11 +80,18 @@ const initWorker = ({
} }
if (actionHandlers.includes(job.name as JobName)) { 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({ await runActionHandlerTask({
queue, queue,
job, job,
logger: workerLogger, ctx,
db,
useBackup, useBackup,
}); });
return; return;

View File

@@ -1,5 +1,6 @@
import { import {
type AppEnv, type AppEnv,
AuthType,
createdAtToVersion, createdAtToVersion,
type Feature, type Feature,
type FullCusEntWithFullCusProduct, type FullCusEntWithFullCusProduct,
@@ -10,10 +11,12 @@ import {
} from "@autumn/shared"; } from "@autumn/shared";
import type { DrizzleCli } from "@/db/initDrizzle.js"; import type { DrizzleCli } from "@/db/initDrizzle.js";
import { sendSvixEvent } from "@/external/svix/svixHelpers.js"; import { sendSvixEvent } from "@/external/svix/svixHelpers.js";
import type { AutumnContext } from "@/honoUtils/HonoEnv.js";
import { getSingleEntityResponse } from "@/internal/api/entities/getEntityUtils.js"; import { getSingleEntityResponse } from "@/internal/api/entities/getEntityUtils.js";
import { getV2CheckResponse } from "@/internal/api/entitled/checkUtils/getV2CheckResponse.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 { toApiFeature } from "@/internal/features/utils/mapFeatureUtils.js";
import { generateId } from "@/utils/genUtils.js";
export const mergeNewCusEntsIntoCusProducts = ({ export const mergeNewCusEntsIntoCusProducts = ({
cusProducts, cusProducts,
@@ -39,45 +42,27 @@ export const mergeNewCusEntsIntoCusProducts = ({
}; };
export const sendSvixThresholdReachedEvent = async ({ export const sendSvixThresholdReachedEvent = async ({
db, ctx,
org,
env,
features,
logger,
feature, feature,
fullCus, fullCus,
thresholdType, thresholdType,
}: { }: {
db: DrizzleCli; ctx: AutumnContext;
org: Organization;
env: AppEnv;
features: Feature[];
logger: any;
feature: Feature; feature: Feature;
fullCus: FullCustomer; fullCus: FullCustomer;
thresholdType: "limit_reached" | "allowance_used"; thresholdType: "limit_reached" | "allowance_used";
}) => { }) => {
const apiVersion = createdAtToVersion({ const { org, env, logger } = ctx;
createdAt: org.created_at || undefined,
});
const cusDetails = await getCustomerDetails({ const cusDetails = await getApiCustomer({
db, ctx,
customer: fullCus, fullCus,
org,
env,
features,
logger,
cusProducts: fullCus.customer_products,
expand: [], expand: [],
apiVersion,
}); });
if (fullCus.entity) { if (fullCus.entity) {
await getSingleEntityResponse({ await getSingleEntityResponse({
org, ctx,
env,
features,
fullCus, fullCus,
entity: fullCus.entity, entity: fullCus.entity,
entityId: fullCus.entity.id, entityId: fullCus.entity.id,
@@ -100,31 +85,20 @@ export const sendSvixThresholdReachedEvent = async ({
}; };
export const handleAllowanceUsed = async ({ export const handleAllowanceUsed = async ({
db, ctx,
org,
env,
features,
logger,
cusEnts, cusEnts,
newCusEnts, newCusEnts,
feature, feature,
fullCus, fullCus,
}: { }: {
db: DrizzleCli; ctx: AutumnContext;
org: Organization;
env: AppEnv;
cusEnts: FullCusEntWithFullCusProduct[]; cusEnts: FullCusEntWithFullCusProduct[];
newCusEnts: FullCusEntWithFullCusProduct[]; newCusEnts: FullCusEntWithFullCusProduct[];
feature: Feature; feature: Feature;
fullCus: FullCustomer; fullCus: FullCustomer;
features: Feature[];
logger: any;
}) => { }) => {
const apiVersion = createdAtToVersion({ const { org, env, apiVersion } = ctx;
createdAt: org.created_at || undefined,
});
// Allowance used...
// Make sure overage allowed is false // Make sure overage allowed is false
const oldCusEnts = structuredClone(cusEnts); const oldCusEnts = structuredClone(cusEnts);
for (const cusEnt of oldCusEnts) { for (const cusEnt of oldCusEnts) {
@@ -166,13 +140,9 @@ export const handleAllowanceUsed = async ({
if (prevCheckResponse.allowed === true && v2CheckResponse.allowed === false) { if (prevCheckResponse.allowed === true && v2CheckResponse.allowed === false) {
await sendSvixThresholdReachedEvent({ await sendSvixThresholdReachedEvent({
db, ctx,
org,
env,
features,
logger,
feature,
fullCus, fullCus,
feature,
thresholdType: "allowance_used", thresholdType: "allowance_used",
}); });
} }
@@ -200,6 +170,21 @@ export const handleThresholdReached = async ({
features: Feature[]; features: Feature[];
logger: any; 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 { try {
const apiVersion = createdAtToVersion({ const apiVersion = createdAtToVersion({
createdAt: org.created_at || undefined, createdAt: org.created_at || undefined,
@@ -236,26 +221,18 @@ export const handleThresholdReached = async ({
prevCheckResponse.allowed === true && prevCheckResponse.allowed === true &&
v2CheckResponse.allowed === false v2CheckResponse.allowed === false
) { ) {
const cusDetails = await getCustomerDetails({ const cusDetails = await getApiCustomer({
db, ctx,
customer: fullCus, fullCus,
org,
env,
features,
logger,
cusProducts: newCusProducts,
expand: [], expand: [],
apiVersion,
}); });
if (fullCus.entity) { if (fullCus.entity) {
await getSingleEntityResponse({ await getSingleEntityResponse({
org, ctx,
env,
features,
entity: fullCus.entity,
fullCus,
entityId: fullCus.entity.id, entityId: fullCus.entity.id,
fullCus,
entity: fullCus.entity,
}); });
} }
@@ -276,11 +253,7 @@ export const handleThresholdReached = async ({
return; return;
} }
await handleAllowanceUsed({ await handleAllowanceUsed({
db, ctx,
org,
env,
features,
logger,
cusEnts, cusEnts,
newCusEnts, newCusEnts,
feature, feature,

View File

@@ -2,7 +2,8 @@ import { ErrCode } from "@autumn/shared";
import chalk from "chalk"; import chalk from "chalk";
import { StatusCodes } from "http-status-codes"; import { StatusCodes } from "http-status-codes";
import Stripe from "stripe"; 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) => { export const isPaymentDeclined = (error: any) => {
return ( 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 = ({ export const handleRequestError = ({
error, error,
req, req,

View File

@@ -3,11 +3,9 @@ import { StatusCodes } from "http-status-codes";
import qs from "qs"; import qs from "qs";
import Stripe from "stripe"; import Stripe from "stripe";
import { ZodAny, ZodError, ZodObject } from "zod"; import { ZodAny, ZodError, ZodObject } from "zod";
import { formatZodError } from "@/errors/formatZodError.js";
import { withSpan as withSpanTracer } from "@/internal/analytics/tracer/spanUtils.js"; import { withSpan as withSpanTracer } from "@/internal/analytics/tracer/spanUtils.js";
import RecaseError, { import RecaseError, { handleRequestError } from "./errorUtils.js";
formatZodError,
handleRequestError,
} from "./errorUtils.js";
import type { ExtendedRequest } from "./models/Request.js"; import type { ExtendedRequest } from "./models/Request.js";
/** /**

View 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;
};

View File

@@ -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;
}

View File

@@ -81,7 +81,7 @@ export const expectSubToBeCorrect = async ({
existingUsage: 0, existingUsage: 0,
withEntity: true, withEntity: true,
isCheckout: false, isCheckout: false,
apiVersion: ApiVersion.Beta, apiVersion: ApiVersion.V1_Beta,
}); });
const lineItem: any = res?.lineItem; const lineItem: any = res?.lineItem;

View File

@@ -2,7 +2,7 @@ import { EntityDataSchema } from "@api/common/entityData.js";
import { ApiProductSchema } from "@api/products/previousVersions/apiProduct.js"; import { ApiProductSchema } from "@api/products/previousVersions/apiProduct.js";
import { z } from "zod/v4"; import { z } from "zod/v4";
import { CustomerDataSchema } from "../common/customerData.js"; import { CustomerDataSchema } from "../common/customerData.js";
import { CoreCusFeatureSchema } from "../customers/cusFeatures/apiCusFeature.js"; import { CoreCusFeatureSchema } from "../customers/cusFeatures/previousVersions/apiCusFeatureV3.js";
// Check Feature Enums // Check Feature Enums
export const CheckFeatureScenarioSchema = z.enum([ export const CheckFeatureScenarioSchema = z.enum([

View File

@@ -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 { ApiBaseEntitySchema } from "@api/entities/apiEntity.js";
import { ApiCusRewardsSchema } from "@api/others/apiDiscount.js"; import { ApiCusRewardsSchema } from "@api/others/apiDiscount.js";
import { ApiInvoiceSchema } from "@api/others/apiInvoice.js"; import { ApiInvoiceSchema } from "@api/others/apiInvoice.js";
import { AppEnv } from "@models/genModels/genEnums.js"; import { AppEnv } from "@models/genModels/genEnums.js";
import { z } from "zod/v4"; import { z } from "zod/v4";
import { ApiCusReferralSchema } from "./components/apiCusReferral.js";
import { ApiCusUpcomingInvoiceSchema } from "./components/apiCusUpcomingInvoice.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({ export const ApiTrialsUsedSchema = z.object({
product_id: z.string(), product_id: z.string(),
@@ -25,47 +25,20 @@ export const ApiCusExpandSchema = z.object({
}); });
export const ApiCustomerSchema = z.object({ export const ApiCustomerSchema = z.object({
// Internal fields id: z.string(),
id: z.string().nullable().meta({ name: z.string().nullable(),
description: "Your internal ID for the customer", email: z.string().nullable(),
example: "cus_123", created_at: z.number(),
}), fingerprint: z.string().nullable(),
created_at: z.number().meta({ stripe_id: z.string().nullable(),
description: "Timestamp of customer creation in milliseconds since epoch", env: z.enum(AppEnv),
example: 1717000000, metadata: z.record(z.any(), z.any()),
}),
name: z.string().nullable().meta({ plans: z.array(ApiCusPlanSchema),
description: "Customers name",
example: "John Doe", features: z.record(z.string(), ApiCusFeatureSchema),
}),
email: z.string().nullable().meta({
description: "Customers 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: {},
}),
...ApiCusExpandSchema.shape, ...ApiCusExpandSchema.shape,
}); });
export type ApiCustomer = z.infer<typeof ApiCustomerSchema>; export type ApiCustomer = z.infer<typeof ApiCustomerSchema>;
export type ApiCustomerExpand = z.infer<typeof ApiCusExpandSchema>; export type ApiCusExpand = z.infer<typeof ApiCusExpandSchema>;

View File

@@ -9,7 +9,7 @@ import {
ApiCusFeatureV1Schema, ApiCusFeatureV1Schema,
} from "../cusFeatures/previousVersions/apiCusFeatureV1.js"; } from "../cusFeatures/previousVersions/apiCusFeatureV1.js";
import type { ApiCusFeatureV2 } from "../cusFeatures/previousVersions/apiCusFeatureV2.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 { import {
type CustomerLegacyData, type CustomerLegacyData,
CustomerLegacyDataSchema, CustomerLegacyDataSchema,

View File

@@ -1,4 +1,3 @@
import { ApiCustomerSchema } from "@api/customers/apiCustomer.js";
import { ApiFeatureType } from "@api/features/apiFeature.js"; import { ApiFeatureType } from "@api/features/apiFeature.js";
import { ApiVersion } from "@api/versionUtils/ApiVersion.js"; import { ApiVersion } from "@api/versionUtils/ApiVersion.js";
import { import {
@@ -10,6 +9,7 @@ import {
type ApiCusFeatureV2, type ApiCusFeatureV2,
ApiCusFeatureV2Schema, ApiCusFeatureV2Schema,
} from "../cusFeatures/previousVersions/apiCusFeatureV2.js"; } from "../cusFeatures/previousVersions/apiCusFeatureV2.js";
import { ApiCustomerV3Schema } from "../previousVersions/apiCustomerV3.js";
/** /**
* V1_2: Features changed from array to object AND merged multi-interval features * 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) // 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) // 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), features: z.array(ApiCusFeatureV2Schema),
}); });

View 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>;
},
});

View File

@@ -1,138 +1,56 @@
import { ApiFeatureType } from "@api/features/apiFeature.js"; import { ApiFeatureSchema } from "@api/features/apiFeature.js";
import { EntInterval } from "@models/productModels/entModels/entEnums.js"; import { ResetInterval } from "@api/products/planEnums.js";
import { z } from "zod/v4"; import { z } from "zod/v4";
export const ApiCusRolloverSchema = z.object({ export const ApiCusRolloverSchema = z.object({
balance: z.number().meta({ balance: z.number(),
description: "The remaining balance amount that has rolled over", expires_at: z.number(),
example: 100,
}),
expires_at: z.number().meta({
description: "Timestamp when the rollover balance expires",
example: 1759247877000,
}),
}); });
// Version 3 of cus feature response
export const ApiCusFeatureBreakdownSchema = z.object({ export const ApiCusFeatureBreakdownSchema = z.object({
interval: z.enum(EntInterval).meta({ reset: z
description: "The billing interval for this feature breakdown", .object({
example: "month", interval: z.enum(ResetInterval),
}), interval_count: z.number().optional(),
interval_count: z.number().nullish().meta({ when_enabled: z.boolean().optional(),
description: "The number of intervals between resets", })
example: 1, .optional(),
}),
balance: z.number().nullish().meta({ starting_balance: z.number(),
description: "The remaining balance for this interval", balance: z.number(),
example: 500, usage: z.number(),
}), resets_at: z.number().nullable(),
usage: z.number().nullish().meta({
description: "The current usage amount", max_purchase: z.number().optional(),
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 }],
}),
}); });
export const CoreCusFeatureSchema = z.object({ export const ApiCusFeatureSchema = z.object({
interval: z.enum(EntInterval).or(z.literal("multiple")).nullish().meta({ feature_id: z.string(),
description: "The billing interval or 'multiple' if the feature has multiple intervals", feature: ApiFeatureSchema.optional(),
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(ApiCusFeatureBreakdownSchema).nullish().meta({ unlimited: z.boolean(),
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({ granted_balance: z.number(), // 0
description: "The maximum usage allowed", purchased_balance: z.number(), // 3
example: 1000, current_balance: z.number(), // 1
}), usage: z.number(), // 2
rollovers: z.array(ApiCusRolloverSchema).nullish().meta({
description: "Array of rollover balances from previous periods", resets_at: z.number().nullable(),
example: [{ balance: 100, expires_at: 1759247877000 }],
}), 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 ApiCusFeature = z.infer<typeof ApiCusFeatureSchema>;
export type ApiCusRollover = z.infer<typeof ApiCusRolloverSchema>; export type ApiCusRollover = z.infer<typeof ApiCusRolloverSchema>;
export type ApiCusFeatureBreakdown = z.infer< export type ApiCusFeatureBreakdown = z.infer<

View File

@@ -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,
});

View File

@@ -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
>;

View 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>;

View File

@@ -10,8 +10,11 @@ import {
isPriceItem, isPriceItem,
} from "@utils/productV2Utils/productItemUtils/getItemType.js"; } from "@utils/productV2Utils/productItemUtils/getItemType.js";
import { notNullish } from "@utils/utils.js"; import { notNullish } from "@utils/utils.js";
import { z } from "zod/v4"; import type { z } from "zod/v4";
import type { CusProductLegacyData } from "../cusProductLegacyData.js"; import {
type CusProductLegacyData,
CusProductLegacyDataSchema,
} from "../cusProductLegacyData.js";
import { ApiCusProductV1Schema } from "../previousVersions/apiCusProductV1.js"; import { ApiCusProductV1Schema } from "../previousVersions/apiCusProductV1.js";
import { ApiCusProductV2Schema } from "../previousVersions/apiCusProductV2.js"; import { ApiCusProductV2Schema } from "../previousVersions/apiCusProductV2.js";
@@ -132,10 +135,6 @@ export function transformCusProductV2ToV1({
return v1CusProduct; return v1CusProduct;
} }
const CusProductLegacyDataSchema = z.object({
subscription_id: z.string().optional(),
});
export const V0_2_CusProductChange = defineVersionChange({ export const V0_2_CusProductChange = defineVersionChange({
newVersion: ApiVersion.V1_1, // Breaking change introduced in V1_1 newVersion: ApiVersion.V1_1, // Breaking change introduced in V1_1
oldVersion: ApiVersion.V0_2, // Applied when targetVersion <= V0_2 oldVersion: ApiVersion.V0_2, // Applied when targetVersion <= V0_2

View 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,
});

View 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>;

View File

@@ -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>;

View File

@@ -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>;

View File

@@ -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>;

View File

@@ -1,5 +1,5 @@
import { z } from "zod/v4"; import { z } from "zod/v4";
import { CusProductLegacyDataSchema } from "./cusProducts/cusProductLegacyData.js"; import { CusProductLegacyDataSchema } from "./cusPlans/cusProductLegacyData.js";
export const CustomerLegacyDataSchema = z.object({ export const CustomerLegacyDataSchema = z.object({
cusProductLegacyData: z.record(z.string(), CusProductLegacyDataSchema), cusProductLegacyData: z.record(z.string(), CusProductLegacyDataSchema),

View File

@@ -1,9 +1,9 @@
import { ApiCusFeatureV0Schema } from "@api/customers/cusFeatures/previousVersions/apiCusFeatureV0.js"; 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 { ApiInvoiceSchema } from "@api/others/apiInvoice.js";
import { AppEnv } from "@models/genModels/genEnums.js"; import { AppEnv } from "@models/genModels/genEnums.js";
import { z } from "zod/v4"; import { z } from "zod/v4";
import { ApiTrialsUsedSchema } from "../apiCustomer.js"; import { ApiTrialsUsedSchema } from "./apiCustomerV3.js";
/** /**
* ApiCustomerV0Schema - Customer response format for API V0_1 * ApiCustomerV0Schema - Customer response format for API V0_1

View File

@@ -2,8 +2,8 @@ import { ApiCusFeatureV1Schema } from "@api/customers/cusFeatures/previousVersio
import { ApiInvoiceSchema } from "@api/others/apiInvoice.js"; import { ApiInvoiceSchema } from "@api/others/apiInvoice.js";
import { AppEnv } from "@models/genModels/genEnums.js"; import { AppEnv } from "@models/genModels/genEnums.js";
import { z } from "zod/v4"; import { z } from "zod/v4";
import { ApiTrialsUsedSchema } from "../apiCustomer.js"; import { ApiCusProductV1Schema } from "../cusPlans/previousVersions/apiCusProductV1.js";
import { ApiCusProductV1Schema } from "../cusProducts/previousVersions/apiCusProductV1.js"; import { ApiTrialsUsedSchema } from "./apiCustomerV3.js";
/** /**
* ApiCustomerV1Schema - Customer response format for API V0_2/V1_0/V1_1 (pre V1_2) * ApiCustomerV1Schema - Customer response format for API V0_2/V1_0/V1_1 (pre V1_2)

View File

@@ -1,7 +1,7 @@
import { ApiCusReferralSchema } from "@api/customers/components/apiCusReferral.js"; import { ApiCusReferralSchema } from "@api/customers/components/apiCusReferral.js";
import { ApiCusUpcomingInvoiceSchema } from "@api/customers/components/apiCusUpcomingInvoice.js"; import { ApiCusUpcomingInvoiceSchema } from "@api/customers/components/apiCusUpcomingInvoice.js";
import { ApiCusFeatureV2Schema } from "@api/customers/cusFeatures/previousVersions/apiCusFeatureV2.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 { ApiEntitySchema } from "@api/entities/apiEntity.js";
import { ApiCusRewardsSchema } from "@api/models.js"; import { ApiCusRewardsSchema } from "@api/models.js";
import { ApiInvoiceSchema } from "@api/others/apiInvoice.js"; import { ApiInvoiceSchema } from "@api/others/apiInvoice.js";

View 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>;

View File

@@ -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>;
},
});

View File

@@ -1,5 +1,5 @@
import { ApiCusFeatureSchema } from "@api/customers/cusFeatures/apiCusFeature.js"; import { ApiCusFeatureV3Schema } from "@api/customers/cusFeatures/previousVersions/apiCusFeatureV3.js";
import { ApiCusProductSchema } from "@api/customers/cusProducts/apiCusProduct.js"; import { ApiCusPlanSchema } from "@api/customers/cusPlans/apiCusPlan.js";
import { ApiInvoiceSchema } from "@api/others/apiInvoice.js"; import { ApiInvoiceSchema } from "@api/others/apiInvoice.js";
import { AppEnv } from "@models/genModels/genEnums.js"; import { AppEnv } from "@models/genModels/genEnums.js";
import { z } from "zod/v4"; import { z } from "zod/v4";
@@ -29,11 +29,11 @@ export const ApiBaseEntitySchema = z.object({
}); });
export const ApiEntitySchema = ApiBaseEntitySchema.extend({ export const ApiEntitySchema = ApiBaseEntitySchema.extend({
products: z.array(ApiCusProductSchema).optional().meta({ plans: z.array(ApiCusPlanSchema).optional().meta({
description: "Products associated with this entity", description: "Plans associated with this entity",
example: [], example: [],
}), }),
features: z.record(z.string(), ApiCusFeatureSchema).optional().meta({ features: z.record(z.string(), ApiCusFeatureV3Schema).optional().meta({
description: "Features associated with this entity", description: "Features associated with this entity",
example: {}, example: {},
}), }),
@@ -44,4 +44,4 @@ export const ApiEntitySchema = ApiBaseEntitySchema.extend({
}), }),
}); });
export type EntityResponse = z.infer<typeof ApiEntitySchema>; export type ApiEntity = z.infer<typeof ApiEntitySchema>;

View File

@@ -18,8 +18,8 @@ export * from "./customers/cusFeatures/apiCusFeature.js";
export * from "./customers/cusFeatures/previousVersions/apiCusFeatureV0.js"; export * from "./customers/cusFeatures/previousVersions/apiCusFeatureV0.js";
export * from "./customers/cusFeatures/previousVersions/apiCusFeatureV1.js"; export * from "./customers/cusFeatures/previousVersions/apiCusFeatureV1.js";
export * from "./customers/cusFeatures/previousVersions/apiCusFeatureV2.js"; export * from "./customers/cusFeatures/previousVersions/apiCusFeatureV2.js";
export * from "./customers/cusProducts/apiCusProduct.js"; export * from "./customers/cusPlans/apiCusPlan.js";
export * from "./customers/cusProducts/cusProductLegacyData.js"; export * from "./customers/cusPlans/cusProductLegacyData.js";
export * from "./customers/customerLegacyData.js"; export * from "./customers/customerLegacyData.js";
export * from "./customers/customerOpModels.js"; export * from "./customers/customerOpModels.js";
export * from "./customers/previousVersions/apiCustomerV2.js"; export * from "./customers/previousVersions/apiCustomerV2.js";

View File

@@ -9,7 +9,7 @@ import { CustomerDataSchema } from "./common/customerData.js";
import { EntityDataSchema } from "./common/entityData.js"; import { EntityDataSchema } from "./common/entityData.js";
import { coreOps } from "./core/coreOpenApi.js"; import { coreOps } from "./core/coreOpenApi.js";
import { ApiCusFeatureSchema } from "./customers/cusFeatures/apiCusFeature.js"; import { ApiCusFeatureSchema } from "./customers/cusFeatures/apiCusFeature.js";
import { ApiCusProductSchema } from "./customers/cusProducts/apiCusProduct.js"; import { ApiCusPlanSchema } from "./customers/cusPlans/apiCusPlan.js";
import { import {
ApiCustomerWithMeta, ApiCustomerWithMeta,
customerOps, customerOps,
@@ -65,9 +65,9 @@ const document = createDocument({
id: "Customer", id: "Customer",
description: "Customer object returned by the API", description: "Customer object returned by the API",
}), }),
CustomerProduct: ApiCusProductSchema.meta({ CustomerPlan: ApiCusPlanSchema.meta({
id: "CustomerProduct", id: "CustomerPlan",
description: "Customer product object returned by the API", description: "Customer plan object returned by the API",
}), }),
CustomerFeature: ApiCusFeatureSchema.meta({ CustomerFeature: ApiCusFeatureSchema.meta({
id: "CustomerFeature", id: "CustomerFeature",
@@ -79,7 +79,8 @@ const document = createDocument({
}), }),
ProductItem: ApiProductItemWithMeta.meta({ ProductItem: ApiProductItemWithMeta.meta({
id: "ProductItem", 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({ Feature: ApiFeatureWithMeta.meta({
id: "Feature", id: "Feature",

View File

@@ -27,10 +27,15 @@ export const ApiPlanSchema = z.object({
add_on: z.boolean(), add_on: z.boolean(),
default: z.boolean(), default: z.boolean(),
price: z.object({ // Change
amount: z.number(), price: z
interval: z.enum(BillingInterval), .object({
}), amount: z.number(),
interval: z.enum(BillingInterval),
interval_count: z.number().optional(),
// tiers: z.array(UsageTierSchema).optional(),
})
.nullable(),
features: z.array(ApiPlanFeatureSchema), features: z.array(ApiPlanFeatureSchema),
free_trial: ApiFreeTrialV2Schema.nullable().optional(), free_trial: ApiFreeTrialV2Schema.nullable().optional(),

View File

@@ -40,7 +40,7 @@ import {
export const V1_2_ProductChanges = defineVersionChange({ export const V1_2_ProductChanges = defineVersionChange({
newVersion: ApiVersion.V2, // Breaking change introduced in V2 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: [ description: [
"Product format changed from Plan to ProductV2 schema", "Product format changed from Plan to ProductV2 schema",
"Features array renamed to items with different structure", "Features array renamed to items with different structure",

View File

@@ -11,12 +11,11 @@ import { ResetInterval } from "../planEnums.js";
export const ApiPlanFeatureSchema = z export const ApiPlanFeatureSchema = z
.object({ .object({
feature_id: z.string(), feature_id: z.string(),
granted: z.number(), granted_balance: z.number(),
unlimited: z.boolean(), unlimited: z.boolean(),
reset_interval: z.enum(ResetInterval).optional(), reset_interval: z.enum(ResetInterval).optional(),
reset_interval_count: z.number().optional(), reset_interval_count: z.number().optional(),
reset_usage_on_enabled: z.boolean().optional(), reset_usage_when_enabled: z.boolean().optional(),
price: z price: z
.object({ .object({
@@ -48,6 +47,13 @@ export const ApiPlanFeatureSchema = z
expiry_duration_length: z.number().optional(), expiry_duration_length: z.number().optional(),
}) })
.optional(), .optional(),
display: z
.object({
primary_text: z.string(),
secondary_text: z.string().optional(),
})
.optional(),
}) })
.check((ctx) => { .check((ctx) => {
const resetGroup = const resetGroup =

View File

@@ -5,7 +5,7 @@
*/ */
export enum ApiVersion { export enum ApiVersion {
V2 = "2.0.0", V2 = "2.0.0",
Beta = "beta", V1_Beta = "beta",
V1_2 = "1.2.0", V1_2 = "1.2.0",
V1_1 = "1.1.0", V1_1 = "1.1.0",
V0_2 = "0.2.0", V0_2 = "0.2.0",

View File

@@ -52,7 +52,7 @@ export function legacyToSemVer({
return ApiVersion.V1_2; return ApiVersion.V1_2;
case LegacyVersion.v1_4: case LegacyVersion.v1_4:
case 1.4: case 1.4:
return ApiVersion.Beta; return ApiVersion.V1_Beta;
default: default:
return null; return null;
} }
@@ -76,7 +76,7 @@ export function semVerToLegacy({
return LegacyVersion.v1_1; return LegacyVersion.v1_1;
case ApiVersion.V1_2: case ApiVersion.V1_2:
return LegacyVersion.v1_2; return LegacyVersion.v1_2;
case ApiVersion.Beta: case ApiVersion.V1_Beta:
return LegacyVersion.v1_4; return LegacyVersion.v1_4;
default: default:
return null; return null;
@@ -86,7 +86,10 @@ export function semVerToLegacy({
/** /**
* Parse version string (CalVer, SemVer, or legacy) * Parse version string (CalVer, SemVer, or legacy)
* Supports .clover suffix: "2025-04-17.clover" * 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: "2025-04-17" }) // ApiVersion.V1_1
* @example parseVersion({ versionStr: "2" }) // ApiVersion.V2
* @example parseVersion({ versionStr: "2.0" }) // ApiVersion.V2
*/ */
export function parseVersion({ export function parseVersion({
versionStr, versionStr,
@@ -98,7 +101,18 @@ export function parseVersion({
return calVerToSemVer({ calver: versionStr }); 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)) { if (Object.values(ApiVersion).includes(versionStr as ApiVersion)) {
return versionStr as ApiVersion; return versionStr as ApiVersion;
} }
@@ -143,8 +157,8 @@ export function createdAtToVersion({
version = ApiVersion.V0_1; version = ApiVersion.V0_1;
} }
// return new ApiVersionClass(version); return new ApiVersionClass(version);
return new ApiVersionClass(ApiVersion.V2); // return new ApiVersionClass(ApiVersion.V2);
} }
// Convert org creation date // Convert org creation date

View File

@@ -9,7 +9,6 @@ import type {
* Maps versions → change classes * Maps versions → change classes
*/ */
const changes = new Map<ApiVersion, VersionChangeConstructor[]>(); const changes = new Map<ApiVersion, VersionChangeConstructor[]>();
const instances = new Map<string, VersionChange>();
export const VersionChangeRegistryClass = { export const VersionChangeRegistryClass = {
register({ register({
@@ -24,17 +23,8 @@ export const VersionChangeRegistryClass = {
getChangesForVersion({ version }: { version: ApiVersion }): VersionChange[] { getChangesForVersion({ version }: { version: ApiVersion }): VersionChange[] {
const changeClasses = changes.get(version) || []; const changeClasses = changes.get(version) || [];
return changeClasses.map((ChangeClass) => { // Simply instantiate each change class - no caching needed since they're stateless
const key = `${version}-${ChangeClass.name}`; return changeClasses.map((ChangeClass) => new ChangeClass());
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;
});
}, },
getRegisteredVersions(): ApiVersion[] { getRegisteredVersions(): ApiVersion[] {
@@ -54,7 +44,6 @@ export const VersionChangeRegistryClass = {
clear() { clear() {
changes.clear(); changes.clear();
instances.clear();
}, },
}; };

View File

@@ -1,7 +1,13 @@
import { type ApiVersion, LATEST_VERSION } from "../ApiVersion.js"; import { type ApiVersion, LATEST_VERSION } from "../ApiVersion.js";
import { ApiVersionClass } from "../ApiVersionClass.js"; import { ApiVersionClass } from "../ApiVersionClass.js";
import { getVersionsBetween } from "../versionRegistryUtils.js"; import {
import type { AffectedResource } from "./VersionChange.js"; getVersionsBetween,
getVersionsForRequestTransform,
} from "../versionRegistryUtils.js";
import type {
AffectedResource,
VersionChangeConstructor,
} from "./VersionChange.js";
import { VersionChangeRegistryClass } from "./VersionChangeRegistryClass.js"; import { VersionChangeRegistryClass } from "./VersionChangeRegistryClass.js";
/** /**
@@ -33,6 +39,7 @@ export function applyResponseVersionChanges<T = any, TLegacyData = any>({
}): T { }): T {
// Default currentVersion to latest if not provided // Default currentVersion to latest if not provided
const _currentVersion = currentVersion || new ApiVersionClass(LATEST_VERSION); const _currentVersion = currentVersion || new ApiVersionClass(LATEST_VERSION);
// If versions are equal, no transformation needed // If versions are equal, no transformation needed
if (_currentVersion.eq(targetVersion)) { if (_currentVersion.eq(targetVersion)) {
return input; return input;
@@ -58,6 +65,7 @@ export function applyResponseVersionChanges<T = any, TLegacyData = any>({
// Apply each version's changes // Apply each version's changes
let transformedData = input; let transformedData = input;
for (const version of versionsToApply) { for (const version of versionsToApply) {
const changes = VersionChangeRegistryClass.getChangesForVersion({ const changes = VersionChangeRegistryClass.getChangesForVersion({
version, version,
@@ -88,7 +96,7 @@ export function applyResponseVersionChanges<T = any, TLegacyData = any>({
? change.description.join("; ") ? change.description.join("; ")
: change.description; : change.description;
// console.log(`Applying change [${change.oldVersion}]: ${description}`); console.log(`Applying change [${change.oldVersion}]: ${description}`);
// Apply the response transformation (backward) // Apply the response transformation (backward)
transformedData = change.transformResponse({ transformedData = change.transformResponse({
input: transformedData, input: transformedData,
@@ -127,34 +135,34 @@ export function applyResponseVersionChanges<T = any, TLegacyData = any>({
export function applyRequestVersionChanges<T = any, TLegacyData = any>({ export function applyRequestVersionChanges<T = any, TLegacyData = any>({
input, input,
legacyData, legacyData,
targetVersion, fromVersion,
currentVersion, toVersion,
resource, resource,
}: { }: {
input: T; input: T;
legacyData?: TLegacyData; legacyData?: TLegacyData;
targetVersion: ApiVersionClass; // User's version (old) fromVersion: ApiVersionClass; // User's version (old)
currentVersion: ApiVersionClass; // Latest version (new) toVersion: ApiVersionClass; // Latest version (new)
resource: AffectedResource; resource: AffectedResource;
}): T { }): T {
// If versions are equal, no transformation needed // If versions are equal, no transformation needed
if (targetVersion.eq(currentVersion)) { if (fromVersion.eq(toVersion)) {
return input; return input;
} }
// If target is newer than current, throw error // If target is newer than current, throw error
if (targetVersion.gt(currentVersion)) { if (fromVersion.gt(toVersion)) {
throw new Error( throw new Error(
`Cannot transform forward from ${currentVersion} to ${targetVersion}. ` + `Cannot transform forward from ${fromVersion} to ${toVersion}. ` +
"Current version should be >= target version.", "Current version should be >= target version.",
); );
} }
// Get all versions between target and current (exclusive of current, inclusive of target) // Get all versions between target and current (exclusive of current, inclusive of target)
const versionsToApply = getVersionsBetween({ const versionsToApply = getVersionsForRequestTransform({
from: targetVersion.value, from: fromVersion.value,
to: currentVersion.value, to: toVersion.value,
}).filter((v) => v !== currentVersion.value); // Exclude current itself });
// Don't reverse - we want to go forward (old → new) // Don't reverse - we want to go forward (old → new)
// versionsToApply is already in ascending order from getVersionsBetween // versionsToApply is already in ascending order from getVersionsBetween
@@ -213,7 +221,7 @@ export function backwardsChangeActive({
versionChange, versionChange,
}: { }: {
apiVersion: ApiVersionClass; apiVersion: ApiVersionClass;
versionChange: new () => any; versionChange: VersionChangeConstructor;
}): boolean { }): boolean {
const change = new versionChange(); const change = new versionChange();
return apiVersion.lte(change.oldVersion); return apiVersion.lte(change.oldVersion);
@@ -254,22 +262,22 @@ export function applyResponseVersionChangesToArray<T = any, TLegacyData = any>({
export function applyRequestVersionChangesToArray<T = any, TLegacyData = any>({ export function applyRequestVersionChangesToArray<T = any, TLegacyData = any>({
inputArray, inputArray,
legacyData, legacyData,
targetVersion, fromVersion,
currentVersion, toVersion,
resource, resource,
}: { }: {
inputArray: T[]; inputArray: T[];
legacyData?: TLegacyData; legacyData?: TLegacyData;
targetVersion: ApiVersionClass; fromVersion: ApiVersionClass;
currentVersion: ApiVersionClass; toVersion: ApiVersionClass;
resource: AffectedResource; resource: AffectedResource;
}): T[] { }): T[] {
return inputArray.map((item) => return inputArray.map((item) =>
applyRequestVersionChanges({ applyRequestVersionChanges({
input: item, input: item,
legacyData, legacyData,
targetVersion, fromVersion,
currentVersion, toVersion,
resource, resource,
}), }),
); );

View File

@@ -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_CustomerChange } from "@api/customers/changes/V0.2_CustomerChange.js";
import { V0_2_InvoicesAlwaysExpanded } from "@api/customers/changes/V0.2_InvoicesAlwaysExpanded.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 { V1_1_FeaturesArrayToObject } from "@api/customers/changes/V1.1_FeaturesArrayToObject.js";
// Import customer product changes // 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 product changes
import { V1_2_ProductChanges } from "@api/products/changes/V1.2_ProductChanges.js"; import { V1_2_ProductChanges } from "@api/products/changes/V1.2_ProductChanges.js";
import { ApiVersion } from "../ApiVersion.js"; import { ApiVersion } from "../ApiVersion.js";
import type { VersionChangeConstructor } from "./VersionChange.js"; import type { VersionChangeConstructor } from "./VersionChange.js";
import { VersionChangeRegistryClass } from "./VersionChangeRegistryClass.js"; import { VersionChangeRegistryClass } from "./VersionChangeRegistryClass.js";
export const V2_CHANGES: VersionChangeConstructor[] = [ 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 V1_2_ProductChanges, // Transforms Product TO V1.2 format from V2 Plan format
]; ];
@@ -42,7 +46,7 @@ export function registerAllVersionChanges() {
changes: V2_CHANGES, changes: V2_CHANGES,
}); });
VersionChangeRegistryClass.register({ VersionChangeRegistryClass.register({
version: ApiVersion.Beta, version: ApiVersion.V1_Beta,
changes: V1_4_CHANGES, changes: V1_4_CHANGES,
}); });
VersionChangeRegistryClass.register({ VersionChangeRegistryClass.register({

View File

@@ -20,8 +20,9 @@ export const VERSION_REGISTRY: Record<ApiVersion, VersionMetadata> = {
releasedAt: new Date("2025-10-16").getTime(), releasedAt: new Date("2025-10-16").getTime(),
description: "Plan features with max_purchase", description: "Plan features with max_purchase",
}, },
[ApiVersion.Beta]: {
semver: ApiVersion.Beta, [ApiVersion.V1_Beta]: {
semver: ApiVersion.V1_Beta,
calver: "beta", calver: "beta",
releasedAt: new Date("2025-06-01").getTime(), releasedAt: new Date("2025-06-01").getTime(),
description: "Beta version with experimental features", description: "Beta version with experimental features",

Some files were not shown because too many files have changed in this diff Show More