chore: solving type errors in test files
This commit is contained in:
@@ -1 +1 @@
|
|||||||
1.2.19
|
1.3.2
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
- JS Doc comments should be SHORT and SWEET. Don't need examples unless ABSOLUTELY necessary
|
- JS Doc comments should be SHORT and SWEET. Don't need examples unless ABSOLUTELY necessary
|
||||||
- When writing DB queries, for the `customers`, `products` and `features` tables (and others possibly not mentioned here), the primary key when updating is `internal_id`, not `id`
|
- When writing DB queries, for the `customers`, `products` and `features` tables (and others possibly not mentioned here), the primary key when updating is `internal_id`, not `id`
|
||||||
- When using db schemas in Drizzle, import them from '@autumn/shared', and don't do schemas.
|
- When using db schemas in Drizzle, import them from '@autumn/shared', and don't do schemas.
|
||||||
|
- Do NOT use "any" type.
|
||||||
|
|
||||||
# Testing
|
# Testing
|
||||||
- When writing tests, ALWAYS read:
|
- When writing tests, ALWAYS read:
|
||||||
|
|||||||
37
bun.lock
37
bun.lock
@@ -120,7 +120,7 @@
|
|||||||
"recaseai": "^0.0.37",
|
"recaseai": "^0.0.37",
|
||||||
"resend": "^4.1.1",
|
"resend": "^4.1.1",
|
||||||
"semver": "^7.7.2",
|
"semver": "^7.7.2",
|
||||||
"stripe": "18.4.0-beta.2",
|
"stripe": "catalog:",
|
||||||
"svix": "^1.45.1",
|
"svix": "^1.45.1",
|
||||||
"tsc-alias": "^1.8.16",
|
"tsc-alias": "^1.8.16",
|
||||||
"ws": "^8.18.0",
|
"ws": "^8.18.0",
|
||||||
@@ -159,13 +159,13 @@
|
|||||||
"dotenv": "^16.5.0",
|
"dotenv": "^16.5.0",
|
||||||
"drizzle-kit": "catalog:",
|
"drizzle-kit": "catalog:",
|
||||||
"drizzle-orm": "catalog:",
|
"drizzle-orm": "catalog:",
|
||||||
|
"stripe": "catalog:",
|
||||||
"yaml": "^2.8.1",
|
"yaml": "^2.8.1",
|
||||||
"zod-openapi": "^5.4.1",
|
"zod-openapi": "^5.4.1",
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bun": "latest",
|
"@types/bun": "latest",
|
||||||
"@types/node": "^24.0.3",
|
"@types/node": "^24.0.3",
|
||||||
"@typescript/native-preview": "^7.0.0-dev.20250731.1",
|
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"nodemon": "^3.1.7",
|
"nodemon": "^3.1.7",
|
||||||
"tsx": "^4.19.4",
|
"tsx": "^4.19.4",
|
||||||
@@ -262,6 +262,7 @@
|
|||||||
"@types/react": "^18.3.18",
|
"@types/react": "^18.3.18",
|
||||||
"@types/react-dom": "^18.3.5",
|
"@types/react-dom": "^18.3.5",
|
||||||
"@types/react-router-dom": "^5.3.3",
|
"@types/react-router-dom": "^5.3.3",
|
||||||
|
"@typescript/native-preview": "^7.0.0-dev.20251111.1",
|
||||||
"@vitejs/plugin-react": "^4.3.4",
|
"@vitejs/plugin-react": "^4.3.4",
|
||||||
"eslint": "^9.21.0",
|
"eslint": "^9.21.0",
|
||||||
"eslint-plugin-react-hooks": "^5.1.0",
|
"eslint-plugin-react-hooks": "^5.1.0",
|
||||||
@@ -270,12 +271,14 @@
|
|||||||
"typescript": "~5.7.2",
|
"typescript": "~5.7.2",
|
||||||
"typescript-eslint": "^8.24.1",
|
"typescript-eslint": "^8.24.1",
|
||||||
"vite": "^6.2.0",
|
"vite": "^6.2.0",
|
||||||
|
"vite-tsconfig-paths": "^5.1.4",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"catalog": {
|
"catalog": {
|
||||||
"drizzle-kit": "^0.31.1",
|
"drizzle-kit": "^0.31.1",
|
||||||
"drizzle-orm": "0.43.1",
|
"drizzle-orm": "0.43.1",
|
||||||
|
"stripe": "18.4.0-beta.2",
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"@ai-sdk/anthropic": ["@ai-sdk/anthropic@1.2.12", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "@ai-sdk/provider-utils": "2.2.8" }, "peerDependencies": { "zod": "^3.0.0" } }, "sha512-YSzjlko7JvuiyQFmI9RN1tNZdEiZxc+6xld/0tq/VkJaHpEzGAb1yiNxxvmYVcjvfu/PcvCxAAYXmTYQQ63IHQ=="],
|
"@ai-sdk/anthropic": ["@ai-sdk/anthropic@1.2.12", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "@ai-sdk/provider-utils": "2.2.8" }, "peerDependencies": { "zod": "^3.0.0" } }, "sha512-YSzjlko7JvuiyQFmI9RN1tNZdEiZxc+6xld/0tq/VkJaHpEzGAb1yiNxxvmYVcjvfu/PcvCxAAYXmTYQQ63IHQ=="],
|
||||||
@@ -486,23 +489,23 @@
|
|||||||
|
|
||||||
"@better-fetch/fetch": ["@better-fetch/fetch@1.1.18", "", {}, "sha512-rEFOE1MYIsBmoMJtQbl32PGHHXuG2hDxvEd7rUHE0vCBoFQVSDqaVs9hkZEtHCxRoY+CljXKFCOuJ8uxqw1LcA=="],
|
"@better-fetch/fetch": ["@better-fetch/fetch@1.1.18", "", {}, "sha512-rEFOE1MYIsBmoMJtQbl32PGHHXuG2hDxvEd7rUHE0vCBoFQVSDqaVs9hkZEtHCxRoY+CljXKFCOuJ8uxqw1LcA=="],
|
||||||
|
|
||||||
"@biomejs/biome": ["@biomejs/biome@2.3.4", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.3.4", "@biomejs/cli-darwin-x64": "2.3.4", "@biomejs/cli-linux-arm64": "2.3.4", "@biomejs/cli-linux-arm64-musl": "2.3.4", "@biomejs/cli-linux-x64": "2.3.4", "@biomejs/cli-linux-x64-musl": "2.3.4", "@biomejs/cli-win32-arm64": "2.3.4", "@biomejs/cli-win32-x64": "2.3.4" }, "bin": { "biome": "bin/biome" } }, "sha512-TU08LXjBHdy0mEY9APtEtZdNQQijXUDSXR7IK1i45wgoPD5R0muK7s61QcFir6FpOj/RP1+YkPx5QJlycXUU3w=="],
|
"@biomejs/biome": ["@biomejs/biome@2.3.5", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.3.5", "@biomejs/cli-darwin-x64": "2.3.5", "@biomejs/cli-linux-arm64": "2.3.5", "@biomejs/cli-linux-arm64-musl": "2.3.5", "@biomejs/cli-linux-x64": "2.3.5", "@biomejs/cli-linux-x64-musl": "2.3.5", "@biomejs/cli-win32-arm64": "2.3.5", "@biomejs/cli-win32-x64": "2.3.5" }, "bin": { "biome": "bin/biome" } }, "sha512-HvLhNlIlBIbAV77VysRIBEwp55oM/QAjQEin74QQX9Xb259/XP/D5AGGnZMOyF1el4zcvlNYYR3AyTMUV3ILhg=="],
|
||||||
|
|
||||||
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.3.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-w40GvlNzLaqmuWYiDU6Ys9FNhJiclngKqcGld3iJIiy2bpJ0Q+8n3haiaC81uTPY/NA0d8Q/I3Z9+ajc14102Q=="],
|
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.3.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-fLdTur8cJU33HxHUUsii3GLx/TR0BsfQx8FkeqIiW33cGMtUD56fAtrh+2Fx1uhiCsVZlFh6iLKUU3pniZREQw=="],
|
||||||
|
|
||||||
"@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.3.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-3s7TLVtjJ7ni1xADXsS7x7GMUrLBZXg8SemXc3T0XLslzvqKj/dq1xGeBQ+pOWQzng9MaozfacIHdK2UlJ3jGA=="],
|
"@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.3.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-qpT8XDqeUlzrOW8zb4k3tjhT7rmvVRumhi2657I2aGcY4B+Ft5fNwDdZGACzn8zj7/K1fdWjgwYE3i2mSZ+vOA=="],
|
||||||
|
|
||||||
"@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.3.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-y7efHyyM2gYmHy/AdWEip+VgTMe9973aP7XYKPzu/j8JxnPHuSUXftzmPhkVw0lfm4ECGbdBdGD6+rLmTgNZaA=="],
|
"@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.3.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-u/pybjTBPGBHB66ku4pK1gj+Dxgx7/+Z0jAriZISPX1ocTO8aHh8x8e7Kb1rB4Ms0nA/SzjtNOVJ4exVavQBCw=="],
|
||||||
|
|
||||||
"@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.3.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-IruVGQRwMURivWazchiq7gKAqZSFs5so6gi0hJyxk7x6HR+iwZbO2IxNOqyLURBvL06qkIHs7Wffl6Bw30vCbQ=="],
|
"@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.3.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-eGUG7+hcLgGnMNl1KHVZUYxahYAhC462jF/wQolqu4qso2MSk32Q+QrpN7eN4jAHAg7FUMIo897muIhK4hXhqg=="],
|
||||||
|
|
||||||
"@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.3.4", "", { "os": "linux", "cpu": "x64" }, "sha512-gKfjWR/6/dfIxPJCw8REdEowiXCkIpl9jycpNVHux8aX2yhWPLjydOshkDL6Y/82PcQJHn95VCj7J+BRcE5o1Q=="],
|
"@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.3.5", "", { "os": "linux", "cpu": "x64" }, "sha512-XrIVi9YAW6ye0CGQ+yax0gLfx+BFOtKaNX74n+xHWla6Cl6huUmcKNO7HPx7BiKnJUzrxXY1qYlm7xMvi08X4g=="],
|
||||||
|
|
||||||
"@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.3.4", "", { "os": "linux", "cpu": "x64" }, "sha512-mzKFFv/w66e4/jCobFmD3kymCqG+FuWE7sVa4Yjqd9v7qt2UhXo67MSZKY9Ih18V2IwPzRKQPCw6KwdZs6AXSA=="],
|
"@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.3.5", "", { "os": "linux", "cpu": "x64" }, "sha512-awVuycTPpVTH/+WDVnEEYSf6nbCBHf/4wB3lquwT7puhNg8R4XvonWNZzUsfHZrCkjkLhFH/vCZK5jHatD9FEg=="],
|
||||||
|
|
||||||
"@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.3.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-5TJ6JfVez+yyupJ/iGUici2wzKf0RrSAxJhghQXtAEsc67OIpdwSKAQboemILrwKfHDi5s6mu7mX+VTCTUydkw=="],
|
"@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.3.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-DlBiMlBZZ9eIq4H7RimDSGsYcOtfOIfZOaI5CqsWiSlbTfqbPVfWtCf92wNzx8GNMbu1s7/g3ZZESr6+GwM/SA=="],
|
||||||
|
|
||||||
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.3.4", "", { "os": "win32", "cpu": "x64" }, "sha512-FGCijXecmC4IedQ0esdYNlMpx0Jxgf4zceCaMu6fkjWyjgn50ZQtMiqZZQ0Q/77yqPxvtkgZAvt5uGw0gAAjig=="],
|
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.3.5", "", { "os": "win32", "cpu": "x64" }, "sha512-nUmR8gb6yvrKhtRgzwo/gDimPwnO5a4sCydf8ZS2kHIJhEmSmk+STsusr1LHTuM//wXppBawvSQi2xFXJCdgKQ=="],
|
||||||
|
|
||||||
"@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=="],
|
||||||
|
|
||||||
@@ -1684,7 +1687,7 @@
|
|||||||
|
|
||||||
"base64id": ["base64id@2.0.0", "", {}, "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog=="],
|
"base64id": ["base64id@2.0.0", "", {}, "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog=="],
|
||||||
|
|
||||||
"baseline-browser-mapping": ["baseline-browser-mapping@2.8.25", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-2NovHVesVF5TXefsGX1yzx1xgr7+m9JQenvz6FQY3qd+YXkKkYiv+vTCc7OriP9mcDZpTC5mAOYN4ocd29+erA=="],
|
"baseline-browser-mapping": ["baseline-browser-mapping@2.8.26", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-73lC1ugzwoaWCLJ1LvOgrR5xsMLTqSKIEoMHVtL9E/HNk0PXtTM76ZIm84856/SF7Nv8mPZxKoBsgpm0tR1u1Q=="],
|
||||||
|
|
||||||
"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=="],
|
||||||
|
|
||||||
@@ -2154,6 +2157,8 @@
|
|||||||
|
|
||||||
"globby": ["globby@11.1.0", "", { "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", "fast-glob": "^3.2.9", "ignore": "^5.2.0", "merge2": "^1.4.1", "slash": "^3.0.0" } }, "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g=="],
|
"globby": ["globby@11.1.0", "", { "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", "fast-glob": "^3.2.9", "ignore": "^5.2.0", "merge2": "^1.4.1", "slash": "^3.0.0" } }, "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g=="],
|
||||||
|
|
||||||
|
"globrex": ["globrex@0.1.2", "", {}, "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg=="],
|
||||||
|
|
||||||
"goober": ["goober@2.1.18", "", { "peerDependencies": { "csstype": "^3.0.10" } }, "sha512-2vFqsaDVIT9Gz7N6kAL++pLpp41l3PfDuusHcjnGLfR6+huZkl6ziX+zgVC3ZxpqWhzH6pyDdGrCeDhMIvwaxw=="],
|
"goober": ["goober@2.1.18", "", { "peerDependencies": { "csstype": "^3.0.10" } }, "sha512-2vFqsaDVIT9Gz7N6kAL++pLpp41l3PfDuusHcjnGLfR6+huZkl6ziX+zgVC3ZxpqWhzH6pyDdGrCeDhMIvwaxw=="],
|
||||||
|
|
||||||
"google-logging-utils": ["google-logging-utils@0.0.2", "", {}, "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ=="],
|
"google-logging-utils": ["google-logging-utils@0.0.2", "", {}, "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ=="],
|
||||||
@@ -2182,7 +2187,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=="],
|
||||||
|
|
||||||
"hono": ["hono@4.10.4", "", {}, "sha512-YG/fo7zlU3KwrBL5vDpWKisLYiM+nVstBQqfr7gCPbSYURnNEP9BDxEMz8KfsDR9JX0lJWDRNc6nXX31v7ZEyg=="],
|
"hono": ["hono@4.10.5", "", {}, "sha512-h/MXuTkoAK8NG1EfDp0jI1YLf6yGdDnfkebRO2pwEh5+hE3RAJFXkCsnD0vamSiARK4ZrB6MY+o3E/hCnOyHrQ=="],
|
||||||
|
|
||||||
"hono-rate-limiter": ["hono-rate-limiter@0.4.2", "", { "peerDependencies": { "hono": "^4.1.1" } }, "sha512-AAtFqgADyrmbDijcRTT/HJfwqfvhalya2Zo+MgfdrMPas3zSMD8SU03cv+ZsYwRU1swv7zgVt0shwN059yzhjw=="],
|
"hono-rate-limiter": ["hono-rate-limiter@0.4.2", "", { "peerDependencies": { "hono": "^4.1.1" } }, "sha512-AAtFqgADyrmbDijcRTT/HJfwqfvhalya2Zo+MgfdrMPas3zSMD8SU03cv+ZsYwRU1swv7zgVt0shwN059yzhjw=="],
|
||||||
|
|
||||||
@@ -2976,6 +2981,8 @@
|
|||||||
|
|
||||||
"tsc-alias": ["tsc-alias@1.8.16", "", { "dependencies": { "chokidar": "^3.5.3", "commander": "^9.0.0", "get-tsconfig": "^4.10.0", "globby": "^11.0.4", "mylas": "^2.1.9", "normalize-path": "^3.0.0", "plimit-lit": "^1.2.6" }, "bin": { "tsc-alias": "dist/bin/index.js" } }, "sha512-QjCyu55NFyRSBAl6+MTFwplpFcnm2Pq01rR/uxfqJoLMm6X3O14KEGtaSDZpJYaE1bJBGDjD0eSuiIWPe2T58g=="],
|
"tsc-alias": ["tsc-alias@1.8.16", "", { "dependencies": { "chokidar": "^3.5.3", "commander": "^9.0.0", "get-tsconfig": "^4.10.0", "globby": "^11.0.4", "mylas": "^2.1.9", "normalize-path": "^3.0.0", "plimit-lit": "^1.2.6" }, "bin": { "tsc-alias": "dist/bin/index.js" } }, "sha512-QjCyu55NFyRSBAl6+MTFwplpFcnm2Pq01rR/uxfqJoLMm6X3O14KEGtaSDZpJYaE1bJBGDjD0eSuiIWPe2T58g=="],
|
||||||
|
|
||||||
|
"tsconfck": ["tsconfck@3.1.6", "", { "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"], "bin": { "tsconfck": "bin/tsconfck.js" } }, "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w=="],
|
||||||
|
|
||||||
"tsconfig-paths": ["tsconfig-paths@4.2.0", "", { "dependencies": { "json5": "^2.2.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg=="],
|
"tsconfig-paths": ["tsconfig-paths@4.2.0", "", { "dependencies": { "json5": "^2.2.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg=="],
|
||||||
|
|
||||||
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||||
@@ -3048,6 +3055,8 @@
|
|||||||
|
|
||||||
"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.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-tsconfig-paths": ["vite-tsconfig-paths@5.1.4", "", { "dependencies": { "debug": "^4.1.1", "globrex": "^0.1.2", "tsconfck": "^3.0.3" }, "peerDependencies": { "vite": "*" }, "optionalPeers": ["vite"] }, "sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w=="],
|
||||||
|
|
||||||
"vscode-oniguruma": ["vscode-oniguruma@2.0.1", "", {}, "sha512-poJU8iHIWnC3vgphJnrLZyI3YdqRlR27xzqDmpPXYzA93R4Gk8z7T6oqDzDoHjoikA2aS82crdXFkjELCdJsjQ=="],
|
"vscode-oniguruma": ["vscode-oniguruma@2.0.1", "", {}, "sha512-poJU8iHIWnC3vgphJnrLZyI3YdqRlR27xzqDmpPXYzA93R4Gk8z7T6oqDzDoHjoikA2aS82crdXFkjELCdJsjQ=="],
|
||||||
|
|
||||||
"vscode-textmate": ["vscode-textmate@9.2.1", "", {}, "sha512-eXiUi2yYFv9bdvgrYtJynA7UemCEkpVNE50S9iBBA08LYG5t9+/TB+8IRS/YoYOubCez2OkSyZ1Q12eQMwzbrw=="],
|
"vscode-textmate": ["vscode-textmate@9.2.1", "", {}, "sha512-eXiUi2yYFv9bdvgrYtJynA7UemCEkpVNE50S9iBBA08LYG5t9+/TB+8IRS/YoYOubCez2OkSyZ1Q12eQMwzbrw=="],
|
||||||
@@ -3938,8 +3947,6 @@
|
|||||||
|
|
||||||
"cloudflare/@types/node": ["@types/node@18.19.130", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg=="],
|
"cloudflare/@types/node": ["@types/node@18.19.130", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg=="],
|
||||||
|
|
||||||
"cmdk/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.4", "", { "dependencies": { "@radix-ui/react-slot": "1.2.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg=="],
|
|
||||||
|
|
||||||
"concurrently/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
"concurrently/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
||||||
|
|
||||||
"convex/esbuild": ["esbuild@0.25.4", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.4", "@esbuild/android-arm": "0.25.4", "@esbuild/android-arm64": "0.25.4", "@esbuild/android-x64": "0.25.4", "@esbuild/darwin-arm64": "0.25.4", "@esbuild/darwin-x64": "0.25.4", "@esbuild/freebsd-arm64": "0.25.4", "@esbuild/freebsd-x64": "0.25.4", "@esbuild/linux-arm": "0.25.4", "@esbuild/linux-arm64": "0.25.4", "@esbuild/linux-ia32": "0.25.4", "@esbuild/linux-loong64": "0.25.4", "@esbuild/linux-mips64el": "0.25.4", "@esbuild/linux-ppc64": "0.25.4", "@esbuild/linux-riscv64": "0.25.4", "@esbuild/linux-s390x": "0.25.4", "@esbuild/linux-x64": "0.25.4", "@esbuild/netbsd-arm64": "0.25.4", "@esbuild/netbsd-x64": "0.25.4", "@esbuild/openbsd-arm64": "0.25.4", "@esbuild/openbsd-x64": "0.25.4", "@esbuild/sunos-x64": "0.25.4", "@esbuild/win32-arm64": "0.25.4", "@esbuild/win32-ia32": "0.25.4", "@esbuild/win32-x64": "0.25.4" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q=="],
|
"convex/esbuild": ["esbuild@0.25.4", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.4", "@esbuild/android-arm": "0.25.4", "@esbuild/android-arm64": "0.25.4", "@esbuild/android-x64": "0.25.4", "@esbuild/darwin-arm64": "0.25.4", "@esbuild/darwin-x64": "0.25.4", "@esbuild/freebsd-arm64": "0.25.4", "@esbuild/freebsd-x64": "0.25.4", "@esbuild/linux-arm": "0.25.4", "@esbuild/linux-arm64": "0.25.4", "@esbuild/linux-ia32": "0.25.4", "@esbuild/linux-loong64": "0.25.4", "@esbuild/linux-mips64el": "0.25.4", "@esbuild/linux-ppc64": "0.25.4", "@esbuild/linux-riscv64": "0.25.4", "@esbuild/linux-s390x": "0.25.4", "@esbuild/linux-x64": "0.25.4", "@esbuild/netbsd-arm64": "0.25.4", "@esbuild/netbsd-x64": "0.25.4", "@esbuild/openbsd-arm64": "0.25.4", "@esbuild/openbsd-x64": "0.25.4", "@esbuild/sunos-x64": "0.25.4", "@esbuild/win32-arm64": "0.25.4", "@esbuild/win32-ia32": "0.25.4", "@esbuild/win32-x64": "0.25.4" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q=="],
|
||||||
|
|||||||
@@ -126,9 +126,7 @@ fi
|
|||||||
# echo "✅ Copied migration files"
|
# echo "✅ Copied migration files"
|
||||||
# fi
|
# fi
|
||||||
|
|
||||||
# Build shared workspace (required for other workspaces)
|
# Shared workspace is now used directly from source (no build needed)
|
||||||
echo "🔨 Building shared workspace..."
|
|
||||||
bun -F @autumn/shared build
|
|
||||||
|
|
||||||
# # Run database migrations if DATABASE_URL exists
|
# # Run database migrations if DATABASE_URL exists
|
||||||
# if grep -q "DATABASE_URL=" server/.env 2>/dev/null; then
|
# if grep -q "DATABASE_URL=" server/.env 2>/dev/null; then
|
||||||
|
|||||||
@@ -1,64 +0,0 @@
|
|||||||
|
|
||||||
services:
|
|
||||||
valkey:
|
|
||||||
image: docker.io/valkey/valkey:8.0
|
|
||||||
environment:
|
|
||||||
- ALLOW_EMPTY_PASSWORD=yes
|
|
||||||
- VALKEY_DISABLE_COMMANDS=FLUSHDB,FLUSHALL
|
|
||||||
volumes:
|
|
||||||
- valkey-data:/bitnami/valkey/data
|
|
||||||
healthcheck:
|
|
||||||
test: ['CMD', 'redis-cli', '-h', 'localhost', '-p', '6379', 'ping']
|
|
||||||
interval: 10s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 5
|
|
||||||
ports:
|
|
||||||
- "6379:6379"
|
|
||||||
restart: unless-stopped
|
|
||||||
|
|
||||||
vite:
|
|
||||||
build:
|
|
||||||
context: .
|
|
||||||
dockerfile: docker/prod.dockerfile
|
|
||||||
target: vite-prod
|
|
||||||
ports:
|
|
||||||
- "3000:3000"
|
|
||||||
restart: always
|
|
||||||
|
|
||||||
server:
|
|
||||||
environment:
|
|
||||||
- REDIS_URL=redis://valkey:6379
|
|
||||||
build:
|
|
||||||
context: .
|
|
||||||
dockerfile: docker/prod.dockerfile
|
|
||||||
target: server-prod
|
|
||||||
ports:
|
|
||||||
- "8080:8080"
|
|
||||||
restart: always
|
|
||||||
|
|
||||||
localtunnel:
|
|
||||||
image: oven/bun:latest
|
|
||||||
build:
|
|
||||||
dockerfile: docker/prod.dockerfile
|
|
||||||
context: .
|
|
||||||
target: localtunnel
|
|
||||||
volumes:
|
|
||||||
- ./server:/app/server
|
|
||||||
depends_on:
|
|
||||||
- server
|
|
||||||
restart: unless-stopped
|
|
||||||
|
|
||||||
workers:
|
|
||||||
environment:
|
|
||||||
- REDIS_URL=redis://valkey:6379
|
|
||||||
build:
|
|
||||||
context: .
|
|
||||||
dockerfile: docker/prod.dockerfile
|
|
||||||
target: workers-prod
|
|
||||||
restart: always
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
valkey-data:
|
|
||||||
shared-dist:
|
|
||||||
shared-node-modules:
|
|
||||||
root-node-modules:
|
|
||||||
@@ -21,32 +21,25 @@ WORKDIR /app
|
|||||||
COPY localtunnel-start.sh ./
|
COPY localtunnel-start.sh ./
|
||||||
CMD ["sh", "localtunnel-start.sh"]
|
CMD ["sh", "localtunnel-start.sh"]
|
||||||
|
|
||||||
# Stage 2: /shared
|
# Stage 2: /vite
|
||||||
FROM base AS shared
|
|
||||||
COPY shared/ ./shared/
|
|
||||||
WORKDIR /app/shared
|
|
||||||
RUN bun run build
|
|
||||||
CMD ["bun", "dev"]
|
|
||||||
|
|
||||||
# Stage 3: /vite
|
|
||||||
FROM base AS vite
|
FROM base AS vite
|
||||||
COPY --from=shared /app/shared/dist ./shared/dist
|
COPY shared/ ./shared/
|
||||||
WORKDIR /app/vite
|
WORKDIR /app/vite
|
||||||
COPY vite/ ./
|
COPY vite/ ./
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
CMD ["bun", "dev"]
|
CMD ["bun", "dev"]
|
||||||
|
|
||||||
# Stage 4: /server
|
# Stage 3: /server
|
||||||
FROM base AS server
|
FROM base AS server
|
||||||
COPY --from=shared /app/shared/dist ./shared/dist
|
COPY shared/ ./shared/
|
||||||
COPY server/ ./server/
|
COPY server/ ./server/
|
||||||
WORKDIR /app/server
|
WORKDIR /app/server
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
CMD ["bun", "dev"]
|
CMD ["bun", "dev"]
|
||||||
|
|
||||||
# Stage 5: Workers
|
# Stage 4: Workers
|
||||||
FROM base AS workers
|
FROM base AS workers
|
||||||
COPY --from=shared /app/shared/dist ./shared/dist
|
COPY shared/ ./shared/
|
||||||
COPY server/ ./server/
|
COPY server/ ./server/
|
||||||
WORKDIR /app/server
|
WORKDIR /app/server
|
||||||
CMD ["bun", "workers:dev"]
|
CMD ["bun", "workers:dev"]
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
# ---- Base dependencies ----
|
|
||||||
FROM oven/bun:latest AS base
|
|
||||||
WORKDIR /app
|
|
||||||
RUN bun install -g serve typescript tsc tsc-alias tsx
|
|
||||||
|
|
||||||
COPY package.json bun.lock ./
|
|
||||||
COPY shared/package*.json ./shared/
|
|
||||||
COPY server/package*.json ./server/
|
|
||||||
COPY vite/package*.json ./vite/
|
|
||||||
|
|
||||||
RUN bun install
|
|
||||||
|
|
||||||
FROM base AS localtunnel
|
|
||||||
WORKDIR /app
|
|
||||||
COPY localtunnel-start.sh ./
|
|
||||||
CMD ["sh", "localtunnel-start.sh"]
|
|
||||||
|
|
||||||
# ---- Build shared package ----
|
|
||||||
FROM base AS shared-build
|
|
||||||
COPY shared/ ./shared/
|
|
||||||
WORKDIR /app/shared
|
|
||||||
RUN bun run build
|
|
||||||
|
|
||||||
# ---- Build frontend (vite) ----
|
|
||||||
FROM base AS vite-build
|
|
||||||
COPY --from=shared-build /app/shared/dist ./shared/dist
|
|
||||||
COPY . .
|
|
||||||
WORKDIR /app
|
|
||||||
RUN bun run build
|
|
||||||
|
|
||||||
# ---- Build backend (server) ----
|
|
||||||
FROM base AS server-build
|
|
||||||
COPY --from=shared-build /app/shared/dist ./shared/dist
|
|
||||||
COPY . .
|
|
||||||
WORKDIR /app
|
|
||||||
# RUN bun run server:build:bun
|
|
||||||
|
|
||||||
# ---- Production frontend image ----
|
|
||||||
FROM vite-build AS vite-prod
|
|
||||||
EXPOSE 3000
|
|
||||||
WORKDIR /app
|
|
||||||
CMD ["bun", "start"]
|
|
||||||
|
|
||||||
FROM server-build AS server-prod
|
|
||||||
EXPOSE 8080
|
|
||||||
WORKDIR /app/server
|
|
||||||
CMD ["bun", "start"]
|
|
||||||
|
|
||||||
FROM server-build AS workers-prod
|
|
||||||
EXPOSE 8080
|
|
||||||
WORKDIR /app/server
|
|
||||||
CMD ["bun", "workers"]
|
|
||||||
19
package.json
19
package.json
@@ -9,6 +9,7 @@
|
|||||||
"scripts"
|
"scripts"
|
||||||
],
|
],
|
||||||
"catalog": {
|
"catalog": {
|
||||||
|
"stripe": "18.4.0-beta.2",
|
||||||
"drizzle-orm": "0.43.1",
|
"drizzle-orm": "0.43.1",
|
||||||
"drizzle-kit": "^0.31.1"
|
"drizzle-kit": "^0.31.1"
|
||||||
}
|
}
|
||||||
@@ -16,32 +17,18 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "bun scripts/start-dev.js",
|
"dev": "bun scripts/start-dev.js",
|
||||||
|
|
||||||
"d": "ENV_FILE=.env infisical run --env=dev -- bun scripts/start-dev.js",
|
"d": "ENV_FILE=.env infisical run --env=dev -- bun scripts/start-dev.js",
|
||||||
"p": "ENV_FILE=.env.prod infisical run --env=prod -- bun scripts/start-dev.js",
|
"p": "ENV_FILE=.env.prod infisical run --env=prod -- bun scripts/start-dev.js",
|
||||||
|
|
||||||
"vite:build": "bun -F @autumn/shared build && bun -F @autumn/vite build:bun",
|
|
||||||
"vite:start": "bun -F @autumn/vite start:bun",
|
|
||||||
"shared": "bun -F @autumn/shared build",
|
|
||||||
"server": "cd server && bun start",
|
|
||||||
"workers": "bun -F @autumn/shared build && bun -F @autumn/server workers",
|
|
||||||
"cron": "bun -F @autumn/shared build && bun -F @autumn/server cron",
|
|
||||||
"check": "bun -F @autumn/shared build && bun -F @autumn/server check",
|
|
||||||
"server:cron": "pnpm -F server cron:start",
|
|
||||||
"server:check": "NODE_ENV=production pnpm -F server check",
|
|
||||||
"setup": "node scripts/setup.js",
|
"setup": "node scripts/setup.js",
|
||||||
"setup:test": "bun scripts/setup-test.ts",
|
"setup:test": "bun scripts/setup-test.ts",
|
||||||
"tests": "bun scripts/test.ts",
|
"tests": "bun scripts/test.ts",
|
||||||
|
|
||||||
"setupci": "node scripts/setupci.js",
|
"setupci": "node scripts/setupci.js",
|
||||||
"replicate": "bun scripts/replicate.ts",
|
"replicate": "bun scripts/replicate.ts",
|
||||||
"db:push": " bun -F @autumn/shared db:push",
|
"db:push": " bun -F @autumn/shared db:push",
|
||||||
"db:generate": "bun -F @autumn/shared db:generate",
|
"db:generate": "bun -F @autumn/shared db:generate",
|
||||||
"db:migrate": " bun -F @autumn/shared db:migrate",
|
"db:migrate": " bun -F @autumn/shared db:migrate"
|
||||||
"docker:up": "docker compose -f docker-compose.dev.yml up --build",
|
|
||||||
"docker:up:unix": "docker compose -f docker-compose.unix.yml up --build",
|
|
||||||
"docker:up:ci": "docker compose -f docker-compose.ci.yml up --build",
|
|
||||||
"vite:build:bun": "bun -F @autumn/shared build && bun -F @autumn/vite build:bun",
|
|
||||||
"vite:start:bun": "bun -F @autumn/shared build && bun -F @autumn/vite start:bun"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@wooorm/starry-night": "^3.8.0",
|
"@wooorm/starry-night": "^3.8.0",
|
||||||
|
|||||||
@@ -145,27 +145,6 @@ async function startDev() {
|
|||||||
// await handlePorts();
|
// await handlePorts();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 1: Build shared package first (initial build)
|
|
||||||
console.log("\n📦 Building shared package...\n");
|
|
||||||
const buildShared = spawn("bun", ["run", "build"], {
|
|
||||||
cwd: "shared",
|
|
||||||
stdio: "inherit",
|
|
||||||
shell: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
await new Promise((resolve, reject) => {
|
|
||||||
buildShared.on("close", (code) => {
|
|
||||||
if (code !== 0) {
|
|
||||||
reject(new Error(`Shared package build failed with code ${code}`));
|
|
||||||
} else {
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
buildShared.on("error", reject);
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log("\n✅ Shared package built successfully!\n");
|
|
||||||
|
|
||||||
// Clear Vite cache to prevent dep optimization issues
|
// Clear Vite cache to prevent dep optimization issues
|
||||||
const viteCachePath = path.join(
|
const viteCachePath = path.join(
|
||||||
projectRoot,
|
projectRoot,
|
||||||
@@ -180,19 +159,18 @@ async function startDev() {
|
|||||||
|
|
||||||
console.log("🚀 Starting development servers in watch mode...\n");
|
console.log("🚀 Starting development servers in watch mode...\n");
|
||||||
|
|
||||||
// Step 2: Start server, workers, and vite first (they'll use the built shared package)
|
// Start server, workers, and vite (they'll use the shared package source files directly)
|
||||||
const concurrentlyCmd = spawn(
|
const concurrentlyCmd = spawn(
|
||||||
"bunx",
|
"bunx",
|
||||||
[
|
[
|
||||||
"concurrently",
|
"concurrently",
|
||||||
"-n",
|
"-n",
|
||||||
"server,workers,vite,shared",
|
"server,workers,vite",
|
||||||
"-c",
|
"-c",
|
||||||
"green,yellow,blue,cyan",
|
"green,yellow,blue",
|
||||||
`"cd server && SERVER_PORT=${SERVER_PORT} bun dev"`,
|
`"cd server && SERVER_PORT=${SERVER_PORT} bun dev"`,
|
||||||
`"cd server && bun workers:dev"`,
|
`"cd server && bun workers:dev"`,
|
||||||
`"cd vite && VITE_PORT=${VITE_PORT} bun dev"`,
|
`"cd vite && VITE_PORT=${VITE_PORT} bun dev"`,
|
||||||
`"cd shared && bun run dev:watch"`,
|
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
stdio: "inherit",
|
stdio: "inherit",
|
||||||
|
|||||||
1
server/.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc
Symbolic link
1
server/.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../CLAUDE.md
|
||||||
111
server/CLAUDE.md
Normal file
111
server/CLAUDE.md
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
---
|
||||||
|
description: Use Bun instead of Node.js, npm, pnpm, or vite.
|
||||||
|
globs: "*.ts, *.tsx, *.html, *.css, *.js, *.jsx, package.json"
|
||||||
|
alwaysApply: false
|
||||||
|
---
|
||||||
|
|
||||||
|
Default to using Bun instead of Node.js.
|
||||||
|
|
||||||
|
- Use `bun <file>` instead of `node <file>` or `ts-node <file>`
|
||||||
|
- Use `bun test` instead of `jest` or `vitest`
|
||||||
|
- Use `bun build <file.html|file.ts|file.css>` instead of `webpack` or `esbuild`
|
||||||
|
- Use `bun install` instead of `npm install` or `yarn install` or `pnpm install`
|
||||||
|
- Use `bun run <script>` instead of `npm run <script>` or `yarn run <script>` or `pnpm run <script>`
|
||||||
|
- Bun automatically loads .env, so don't use dotenv.
|
||||||
|
|
||||||
|
## APIs
|
||||||
|
|
||||||
|
- `Bun.serve()` supports WebSockets, HTTPS, and routes. Don't use `express`.
|
||||||
|
- `bun:sqlite` for SQLite. Don't use `better-sqlite3`.
|
||||||
|
- `Bun.redis` for Redis. Don't use `ioredis`.
|
||||||
|
- `Bun.sql` for Postgres. Don't use `pg` or `postgres.js`.
|
||||||
|
- `WebSocket` is built-in. Don't use `ws`.
|
||||||
|
- Prefer `Bun.file` over `node:fs`'s readFile/writeFile
|
||||||
|
- Bun.$`ls` instead of execa.
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
Use `bun test` to run tests.
|
||||||
|
|
||||||
|
```ts#index.test.ts
|
||||||
|
import { test, expect } from "bun:test";
|
||||||
|
|
||||||
|
test("hello world", () => {
|
||||||
|
expect(1).toBe(1);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Frontend
|
||||||
|
|
||||||
|
Use HTML imports with `Bun.serve()`. Don't use `vite`. HTML imports fully support React, CSS, Tailwind.
|
||||||
|
|
||||||
|
Server:
|
||||||
|
|
||||||
|
```ts#index.ts
|
||||||
|
import index from "./index.html"
|
||||||
|
|
||||||
|
Bun.serve({
|
||||||
|
routes: {
|
||||||
|
"/": index,
|
||||||
|
"/api/users/:id": {
|
||||||
|
GET: (req) => {
|
||||||
|
return new Response(JSON.stringify({ id: req.params.id }));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// optional websocket support
|
||||||
|
websocket: {
|
||||||
|
open: (ws) => {
|
||||||
|
ws.send("Hello, world!");
|
||||||
|
},
|
||||||
|
message: (ws, message) => {
|
||||||
|
ws.send(message);
|
||||||
|
},
|
||||||
|
close: (ws) => {
|
||||||
|
// handle close
|
||||||
|
}
|
||||||
|
},
|
||||||
|
development: {
|
||||||
|
hmr: true,
|
||||||
|
console: true,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
HTML files can import .tsx, .jsx or .js files directly and Bun's bundler will transpile & bundle automatically. `<link>` tags can point to stylesheets and Bun's CSS bundler will bundle.
|
||||||
|
|
||||||
|
```html#index.html
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<h1>Hello, world!</h1>
|
||||||
|
<script type="module" src="./frontend.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
With the following `frontend.tsx`:
|
||||||
|
|
||||||
|
```tsx#frontend.tsx
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
// import .css files directly and it works
|
||||||
|
import './index.css';
|
||||||
|
|
||||||
|
import { createRoot } from "react-dom/client";
|
||||||
|
|
||||||
|
const root = createRoot(document.body);
|
||||||
|
|
||||||
|
export default function Frontend() {
|
||||||
|
return <h1>Hello, world!</h1>;
|
||||||
|
}
|
||||||
|
|
||||||
|
root.render(<Frontend />);
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, run index.ts
|
||||||
|
|
||||||
|
```sh
|
||||||
|
bun --hot ./index.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
For more information, read the Bun API docs in `node_modules/bun-types/docs/**.md`.
|
||||||
15
server/README.md
Normal file
15
server/README.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# @autumn/server
|
||||||
|
|
||||||
|
To install dependencies:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun install
|
||||||
|
```
|
||||||
|
|
||||||
|
To run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun run index.js
|
||||||
|
```
|
||||||
|
|
||||||
|
This project was created using `bun init` in bun v1.3.2. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
|
||||||
@@ -1,173 +0,0 @@
|
|||||||
import { Redis } from "ioredis";
|
|
||||||
import { AutumnInt } from "../src/external/autumn/autumnCli.js";
|
|
||||||
import { readFileSync } from "node:fs";
|
|
||||||
import { join } from "node:path";
|
|
||||||
import crypto from "node:crypto";
|
|
||||||
|
|
||||||
const client = new Redis("redis://localhost:6379");
|
|
||||||
|
|
||||||
// Load Lua scripts
|
|
||||||
const setCustomerScript = readFileSync(
|
|
||||||
join(import.meta.dir, "setCustomer.lua"),
|
|
||||||
"utf-8",
|
|
||||||
);
|
|
||||||
const getCustomerScript = readFileSync(
|
|
||||||
join(import.meta.dir, "getCustomer.lua"),
|
|
||||||
"utf-8",
|
|
||||||
);
|
|
||||||
|
|
||||||
// Calculate SHA1 hashes for script caching
|
|
||||||
const setCustomerSha = crypto
|
|
||||||
.createHash("sha1")
|
|
||||||
.update(setCustomerScript)
|
|
||||||
.digest("hex");
|
|
||||||
const getCustomerSha = crypto
|
|
||||||
.createHash("sha1")
|
|
||||||
.update(getCustomerScript)
|
|
||||||
.digest("hex");
|
|
||||||
|
|
||||||
// Load scripts into Redis
|
|
||||||
await client.script("LOAD", setCustomerScript);
|
|
||||||
await client.script("LOAD", getCustomerScript);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Atomically stores a customer object in Redis with features as HSETs
|
|
||||||
*/
|
|
||||||
async function setCustomer({ customerData }: { customerData: any }) {
|
|
||||||
const customerId = customerData.id;
|
|
||||||
try {
|
|
||||||
const result = await client.evalsha(
|
|
||||||
setCustomerSha,
|
|
||||||
1,
|
|
||||||
customerId,
|
|
||||||
JSON.stringify(customerData),
|
|
||||||
);
|
|
||||||
return result;
|
|
||||||
} catch (error: any) {
|
|
||||||
// If script not found, reload and retry
|
|
||||||
if (error.message.includes("NOSCRIPT")) {
|
|
||||||
await client.script("LOAD", setCustomerScript);
|
|
||||||
return await client.evalsha(
|
|
||||||
setCustomerSha,
|
|
||||||
1,
|
|
||||||
customerId,
|
|
||||||
JSON.stringify(customerData),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Atomically retrieves a customer object from Redis, reconstructing from HSETs
|
|
||||||
*/
|
|
||||||
async function getCustomer({ customerId }: { customerId: string }) {
|
|
||||||
try {
|
|
||||||
const result = await client.evalsha(getCustomerSha, 1, customerId);
|
|
||||||
if (!result) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return JSON.parse(result as string);
|
|
||||||
} catch (error: any) {
|
|
||||||
// If script not found, reload and retry
|
|
||||||
if (error.message.includes("NOSCRIPT")) {
|
|
||||||
await client.script("LOAD", getCustomerScript);
|
|
||||||
const retryResult = await client.evalsha(getCustomerSha, 1, customerId);
|
|
||||||
if (!retryResult) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return JSON.parse(retryResult as string);
|
|
||||||
}
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Atomically updates a feature balance using HINCRBYFLOAT
|
|
||||||
*/
|
|
||||||
async function updateFeatureBalance({
|
|
||||||
customerId,
|
|
||||||
featureId,
|
|
||||||
amount,
|
|
||||||
breakdownIndex,
|
|
||||||
}: {
|
|
||||||
customerId: string;
|
|
||||||
featureId: string;
|
|
||||||
amount: number;
|
|
||||||
breakdownIndex?: number;
|
|
||||||
}) {
|
|
||||||
// Update breakdown-specific balance if index provided
|
|
||||||
if (breakdownIndex !== undefined) {
|
|
||||||
const breakdownKey = `customer:${customerId}:features:${featureId}:breakdown:${breakdownIndex}`;
|
|
||||||
await client.hincrbyfloat(breakdownKey, "balance", amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Always update aggregate feature balance
|
|
||||||
const featureKey = `customer:${customerId}:features:${featureId}`;
|
|
||||||
await client.hincrbyfloat(featureKey, "balance", amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Atomically updates a feature usage using HINCRBYFLOAT
|
|
||||||
*/
|
|
||||||
async function updateFeatureUsage({
|
|
||||||
customerId,
|
|
||||||
featureId,
|
|
||||||
amount,
|
|
||||||
breakdownIndex,
|
|
||||||
}: {
|
|
||||||
customerId: string;
|
|
||||||
featureId: string;
|
|
||||||
amount: number;
|
|
||||||
breakdownIndex?: number;
|
|
||||||
}) {
|
|
||||||
// Update breakdown-specific usage if index provided
|
|
||||||
if (breakdownIndex !== undefined) {
|
|
||||||
const breakdownKey = `customer:${customerId}:features:${featureId}:breakdown:${breakdownIndex}`;
|
|
||||||
await client.hincrbyfloat(breakdownKey, "usage", amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Always update aggregate feature usage
|
|
||||||
const featureKey = `customer:${customerId}:features:${featureId}`;
|
|
||||||
await client.hincrbyfloat(featureKey, "usage", amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
const main = async () => {
|
|
||||||
const autumn = new AutumnInt({ secretKey: process.env.JDEV! });
|
|
||||||
|
|
||||||
const customer = await autumn.customers.get("john");
|
|
||||||
|
|
||||||
// 1. Set customer
|
|
||||||
const setStart = performance.now();
|
|
||||||
await setCustomer({
|
|
||||||
customerData: customer,
|
|
||||||
});
|
|
||||||
const setEnd = performance.now();
|
|
||||||
console.log(`✓ Stored customer in Redis (${(setEnd - setStart).toFixed(2)}ms)`);
|
|
||||||
|
|
||||||
// 2. Get customer
|
|
||||||
console.time("Get cached customer");
|
|
||||||
const cachedCustomer = await getCustomer({ customerId: "john" });
|
|
||||||
console.timeEnd("Get cached customer");
|
|
||||||
|
|
||||||
// Compare features
|
|
||||||
console.log("\n=== Comparison ===");
|
|
||||||
console.log(`Original credits feature balance: `, cachedCustomer?.features?.credits?.balance);
|
|
||||||
|
|
||||||
// Time the decrement of lifetime balance using HDECRBYFLOAT
|
|
||||||
console.time("Decrement lifetime balance");
|
|
||||||
await client.hincrbyfloat("customer:john:features:credits", "balance", -1.42513);
|
|
||||||
console.timeEnd("Decrement lifetime balance");
|
|
||||||
|
|
||||||
// Get updated customer
|
|
||||||
const updatedCustomer = await getCustomer({ customerId: "john" });
|
|
||||||
console.log('Updated credits feature balance:', updatedCustomer?.features?.credits?.balance);
|
|
||||||
|
|
||||||
// Time getting the customer object
|
|
||||||
console.time("Get base customer");
|
|
||||||
await client.get("customer:john");
|
|
||||||
console.timeEnd("Get base customer");
|
|
||||||
};
|
|
||||||
|
|
||||||
await main();
|
|
||||||
process.exit(0);
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
import { AppEnv } from "@autumn/shared";
|
|
||||||
import {
|
|
||||||
buildCachedApiCustomerKey,
|
|
||||||
getCachedApiCustomer,
|
|
||||||
} from "../src/internal/customers/cusUtils/apiCusCacheUtils/getCachedApiCustomer.js";
|
|
||||||
import { initDrizzle } from "../src/db/initDrizzle.js";
|
|
||||||
import { initScript } from "../src/utils/scriptUtils/scriptUtils.js";
|
|
||||||
import { redis } from "../src/external/redis/initRedis.js";
|
|
||||||
import { AutumnInt } from "../src/external/autumn/autumnCli.js";
|
|
||||||
|
|
||||||
const DEDUCTION_COUNT = 15_000;
|
|
||||||
const DEDUCTION_AMOUNT = 1;
|
|
||||||
|
|
||||||
const logCredits = (label: string, customer: Awaited<ReturnType<typeof getCachedApiCustomer>>) => {
|
|
||||||
const credits = customer?.features?.credits;
|
|
||||||
console.log(`\n${label}`);
|
|
||||||
console.log(` Total Balance: ${credits?.balance ?? "N/A"}`);
|
|
||||||
console.log(` Monthly Credits: ${credits?.breakdown?.[0]?.balance ?? "N/A"}`);
|
|
||||||
console.log(` Lifetime Credits: ${credits?.breakdown?.[1]?.balance ?? "N/A"}`);
|
|
||||||
};
|
|
||||||
|
|
||||||
const main = async () => {
|
|
||||||
const orgId = "org_2s4vfEyYVgFZDlOwcMHjsHR0eef";
|
|
||||||
const env = AppEnv.Sandbox;
|
|
||||||
const customerId = "john";
|
|
||||||
|
|
||||||
|
|
||||||
const { db } = initDrizzle();
|
|
||||||
const { req } = await initScript({ orgId, env });
|
|
||||||
const autumn = new AutumnInt({
|
|
||||||
secretKey: process.env.JDEV!,
|
|
||||||
});
|
|
||||||
|
|
||||||
await redis.del(buildCachedApiCustomerKey({ customerId, orgId, env }));
|
|
||||||
|
|
||||||
const customerBefore = await getCachedApiCustomer({
|
|
||||||
ctx: req as any,
|
|
||||||
customerId,
|
|
||||||
});
|
|
||||||
logCredits("📊 Credits Before:", customerBefore);
|
|
||||||
|
|
||||||
console.log(`\n⏳ Processing ${DEDUCTION_COUNT.toLocaleString()} deductions...`);
|
|
||||||
const start = Date.now();
|
|
||||||
|
|
||||||
|
|
||||||
const promises = Array.from({ length: DEDUCTION_COUNT }, () =>
|
|
||||||
autumn.track({
|
|
||||||
customer_id: customerId,
|
|
||||||
feature_id: "credits",
|
|
||||||
value: DEDUCTION_AMOUNT,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
await Promise.all(promises);
|
|
||||||
const elapsed = Date.now() - start;
|
|
||||||
|
|
||||||
const customerAfter = await getCachedApiCustomer({
|
|
||||||
ctx: req as any,
|
|
||||||
customerId,
|
|
||||||
});
|
|
||||||
logCredits("📊 Credits After:", customerAfter);
|
|
||||||
|
|
||||||
const deductionDiff = (customerBefore?.features?.credits?.balance ?? 0) - (customerAfter?.features?.credits?.balance ?? 0);
|
|
||||||
|
|
||||||
console.log("\n✅ Test Complete!");
|
|
||||||
console.log(` Time Elapsed: ${elapsed.toLocaleString()}ms`);
|
|
||||||
console.log(` Total Deducted: ${deductionDiff}`);
|
|
||||||
console.log(` Avg per Deduction: ${(elapsed / DEDUCTION_COUNT).toFixed(3)}ms\n`);
|
|
||||||
};
|
|
||||||
|
|
||||||
await main();
|
|
||||||
process.exit(0);
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
import { globalBatchingManager } from "../src/internal/balances/track/redisTrackUtils/BatchingManager.js";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test script to debug the simplified batchDeduction.lua
|
|
||||||
* This will show you what the Lua script can retrieve about a feature
|
|
||||||
*/
|
|
||||||
async function testLuaDebug() {
|
|
||||||
// Replace these with real values from your test data
|
|
||||||
const customerId = "your-customer-id";
|
|
||||||
const orgId = "your-org-id";
|
|
||||||
const env = "development";
|
|
||||||
|
|
||||||
console.log("Testing Lua script with:");
|
|
||||||
console.log("- Customer ID:", customerId);
|
|
||||||
console.log("- Org ID:", orgId);
|
|
||||||
console.log("- Environment:", env);
|
|
||||||
console.log("");
|
|
||||||
|
|
||||||
try {
|
|
||||||
const result = await globalBatchingManager.deduct({
|
|
||||||
customerId,
|
|
||||||
featureDeductions: [
|
|
||||||
{ featureId: "credits", amount: 10 },
|
|
||||||
{ featureId: "api_calls", amount: 5 },
|
|
||||||
],
|
|
||||||
orgId,
|
|
||||||
env,
|
|
||||||
overageBehavior: "cap",
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log("📦 Batching manager result:");
|
|
||||||
console.log(JSON.stringify(result, null, 2));
|
|
||||||
} catch (error) {
|
|
||||||
console.error("❌ Error:", error);
|
|
||||||
}
|
|
||||||
|
|
||||||
process.exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
testLuaDebug();
|
|
||||||
|
|
||||||
1
server/index.js
Normal file
1
server/index.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
console.log("Hello via Bun!");
|
||||||
@@ -23,7 +23,9 @@
|
|||||||
"parallel-tests:v1": "ENV_FILE=.env infisical run --env=dev -- bun tests/testRunner/runParallelGroups.ts",
|
"parallel-tests:v1": "ENV_FILE=.env infisical run --env=dev -- bun tests/testRunner/runParallelGroups.ts",
|
||||||
"parallel-tests:verbose": "ENV_FILE=.env infisical run --env=dev -- bun tests/testRunner/runParallelGroups.ts --verbose",
|
"parallel-tests:verbose": "ENV_FILE=.env infisical run --env=dev -- bun tests/testRunner/runParallelGroups.ts --verbose",
|
||||||
"parallel-tests:debug": "ENV_FILE=.env infisical run --env=dev -- bun tests/testRunner/runParallelGroups.ts --debug",
|
"parallel-tests:debug": "ENV_FILE=.env infisical run --env=dev -- bun tests/testRunner/runParallelGroups.ts --debug",
|
||||||
"clear-master": "ENV_FILE=.env infisical run --env=dev -- bun tests/clearMasterOrg.ts"
|
"clear-master": "ENV_FILE=.env infisical run --env=dev -- bun tests/clearMasterOrg.ts",
|
||||||
|
|
||||||
|
"ts": "bunx tsgo --build --noEmit"
|
||||||
},
|
},
|
||||||
"mocha": {
|
"mocha": {
|
||||||
"node-option": [
|
"node-option": [
|
||||||
@@ -113,7 +115,7 @@
|
|||||||
"recaseai": "^0.0.37",
|
"recaseai": "^0.0.37",
|
||||||
"resend": "^4.1.1",
|
"resend": "^4.1.1",
|
||||||
"semver": "^7.7.2",
|
"semver": "^7.7.2",
|
||||||
"stripe": "18.4.0-beta.2",
|
"stripe": "catalog:",
|
||||||
"svix": "^1.45.1",
|
"svix": "^1.45.1",
|
||||||
"tsc-alias": "^1.8.16",
|
"tsc-alias": "^1.8.16",
|
||||||
"ws": "^8.18.0",
|
"ws": "^8.18.0",
|
||||||
|
|||||||
@@ -36,16 +36,16 @@ import { checkCusSubCorrect } from "./utils/checkUtils/checkCustomerCorrect.js";
|
|||||||
|
|
||||||
const { db } = initDrizzle({ maxConnections: 5 });
|
const { db } = initDrizzle({ maxConnections: 5 });
|
||||||
|
|
||||||
let orgSlugs = process.env.ORG_SLUGS!.split(",");
|
const orgSlugs = process.env.ORG_SLUGS!.split(",");
|
||||||
const skipEmails = process.env.SKIP_EMAILS!.split(",");
|
const skipEmails = process.env.SKIP_EMAILS!.split(",");
|
||||||
const skipIds = [
|
const skipIds = [
|
||||||
"cus_2tXCCwC6iyiftgA6ndSo1Ubb2dx",
|
"cus_2tXCCwC6iyiftgA6ndSo1Ubb2dx",
|
||||||
"DxG668K7uDd0Vahk54YWjvCGVgf2",
|
"DxG668K7uDd0Vahk54YWjvCGVgf2",
|
||||||
];
|
];
|
||||||
|
|
||||||
orgSlugs = ["welcome-back-1747670264"];
|
// orgSlugs = ["welcome-back-1747670264"];
|
||||||
let customerId = null;
|
// let customerId = null;
|
||||||
customerId = "56";
|
const customerId = null;
|
||||||
|
|
||||||
const getSingleCustomer = async ({
|
const getSingleCustomer = async ({
|
||||||
stripeCli,
|
stripeCli,
|
||||||
|
|||||||
2
server/src/external/autumn/autumnCli.ts
vendored
2
server/src/external/autumn/autumnCli.ts
vendored
@@ -18,6 +18,7 @@ import {
|
|||||||
type RewardRedemption,
|
type RewardRedemption,
|
||||||
type TrackParams,
|
type TrackParams,
|
||||||
} from "@autumn/shared";
|
} from "@autumn/shared";
|
||||||
|
import { defaultApiVersion } from "@tests/constants.js";
|
||||||
import type {
|
import type {
|
||||||
CancelParams,
|
CancelParams,
|
||||||
CheckoutParams,
|
CheckoutParams,
|
||||||
@@ -27,7 +28,6 @@ import type {
|
|||||||
Customer,
|
Customer,
|
||||||
UsageParams,
|
UsageParams,
|
||||||
} from "autumn-js";
|
} from "autumn-js";
|
||||||
import { defaultApiVersion } from "tests/constants.js";
|
|
||||||
|
|
||||||
export default class AutumnError extends Error {
|
export default class AutumnError extends Error {
|
||||||
message: string;
|
message: string;
|
||||||
|
|||||||
4
server/src/external/autumn/autumnCliV2.ts
vendored
4
server/src/external/autumn/autumnCliV2.ts
vendored
@@ -5,7 +5,7 @@ dotenv.config();
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
type AttachBody,
|
type AttachBody,
|
||||||
type CreateEntity,
|
type CreateEntityParams,
|
||||||
type CreateRewardProgram,
|
type CreateRewardProgram,
|
||||||
CusExpand,
|
CusExpand,
|
||||||
EntityExpand,
|
EntityExpand,
|
||||||
@@ -321,7 +321,7 @@ export class AutumnCliV2 {
|
|||||||
|
|
||||||
create: async (
|
create: async (
|
||||||
customerId: string,
|
customerId: string,
|
||||||
entity: CreateEntity | CreateEntity[],
|
entity: CreateEntityParams | CreateEntityParams[],
|
||||||
) => {
|
) => {
|
||||||
return await this.post(
|
return await this.post(
|
||||||
`/customers/${customerId}/entities?with_autumn_id=true`,
|
`/customers/${customerId}/entities?with_autumn_id=true`,
|
||||||
|
|||||||
6
server/src/external/llm/llmUtils.ts
vendored
6
server/src/external/llm/llmUtils.ts
vendored
@@ -1,5 +1,5 @@
|
|||||||
import { createAnthropic } from "@ai-sdk/anthropic";
|
import { createAnthropic } from "@ai-sdk/anthropic";
|
||||||
import { Feature } from "@autumn/shared";
|
import type { Feature } from "@autumn/shared";
|
||||||
import { generateObject } from "ai";
|
import { generateObject } from "ai";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
@@ -20,8 +20,8 @@ export const generateFeatureDisplay = async (feature: Feature) => {
|
|||||||
</feature>
|
</feature>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
let { object } = await generateObject({
|
const { object } = await generateObject({
|
||||||
model: anthropic("claude-3-5-sonnet-latest"),
|
model: anthropic("claude-haiku-4-5"),
|
||||||
schema: z.object({
|
schema: z.object({
|
||||||
singular: z.string(),
|
singular: z.string(),
|
||||||
plural: z.string(),
|
plural: z.string(),
|
||||||
|
|||||||
@@ -155,7 +155,7 @@ export const handleStripeWebhookEvent = async ({
|
|||||||
case "customer.subscription.updated": {
|
case "customer.subscription.updated": {
|
||||||
const subscription = event.data.object;
|
const subscription = event.data.object;
|
||||||
await handleSubscriptionUpdated({
|
await handleSubscriptionUpdated({
|
||||||
req: ctx as ExtendedRequest,
|
req: ctx as unknown as ExtendedRequest,
|
||||||
db,
|
db,
|
||||||
org,
|
org,
|
||||||
subscription,
|
subscription,
|
||||||
@@ -168,7 +168,7 @@ export const handleStripeWebhookEvent = async ({
|
|||||||
|
|
||||||
case "customer.subscription.deleted":
|
case "customer.subscription.deleted":
|
||||||
await handleSubDeleted({
|
await handleSubDeleted({
|
||||||
req: ctx as ExtendedRequest,
|
req: ctx as unknown as ExtendedRequest,
|
||||||
stripeCli,
|
stripeCli,
|
||||||
data: event.data.object,
|
data: event.data.object,
|
||||||
logger,
|
logger,
|
||||||
@@ -178,7 +178,7 @@ export const handleStripeWebhookEvent = async ({
|
|||||||
case "checkout.session.completed": {
|
case "checkout.session.completed": {
|
||||||
const checkoutSession = event.data.object;
|
const checkoutSession = event.data.object;
|
||||||
await handleCheckoutSessionCompleted({
|
await handleCheckoutSessionCompleted({
|
||||||
req: ctx as ExtendedRequest,
|
req: ctx as unknown as ExtendedRequest,
|
||||||
db,
|
db,
|
||||||
data: checkoutSession,
|
data: checkoutSession,
|
||||||
org,
|
org,
|
||||||
@@ -196,7 +196,7 @@ export const handleStripeWebhookEvent = async ({
|
|||||||
invoiceData: invoice,
|
invoiceData: invoice,
|
||||||
env,
|
env,
|
||||||
event,
|
event,
|
||||||
req: ctx as ExtendedRequest,
|
req: ctx as unknown as ExtendedRequest,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -206,7 +206,7 @@ export const handleStripeWebhookEvent = async ({
|
|||||||
stripeCli,
|
stripeCli,
|
||||||
env,
|
env,
|
||||||
event,
|
event,
|
||||||
req: ctx as ExtendedRequest,
|
req: ctx as unknown as ExtendedRequest,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
6
server/src/external/stripe/stripeSubUtils.ts
vendored
6
server/src/external/stripe/stripeSubUtils.ts
vendored
@@ -141,7 +141,7 @@ export const getUsageBasedSub = async ({
|
|||||||
let usageFeatures: string[] | null = null;
|
let usageFeatures: string[] | null = null;
|
||||||
|
|
||||||
// 1. Check if there's autumn sub
|
// 1. Check if there's autumn sub
|
||||||
const autumnSub = autumnSubs?.find((sub) => sub.stripe_id == stripeSub.id);
|
const autumnSub = autumnSubs?.find((sub) => sub.stripe_id === stripeSub.id);
|
||||||
if (autumnSub) {
|
if (autumnSub) {
|
||||||
const containsFeature = autumnSub.usage_features.includes(
|
const containsFeature = autumnSub.usage_features.includes(
|
||||||
feature.internal_id!,
|
feature.internal_id!,
|
||||||
@@ -153,14 +153,14 @@ export const getUsageBasedSub = async ({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
usageFeatures = JSON.parse(stripeSub.metadata.usage_features);
|
usageFeatures = JSON.parse(stripeSub.metadata.usage_features);
|
||||||
} catch (error) {
|
} catch {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!usageFeatures ||
|
!usageFeatures ||
|
||||||
usageFeatures.find(
|
usageFeatures.find(
|
||||||
(feat: any) => feat.internal_id == feature.internal_id,
|
(feat: any) => feat.internal_id === feature.internal_id,
|
||||||
) === undefined
|
) === undefined
|
||||||
) {
|
) {
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -301,8 +301,6 @@ export const handleInvoiceCreated = async ({
|
|||||||
|
|
||||||
const subId = invoiceToSubId({ invoice });
|
const subId = invoiceToSubId({ invoice });
|
||||||
|
|
||||||
console.log("HANDLING INVOICE CREATED", invoice.id);
|
|
||||||
|
|
||||||
if (subId) {
|
if (subId) {
|
||||||
const activeProducts = await CusProductService.getByStripeSubId({
|
const activeProducts = await CusProductService.getByStripeSubId({
|
||||||
db,
|
db,
|
||||||
|
|||||||
@@ -4,16 +4,33 @@ import type { HonoEnv } from "@/honoUtils/HonoEnv.js";
|
|||||||
/**
|
/**
|
||||||
* Extracts expand from validated query and sets it in context.
|
* Extracts expand from validated query and sets it in context.
|
||||||
* Must run AFTER versionedValidator so expand has been transformed.
|
* Must run AFTER versionedValidator so expand has been transformed.
|
||||||
|
* Uses c.req.query("expand") to access the parsed expand field.
|
||||||
*/
|
*/
|
||||||
export const expandMiddleware = (): MiddlewareHandler<HonoEnv> => {
|
export const expandMiddleware = (): MiddlewareHandler<HonoEnv> => {
|
||||||
return async (c, next) => {
|
return async (c, next) => {
|
||||||
const validatedQuery = c.req.valid("query");
|
// Query is parsed by queryMiddleware and validated by versionedValidator/validator
|
||||||
const expand = validatedQuery?.expand || [];
|
// queryStringArray normalizes expand to an array during validation, but we access
|
||||||
|
// the parsed query which may still be a string for single values
|
||||||
|
const expandValue = c.req.query("expand");
|
||||||
|
// queryMiddleware converts "true"/"false" strings to boolean values
|
||||||
|
// Handle both boolean (from queryMiddleware) and string (fallback) cases
|
||||||
|
const skipCacheQuery = c.req.query("skip_cache");
|
||||||
|
const skipCacheValue =
|
||||||
|
(typeof skipCacheQuery === "boolean" && skipCacheQuery === true) ||
|
||||||
|
(typeof skipCacheQuery === "string" && skipCacheQuery === "true");
|
||||||
|
|
||||||
|
// Normalize to array: undefined -> [], string -> [string], array -> array
|
||||||
|
const expand: string[] = !expandValue
|
||||||
|
? []
|
||||||
|
: Array.isArray(expandValue)
|
||||||
|
? expandValue
|
||||||
|
: [expandValue];
|
||||||
|
|
||||||
const ctx = c.get("ctx");
|
const ctx = c.get("ctx");
|
||||||
c.set("ctx", {
|
c.set("ctx", {
|
||||||
...ctx,
|
...ctx,
|
||||||
expand: Array.isArray(expand) ? expand : [expand],
|
expand,
|
||||||
|
skipCache: skipCacheValue,
|
||||||
});
|
});
|
||||||
|
|
||||||
await next();
|
await next();
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ export const versionedValidator = ({
|
|||||||
throw result.error;
|
throw result.error;
|
||||||
}
|
}
|
||||||
validatedData = result.data as Record<string, unknown>;
|
validatedData = result.data as Record<string, unknown>;
|
||||||
c.req.addValidatedData(target, result.data);
|
c.req.addValidatedData(target, validatedData);
|
||||||
} else {
|
} else {
|
||||||
// For other targets, use zValidator
|
// For other targets, use zValidator
|
||||||
const validatorMiddleware = zValidator(target, schema, (result, _c) => {
|
const validatorMiddleware = zValidator(target, schema, (result, _c) => {
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ export type RequestContext = {
|
|||||||
|
|
||||||
// Query params
|
// Query params
|
||||||
expand: string[];
|
expand: string[];
|
||||||
|
skipCache: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AutumnContext = RequestContext;
|
export type AutumnContext = RequestContext;
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ analyticsRouter.post("", (req, res) =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
events.data.forEach((event: any) => {
|
events.data.forEach((event: any) => {
|
||||||
event["period"] = parseInt(format(new Date(event["period"]), "T"));
|
event.period = parseInt(format(new Date(event.period), "T"));
|
||||||
});
|
});
|
||||||
|
|
||||||
const usageList = events.data.filter(
|
const usageList = events.data.filter(
|
||||||
|
|||||||
@@ -404,6 +404,7 @@ export const createFullCusProduct = async ({
|
|||||||
isDowngrade,
|
isDowngrade,
|
||||||
entities: attachParams.entities,
|
entities: attachParams.entities,
|
||||||
features: attachParams.features,
|
features: attachParams.features,
|
||||||
|
prices,
|
||||||
});
|
});
|
||||||
|
|
||||||
// 4. Get new rollovers
|
// 4. Get new rollovers
|
||||||
|
|||||||
@@ -102,7 +102,6 @@ export const handleScheduleFunction2 = async ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const currentPhaseIndex = getCurrentPhaseIndex({
|
const currentPhaseIndex = getCurrentPhaseIndex({
|
||||||
// biome-ignore lint/suspicious/noExplicitAny: ok
|
|
||||||
schedule: { phases: newItems.phases } as any,
|
schedule: { phases: newItems.phases } as any,
|
||||||
now: attachParams.now,
|
now: attachParams.now,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ export const getPricesAndEnts = async ({
|
|||||||
optionsInput: optionsInput || [],
|
optionsInput: optionsInput || [],
|
||||||
features,
|
features,
|
||||||
prices,
|
prices,
|
||||||
curCusProduct: curMainProduct,
|
curCusProduct,
|
||||||
}),
|
}),
|
||||||
prices,
|
prices,
|
||||||
entitlements: getEntsWithFeature({
|
entitlements: getEntsWithFeature({
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
intervalToValue,
|
intervalToValue,
|
||||||
ProrationBehavior,
|
ProrationBehavior,
|
||||||
} from "@autumn/shared";
|
} from "@autumn/shared";
|
||||||
|
import { isDefaultTrialFullProduct } from "@/internal/products/productUtils/classifyProduct.js";
|
||||||
import { isFreeProduct } 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 { AttachParams } from "../../cusProducts/AttachParams.js";
|
import type { AttachParams } from "../../cusProducts/AttachParams.js";
|
||||||
@@ -16,7 +17,6 @@ import {
|
|||||||
attachParamsToProduct,
|
attachParamsToProduct,
|
||||||
attachParamToCusProducts,
|
attachParamToCusProducts,
|
||||||
} from "./convertAttachParams.js";
|
} from "./convertAttachParams.js";
|
||||||
import { isDefaultTrialFullProduct } from "@/internal/products/productUtils/classifyProduct.js";
|
|
||||||
|
|
||||||
export const intervalsAreSame = ({
|
export const intervalsAreSame = ({
|
||||||
attachParams,
|
attachParams,
|
||||||
@@ -118,10 +118,11 @@ export const getAttachConfig = async ({
|
|||||||
let isUpgradingFromDefaultTrial = false;
|
let isUpgradingFromDefaultTrial = false;
|
||||||
if (curMainProduct && branch === AttachBranch.Upgrade) {
|
if (curMainProduct && branch === AttachBranch.Upgrade) {
|
||||||
const product = cusProductToProduct({ cusProduct: curMainProduct });
|
const product = cusProductToProduct({ cusProduct: curMainProduct });
|
||||||
isUpgradingFromDefaultTrial = isDefaultTrialFullProduct({
|
isUpgradingFromDefaultTrial =
|
||||||
product,
|
isDefaultTrialFullProduct({
|
||||||
skipDefault: true,
|
product,
|
||||||
}) || false;
|
skipDefault: true,
|
||||||
|
}) || false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const checkoutFlow =
|
const checkoutFlow =
|
||||||
@@ -172,7 +173,6 @@ export const getAttachConfig = async ({
|
|||||||
? attachBody.finalize_invoice!
|
? attachBody.finalize_invoice!
|
||||||
: true,
|
: true,
|
||||||
requirePaymentMethod: paymentMethodRequired,
|
requirePaymentMethod: paymentMethodRequired,
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return { flags, config };
|
return { flags, config };
|
||||||
|
|||||||
@@ -99,6 +99,10 @@ export const getNewProductPreview = async ({
|
|||||||
|
|
||||||
let trialEnds: number | undefined;
|
let trialEnds: number | undefined;
|
||||||
|
|
||||||
|
if (config.disableTrial) {
|
||||||
|
attachParams.freeTrial = null;
|
||||||
|
}
|
||||||
|
|
||||||
// Scenario where we update a current sub with new product (so no create sub)
|
// Scenario where we update a current sub with new product (so no create sub)
|
||||||
let anchor: number | undefined;
|
let anchor: number | undefined;
|
||||||
if (mergeSub && !config.disableMerge) {
|
if (mergeSub && !config.disableMerge) {
|
||||||
|
|||||||
@@ -1,20 +1,25 @@
|
|||||||
import Stripe from "stripe";
|
import {
|
||||||
import { AttachParams } from "../../cusProducts/AttachParams.js";
|
type AttachConfig,
|
||||||
|
CusProductStatus,
|
||||||
|
cusProductToPrices,
|
||||||
|
cusProductToProduct,
|
||||||
|
type FullCusProduct,
|
||||||
|
} from "@autumn/shared";
|
||||||
|
import type Stripe from "stripe";
|
||||||
import { getStripeSubItems2 } from "@/external/stripe/stripeSubUtils/getStripeSubItems.js";
|
import { getStripeSubItems2 } from "@/external/stripe/stripeSubUtils/getStripeSubItems.js";
|
||||||
import { AttachConfig, CusProductStatus, FullCusProduct } from "@autumn/shared";
|
|
||||||
import { getExistingCusProducts } from "../../cusProducts/cusProductUtils/getExistingCusProducts.js";
|
|
||||||
import { ExtendedRequest } from "@/utils/models/Request.js";
|
|
||||||
import { cusProductToPrices, cusProductToProduct } from "@autumn/shared";
|
|
||||||
import { isArrearPrice } from "@/internal/products/prices/priceUtils/usagePriceUtils/classifyUsagePrice.js";
|
|
||||||
import {
|
import {
|
||||||
findStripeItemForPrice,
|
findStripeItemForPrice,
|
||||||
subItemInCusProduct,
|
subItemInCusProduct,
|
||||||
} from "@/external/stripe/stripeSubUtils/stripeSubItemUtils.js";
|
} from "@/external/stripe/stripeSubUtils/stripeSubItemUtils.js";
|
||||||
import { mergeNewSubItems } from "./mergeNewSubItems.js";
|
import { isArrearPrice } from "@/internal/products/prices/priceUtils/usagePriceUtils/classifyUsagePrice.js";
|
||||||
import { formatPrice } from "@/internal/products/prices/priceUtils.js";
|
import { formatPrice } from "@/internal/products/prices/priceUtils.js";
|
||||||
import { getQuantityToRemove } from "./mergeUtils.js";
|
|
||||||
import { notNullish } from "@/utils/genUtils.js";
|
import { notNullish } from "@/utils/genUtils.js";
|
||||||
import { ItemSet } from "@/utils/models/ItemSet.js";
|
import type { ItemSet } from "@/utils/models/ItemSet.js";
|
||||||
|
import type { ExtendedRequest } from "@/utils/models/Request.js";
|
||||||
|
import type { AttachParams } from "../../cusProducts/AttachParams.js";
|
||||||
|
import { getExistingCusProducts } from "../../cusProducts/cusProductUtils/getExistingCusProducts.js";
|
||||||
|
import { mergeNewSubItems } from "./mergeNewSubItems.js";
|
||||||
|
import { getQuantityToRemove } from "./mergeUtils.js";
|
||||||
|
|
||||||
export const getCusProductsToRemove = ({
|
export const getCusProductsToRemove = ({
|
||||||
attachParams,
|
attachParams,
|
||||||
@@ -53,12 +58,12 @@ export const getCusProductsToRemove = ({
|
|||||||
cusProductsToRemove.push(curScheduledProduct);
|
cusProductsToRemove.push(curScheduledProduct);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. If product is an add on, and there's current same, add it
|
// 2. If cancelling, and same product exists (add on / main), add it.
|
||||||
else if (curSameProduct) {
|
else if (curSameProduct && attachParams.fromCancel) {
|
||||||
cusProductsToRemove.push(curSameProduct);
|
cusProductsToRemove.push(curSameProduct);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. If product is a main product, add curMain
|
// 3. If product is a main product, add curMain
|
||||||
else if (!product.is_add_on && curMainProduct) {
|
else if (!product.is_add_on && curMainProduct) {
|
||||||
cusProductsToRemove.push(curMainProduct);
|
cusProductsToRemove.push(curMainProduct);
|
||||||
}
|
}
|
||||||
@@ -100,8 +105,7 @@ export const paramsToSubItems = async ({
|
|||||||
removeCusProducts?: FullCusProduct[];
|
removeCusProducts?: FullCusProduct[];
|
||||||
addItemSet?: ItemSet;
|
addItemSet?: ItemSet;
|
||||||
}) => {
|
}) => {
|
||||||
const { logger } = req;
|
const curSubItems = sub?.items.data || [];
|
||||||
let curSubItems = sub?.items.data || [];
|
|
||||||
|
|
||||||
const itemSet = notNullish(addItemSet)
|
const itemSet = notNullish(addItemSet)
|
||||||
? addItemSet!
|
? addItemSet!
|
||||||
@@ -120,7 +124,7 @@ export const paramsToSubItems = async ({
|
|||||||
cusProductsToRemove.map((cp) => cp.product.name),
|
cusProductsToRemove.map((cp) => cp.product.name),
|
||||||
);
|
);
|
||||||
|
|
||||||
let newSubItems = mergeNewSubItems({
|
const newSubItems = mergeNewSubItems({
|
||||||
itemSet,
|
itemSet,
|
||||||
curSubItems,
|
curSubItems,
|
||||||
});
|
});
|
||||||
@@ -165,7 +169,7 @@ export const paramsToSubItems = async ({
|
|||||||
allCusProducts.some((cp) => {
|
allCusProducts.some((cp) => {
|
||||||
if (cp.id === cusProduct.id) return false;
|
if (cp.id === cusProduct.id) return false;
|
||||||
|
|
||||||
if (cp.status == CusProductStatus.Scheduled) return false;
|
if (cp.status === CusProductStatus.Scheduled) return false;
|
||||||
|
|
||||||
return subItemInCusProduct({
|
return subItemInCusProduct({
|
||||||
cusProduct: cp,
|
cusProduct: cp,
|
||||||
@@ -177,7 +181,7 @@ export const paramsToSubItems = async ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
itemSet.subItems.some((si) => si.price == existingSubItem.price?.id)
|
itemSet.subItems.some((si) => si.price === existingSubItem.price?.id)
|
||||||
) {
|
) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,9 +33,10 @@ export const subToNewSchedule = async ({
|
|||||||
attachParams,
|
attachParams,
|
||||||
includeCanceled: true,
|
includeCanceled: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
`REMOVING CUS PRODUCTS: ${cusProductsToRemove.map((cp) => `${cp.product.id} (E: ${cp.entity_id})`).join(", ")}`,
|
`REMOVING CUS PRODUCTS: ${cusProductsToRemove.map((cp) => `${cp.product.id} (E: ${cp.entity_id})`).join(", ")}`,
|
||||||
); // See mergedDowngrade5.test.ts -- if there's no schedule and creating one, might need to include canceled cus products
|
);
|
||||||
|
|
||||||
const res = await paramsToScheduleItems({
|
const res = await paramsToScheduleItems({
|
||||||
req,
|
req,
|
||||||
|
|||||||
@@ -158,6 +158,7 @@ export const handleCancelProduct = async ({
|
|||||||
replaceables: [],
|
replaceables: [],
|
||||||
entities: fullCus.entities,
|
entities: fullCus.entities,
|
||||||
features: req.features,
|
features: req.features,
|
||||||
|
fromCancel: true,
|
||||||
},
|
},
|
||||||
config: getDefaultAttachConfig(),
|
config: getDefaultAttachConfig(),
|
||||||
skipInsertCusProduct,
|
skipInsertCusProduct,
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
import { getEntOptions } from "@/internal/products/prices/priceUtils.js";
|
import { getEntOptions } from "@/internal/products/prices/priceUtils.js";
|
||||||
import { performDeductionOnCusEnt } from "@/trigger/updateBalanceTask.js";
|
import { performDeductionOnCusEnt } from "@/trigger/updateBalanceTask.js";
|
||||||
import { notNullish, nullish } from "@/utils/genUtils.js";
|
import { notNullish, nullish } from "@/utils/genUtils.js";
|
||||||
|
import { isOneOff } from "../../../../products/productUtils.js";
|
||||||
import {
|
import {
|
||||||
getRelatedCusPrice,
|
getRelatedCusPrice,
|
||||||
getUnlimitedAndUsageAllowed,
|
getUnlimitedAndUsageAllowed,
|
||||||
@@ -161,6 +162,7 @@ export const getExistingUsages = ({
|
|||||||
export const addExistingUsagesToCusEnts = ({
|
export const addExistingUsagesToCusEnts = ({
|
||||||
cusEnts,
|
cusEnts,
|
||||||
entitlements,
|
entitlements,
|
||||||
|
prices,
|
||||||
curCusProduct,
|
curCusProduct,
|
||||||
carryExistingUsages = false,
|
carryExistingUsages = false,
|
||||||
printLogs = false,
|
printLogs = false,
|
||||||
@@ -170,6 +172,7 @@ export const addExistingUsagesToCusEnts = ({
|
|||||||
}: {
|
}: {
|
||||||
cusEnts: CustomerEntitlement[];
|
cusEnts: CustomerEntitlement[];
|
||||||
entitlements: EntitlementWithFeature[];
|
entitlements: EntitlementWithFeature[];
|
||||||
|
prices: Price[];
|
||||||
curCusProduct: FullCusProduct;
|
curCusProduct: FullCusProduct;
|
||||||
carryExistingUsages?: boolean;
|
carryExistingUsages?: boolean;
|
||||||
printLogs?: boolean;
|
printLogs?: boolean;
|
||||||
@@ -177,9 +180,7 @@ export const addExistingUsagesToCusEnts = ({
|
|||||||
entities: Entity[];
|
entities: Entity[];
|
||||||
features: Feature[];
|
features: Feature[];
|
||||||
}) => {
|
}) => {
|
||||||
if (isDowngrade) {
|
if (isDowngrade || isOneOff(prices)) return cusEnts;
|
||||||
return cusEnts;
|
|
||||||
}
|
|
||||||
|
|
||||||
const existingUsages = getExistingUsages({
|
const existingUsages = getExistingUsages({
|
||||||
curCusProduct,
|
curCusProduct,
|
||||||
|
|||||||
@@ -63,13 +63,6 @@ export const initNextResetAt = ({
|
|||||||
entitlement.interval_count || 1,
|
entitlement.interval_count || 1,
|
||||||
).getTime();
|
).getTime();
|
||||||
|
|
||||||
// console.log(`--------------------------------`);
|
|
||||||
// console.log(`Interval: `, entitlement.interval);
|
|
||||||
// console.log(`Interval count: `, entitlement.interval_count);
|
|
||||||
// console.log(`Now: `, formatUnixToDateTime(now));
|
|
||||||
// console.log(`Next reset at: `, formatUnixToDateTime(nextResetAtCalculated));
|
|
||||||
// console.log(`Anchor to unix: `, formatUnixToDateTime(anchorToUnix));
|
|
||||||
|
|
||||||
// If anchorToUnix, align next reset at to anchorToUnix...
|
// If anchorToUnix, align next reset at to anchorToUnix...
|
||||||
if (
|
if (
|
||||||
anchorToUnix &&
|
anchorToUnix &&
|
||||||
|
|||||||
@@ -38,17 +38,15 @@ export const buildCachedApiCustomerKey = ({
|
|||||||
export const getCachedApiCustomer = async ({
|
export const getCachedApiCustomer = async ({
|
||||||
ctx,
|
ctx,
|
||||||
customerId,
|
customerId,
|
||||||
skipCache = false,
|
|
||||||
skipEntityMerge = false,
|
skipEntityMerge = false,
|
||||||
source,
|
source,
|
||||||
}: {
|
}: {
|
||||||
ctx: AutumnContext;
|
ctx: AutumnContext;
|
||||||
customerId: string;
|
customerId: string;
|
||||||
skipCache?: boolean;
|
|
||||||
skipEntityMerge?: boolean; // If true, returns only customer's own features (no entity merging)
|
skipEntityMerge?: boolean; // If true, returns only customer's own features (no entity merging)
|
||||||
source?: string;
|
source?: string;
|
||||||
}): Promise<{ apiCustomer: ApiCustomer; legacyData: CustomerLegacyData }> => {
|
}): Promise<{ apiCustomer: ApiCustomer; legacyData: CustomerLegacyData }> => {
|
||||||
const { org, env, db } = ctx;
|
const { org, env, db, skipCache } = ctx;
|
||||||
|
|
||||||
const getExpandedApiCustomer = async () => {
|
const getExpandedApiCustomer = async () => {
|
||||||
// await redis.del(
|
// await redis.del(
|
||||||
|
|||||||
@@ -166,12 +166,12 @@ export const getApiBalance = ({
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
const totalUnused = sumValues(
|
// const totalUnused = sumValues(
|
||||||
cusEnts.map((cusEnt) => {
|
// cusEnts.map((cusEnt) => {
|
||||||
const { unused } = getCusEntBalance({ cusEnt, entityId });
|
// const { unused } = getCusEntBalance({ cusEnt, entityId });
|
||||||
return unused;
|
// return unused;
|
||||||
}),
|
// }),
|
||||||
);
|
// );
|
||||||
|
|
||||||
const totalAdditionalBalance = sumValues(
|
const totalAdditionalBalance = sumValues(
|
||||||
cusEnts.map((cusEnt) => {
|
cusEnts.map((cusEnt) => {
|
||||||
@@ -183,25 +183,6 @@ export const getApiBalance = ({
|
|||||||
const totalMaxPurchase = sumValues(
|
const totalMaxPurchase = sumValues(
|
||||||
cusEnts.map((cusEnt) => cusEntToMaxPurchase({ cusEnt })),
|
cusEnts.map((cusEnt) => cusEntToMaxPurchase({ cusEnt })),
|
||||||
);
|
);
|
||||||
// const totalUsageLimit = sumValues(
|
|
||||||
// cusEnts.map((cusEnt) => cusEntToUsageLimit({ cusEnt, entityId })),
|
|
||||||
// );
|
|
||||||
|
|
||||||
// const totalIncludedUsage = sumValues(
|
|
||||||
// cusEnts.map((cusEnt) => cusEntToIncludedUsage({ cusEnt, entityId })),
|
|
||||||
// );
|
|
||||||
|
|
||||||
// const totalIncludedUsageWithRollovers = sumValues(
|
|
||||||
// cusEnts.map((cusEnt) =>
|
|
||||||
// cusEntToIncludedUsage({ cusEnt, entityId, withRollovers: true }),
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
|
|
||||||
// const totalUsage = new Decimal(totalIncludedUsageWithRollovers)
|
|
||||||
// .add(totalAdjustment)
|
|
||||||
// .sub(totalBalanceWithRollovers)
|
|
||||||
// .sub(totalUnused)
|
|
||||||
// .toNumber();
|
|
||||||
|
|
||||||
// 1. Granted balance
|
// 1. Granted balance
|
||||||
const totalGrantedBalanceWithRollovers = sumValues(
|
const totalGrantedBalanceWithRollovers = sumValues(
|
||||||
@@ -257,10 +238,6 @@ export const getApiBalance = ({
|
|||||||
const reset = cusEntsToReset({ cusEnts, feature });
|
const reset = cusEntsToReset({ cusEnts, feature });
|
||||||
const rollovers = cusEntsToRollovers({ cusEnts, entityId });
|
const rollovers = cusEntsToRollovers({ cusEnts, entityId });
|
||||||
|
|
||||||
// console.log(
|
|
||||||
// `Feature: ${feature.id}, Total Max Purchase: ${totalMaxPurchase}, Usage Allowed: ${usageAllowed}`,
|
|
||||||
// );
|
|
||||||
|
|
||||||
const { data: apiBalance, error } = ApiBalanceSchema.safeParse({
|
const { data: apiBalance, error } = ApiBalanceSchema.safeParse({
|
||||||
feature: ctx.expand.includes(CusExpand.BalanceFeature)
|
feature: ctx.expand.includes(CusExpand.BalanceFeature)
|
||||||
? apiFeature
|
? apiFeature
|
||||||
|
|||||||
@@ -82,13 +82,13 @@ export const handleUpdateEntitlement = async (req: any, res: any) => {
|
|||||||
env: req.env,
|
env: req.env,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (balance < 0 && !cusEnt.usage_allowed) {
|
// if (balance < 0 && !cusEnt.usage_allowed) {
|
||||||
throw new RecaseError({
|
// throw new RecaseError({
|
||||||
message: "Entitlement does not allow usage",
|
// message: "Entitlement does not allow usage",
|
||||||
code: ErrCode.InvalidRequest,
|
// code: ErrCode.InvalidRequest,
|
||||||
statusCode: StatusCodes.BAD_REQUEST,
|
// statusCode: StatusCodes.BAD_REQUEST,
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (cusEnt.unlimited) {
|
if (cusEnt.unlimited) {
|
||||||
throw new RecaseError({
|
throw new RecaseError({
|
||||||
@@ -115,7 +115,7 @@ export const handleUpdateEntitlement = async (req: any, res: any) => {
|
|||||||
},
|
},
|
||||||
toDeduct: deducted,
|
toDeduct: deducted,
|
||||||
addAdjustment: true,
|
addAdjustment: true,
|
||||||
allowNegativeBalance: cusEnt.usage_allowed || false,
|
allowNegativeBalance: true,
|
||||||
entityId: entity_id,
|
entityId: entity_id,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -24,6 +24,18 @@ export const handleFeatureTypeChanged = async ({
|
|||||||
const { linkedEntitlements, entitlements, prices, creditSystems, cusEnts } =
|
const { linkedEntitlements, entitlements, prices, creditSystems, cusEnts } =
|
||||||
objectsUsingFeature;
|
objectsUsingFeature;
|
||||||
|
|
||||||
|
// Cannot switch between credit system <-> metered / boolean
|
||||||
|
if (
|
||||||
|
(feature.type === FeatureType.CreditSystem &&
|
||||||
|
newType !== FeatureType.CreditSystem) ||
|
||||||
|
(feature.type !== FeatureType.CreditSystem &&
|
||||||
|
newType === FeatureType.CreditSystem)
|
||||||
|
) {
|
||||||
|
throw new RecaseError({
|
||||||
|
message: `Cannot change type of feature ${feature.id} from ${feature.type} to ${newType}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (cusEnts.length > 0) {
|
if (cusEnts.length > 0) {
|
||||||
throw new RecaseError({
|
throw new RecaseError({
|
||||||
message: `Cannot change type of feature ${feature.id} because it has been attached to a customer before`,
|
message: `Cannot change type of feature ${feature.id} because it has been attached to a customer before`,
|
||||||
|
|||||||
@@ -60,11 +60,17 @@ export class InvoiceService {
|
|||||||
with: {
|
with: {
|
||||||
customer: {
|
customer: {
|
||||||
with: {
|
with: {
|
||||||
org: true,
|
org: {
|
||||||
|
with: {
|
||||||
|
master: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})) as Invoice & { customer: Customer & { org: Organization } };
|
})) as Invoice & {
|
||||||
|
customer: Customer & { org: Organization & { master: Organization } };
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static async list({
|
static async list({
|
||||||
|
|||||||
@@ -2,12 +2,13 @@ import {
|
|||||||
type EntitlementWithFeature,
|
type EntitlementWithFeature,
|
||||||
type Feature,
|
type Feature,
|
||||||
type FullProduct,
|
type FullProduct,
|
||||||
|
getProductItemDisplay,
|
||||||
type Organization,
|
type Organization,
|
||||||
type Price,
|
type Price,
|
||||||
} from "@autumn/shared";
|
} from "@autumn/shared";
|
||||||
import { mapToProductItems } from "@/internal/products/productV2Utils.js";
|
import { mapToProductItems } from "@/internal/products/productV2Utils.js";
|
||||||
import { isFeatureItem } from "../products/product-items/productItemUtils/getItemType.js";
|
import { isFeatureItem } from "../products/product-items/productItemUtils/getItemType.js";
|
||||||
import { getProductItemDisplay } from "../products/productUtils/productResponseUtils/getProductItemDisplay.js";
|
// import { getProductItemDisplay } from "../products/productUtils/productResponseUtils/getProductItemDisplay.js";
|
||||||
|
|
||||||
export const buildInvoiceMemo = async ({
|
export const buildInvoiceMemo = async ({
|
||||||
org,
|
org,
|
||||||
|
|||||||
@@ -63,10 +63,12 @@ mainRouter.use(
|
|||||||
try {
|
try {
|
||||||
const org = invoice.customer.org;
|
const org = invoice.customer.org;
|
||||||
const env = invoice.customer.env;
|
const env = invoice.customer.env;
|
||||||
|
|
||||||
const stripeCli = createStripeCli({
|
const stripeCli = createStripeCli({
|
||||||
org,
|
org,
|
||||||
env,
|
env,
|
||||||
});
|
});
|
||||||
|
|
||||||
const stripeInvoice = await stripeCli.invoices.retrieve(
|
const stripeInvoice = await stripeCli.invoices.retrieve(
|
||||||
invoice.stripe_id,
|
invoice.stripe_id,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -22,16 +22,12 @@ export const getReplaceables = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const numReplaceables = prevOverage - newOverage;
|
const numReplaceables = prevOverage - newOverage;
|
||||||
const newReplaceables = Array.from(
|
const newReplaceables = Array.from({ length: numReplaceables }, (_, i) => ({
|
||||||
{ length: numReplaceables },
|
id: generateId("rep"),
|
||||||
(_, i) =>
|
cus_ent_id: cusEnt.id,
|
||||||
({
|
created_at: Date.now(),
|
||||||
id: generateId("rep"),
|
delete_next_cycle: deleteNextCycle,
|
||||||
cus_ent_id: cusEnt.id,
|
}));
|
||||||
created_at: Date.now(),
|
|
||||||
delete_next_cycle: deleteNextCycle,
|
|
||||||
}) satisfies InsertReplaceable,
|
|
||||||
);
|
|
||||||
|
|
||||||
return newReplaceables;
|
return newReplaceables;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -16,6 +16,26 @@ export const shouldBillNow = (onIncrease: OnIncrease | OnDecrease) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const shouldProrateDowngradeNow = ({
|
||||||
|
onIncrease,
|
||||||
|
onDecrease,
|
||||||
|
}: {
|
||||||
|
onIncrease: OnIncrease;
|
||||||
|
onDecrease: OnDecrease;
|
||||||
|
}) => {
|
||||||
|
if (
|
||||||
|
onDecrease === OnDecrease.NoProrations ||
|
||||||
|
onDecrease === OnDecrease.None
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
onIncrease === OnIncrease.ProrateImmediately ||
|
||||||
|
onIncrease === OnIncrease.BillImmediately
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const shouldProrate = (onIncrease?: OnIncrease | OnDecrease) => {
|
export const shouldProrate = (onIncrease?: OnIncrease | OnDecrease) => {
|
||||||
if (!onIncrease) {
|
if (!onIncrease) {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -256,7 +256,8 @@ export const toFeatureAndPrice = ({
|
|||||||
|
|
||||||
if (shouldProrate(onDecrease) || onDecrease === OnDecrease.Prorate) {
|
if (shouldProrate(onDecrease) || onDecrease === OnDecrease.Prorate) {
|
||||||
onDecrease =
|
onDecrease =
|
||||||
onIncrease === OnIncrease.ProrateImmediately
|
onIncrease === OnIncrease.ProrateImmediately ||
|
||||||
|
onIncrease === OnIncrease.BillImmediately
|
||||||
? OnDecrease.ProrateImmediately
|
? OnDecrease.ProrateImmediately
|
||||||
: OnDecrease.ProrateNextCycle;
|
: OnDecrease.ProrateNextCycle;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { ErrCode } from "@autumn/shared";
|
import { ErrCode } from "@autumn/shared";
|
||||||
import { type Response } from "express";
|
import type { Response } from "express";
|
||||||
import { nanoid } from "nanoid";
|
import { nanoid } from "nanoid";
|
||||||
import { CacheManager } from "@/utils/cacheUtils/CacheManager.js";
|
import { CacheManager } from "@/utils/cacheUtils/CacheManager.js";
|
||||||
import RecaseError from "@/utils/errorUtils.js";
|
import RecaseError from "@/utils/errorUtils.js";
|
||||||
|
|||||||
@@ -74,10 +74,9 @@ const initWorker = ({ id, db }: { id: number; db: DrizzleCli }) => {
|
|||||||
|
|
||||||
if (actionHandlers.includes(job.name as JobName)) {
|
if (actionHandlers.includes(job.name as JobName)) {
|
||||||
await runActionHandlerTask({
|
await runActionHandlerTask({
|
||||||
queue: queue,
|
jobName: job.name as JobName,
|
||||||
job,
|
payload: job.data,
|
||||||
logger: workerLogger,
|
ctx,
|
||||||
db,
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,10 +17,10 @@ import type Stripe from "stripe";
|
|||||||
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 { findStripeItemForPrice } from "@/external/stripe/stripeSubUtils/stripeSubItemUtils.js";
|
import { findStripeItemForPrice } from "@/external/stripe/stripeSubUtils/stripeSubItemUtils.js";
|
||||||
import { getUsageBasedSub } from "@/external/stripe/stripeSubUtils.js";
|
|
||||||
import { getRelatedCusPrice } from "@/internal/customers/cusProducts/cusEnts/cusEntUtils.js";
|
import { getRelatedCusPrice } from "@/internal/customers/cusProducts/cusEnts/cusEntUtils.js";
|
||||||
import { getBillingType } from "@/internal/products/prices/priceUtils.js";
|
import { getBillingType } from "@/internal/products/prices/priceUtils.js";
|
||||||
import RecaseError from "@/utils/errorUtils.js";
|
import RecaseError from "@/utils/errorUtils.js";
|
||||||
|
import { cusProductToSub } from "../internal/customers/cusProducts/cusProductUtils/convertCusProduct.js";
|
||||||
import { handleProratedDowngrade } from "./arrearProratedUsage/handleProratedDowngrade.js";
|
import { handleProratedDowngrade } from "./arrearProratedUsage/handleProratedDowngrade.js";
|
||||||
import { handleProratedUpgrade } from "./arrearProratedUsage/handleProratedUpgrade.js";
|
import { handleProratedUpgrade } from "./arrearProratedUsage/handleProratedUpgrade.js";
|
||||||
|
|
||||||
@@ -115,13 +115,18 @@ export const adjustAllowance = async ({
|
|||||||
logger.info(`Customer: ${customer.name}, Org: ${org.slug}`);
|
logger.info(`Customer: ${customer.name}, Org: ${org.slug}`);
|
||||||
|
|
||||||
const stripeCli = createStripeCli({ org, env });
|
const stripeCli = createStripeCli({ org, env });
|
||||||
const sub = await getUsageBasedSub({
|
const sub = await cusProductToSub({
|
||||||
db,
|
cusProduct,
|
||||||
stripeCli,
|
stripeCli,
|
||||||
subIds: cusProduct.subscription_ids!,
|
|
||||||
feature: affectedFeature,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// const sub = await getUsageBasedSub({
|
||||||
|
// db,
|
||||||
|
// stripeCli,
|
||||||
|
// subIds: cusProduct.subscription_ids!,
|
||||||
|
// feature: affectedFeature,
|
||||||
|
// });
|
||||||
|
|
||||||
if (!sub) {
|
if (!sub) {
|
||||||
logger.error("adjustAllowance: no usage-based sub found");
|
logger.error("adjustAllowance: no usage-based sub found");
|
||||||
return { newReplaceables: null, invoice: null, deletedReplaceables: null };
|
return { newReplaceables: null, invoice: null, deletedReplaceables: null };
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
getFeatureInvoiceDescription,
|
getFeatureInvoiceDescription,
|
||||||
type InsertReplaceable,
|
type InsertReplaceable,
|
||||||
OnDecrease,
|
OnDecrease,
|
||||||
|
OnIncrease,
|
||||||
type Organization,
|
type Organization,
|
||||||
type Product,
|
type Product,
|
||||||
type UsagePriceConfig,
|
type UsagePriceConfig,
|
||||||
@@ -21,12 +22,12 @@ import { priceToInvoiceAmount } from "@/internal/products/prices/priceUtils/pric
|
|||||||
import {
|
import {
|
||||||
shouldBillNow,
|
shouldBillNow,
|
||||||
shouldProrate,
|
shouldProrate,
|
||||||
|
shouldProrateDowngradeNow,
|
||||||
} from "@/internal/products/prices/priceUtils/prorationConfigUtils.js";
|
} from "@/internal/products/prices/priceUtils/prorationConfigUtils.js";
|
||||||
import { roundUsage } from "@/internal/products/prices/priceUtils/usagePriceUtils/classifyUsagePrice.js";
|
import { roundUsage } from "@/internal/products/prices/priceUtils/usagePriceUtils/classifyUsagePrice.js";
|
||||||
import { formatUnixToDate } from "@/utils/genUtils.js";
|
import { formatUnixToDate } from "@/utils/genUtils.js";
|
||||||
import { getStripeNow } from "@/utils/scriptUtils/testClockUtils.js";
|
import { getStripeNow } from "@/utils/scriptUtils/testClockUtils.js";
|
||||||
import { getUsageFromBalance } from "../adjustAllowance.js";
|
import { getUsageFromBalance } from "../adjustAllowance.js";
|
||||||
|
|
||||||
export const createDowngradeProrationInvoice = async ({
|
export const createDowngradeProrationInvoice = async ({
|
||||||
org,
|
org,
|
||||||
cusPrice,
|
cusPrice,
|
||||||
@@ -38,6 +39,7 @@ export const createDowngradeProrationInvoice = async ({
|
|||||||
newRoundedUsage,
|
newRoundedUsage,
|
||||||
feature,
|
feature,
|
||||||
product,
|
product,
|
||||||
|
onIncrease,
|
||||||
onDecrease,
|
onDecrease,
|
||||||
logger,
|
logger,
|
||||||
}: {
|
}: {
|
||||||
@@ -51,6 +53,7 @@ export const createDowngradeProrationInvoice = async ({
|
|||||||
newRoundedUsage: number;
|
newRoundedUsage: number;
|
||||||
feature: Feature;
|
feature: Feature;
|
||||||
product: Product;
|
product: Product;
|
||||||
|
onIncrease: OnIncrease;
|
||||||
onDecrease: OnDecrease;
|
onDecrease: OnDecrease;
|
||||||
logger: any;
|
logger: any;
|
||||||
}) => {
|
}) => {
|
||||||
@@ -102,7 +105,12 @@ export const createDowngradeProrationInvoice = async ({
|
|||||||
await stripeCli.invoiceItems.create(invoiceItem);
|
await stripeCli.invoiceItems.create(invoiceItem);
|
||||||
let invoice = null;
|
let invoice = null;
|
||||||
|
|
||||||
if (shouldBillNow(onDecrease)) {
|
if (
|
||||||
|
shouldProrateDowngradeNow({
|
||||||
|
onIncrease,
|
||||||
|
onDecrease,
|
||||||
|
})
|
||||||
|
) {
|
||||||
const { invoice: finalInvoice } = await createAndFinalizeInvoice({
|
const { invoice: finalInvoice } = await createAndFinalizeInvoice({
|
||||||
stripeCli,
|
stripeCli,
|
||||||
paymentMethod: null,
|
paymentMethod: null,
|
||||||
@@ -196,6 +204,9 @@ export const handleProratedDowngrade = async ({
|
|||||||
}),
|
}),
|
||||||
feature,
|
feature,
|
||||||
product,
|
product,
|
||||||
|
onIncrease:
|
||||||
|
cusPrice.price.proration_config?.on_increase ||
|
||||||
|
OnIncrease.ProrateImmediately,
|
||||||
onDecrease,
|
onDecrease,
|
||||||
logger,
|
logger,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
import "dotenv/config";
|
import "dotenv/config";
|
||||||
|
|
||||||
import { invitation } from "@autumn/shared";
|
import { invitation } from "@autumn/shared";
|
||||||
import { betterAuth } from "better-auth";
|
import { betterAuth, type User } from "better-auth";
|
||||||
import { drizzleAdapter } from "better-auth/adapters/drizzle";
|
import { drizzleAdapter } from "better-auth/adapters/drizzle";
|
||||||
import { admin, emailOTP, organization } from "better-auth/plugins";
|
import {
|
||||||
|
admin,
|
||||||
|
emailOTP,
|
||||||
|
type Organization,
|
||||||
|
organization,
|
||||||
|
} from "better-auth/plugins";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { db } from "@/db/initDrizzle.js";
|
import { db } from "@/db/initDrizzle.js";
|
||||||
import { logger } from "@/external/logtail/logtailUtils.js";
|
import { logger } from "@/external/logtail/logtailUtils.js";
|
||||||
@@ -104,7 +109,11 @@ export const auth = betterAuth({
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
organization({
|
organization({
|
||||||
async sendInvitationEmail(data) {
|
async sendInvitationEmail(data: {
|
||||||
|
id: string;
|
||||||
|
email: string;
|
||||||
|
organization: Organization;
|
||||||
|
}) {
|
||||||
const inviteLink = `${process.env.CLIENT_URL}/accept?id=${data.id}`;
|
const inviteLink = `${process.env.CLIENT_URL}/accept?id=${data.id}`;
|
||||||
await sendInvitationEmail({
|
await sendInvitationEmail({
|
||||||
email: data.email,
|
email: data.email,
|
||||||
@@ -135,7 +144,13 @@ export const auth = betterAuth({
|
|||||||
|
|
||||||
organizationCreation: {
|
organizationCreation: {
|
||||||
disabled: false,
|
disabled: false,
|
||||||
afterCreate: async ({ organization, user }) => {
|
afterCreate: async ({
|
||||||
|
organization,
|
||||||
|
user,
|
||||||
|
}: {
|
||||||
|
organization: Organization;
|
||||||
|
user: User;
|
||||||
|
}) => {
|
||||||
await afterOrgCreated({ org: organization, user });
|
await afterOrgCreated({ org: organization, user });
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { ApiVersion, type ProductV2 } from "@autumn/shared";
|
import { ApiVersion, type ProductV2 } from "@autumn/shared";
|
||||||
import { addPrefixToProducts } from "tests/attach/utils.js";
|
import { createProducts } from "@tests/utils/productUtils.js";
|
||||||
import { createProducts } from "tests/utils/productUtils.js";
|
import type { TestContext } from "@tests/utils/testInitUtils/createTestContext.js";
|
||||||
import type { TestContext } from "tests/utils/testInitUtils/createTestContext.js";
|
import { addPrefixToProducts } from "@tests/utils/testProductUtils/testProductUtils.js";
|
||||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||||
|
|
||||||
export const initProductsV0 = async ({
|
export const initProductsV0 = async ({
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import dotenv from "dotenv";
|
|||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
|
||||||
import { AppEnv } from "@autumn/shared";
|
import { AppEnv } from "@autumn/shared";
|
||||||
import { clearOrg, setupOrg } from "tests/utils/setup.js";
|
import { clearOrg, setupOrg } from "@tests/utils/setup.js";
|
||||||
import { initDrizzle } from "@/db/initDrizzle.js";
|
import { initDrizzle } from "@/db/initDrizzle.js";
|
||||||
import {
|
import {
|
||||||
advanceProducts,
|
advanceProducts,
|
||||||
@@ -33,16 +33,6 @@ describe("Initialize org for tests", () => {
|
|||||||
await setupOrg({
|
await setupOrg({
|
||||||
orgId: this.org.id,
|
orgId: this.org.id,
|
||||||
env: DEFAULT_ENV,
|
env: DEFAULT_ENV,
|
||||||
features: { ...features, ...creditSystems } as any,
|
|
||||||
products: {
|
|
||||||
...products,
|
|
||||||
...advanceProducts,
|
|
||||||
...attachProducts,
|
|
||||||
...oneTimeProducts,
|
|
||||||
...entityProducts,
|
|
||||||
} as any,
|
|
||||||
rewards: { ...rewards } as any,
|
|
||||||
rewardTriggers: { ...referralPrograms } as any,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("--------------------------------");
|
console.log("--------------------------------");
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { beforeAll, describe, expect, test } from "bun:test";
|
import { beforeAll, describe, expect, test } from "bun:test";
|
||||||
import {
|
import {
|
||||||
ApiVersion,
|
ApiVersion,
|
||||||
type CheckResponse,
|
|
||||||
type CheckResponseV0,
|
type CheckResponseV0,
|
||||||
|
type CheckResponseV1,
|
||||||
SuccessCode,
|
SuccessCode,
|
||||||
} from "@autumn/shared";
|
} from "@autumn/shared";
|
||||||
|
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||||
|
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import { TestFeature } from "tests/setup/v2Features.js";
|
|
||||||
import ctx from "tests/utils/testInitUtils/createTestContext.js";
|
|
||||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||||
import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js";
|
import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js";
|
||||||
import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3.js";
|
import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3.js";
|
||||||
@@ -44,7 +44,7 @@ describe(`${chalk.yellowBright("temp: temporary script for testing")}`, () => {
|
|||||||
const res = (await autumnV1.check({
|
const res = (await autumnV1.check({
|
||||||
customer_id: customerId,
|
customer_id: customerId,
|
||||||
feature_id: TestFeature.Messages,
|
feature_id: TestFeature.Messages,
|
||||||
})) as unknown as CheckResponse;
|
})) as unknown as CheckResponseV1;
|
||||||
|
|
||||||
expect(res).toStrictEqual({
|
expect(res).toStrictEqual({
|
||||||
allowed: false,
|
allowed: false,
|
||||||
|
|||||||
118
server/tests/_temp/temp1.test.ts
Normal file
118
server/tests/_temp/temp1.test.ts
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
import { beforeAll, describe, test } from "bun:test";
|
||||||
|
import {
|
||||||
|
type AppEnv,
|
||||||
|
LegacyVersion,
|
||||||
|
OnDecrease,
|
||||||
|
OnIncrease,
|
||||||
|
type Organization,
|
||||||
|
} from "@autumn/shared";
|
||||||
|
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||||
|
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||||
|
import chalk from "chalk";
|
||||||
|
import type { Stripe } from "stripe";
|
||||||
|
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||||
|
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||||
|
import {
|
||||||
|
constructArrearProratedItem,
|
||||||
|
constructFeatureItem,
|
||||||
|
} from "@/utils/scriptUtils/constructItem.js";
|
||||||
|
import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js";
|
||||||
|
import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3.js";
|
||||||
|
import { initProductsV0 } from "@/utils/scriptUtils/testUtils/initProductsV0.js";
|
||||||
|
|
||||||
|
// UNCOMMENT FROM HERE
|
||||||
|
const addOn = constructProduct({
|
||||||
|
type: "pro",
|
||||||
|
isAddOn: true,
|
||||||
|
items: [
|
||||||
|
// constructFeatureItem({ featureId: TestFeature.Words, includedUsage: 100 }),
|
||||||
|
constructArrearProratedItem({
|
||||||
|
featureId: TestFeature.Users,
|
||||||
|
includedUsage: 1,
|
||||||
|
pricePerUnit: 10,
|
||||||
|
config: {
|
||||||
|
on_increase: OnIncrease.BillImmediately,
|
||||||
|
on_decrease: OnDecrease.Prorate,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
describe(`${chalk.yellowBright("temp1: Testing add ons")}`, () => {
|
||||||
|
const customerId = "temp1";
|
||||||
|
const autumn: AutumnInt = new AutumnInt({ version: LegacyVersion.v1_4 });
|
||||||
|
|
||||||
|
let stripeCli: Stripe;
|
||||||
|
let testClockId: string;
|
||||||
|
let curUnix: number;
|
||||||
|
let db: DrizzleCli;
|
||||||
|
let org: Organization;
|
||||||
|
let env: AppEnv;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
db = ctx.db;
|
||||||
|
org = ctx.org;
|
||||||
|
env = ctx.env;
|
||||||
|
stripeCli = ctx.stripeCli;
|
||||||
|
|
||||||
|
const { testClockId: testClockId1 } = await initCustomerV3({
|
||||||
|
ctx,
|
||||||
|
customerId,
|
||||||
|
customerData: {},
|
||||||
|
attachPm: "success",
|
||||||
|
withTestClock: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
await initProductsV0({
|
||||||
|
ctx,
|
||||||
|
products: [addOn],
|
||||||
|
prefix: customerId,
|
||||||
|
});
|
||||||
|
|
||||||
|
testClockId = testClockId1!;
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should attach pro product", async () => {
|
||||||
|
await autumn.attach({
|
||||||
|
customer_id: customerId,
|
||||||
|
product_id: addOn.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
await autumn.entities.create(customerId, [
|
||||||
|
{
|
||||||
|
id: "1",
|
||||||
|
name: "Entity 1",
|
||||||
|
feature_id: TestFeature.Users,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "2",
|
||||||
|
name: "Entity 2",
|
||||||
|
feature_id: TestFeature.Users,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
await autumn.entities.delete(customerId, "1");
|
||||||
|
// await autumn.attach({
|
||||||
|
// customer_id: customerId,
|
||||||
|
// product_id: addOn.id,
|
||||||
|
// });
|
||||||
|
// await autumn.attach({
|
||||||
|
// customer_id: customerId,
|
||||||
|
// product_id: addOn.id,
|
||||||
|
// });
|
||||||
|
});
|
||||||
|
|
||||||
|
// test("should cancel one add on", async () => {
|
||||||
|
// await autumn.cancel({
|
||||||
|
// customer_id: customerId,
|
||||||
|
// product_id: addOn.id,
|
||||||
|
// });
|
||||||
|
|
||||||
|
// await expectSubToBeCorrect({
|
||||||
|
// customerId,
|
||||||
|
// db,
|
||||||
|
// org,
|
||||||
|
// env,
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
});
|
||||||
@@ -1,234 +0,0 @@
|
|||||||
import {
|
|
||||||
type AppEnv,
|
|
||||||
type Customer,
|
|
||||||
LegacyVersion,
|
|
||||||
type Organization,
|
|
||||||
} from "@autumn/shared";
|
|
||||||
import { expect } from "chai";
|
|
||||||
import chalk from "chalk";
|
|
||||||
import { addHours, addMonths } from "date-fns";
|
|
||||||
import type Stripe from "stripe";
|
|
||||||
import { setupBefore } from "tests/before.js";
|
|
||||||
import { rewards } from "tests/global.js";
|
|
||||||
import { TestFeature } from "tests/setup/v2Features.js";
|
|
||||||
import { hoursToFinalizeInvoice } from "tests/utils/constants.js";
|
|
||||||
import { getExpectedInvoiceTotal } from "tests/utils/expectUtils/expectInvoiceUtils.js";
|
|
||||||
import { expectProductAttached } from "tests/utils/expectUtils/expectProductAttached.js";
|
|
||||||
import { timeout } from "tests/utils/genUtils.js";
|
|
||||||
import { createProducts } from "tests/utils/productUtils.js";
|
|
||||||
import {
|
|
||||||
advanceTestClock,
|
|
||||||
completeCheckoutForm,
|
|
||||||
getDiscount,
|
|
||||||
} from "tests/utils/stripeUtils.js";
|
|
||||||
import {
|
|
||||||
addPrefixToProducts,
|
|
||||||
getBasePrice,
|
|
||||||
} from "tests/utils/testProductUtils/testProductUtils.js";
|
|
||||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
|
||||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
|
||||||
import { getOriginalCouponId } from "@/internal/rewards/rewardUtils.js";
|
|
||||||
import { constructArrearItem } from "@/utils/scriptUtils/constructItem.js";
|
|
||||||
import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js";
|
|
||||||
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
|
|
||||||
|
|
||||||
const testCase = "coupon1";
|
|
||||||
|
|
||||||
const pro = constructProduct({
|
|
||||||
type: "pro",
|
|
||||||
items: [constructArrearItem({ featureId: TestFeature.Words })],
|
|
||||||
});
|
|
||||||
|
|
||||||
const simulateOneCycle = async ({
|
|
||||||
customerId,
|
|
||||||
db,
|
|
||||||
org,
|
|
||||||
env,
|
|
||||||
stripeCli,
|
|
||||||
autumn,
|
|
||||||
testClockId,
|
|
||||||
couponAmount,
|
|
||||||
curUnix,
|
|
||||||
}: {
|
|
||||||
customerId: string;
|
|
||||||
db: DrizzleCli;
|
|
||||||
org: Organization;
|
|
||||||
env: AppEnv;
|
|
||||||
stripeCli: Stripe;
|
|
||||||
autumn: AutumnInt;
|
|
||||||
testClockId: string;
|
|
||||||
couponAmount: number;
|
|
||||||
curUnix: number;
|
|
||||||
}) => {
|
|
||||||
const usage = Math.random() * 100000 + 10000;
|
|
||||||
await autumn.track({
|
|
||||||
customer_id: customerId,
|
|
||||||
feature_id: TestFeature.Words,
|
|
||||||
value: usage,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Expected invoice total
|
|
||||||
const expectedTotal = await getExpectedInvoiceTotal({
|
|
||||||
usage: [{ featureId: TestFeature.Words, value: usage }],
|
|
||||||
customerId,
|
|
||||||
productId: pro.id,
|
|
||||||
db,
|
|
||||||
org,
|
|
||||||
env,
|
|
||||||
stripeCli,
|
|
||||||
});
|
|
||||||
|
|
||||||
couponAmount -= expectedTotal;
|
|
||||||
|
|
||||||
curUnix = await advanceTestClock({
|
|
||||||
stripeCli,
|
|
||||||
testClockId,
|
|
||||||
advanceTo: addHours(
|
|
||||||
addMonths(curUnix, 1),
|
|
||||||
hoursToFinalizeInvoice,
|
|
||||||
).getTime(),
|
|
||||||
waitForSeconds: 30,
|
|
||||||
});
|
|
||||||
|
|
||||||
const customer = await autumn.customers.get(customerId);
|
|
||||||
expect(customer.invoices![0].total).to.equal(0);
|
|
||||||
|
|
||||||
const cusDiscount = await getDiscount({
|
|
||||||
stripeCli: stripeCli,
|
|
||||||
stripeId: customer.stripe_id!,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(cusDiscount).to.exist;
|
|
||||||
|
|
||||||
expect(getOriginalCouponId(cusDiscount.coupon?.id)).to.equal(
|
|
||||||
rewards.rolloverAll.id,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(cusDiscount.coupon?.amount_off).to.equal(
|
|
||||||
Math.round(couponAmount * 100),
|
|
||||||
`Expected stripe cus to have coupon amount ${couponAmount * 100}`,
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
couponAmount,
|
|
||||||
curUnix,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
describe(
|
|
||||||
chalk.yellow(
|
|
||||||
`${testCase} - Testing invoice credits reward, apply to all product`,
|
|
||||||
),
|
|
||||||
() => {
|
|
||||||
const customerId = "coupon1";
|
|
||||||
let stripeCli: Stripe;
|
|
||||||
let customer: Customer;
|
|
||||||
let testClockId: string;
|
|
||||||
let db: DrizzleCli;
|
|
||||||
let org: Organization;
|
|
||||||
let env: AppEnv;
|
|
||||||
|
|
||||||
const autumn: AutumnInt = new AutumnInt({ version: LegacyVersion.v1_4 });
|
|
||||||
|
|
||||||
let couponAmount = rewards.rolloverAll.discount_config.discount_value;
|
|
||||||
let curUnix = new Date().getTime();
|
|
||||||
|
|
||||||
before(async function () {
|
|
||||||
await setupBefore(this);
|
|
||||||
db = this.db;
|
|
||||||
org = this.org;
|
|
||||||
env = this.env;
|
|
||||||
stripeCli = this.stripeCli;
|
|
||||||
|
|
||||||
const res = await initCustomer({
|
|
||||||
customerId,
|
|
||||||
org,
|
|
||||||
env,
|
|
||||||
db,
|
|
||||||
autumn: this.autumnJs,
|
|
||||||
});
|
|
||||||
|
|
||||||
addPrefixToProducts({
|
|
||||||
products: [pro],
|
|
||||||
prefix: testCase,
|
|
||||||
});
|
|
||||||
|
|
||||||
await createProducts({
|
|
||||||
products: [pro],
|
|
||||||
orgId: org.id,
|
|
||||||
env,
|
|
||||||
db,
|
|
||||||
autumn,
|
|
||||||
});
|
|
||||||
|
|
||||||
testClockId = res.testClockId;
|
|
||||||
customer = res.customer;
|
|
||||||
});
|
|
||||||
|
|
||||||
// CYCLE 0
|
|
||||||
it("should attach pro", async () => {
|
|
||||||
const res = await autumn.attach({
|
|
||||||
customer_id: customerId,
|
|
||||||
product_id: pro.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
await completeCheckoutForm(
|
|
||||||
res.checkout_url,
|
|
||||||
undefined,
|
|
||||||
rewards.rolloverAll.id,
|
|
||||||
);
|
|
||||||
|
|
||||||
await timeout(10000);
|
|
||||||
|
|
||||||
couponAmount -= getBasePrice({ product: pro });
|
|
||||||
|
|
||||||
const customer = await autumn.customers.get(customerId);
|
|
||||||
expectProductAttached({ customer, product: pro });
|
|
||||||
|
|
||||||
expect(customer.invoices![0].total).to.equal(0);
|
|
||||||
|
|
||||||
const cusDiscount = await getDiscount({
|
|
||||||
stripeCli,
|
|
||||||
stripeId: customer.stripe_id!,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(cusDiscount).to.exist;
|
|
||||||
expect(getOriginalCouponId(cusDiscount.coupon?.id)).to.equal(
|
|
||||||
rewards.rolloverAll.id,
|
|
||||||
);
|
|
||||||
expect(cusDiscount.coupon?.amount_off).to.equal(couponAmount * 100);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should run one cycle and have correct invoice + coupon amount", async () => {
|
|
||||||
const res = await simulateOneCycle({
|
|
||||||
customerId,
|
|
||||||
db,
|
|
||||||
org,
|
|
||||||
env,
|
|
||||||
stripeCli,
|
|
||||||
autumn,
|
|
||||||
testClockId,
|
|
||||||
couponAmount,
|
|
||||||
curUnix: new Date().getTime(),
|
|
||||||
});
|
|
||||||
|
|
||||||
couponAmount = res.couponAmount;
|
|
||||||
curUnix = res.curUnix;
|
|
||||||
});
|
|
||||||
|
|
||||||
// CYCLE 1
|
|
||||||
it("should run another cycle and have correct invoice + coupon amount", async () => {
|
|
||||||
const res = await simulateOneCycle({
|
|
||||||
customerId,
|
|
||||||
db,
|
|
||||||
org,
|
|
||||||
env,
|
|
||||||
stripeCli,
|
|
||||||
autumn,
|
|
||||||
testClockId,
|
|
||||||
couponAmount,
|
|
||||||
curUnix,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
@@ -7,22 +7,22 @@ import {
|
|||||||
type Organization,
|
type Organization,
|
||||||
RewardType,
|
RewardType,
|
||||||
} from "@autumn/shared";
|
} from "@autumn/shared";
|
||||||
import chalk from "chalk";
|
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||||
import { addHours, addMonths } from "date-fns";
|
import { hoursToFinalizeInvoice } from "@tests/utils/constants.js";
|
||||||
import type Stripe from "stripe";
|
import { getExpectedInvoiceTotal } from "@tests/utils/expectUtils/expectInvoiceUtils.js";
|
||||||
import { TestFeature } from "tests/setup/v2Features.js";
|
import { expectProductAttached } from "@tests/utils/expectUtils/expectProductAttached.js";
|
||||||
import { hoursToFinalizeInvoice } from "tests/utils/constants.js";
|
import { timeout } from "@tests/utils/genUtils.js";
|
||||||
import { getExpectedInvoiceTotal } from "tests/utils/expectUtils/expectInvoiceUtils.js";
|
import { createReward } from "@tests/utils/productUtils.js";
|
||||||
import { expectProductAttached } from "tests/utils/expectUtils/expectProductAttached.js";
|
|
||||||
import { timeout } from "tests/utils/genUtils.js";
|
|
||||||
import { createReward } from "tests/utils/productUtils.js";
|
|
||||||
import {
|
import {
|
||||||
advanceTestClock,
|
advanceTestClock,
|
||||||
completeCheckoutForm,
|
completeCheckoutForm,
|
||||||
getDiscount,
|
getDiscount,
|
||||||
} from "tests/utils/stripeUtils.js";
|
} from "@tests/utils/stripeUtils.js";
|
||||||
import ctx from "tests/utils/testInitUtils/createTestContext.js";
|
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||||
import { getBasePrice } from "tests/utils/testProductUtils/testProductUtils.js";
|
import { getBasePrice } from "@tests/utils/testProductUtils/testProductUtils.js";
|
||||||
|
import chalk from "chalk";
|
||||||
|
import { addHours, addMonths } from "date-fns";
|
||||||
|
import type Stripe from "stripe";
|
||||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||||
import { getOriginalCouponId } from "@/internal/rewards/rewardUtils.js";
|
import { getOriginalCouponId } from "@/internal/rewards/rewardUtils.js";
|
||||||
|
|||||||
@@ -1,197 +0,0 @@
|
|||||||
import {
|
|
||||||
type AppEnv,
|
|
||||||
CouponDurationType,
|
|
||||||
type CreateReward,
|
|
||||||
LegacyVersion,
|
|
||||||
type Organization,
|
|
||||||
RewardType,
|
|
||||||
} from "@autumn/shared";
|
|
||||||
import { expect } from "chai";
|
|
||||||
import chalk from "chalk";
|
|
||||||
import { addHours, addMonths } from "date-fns";
|
|
||||||
import { Decimal } from "decimal.js";
|
|
||||||
import type Stripe from "stripe";
|
|
||||||
import { setupBefore } from "tests/before.js";
|
|
||||||
import { TestFeature } from "tests/setup/v2Features.js";
|
|
||||||
import { hoursToFinalizeInvoice } from "tests/utils/constants.js";
|
|
||||||
import { getExpectedInvoiceTotal } from "tests/utils/expectUtils/expectInvoiceUtils.js";
|
|
||||||
import { expectProductAttached } from "tests/utils/expectUtils/expectProductAttached.js";
|
|
||||||
import { timeout } from "tests/utils/genUtils.js";
|
|
||||||
import { createProducts, createReward } from "tests/utils/productUtils.js";
|
|
||||||
import { completeCheckoutForm, getDiscount } from "tests/utils/stripeUtils.js";
|
|
||||||
import {
|
|
||||||
addPrefixToProducts,
|
|
||||||
getBasePrice,
|
|
||||||
} from "tests/utils/testProductUtils/testProductUtils.js";
|
|
||||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
|
||||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
|
||||||
import { getOriginalCouponId } from "@/internal/rewards/rewardUtils.js";
|
|
||||||
import { constructArrearItem } from "@/utils/scriptUtils/constructItem.js";
|
|
||||||
import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js";
|
|
||||||
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
|
|
||||||
import { advanceTestClock } from "@/utils/scriptUtils/testClockUtils.js";
|
|
||||||
|
|
||||||
const pro = constructProduct({
|
|
||||||
type: "pro",
|
|
||||||
items: [constructArrearItem({ featureId: TestFeature.Words })],
|
|
||||||
});
|
|
||||||
|
|
||||||
const testCase = "coupon2";
|
|
||||||
|
|
||||||
// Create reward input
|
|
||||||
const reward: CreateReward = {
|
|
||||||
id: "usage",
|
|
||||||
name: "usage",
|
|
||||||
promo_codes: [{ code: "usage" }],
|
|
||||||
type: RewardType.InvoiceCredits,
|
|
||||||
discount_config: {
|
|
||||||
discount_value: 10000,
|
|
||||||
duration_type: CouponDurationType.Forever,
|
|
||||||
duration_value: 1,
|
|
||||||
should_rollover: true,
|
|
||||||
apply_to_all: false,
|
|
||||||
price_ids: [],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
describe(
|
|
||||||
chalk.yellow(`${testCase} - Testing one-off rollover, apply to usage only`),
|
|
||||||
() => {
|
|
||||||
const customerId = testCase;
|
|
||||||
let stripeCli: Stripe;
|
|
||||||
let testClockId: string;
|
|
||||||
|
|
||||||
const autumn: AutumnInt = new AutumnInt({ version: LegacyVersion.v1_4 });
|
|
||||||
let org: Organization;
|
|
||||||
let env: AppEnv;
|
|
||||||
let db: DrizzleCli;
|
|
||||||
|
|
||||||
let couponAmount = reward.discount_config?.discount_value ?? 0;
|
|
||||||
|
|
||||||
before(async function () {
|
|
||||||
await setupBefore(this);
|
|
||||||
|
|
||||||
org = this.org;
|
|
||||||
env = this.env;
|
|
||||||
db = this.db;
|
|
||||||
stripeCli = this.stripeCli;
|
|
||||||
|
|
||||||
const { testClockId: testClockId1 } = await initCustomer({
|
|
||||||
customerId,
|
|
||||||
org: this.org,
|
|
||||||
env: this.env,
|
|
||||||
db: this.db,
|
|
||||||
autumn: this.autumnJs,
|
|
||||||
});
|
|
||||||
|
|
||||||
testClockId = testClockId1;
|
|
||||||
|
|
||||||
addPrefixToProducts({
|
|
||||||
products: [pro],
|
|
||||||
prefix: testCase,
|
|
||||||
});
|
|
||||||
|
|
||||||
await createProducts({
|
|
||||||
orgId: this.org.id,
|
|
||||||
env: this.env,
|
|
||||||
db: this.db,
|
|
||||||
autumn,
|
|
||||||
products: [pro],
|
|
||||||
});
|
|
||||||
|
|
||||||
await createReward({
|
|
||||||
orgId: org.id,
|
|
||||||
env,
|
|
||||||
db,
|
|
||||||
autumn,
|
|
||||||
reward,
|
|
||||||
productId: pro.id,
|
|
||||||
onlyUsage: true,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// CYCLE 0
|
|
||||||
it("should attach pro with promo code", async () => {
|
|
||||||
const res = await autumn.attach({
|
|
||||||
customer_id: customerId,
|
|
||||||
product_id: pro.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
await completeCheckoutForm(res.checkout_url, undefined, reward.id);
|
|
||||||
|
|
||||||
await timeout(10000);
|
|
||||||
|
|
||||||
const customer = await autumn.customers.get(customerId);
|
|
||||||
expectProductAttached({
|
|
||||||
customer,
|
|
||||||
product: pro,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should have fixed price invoice and correct remaining coupon amount", async () => {
|
|
||||||
const customer = await autumn.customers.get(customerId);
|
|
||||||
const fixedPrice = getBasePrice({ product: pro });
|
|
||||||
expect(customer.invoices![0].total).to.equal(fixedPrice);
|
|
||||||
|
|
||||||
const cusDiscount = await getDiscount({
|
|
||||||
stripeCli,
|
|
||||||
stripeId: customer.stripe_id!,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(getOriginalCouponId(cusDiscount.coupon?.id)).to.equal(reward.id);
|
|
||||||
expect(cusDiscount.coupon?.amount_off).to.equal(couponAmount * 100);
|
|
||||||
});
|
|
||||||
|
|
||||||
// CYCLE 1
|
|
||||||
it("should track usage and have correct invoice amount", async () => {
|
|
||||||
const usage = new Decimal(Math.random() * 1250120 + 10000)
|
|
||||||
.toDecimalPlaces(2)
|
|
||||||
.toNumber();
|
|
||||||
|
|
||||||
await autumn.track({
|
|
||||||
customer_id: customerId,
|
|
||||||
feature_id: TestFeature.Words,
|
|
||||||
value: usage,
|
|
||||||
});
|
|
||||||
|
|
||||||
const usageTotal = await getExpectedInvoiceTotal({
|
|
||||||
org,
|
|
||||||
env,
|
|
||||||
db,
|
|
||||||
customerId,
|
|
||||||
productId: pro.id,
|
|
||||||
usage: [{ featureId: TestFeature.Words, value: usage }],
|
|
||||||
stripeCli,
|
|
||||||
onlyIncludeUsage: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
const basePrice = getBasePrice({ product: pro });
|
|
||||||
|
|
||||||
couponAmount = couponAmount - usageTotal;
|
|
||||||
|
|
||||||
await advanceTestClock({
|
|
||||||
stripeCli,
|
|
||||||
testClockId,
|
|
||||||
advanceTo: addHours(
|
|
||||||
addMonths(new Date(), 1),
|
|
||||||
hoursToFinalizeInvoice,
|
|
||||||
).getTime(),
|
|
||||||
waitForSeconds: 20,
|
|
||||||
});
|
|
||||||
|
|
||||||
const customer = await autumn.customers.get(customerId);
|
|
||||||
expect(customer.invoices![0].total).to.equal(basePrice);
|
|
||||||
|
|
||||||
const cusDiscount = await getDiscount({
|
|
||||||
stripeCli,
|
|
||||||
stripeId: customer.stripe_id!,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(getOriginalCouponId(cusDiscount.coupon?.id)).to.equal(reward.id);
|
|
||||||
|
|
||||||
expect(cusDiscount.coupon?.amount_off).to.equal(
|
|
||||||
Math.round(couponAmount * 100),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
@@ -11,18 +11,18 @@ import chalk from "chalk";
|
|||||||
import { addHours, addMonths } from "date-fns";
|
import { addHours, addMonths } from "date-fns";
|
||||||
import { Decimal } from "decimal.js";
|
import { Decimal } from "decimal.js";
|
||||||
import type Stripe from "stripe";
|
import type Stripe from "stripe";
|
||||||
import { TestFeature } from "tests/setup/v2Features.js";
|
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||||
import { hoursToFinalizeInvoice } from "tests/utils/constants.js";
|
import { hoursToFinalizeInvoice } from "@tests/utils/constants.js";
|
||||||
import { getExpectedInvoiceTotal } from "tests/utils/expectUtils/expectInvoiceUtils.js";
|
import { getExpectedInvoiceTotal } from "@tests/utils/expectUtils/expectInvoiceUtils.js";
|
||||||
import { expectProductAttached } from "tests/utils/expectUtils/expectProductAttached.js";
|
import { expectProductAttached } from "@tests/utils/expectUtils/expectProductAttached.js";
|
||||||
import { timeout } from "tests/utils/genUtils.js";
|
import { timeout } from "@tests/utils/genUtils.js";
|
||||||
import { createProducts, createReward } from "tests/utils/productUtils.js";
|
import { createProducts, createReward } from "@tests/utils/productUtils.js";
|
||||||
import { completeCheckoutForm, getDiscount } from "tests/utils/stripeUtils.js";
|
import { completeCheckoutForm, getDiscount } from "@tests/utils/stripeUtils.js";
|
||||||
import ctx from "tests/utils/testInitUtils/createTestContext.js";
|
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||||
import {
|
import {
|
||||||
addPrefixToProducts,
|
addPrefixToProducts,
|
||||||
getBasePrice,
|
getBasePrice,
|
||||||
} from "tests/utils/testProductUtils/testProductUtils.js";
|
} from "@tests/utils/testProductUtils/testProductUtils.js";
|
||||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||||
import { getOriginalCouponId } from "@/internal/rewards/rewardUtils.js";
|
import { getOriginalCouponId } from "@/internal/rewards/rewardUtils.js";
|
||||||
|
|||||||
@@ -1,176 +0,0 @@
|
|||||||
import {
|
|
||||||
type AppEnv,
|
|
||||||
CouponDurationType,
|
|
||||||
type CreateReward,
|
|
||||||
LegacyVersion,
|
|
||||||
type Organization,
|
|
||||||
RewardType,
|
|
||||||
} from "@autumn/shared";
|
|
||||||
import { expect } from "chai";
|
|
||||||
import chalk from "chalk";
|
|
||||||
import type Stripe from "stripe";
|
|
||||||
import { setupBefore } from "tests/before.js";
|
|
||||||
import { TestFeature } from "tests/setup/v2Features.js";
|
|
||||||
import { expectAttachCorrect } from "tests/utils/expectUtils/expectAttach.js";
|
|
||||||
import { createProducts, createReward } from "tests/utils/productUtils.js";
|
|
||||||
import {
|
|
||||||
addPrefixToProducts,
|
|
||||||
getBasePrice,
|
|
||||||
} from "tests/utils/testProductUtils/testProductUtils.js";
|
|
||||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
|
||||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
|
||||||
import {
|
|
||||||
constructArrearItem,
|
|
||||||
constructFeatureItem,
|
|
||||||
} from "@/utils/scriptUtils/constructItem.js";
|
|
||||||
import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js";
|
|
||||||
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
|
|
||||||
|
|
||||||
const pro = constructProduct({
|
|
||||||
type: "pro",
|
|
||||||
items: [constructArrearItem({ featureId: TestFeature.Words })],
|
|
||||||
});
|
|
||||||
|
|
||||||
const oneOff = constructProduct({
|
|
||||||
type: "one_off",
|
|
||||||
items: [
|
|
||||||
constructFeatureItem({
|
|
||||||
featureId: TestFeature.Words,
|
|
||||||
includedUsage: 500,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create reward input
|
|
||||||
const rewardId = "attach_coupon";
|
|
||||||
const promoCode = "attach_coupon_code";
|
|
||||||
const reward: CreateReward = {
|
|
||||||
id: rewardId,
|
|
||||||
name: "attach_coupon",
|
|
||||||
promo_codes: [{ code: promoCode }],
|
|
||||||
type: RewardType.FixedDiscount,
|
|
||||||
discount_config: {
|
|
||||||
discount_value: 5,
|
|
||||||
duration_type: CouponDurationType.OneOff,
|
|
||||||
duration_value: 1,
|
|
||||||
should_rollover: true,
|
|
||||||
apply_to_all: true,
|
|
||||||
price_ids: [],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const testCase = "coupon3";
|
|
||||||
describe(chalk.yellow(`${testCase} - Testing attach coupon`), () => {
|
|
||||||
const customerId = testCase;
|
|
||||||
let stripeCli: Stripe;
|
|
||||||
let testClockId: string;
|
|
||||||
|
|
||||||
const autumn: AutumnInt = new AutumnInt({ version: LegacyVersion.v1_4 });
|
|
||||||
let org: Organization;
|
|
||||||
let env: AppEnv;
|
|
||||||
let db: DrizzleCli;
|
|
||||||
|
|
||||||
const couponAmount = reward.discount_config!.discount_value;
|
|
||||||
|
|
||||||
before(async function () {
|
|
||||||
await setupBefore(this);
|
|
||||||
|
|
||||||
org = this.org;
|
|
||||||
env = this.env;
|
|
||||||
db = this.db;
|
|
||||||
stripeCli = this.stripeCli;
|
|
||||||
|
|
||||||
const { testClockId: testClockId1 } = await initCustomer({
|
|
||||||
customerId,
|
|
||||||
org: this.org,
|
|
||||||
env: this.env,
|
|
||||||
db: this.db,
|
|
||||||
autumn: this.autumnJs,
|
|
||||||
attachPm: "success",
|
|
||||||
});
|
|
||||||
|
|
||||||
testClockId = testClockId1;
|
|
||||||
|
|
||||||
addPrefixToProducts({
|
|
||||||
products: [pro, oneOff],
|
|
||||||
prefix: testCase,
|
|
||||||
});
|
|
||||||
|
|
||||||
await createProducts({
|
|
||||||
orgId: this.org.id,
|
|
||||||
env: this.env,
|
|
||||||
db: this.db,
|
|
||||||
autumn,
|
|
||||||
products: [pro, oneOff],
|
|
||||||
});
|
|
||||||
|
|
||||||
await createReward({
|
|
||||||
orgId: org.id,
|
|
||||||
env,
|
|
||||||
db,
|
|
||||||
autumn,
|
|
||||||
reward,
|
|
||||||
productId: pro.id,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// CYCLE 0
|
|
||||||
it("should attach pro with reward ID", async () => {
|
|
||||||
const res = await autumn.attach({
|
|
||||||
customer_id: customerId,
|
|
||||||
product_id: pro.id,
|
|
||||||
reward: rewardId,
|
|
||||||
});
|
|
||||||
|
|
||||||
const customer = await autumn.customers.get(customerId);
|
|
||||||
expectAttachCorrect({
|
|
||||||
customer,
|
|
||||||
product: pro,
|
|
||||||
});
|
|
||||||
|
|
||||||
const invoice = customer.invoices![0];
|
|
||||||
const basePrice = getBasePrice({ product: pro });
|
|
||||||
expect(invoice.total).to.equal(basePrice - couponAmount);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should attach one off with reward ID", async () => {
|
|
||||||
await autumn.attach({
|
|
||||||
customer_id: customerId,
|
|
||||||
product_id: oneOff.id,
|
|
||||||
reward: rewardId,
|
|
||||||
});
|
|
||||||
|
|
||||||
const customer = await autumn.customers.get(customerId);
|
|
||||||
expectAttachCorrect({
|
|
||||||
customer,
|
|
||||||
product: oneOff,
|
|
||||||
});
|
|
||||||
|
|
||||||
const invoice = customer.invoices![0];
|
|
||||||
const basePrice = getBasePrice({ product: oneOff });
|
|
||||||
expect(invoice.total).to.equal(basePrice - couponAmount);
|
|
||||||
expect(invoice.product_ids).to.include(oneOff.id);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should attach one off with promo code", async () => {
|
|
||||||
await autumn.attach({
|
|
||||||
customer_id: customerId,
|
|
||||||
product_id: oneOff.id,
|
|
||||||
reward: promoCode,
|
|
||||||
});
|
|
||||||
|
|
||||||
const customer = await autumn.customers.get(customerId);
|
|
||||||
expectAttachCorrect({
|
|
||||||
customer,
|
|
||||||
product: oneOff,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(customer.invoices!.length).to.equal(3);
|
|
||||||
const basePrice = getBasePrice({ product: oneOff });
|
|
||||||
for (let i = 0; i < 2; i++) {
|
|
||||||
const invoice = customer.invoices![i];
|
|
||||||
expect(invoice.total).to.equal(basePrice - couponAmount);
|
|
||||||
expect(invoice.product_ids).to.include(oneOff.id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -9,14 +9,14 @@ import {
|
|||||||
} from "@autumn/shared";
|
} from "@autumn/shared";
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import type Stripe from "stripe";
|
import type Stripe from "stripe";
|
||||||
import { TestFeature } from "tests/setup/v2Features.js";
|
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||||
import { expectAttachCorrect } from "tests/utils/expectUtils/expectAttach.js";
|
import { expectAttachCorrect } from "@tests/utils/expectUtils/expectAttach.js";
|
||||||
import { createProducts, createReward } from "tests/utils/productUtils.js";
|
import { createProducts, createReward } from "@tests/utils/productUtils.js";
|
||||||
import ctx from "tests/utils/testInitUtils/createTestContext.js";
|
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||||
import {
|
import {
|
||||||
addPrefixToProducts,
|
addPrefixToProducts,
|
||||||
getBasePrice,
|
getBasePrice,
|
||||||
} from "tests/utils/testProductUtils/testProductUtils.js";
|
} from "@tests/utils/testProductUtils/testProductUtils.js";
|
||||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -3,14 +3,14 @@ import { expect } from "chai";
|
|||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import { addHours, addMonths } from "date-fns";
|
import { addHours, addMonths } from "date-fns";
|
||||||
import type Stripe from "stripe";
|
import type Stripe from "stripe";
|
||||||
import { addPrefixToProducts } from "tests/attach/utils.js";
|
import { addPrefixToProducts } from "@tests/attach/utils.js";
|
||||||
import { setupBefore } from "tests/before.js";
|
import { setupBefore } from "@tests/before.js";
|
||||||
import { TestFeature } from "tests/setup/v2Features.js";
|
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||||
import { hoursToFinalizeInvoice } from "tests/utils/constants.js";
|
import { hoursToFinalizeInvoice } from "@tests/utils/constants.js";
|
||||||
import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js";
|
import { attachAndExpectCorrect } from "@tests/utils/expectUtils/expectAttach.js";
|
||||||
import { createProducts } from "tests/utils/productUtils.js";
|
import { createProducts } from "@tests/utils/productUtils.js";
|
||||||
import { advanceTestClock } from "tests/utils/stripeUtils.js";
|
import { advanceTestClock } from "@tests/utils/stripeUtils.js";
|
||||||
import { getBasePrice } from "tests/utils/testProductUtils/testProductUtils.js";
|
import { getBasePrice } from "@tests/utils/testProductUtils/testProductUtils.js";
|
||||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||||
import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
|
import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ import { LegacyVersion } from "@autumn/shared";
|
|||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import { addHours, addMonths } from "date-fns";
|
import { addHours, addMonths } from "date-fns";
|
||||||
import type Stripe from "stripe";
|
import type Stripe from "stripe";
|
||||||
import { TestFeature } from "tests/setup/v2Features.js";
|
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||||
import { hoursToFinalizeInvoice } from "tests/utils/constants.js";
|
import { hoursToFinalizeInvoice } from "@tests/utils/constants.js";
|
||||||
import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js";
|
import { attachAndExpectCorrect } from "@tests/utils/expectUtils/expectAttach.js";
|
||||||
import ctx from "tests/utils/testInitUtils/createTestContext.js";
|
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||||
import { getBasePrice } from "tests/utils/testProductUtils/testProductUtils.js";
|
import { getBasePrice } from "@tests/utils/testProductUtils/testProductUtils.js";
|
||||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||||
import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
|
import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
|
||||||
import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js";
|
import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js";
|
||||||
|
|||||||
@@ -3,14 +3,14 @@ import { expect } from "chai";
|
|||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import { addHours, addMonths } from "date-fns";
|
import { addHours, addMonths } from "date-fns";
|
||||||
import type Stripe from "stripe";
|
import type Stripe from "stripe";
|
||||||
import { addPrefixToProducts } from "tests/attach/utils.js";
|
import { addPrefixToProducts } from "@tests/attach/utils.js";
|
||||||
import { setupBefore } from "tests/before.js";
|
import { setupBefore } from "@tests/before.js";
|
||||||
import { TestFeature } from "tests/setup/v2Features.js";
|
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||||
import { hoursToFinalizeInvoice } from "tests/utils/constants.js";
|
import { hoursToFinalizeInvoice } from "@tests/utils/constants.js";
|
||||||
import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js";
|
import { attachAndExpectCorrect } from "@tests/utils/expectUtils/expectAttach.js";
|
||||||
import { createProducts } from "tests/utils/productUtils.js";
|
import { createProducts } from "@tests/utils/productUtils.js";
|
||||||
import { advanceTestClock } from "tests/utils/stripeUtils.js";
|
import { advanceTestClock } from "@tests/utils/stripeUtils.js";
|
||||||
import { getBasePrice } from "tests/utils/testProductUtils/testProductUtils.js";
|
import { getBasePrice } from "@tests/utils/testProductUtils/testProductUtils.js";
|
||||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||||
import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
|
import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
|
||||||
|
|||||||
@@ -3,14 +3,14 @@ import { expect } from "chai";
|
|||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import { addHours, addMonths } from "date-fns";
|
import { addHours, addMonths } from "date-fns";
|
||||||
import type Stripe from "stripe";
|
import type Stripe from "stripe";
|
||||||
import { addPrefixToProducts } from "tests/attach/utils.js";
|
import { addPrefixToProducts } from "@tests/attach/utils.js";
|
||||||
import { setupBefore } from "tests/before.js";
|
import { setupBefore } from "@tests/before.js";
|
||||||
import { TestFeature } from "tests/setup/v2Features.js";
|
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||||
import { hoursToFinalizeInvoice } from "tests/utils/constants.js";
|
import { hoursToFinalizeInvoice } from "@tests/utils/constants.js";
|
||||||
import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js";
|
import { attachAndExpectCorrect } from "@tests/utils/expectUtils/expectAttach.js";
|
||||||
import { getExpectedInvoiceTotal } from "tests/utils/expectUtils/expectInvoiceUtils.js";
|
import { getExpectedInvoiceTotal } from "@tests/utils/expectUtils/expectInvoiceUtils.js";
|
||||||
import { createProducts } from "tests/utils/productUtils.js";
|
import { createProducts } from "@tests/utils/productUtils.js";
|
||||||
import { advanceTestClock } from "tests/utils/stripeUtils.js";
|
import { advanceTestClock } from "@tests/utils/stripeUtils.js";
|
||||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||||
import { constructArrearItem } from "@/utils/scriptUtils/constructItem.js";
|
import { constructArrearItem } from "@/utils/scriptUtils/constructItem.js";
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ import { LegacyVersion } from "@autumn/shared";
|
|||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import { addHours, addMonths } from "date-fns";
|
import { addHours, addMonths } from "date-fns";
|
||||||
import type Stripe from "stripe";
|
import type Stripe from "stripe";
|
||||||
import { TestFeature } from "tests/setup/v2Features.js";
|
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||||
import { hoursToFinalizeInvoice } from "tests/utils/constants.js";
|
import { hoursToFinalizeInvoice } from "@tests/utils/constants.js";
|
||||||
import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js";
|
import { attachAndExpectCorrect } from "@tests/utils/expectUtils/expectAttach.js";
|
||||||
import { getExpectedInvoiceTotal } from "tests/utils/expectUtils/expectInvoiceUtils.js";
|
import { getExpectedInvoiceTotal } from "@tests/utils/expectUtils/expectInvoiceUtils.js";
|
||||||
import ctx from "tests/utils/testInitUtils/createTestContext.js";
|
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||||
import { constructArrearItem } from "@/utils/scriptUtils/constructItem.js";
|
import { constructArrearItem } from "@/utils/scriptUtils/constructItem.js";
|
||||||
import { constructRawProduct } from "@/utils/scriptUtils/createTestProducts.js";
|
import { constructRawProduct } from "@/utils/scriptUtils/createTestProducts.js";
|
||||||
|
|||||||
@@ -3,14 +3,14 @@ import { expect } from "chai";
|
|||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import { addHours, addMonths } from "date-fns";
|
import { addHours, addMonths } from "date-fns";
|
||||||
import type Stripe from "stripe";
|
import type Stripe from "stripe";
|
||||||
import { addPrefixToProducts } from "tests/attach/utils.js";
|
import { addPrefixToProducts } from "@tests/attach/utils.js";
|
||||||
import { setupBefore } from "tests/before.js";
|
import { setupBefore } from "@tests/before.js";
|
||||||
import { TestFeature } from "tests/setup/v2Features.js";
|
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||||
import { hoursToFinalizeInvoice } from "tests/utils/constants.js";
|
import { hoursToFinalizeInvoice } from "@tests/utils/constants.js";
|
||||||
import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js";
|
import { attachAndExpectCorrect } from "@tests/utils/expectUtils/expectAttach.js";
|
||||||
import { getExpectedInvoiceTotal } from "tests/utils/expectUtils/expectInvoiceUtils.js";
|
import { getExpectedInvoiceTotal } from "@tests/utils/expectUtils/expectInvoiceUtils.js";
|
||||||
import { createProducts } from "tests/utils/productUtils.js";
|
import { createProducts } from "@tests/utils/productUtils.js";
|
||||||
import { advanceTestClock } from "tests/utils/stripeUtils.js";
|
import { advanceTestClock } from "@tests/utils/stripeUtils.js";
|
||||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||||
import { constructArrearItem } from "@/utils/scriptUtils/constructItem.js";
|
import { constructArrearItem } from "@/utils/scriptUtils/constructItem.js";
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ import { expect } from "chai";
|
|||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import { addDays, addMonths } from "date-fns";
|
import { addDays, addMonths } from "date-fns";
|
||||||
import type Stripe from "stripe";
|
import type Stripe from "stripe";
|
||||||
import { addPrefixToProducts } from "tests/attach/utils.js";
|
import { addPrefixToProducts } from "@tests/attach/utils.js";
|
||||||
import { setupBefore } from "tests/before.js";
|
import { setupBefore } from "@tests/before.js";
|
||||||
import { TestFeature } from "tests/setup/v2Features.js";
|
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||||
import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js";
|
import { attachAndExpectCorrect } from "@tests/utils/expectUtils/expectAttach.js";
|
||||||
import { expectProductAttached } from "tests/utils/expectUtils/expectProductAttached.js";
|
import { expectProductAttached } from "@tests/utils/expectUtils/expectProductAttached.js";
|
||||||
import { createProducts } from "tests/utils/productUtils.js";
|
import { createProducts } from "@tests/utils/productUtils.js";
|
||||||
import { advanceTestClock } from "tests/utils/stripeUtils.js";
|
import { advanceTestClock } from "@tests/utils/stripeUtils.js";
|
||||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||||
import { calculateProrationAmount } from "@/internal/invoices/prorationUtils.js";
|
import { calculateProrationAmount } from "@/internal/invoices/prorationUtils.js";
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ import { LegacyVersion } from "@autumn/shared";
|
|||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import { addDays, addMonths } from "date-fns";
|
import { addDays, addMonths } from "date-fns";
|
||||||
import type Stripe from "stripe";
|
import type Stripe from "stripe";
|
||||||
import { TestFeature } from "tests/setup/v2Features.js";
|
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||||
import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js";
|
import { attachAndExpectCorrect } from "@tests/utils/expectUtils/expectAttach.js";
|
||||||
import { expectProductAttached } from "tests/utils/expectUtils/expectProductAttached.js";
|
import { expectProductAttached } from "@tests/utils/expectUtils/expectProductAttached.js";
|
||||||
import ctx from "tests/utils/testInitUtils/createTestContext.js";
|
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||||
import { calculateProrationAmount } from "@/internal/invoices/prorationUtils.js";
|
import { calculateProrationAmount } from "@/internal/invoices/prorationUtils.js";
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ import { expect } from "chai";
|
|||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import { addDays, addMonths } from "date-fns";
|
import { addDays, addMonths } from "date-fns";
|
||||||
import type Stripe from "stripe";
|
import type Stripe from "stripe";
|
||||||
import { addPrefixToProducts } from "tests/attach/utils.js";
|
import { addPrefixToProducts } from "@tests/attach/utils.js";
|
||||||
import { setupBefore } from "tests/before.js";
|
import { setupBefore } from "@tests/before.js";
|
||||||
import { TestFeature } from "tests/setup/v2Features.js";
|
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||||
import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js";
|
import { attachAndExpectCorrect } from "@tests/utils/expectUtils/expectAttach.js";
|
||||||
import { expectProductAttached } from "tests/utils/expectUtils/expectProductAttached.js";
|
import { expectProductAttached } from "@tests/utils/expectUtils/expectProductAttached.js";
|
||||||
import { createProducts } from "tests/utils/productUtils.js";
|
import { createProducts } from "@tests/utils/productUtils.js";
|
||||||
import { advanceTestClock } from "tests/utils/stripeUtils.js";
|
import { advanceTestClock } from "@tests/utils/stripeUtils.js";
|
||||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||||
import { calculateProrationAmount } from "@/internal/invoices/prorationUtils.js";
|
import { calculateProrationAmount } from "@/internal/invoices/prorationUtils.js";
|
||||||
|
|||||||
@@ -3,16 +3,16 @@ import { expect } from "chai";
|
|||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import { addMonths } from "date-fns";
|
import { addMonths } from "date-fns";
|
||||||
import type Stripe from "stripe";
|
import type Stripe from "stripe";
|
||||||
import { addPrefixToProducts } from "tests/attach/utils.js";
|
import { addPrefixToProducts } from "@tests/attach/utils.js";
|
||||||
import { setupBefore } from "tests/before.js";
|
import { setupBefore } from "@tests/before.js";
|
||||||
import { TestFeature } from "tests/setup/v2Features.js";
|
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||||
import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js";
|
import { attachAndExpectCorrect } from "@tests/utils/expectUtils/expectAttach.js";
|
||||||
import {
|
import {
|
||||||
expectDowngradeCorrect,
|
expectDowngradeCorrect,
|
||||||
expectNextCycleCorrect,
|
expectNextCycleCorrect,
|
||||||
} from "tests/utils/expectUtils/expectScheduleUtils.js";
|
} from "@tests/utils/expectUtils/expectScheduleUtils.js";
|
||||||
import { createProducts } from "tests/utils/productUtils.js";
|
import { createProducts } from "@tests/utils/productUtils.js";
|
||||||
import { getBasePrice } from "tests/utils/testProductUtils/testProductUtils.js";
|
import { getBasePrice } from "@tests/utils/testProductUtils/testProductUtils.js";
|
||||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||||
import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
|
import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
|
||||||
|
|||||||
@@ -3,14 +3,14 @@ import { LegacyVersion } from "@autumn/shared";
|
|||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import { addMonths } from "date-fns";
|
import { addMonths } from "date-fns";
|
||||||
import type Stripe from "stripe";
|
import type Stripe from "stripe";
|
||||||
import { TestFeature } from "tests/setup/v2Features.js";
|
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||||
import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js";
|
import { attachAndExpectCorrect } from "@tests/utils/expectUtils/expectAttach.js";
|
||||||
import {
|
import {
|
||||||
expectDowngradeCorrect,
|
expectDowngradeCorrect,
|
||||||
expectNextCycleCorrect,
|
expectNextCycleCorrect,
|
||||||
} from "tests/utils/expectUtils/expectScheduleUtils.js";
|
} from "@tests/utils/expectUtils/expectScheduleUtils.js";
|
||||||
import ctx from "tests/utils/testInitUtils/createTestContext.js";
|
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||||
import { getBasePrice } from "tests/utils/testProductUtils/testProductUtils.js";
|
import { getBasePrice } from "@tests/utils/testProductUtils/testProductUtils.js";
|
||||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||||
import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
|
import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
|
||||||
import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js";
|
import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js";
|
||||||
|
|||||||
@@ -3,16 +3,16 @@ import { expect } from "chai";
|
|||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import { addMonths } from "date-fns";
|
import { addMonths } from "date-fns";
|
||||||
import type Stripe from "stripe";
|
import type Stripe from "stripe";
|
||||||
import { addPrefixToProducts } from "tests/attach/utils.js";
|
import { addPrefixToProducts } from "@tests/attach/utils.js";
|
||||||
import { setupBefore } from "tests/before.js";
|
import { setupBefore } from "@tests/before.js";
|
||||||
import { TestFeature } from "tests/setup/v2Features.js";
|
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||||
import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js";
|
import { attachAndExpectCorrect } from "@tests/utils/expectUtils/expectAttach.js";
|
||||||
import {
|
import {
|
||||||
expectDowngradeCorrect,
|
expectDowngradeCorrect,
|
||||||
expectNextCycleCorrect,
|
expectNextCycleCorrect,
|
||||||
} from "tests/utils/expectUtils/expectScheduleUtils.js";
|
} from "@tests/utils/expectUtils/expectScheduleUtils.js";
|
||||||
import { createProducts } from "tests/utils/productUtils.js";
|
import { createProducts } from "@tests/utils/productUtils.js";
|
||||||
import { getBasePrice } from "tests/utils/testProductUtils/testProductUtils.js";
|
import { getBasePrice } from "@tests/utils/testProductUtils/testProductUtils.js";
|
||||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||||
import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
|
import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ import type { Customer } from "autumn-js";
|
|||||||
import { expect } from "chai";
|
import { expect } from "chai";
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import type Stripe from "stripe";
|
import type Stripe from "stripe";
|
||||||
import { addPrefixToProducts } from "tests/attach/utils.js";
|
import { addPrefixToProducts } from "@tests/attach/utils.js";
|
||||||
import { setupBefore } from "tests/before.js";
|
import { setupBefore } from "@tests/before.js";
|
||||||
import { TestFeature } from "tests/setup/v2Features.js";
|
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||||
import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js";
|
import { attachAndExpectCorrect } from "@tests/utils/expectUtils/expectAttach.js";
|
||||||
import { createProducts } from "tests/utils/productUtils.js";
|
import { createProducts } from "@tests/utils/productUtils.js";
|
||||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||||
import { timeout } from "@/utils/genUtils.js";
|
import { timeout } from "@/utils/genUtils.js";
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ import { LegacyVersion } from "@autumn/shared";
|
|||||||
import type { Customer } from "autumn-js";
|
import type { Customer } from "autumn-js";
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import type Stripe from "stripe";
|
import type Stripe from "stripe";
|
||||||
import { TestFeature } from "tests/setup/v2Features.js";
|
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||||
import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js";
|
import { attachAndExpectCorrect } from "@tests/utils/expectUtils/expectAttach.js";
|
||||||
import ctx from "tests/utils/testInitUtils/createTestContext.js";
|
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||||
import { timeout } from "@/utils/genUtils.js";
|
import { timeout } from "@/utils/genUtils.js";
|
||||||
import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
|
import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ import type { Customer } from "autumn-js";
|
|||||||
import { expect } from "chai";
|
import { expect } from "chai";
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import type Stripe from "stripe";
|
import type Stripe from "stripe";
|
||||||
import { addPrefixToProducts } from "tests/attach/utils.js";
|
import { addPrefixToProducts } from "@tests/attach/utils.js";
|
||||||
import { setupBefore } from "tests/before.js";
|
import { setupBefore } from "@tests/before.js";
|
||||||
import { TestFeature } from "tests/setup/v2Features.js";
|
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||||
import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js";
|
import { attachAndExpectCorrect } from "@tests/utils/expectUtils/expectAttach.js";
|
||||||
import { createProducts } from "tests/utils/productUtils.js";
|
import { createProducts } from "@tests/utils/productUtils.js";
|
||||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||||
import { timeout } from "@/utils/genUtils.js";
|
import { timeout } from "@/utils/genUtils.js";
|
||||||
|
|||||||
@@ -1,23 +1,22 @@
|
|||||||
// Manual customer creation - not using initCustomer to control test clock properly
|
// Manual customer creation - not using initCustomer to control test clock properly
|
||||||
|
import { beforeAll, describe, it } from "bun:test";
|
||||||
import {
|
import {
|
||||||
type AppEnv,
|
|
||||||
CusProductStatus,
|
CusProductStatus,
|
||||||
|
FreeTrialDuration,
|
||||||
LegacyVersion,
|
LegacyVersion,
|
||||||
type Organization,
|
ProductItemInterval,
|
||||||
} from "@autumn/shared";
|
} from "@autumn/shared";
|
||||||
|
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||||
|
import { expectProductAttached } from "@tests/utils/expectUtils/expectProductAttached.js";
|
||||||
|
import { advanceTestClock } from "@tests/utils/stripeUtils.js";
|
||||||
|
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import type Stripe from "stripe";
|
import type Stripe from "stripe";
|
||||||
import { setupBefore } from "tests/before.js";
|
|
||||||
import { expectProductAttached } from "tests/utils/expectUtils/expectProductAttached.js";
|
|
||||||
import { advanceTestClock } from "tests/utils/stripeUtils.js";
|
|
||||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
|
||||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||||
import { initCustomerV2 } from "@/utils/scriptUtils/initCustomer.js";
|
import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
|
||||||
import {
|
import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js";
|
||||||
defaultTrialFree,
|
import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3.js";
|
||||||
defaultTrialPro,
|
import { initProductsV0 } from "@/utils/scriptUtils/testUtils/initProductsV0.js";
|
||||||
setupDefaultTrialBefore,
|
|
||||||
} from "./defaultTrialBefore.test.js";
|
|
||||||
|
|
||||||
// Case 1: ✅
|
// Case 1: ✅
|
||||||
// Pro product with default trial exists alongside a free default product
|
// Pro product with default trial exists alongside a free default product
|
||||||
@@ -28,35 +27,66 @@ import {
|
|||||||
// Pro product with default trial exists alone
|
// Pro product with default trial exists alone
|
||||||
// -> Creating a new customer should attach the pro product with default trial
|
// -> Creating a new customer should attach the pro product with default trial
|
||||||
|
|
||||||
|
const defaultTrialPro = constructProduct({
|
||||||
|
items: [
|
||||||
|
constructFeatureItem({
|
||||||
|
featureId: TestFeature.Words,
|
||||||
|
includedUsage: 1500,
|
||||||
|
interval: ProductItemInterval.Month,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
isDefault: true,
|
||||||
|
forcePaidDefault: true,
|
||||||
|
id: "defaultTrial_pro",
|
||||||
|
group: "defaultTrial",
|
||||||
|
type: "pro",
|
||||||
|
freeTrial: {
|
||||||
|
length: 7,
|
||||||
|
duration: FreeTrialDuration.Day,
|
||||||
|
unique_fingerprint: false,
|
||||||
|
card_required: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const defaultTrialFree = constructProduct({
|
||||||
|
items: [
|
||||||
|
constructFeatureItem({
|
||||||
|
featureId: TestFeature.Words,
|
||||||
|
includedUsage: 500,
|
||||||
|
interval: ProductItemInterval.Month,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
id: "defaultTrial_free",
|
||||||
|
group: "defaultTrial",
|
||||||
|
type: "free",
|
||||||
|
isDefault: true,
|
||||||
|
});
|
||||||
|
|
||||||
const testCase = "defaultTrial1";
|
const testCase = "defaultTrial1";
|
||||||
|
|
||||||
describe(`${chalk.yellowBright(`advanced/${testCase}: ensure default trials are attached when creating a customer`)}`, () => {
|
describe(`${chalk.yellowBright(`advanced/${testCase}: ensure default trials are attached when creating a customer`)}`, () => {
|
||||||
const customerId = testCase;
|
const customerId = testCase;
|
||||||
const autumn: AutumnInt = new AutumnInt({ version: LegacyVersion.v1_4 });
|
const autumn: AutumnInt = new AutumnInt({ version: LegacyVersion.v1_4 });
|
||||||
let testClockID: string;
|
let testClockID: string;
|
||||||
let db: DrizzleCli, org: Organization, env: AppEnv;
|
|
||||||
let stripeCli: Stripe;
|
let stripeCli: Stripe;
|
||||||
|
|
||||||
const curUnix = Math.floor(new Date().getTime() / 1000);
|
beforeAll(async () => {
|
||||||
|
// Products must be initialized BEFORE customer creation for default products
|
||||||
|
await initProductsV0({
|
||||||
|
ctx,
|
||||||
|
products: [defaultTrialPro, defaultTrialFree],
|
||||||
|
prefix: testCase,
|
||||||
|
customerId,
|
||||||
|
});
|
||||||
|
|
||||||
beforeAll(async function () {
|
const res = await initCustomerV3({
|
||||||
await setupBefore(this);
|
ctx,
|
||||||
await setupDefaultTrialBefore({});
|
customerId,
|
||||||
const { autumnJs } = this;
|
withTestClock: true,
|
||||||
stripeCli = this.stripeCli;
|
|
||||||
db = this.db;
|
|
||||||
org = this.org;
|
|
||||||
env = this.env;
|
|
||||||
|
|
||||||
const res = await initCustomerV2({
|
|
||||||
autumn: autumnJs,
|
|
||||||
customerId: testCase,
|
|
||||||
db,
|
|
||||||
org,
|
|
||||||
env,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
testClockID = res.testClockId;
|
testClockID = res.testClockId;
|
||||||
|
stripeCli = ctx.stripeCli;
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should create a customer with the paid default trial", async () => {
|
it("should create a customer with the paid default trial", async () => {
|
||||||
|
|||||||
@@ -1,59 +1,76 @@
|
|||||||
// Manual customer creation - not using initCustomer to control test clock properly
|
// Manual customer creation - not using initCustomer to control test clock properly
|
||||||
|
import { beforeAll, describe, it } from "bun:test";
|
||||||
import {
|
import {
|
||||||
type AppEnv,
|
|
||||||
CusProductStatus,
|
CusProductStatus,
|
||||||
|
FreeTrialDuration,
|
||||||
LegacyVersion,
|
LegacyVersion,
|
||||||
type Organization,
|
ProductItemInterval,
|
||||||
} from "@autumn/shared";
|
} from "@autumn/shared";
|
||||||
|
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||||
|
import { hoursToFinalizeInvoice } from "@tests/utils/constants.js";
|
||||||
|
import { expectProductAttached } from "@tests/utils/expectUtils/expectProductAttached.js";
|
||||||
|
import { advanceTestClock } from "@tests/utils/stripeUtils.js";
|
||||||
|
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import { addDays, addHours } from "date-fns";
|
import { addDays, addHours } from "date-fns";
|
||||||
import type Stripe from "stripe";
|
import type Stripe from "stripe";
|
||||||
import { setupBefore } from "tests/before.js";
|
|
||||||
import { hoursToFinalizeInvoice } from "tests/utils/constants.js";
|
|
||||||
import { expectProductAttached } from "tests/utils/expectUtils/expectProductAttached.js";
|
|
||||||
import { advanceTestClock } from "tests/utils/stripeUtils.js";
|
|
||||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
|
||||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||||
import { initCustomerV2 } from "@/utils/scriptUtils/initCustomer.js";
|
import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
|
||||||
import {
|
import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js";
|
||||||
defaultTrialPro,
|
import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3.js";
|
||||||
setupDefaultTrialBefore,
|
import { initProductsV0 } from "@/utils/scriptUtils/testUtils/initProductsV0.js";
|
||||||
} from "./defaultTrialBefore.test.js";
|
|
||||||
|
|
||||||
// 2.2:
|
// 2.2:
|
||||||
// -> Creating a new customer with a payment method should attach the pro product with default trial
|
// -> Creating a new customer with a payment method should attach the pro product with default trial
|
||||||
// --> Advancing the test clock should cancel the trial and attach the pro product
|
// --> Advancing the test clock should cancel the trial and attach the pro product
|
||||||
|
|
||||||
|
const defaultTrialPro = constructProduct({
|
||||||
|
items: [
|
||||||
|
constructFeatureItem({
|
||||||
|
featureId: TestFeature.Words,
|
||||||
|
includedUsage: 1500,
|
||||||
|
interval: ProductItemInterval.Month,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
isDefault: true,
|
||||||
|
forcePaidDefault: true,
|
||||||
|
id: "defaultTrial_pro",
|
||||||
|
group: "defaultTrial",
|
||||||
|
type: "pro",
|
||||||
|
freeTrial: {
|
||||||
|
length: 7,
|
||||||
|
duration: FreeTrialDuration.Day,
|
||||||
|
unique_fingerprint: false,
|
||||||
|
card_required: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const testCase = "defaultTrial2";
|
const testCase = "defaultTrial2";
|
||||||
|
|
||||||
describe(`${chalk.yellowBright(`advanced/${testCase}: ensure trial transitions into full product if payment method is valid`)}`, () => {
|
describe(`${chalk.yellowBright(`advanced/${testCase}: ensure trial transitions into full product if payment method is valid`)}`, () => {
|
||||||
const customerId = testCase;
|
const customerId = testCase;
|
||||||
const autumn: AutumnInt = new AutumnInt({ version: LegacyVersion.v1_4 });
|
const autumn: AutumnInt = new AutumnInt({ version: LegacyVersion.v1_4 });
|
||||||
let testClockID: string;
|
let testClockID: string;
|
||||||
let db: DrizzleCli, org: Organization, env: AppEnv;
|
|
||||||
let stripeCli: Stripe;
|
let stripeCli: Stripe;
|
||||||
|
|
||||||
const curUnix = Math.floor(new Date().getTime() / 1000);
|
beforeAll(async () => {
|
||||||
|
// Products must be initialized BEFORE customer creation for default products
|
||||||
|
await initProductsV0({
|
||||||
|
ctx,
|
||||||
|
products: [defaultTrialPro],
|
||||||
|
prefix: testCase,
|
||||||
|
customerId,
|
||||||
|
});
|
||||||
|
|
||||||
beforeAll(async function () {
|
const res = await initCustomerV3({
|
||||||
await setupBefore(this);
|
ctx,
|
||||||
await setupDefaultTrialBefore({});
|
customerId,
|
||||||
const { autumnJs } = this;
|
|
||||||
stripeCli = this.stripeCli;
|
|
||||||
db = this.db;
|
|
||||||
org = this.org;
|
|
||||||
env = this.env;
|
|
||||||
|
|
||||||
const res = await initCustomerV2({
|
|
||||||
autumn: autumnJs,
|
|
||||||
customerId: testCase,
|
|
||||||
db,
|
|
||||||
org,
|
|
||||||
env,
|
|
||||||
attachPm: "success",
|
attachPm: "success",
|
||||||
|
withTestClock: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
testClockID = res.testClockId;
|
testClockID = res.testClockId;
|
||||||
|
stripeCli = ctx.stripeCli;
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should create a customer with the paid default trial", async () => {
|
it("should create a customer with the paid default trial", async () => {
|
||||||
|
|||||||
@@ -1,59 +1,76 @@
|
|||||||
// Manual customer creation - not using initCustomer to control test clock properly
|
// Manual customer creation - not using initCustomer to control test clock properly
|
||||||
|
import { beforeAll, describe, it } from "bun:test";
|
||||||
import {
|
import {
|
||||||
type AppEnv,
|
|
||||||
CusProductStatus,
|
CusProductStatus,
|
||||||
|
FreeTrialDuration,
|
||||||
LegacyVersion,
|
LegacyVersion,
|
||||||
type Organization,
|
ProductItemInterval,
|
||||||
} from "@autumn/shared";
|
} from "@autumn/shared";
|
||||||
|
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||||
|
import { hoursToFinalizeInvoice } from "@tests/utils/constants.js";
|
||||||
|
import { expectProductAttached } from "@tests/utils/expectUtils/expectProductAttached.js";
|
||||||
|
import { advanceTestClock } from "@tests/utils/stripeUtils.js";
|
||||||
|
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import { addDays, addHours } from "date-fns";
|
import { addDays, addHours } from "date-fns";
|
||||||
import type Stripe from "stripe";
|
import type Stripe from "stripe";
|
||||||
import { setupBefore } from "tests/before.js";
|
|
||||||
import { hoursToFinalizeInvoice } from "tests/utils/constants.js";
|
|
||||||
import { expectProductAttached } from "tests/utils/expectUtils/expectProductAttached.js";
|
|
||||||
import { advanceTestClock } from "tests/utils/stripeUtils.js";
|
|
||||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
|
||||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||||
import { initCustomerV2 } from "@/utils/scriptUtils/initCustomer.js";
|
import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
|
||||||
import {
|
import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js";
|
||||||
defaultTrialPro,
|
import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3.js";
|
||||||
setupDefaultTrialBefore,
|
import { initProductsV0 } from "@/utils/scriptUtils/testUtils/initProductsV0.js";
|
||||||
} from "./defaultTrialBefore.test.js";
|
|
||||||
|
|
||||||
// 2.3:
|
// 2.3:
|
||||||
// -> Creating a new customer with a fake payment method should attach the pro product with default trial
|
// -> Creating a new customer with a fake payment method should attach the pro product with default trial
|
||||||
// --> Advancing the test clock should cancel the trial and attach the free product
|
// --> Advancing the test clock should cancel the trial and attach the free product
|
||||||
|
|
||||||
|
const defaultTrialPro = constructProduct({
|
||||||
|
items: [
|
||||||
|
constructFeatureItem({
|
||||||
|
featureId: TestFeature.Words,
|
||||||
|
includedUsage: 1500,
|
||||||
|
interval: ProductItemInterval.Month,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
isDefault: true,
|
||||||
|
forcePaidDefault: true,
|
||||||
|
id: "defaultTrial_pro",
|
||||||
|
group: "defaultTrial",
|
||||||
|
type: "pro",
|
||||||
|
freeTrial: {
|
||||||
|
length: 7,
|
||||||
|
duration: FreeTrialDuration.Day,
|
||||||
|
unique_fingerprint: false,
|
||||||
|
card_required: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const testCase = "defaultTrial3";
|
const testCase = "defaultTrial3";
|
||||||
|
|
||||||
describe(`${chalk.yellowBright(`advanced/${testCase}: ensure trials cancel with bad payment method`)}`, () => {
|
describe(`${chalk.yellowBright(`advanced/${testCase}: ensure trials cancel with bad payment method`)}`, () => {
|
||||||
const customerId = testCase;
|
const customerId = testCase;
|
||||||
const autumn: AutumnInt = new AutumnInt({ version: LegacyVersion.v1_4 });
|
const autumn: AutumnInt = new AutumnInt({ version: LegacyVersion.v1_4 });
|
||||||
let testClockID: string;
|
let testClockID: string;
|
||||||
let db: DrizzleCli, org: Organization, env: AppEnv;
|
|
||||||
let stripeCli: Stripe;
|
let stripeCli: Stripe;
|
||||||
|
|
||||||
const curUnix = Math.floor(new Date().getTime() / 1000);
|
beforeAll(async () => {
|
||||||
|
// Products must be initialized BEFORE customer creation for default products
|
||||||
|
await initProductsV0({
|
||||||
|
ctx,
|
||||||
|
products: [defaultTrialPro],
|
||||||
|
prefix: testCase,
|
||||||
|
customerId,
|
||||||
|
});
|
||||||
|
|
||||||
beforeAll(async function () {
|
const res = await initCustomerV3({
|
||||||
await setupBefore(this);
|
ctx,
|
||||||
await setupDefaultTrialBefore({});
|
customerId,
|
||||||
const { autumnJs } = this;
|
|
||||||
stripeCli = this.stripeCli;
|
|
||||||
db = this.db;
|
|
||||||
org = this.org;
|
|
||||||
env = this.env;
|
|
||||||
|
|
||||||
const res = await initCustomerV2({
|
|
||||||
autumn: autumnJs,
|
|
||||||
customerId: testCase,
|
|
||||||
db,
|
|
||||||
org,
|
|
||||||
env,
|
|
||||||
attachPm: "fail",
|
attachPm: "fail",
|
||||||
|
withTestClock: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
testClockID = res.testClockId;
|
testClockID = res.testClockId;
|
||||||
|
stripeCli = ctx.stripeCli;
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should create a customer with the paid default trial", async () => {
|
it("should create a customer with the paid default trial", async () => {
|
||||||
|
|||||||
@@ -1,57 +0,0 @@
|
|||||||
import {
|
|
||||||
FreeTrialDuration,
|
|
||||||
LegacyVersion,
|
|
||||||
ProductItemInterval,
|
|
||||||
} from "@autumn/shared";
|
|
||||||
import { TestFeature } from "tests/setup/v2Features.js";
|
|
||||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
|
||||||
import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
|
|
||||||
import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js";
|
|
||||||
|
|
||||||
export const defaultTrialPro = constructProduct({
|
|
||||||
items: [
|
|
||||||
constructFeatureItem({
|
|
||||||
featureId: TestFeature.Words,
|
|
||||||
includedUsage: 1500,
|
|
||||||
interval: ProductItemInterval.Month,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
isDefault: true,
|
|
||||||
forcePaidDefault: true,
|
|
||||||
id: "defaultTrial_pro",
|
|
||||||
group: "defaultTrial",
|
|
||||||
type: "pro",
|
|
||||||
freeTrial: {
|
|
||||||
length: 7,
|
|
||||||
duration: FreeTrialDuration.Day,
|
|
||||||
unique_fingerprint: false,
|
|
||||||
card_required: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export const defaultTrialFree = constructProduct({
|
|
||||||
items: [
|
|
||||||
constructFeatureItem({
|
|
||||||
featureId: TestFeature.Words,
|
|
||||||
includedUsage: 500,
|
|
||||||
interval: ProductItemInterval.Month,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
id: "defaultTrial_free",
|
|
||||||
group: "defaultTrial",
|
|
||||||
type: "free",
|
|
||||||
isDefault: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const setupDefaultTrialBefore = async ({}: {}) => {
|
|
||||||
const autumn = new AutumnInt({ version: LegacyVersion.v1_2 });
|
|
||||||
for (const product of [defaultTrialPro, defaultTrialFree]) {
|
|
||||||
const res = await autumn.products.get(product.id);
|
|
||||||
|
|
||||||
if (res.code === "product_not_found") {
|
|
||||||
try {
|
|
||||||
await autumn.products.create(product);
|
|
||||||
} catch (error) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -2,10 +2,10 @@ import { beforeAll, describe, expect, test } from "bun:test";
|
|||||||
import { type AppEnv, LegacyVersion, type Organization } from "@autumn/shared";
|
import { type AppEnv, LegacyVersion, type Organization } from "@autumn/shared";
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import type { Stripe } from "stripe";
|
import type { Stripe } from "stripe";
|
||||||
import { TestFeature } from "tests/setup/v2Features.js";
|
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||||
import { getMainCusProduct } from "tests/utils/cusProductUtils/cusProductUtils.js";
|
import { getMainCusProduct } from "@tests/utils/cusProductUtils/cusProductUtils.js";
|
||||||
import { expectProductAttached } from "tests/utils/expectUtils/expectProductAttached.js";
|
import { expectProductAttached } from "@tests/utils/expectUtils/expectProductAttached.js";
|
||||||
import ctx from "tests/utils/testInitUtils/createTestContext.js";
|
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||||
import { cusProductToSub } from "@/internal/customers/cusProducts/cusProductUtils/convertCusProduct.js";
|
import { cusProductToSub } from "@/internal/customers/cusProducts/cusProductUtils/convertCusProduct.js";
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { beforeAll, describe, expect, test } from "bun:test";
|
import { beforeAll, describe, expect, test } from "bun:test";
|
||||||
import { ApiVersion, type AppEnv, LegacyVersion } from "@autumn/shared";
|
import { ApiVersion, type AppEnv, LegacyVersion } from "@autumn/shared";
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import { TestFeature } from "tests/setup/v2Features.js";
|
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||||
import {
|
import {
|
||||||
getPrepaidCusEnt,
|
getPrepaidCusEnt,
|
||||||
getUsageCusEnt,
|
getUsageCusEnt,
|
||||||
} from "tests/utils/cusProductUtils/cusEntSearchUtils.js";
|
} from "@tests/utils/cusProductUtils/cusEntSearchUtils.js";
|
||||||
import { getMainCusProduct } from "tests/utils/cusProductUtils/cusProductUtils.js";
|
import { getMainCusProduct } from "@tests/utils/cusProductUtils/cusProductUtils.js";
|
||||||
import ctx from "tests/utils/testInitUtils/createTestContext.js";
|
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||||
import { timeout } from "@/utils/genUtils.js";
|
import { timeout } from "@/utils/genUtils.js";
|
||||||
|
|||||||
@@ -7,14 +7,14 @@ import {
|
|||||||
} from "@autumn/shared";
|
} from "@autumn/shared";
|
||||||
import { expect } from "chai";
|
import { expect } from "chai";
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import { setupBefore } from "tests/before.js";
|
import { setupBefore } from "@tests/before.js";
|
||||||
import { features } from "tests/global.js";
|
import { features } from "@tests/global.js";
|
||||||
import {
|
import {
|
||||||
getPrepaidCusEnt,
|
getPrepaidCusEnt,
|
||||||
getUsageCusEnt,
|
getUsageCusEnt,
|
||||||
} from "tests/utils/cusProductUtils/cusEntSearchUtils.js";
|
} from "@tests/utils/cusProductUtils/cusEntSearchUtils.js";
|
||||||
import { getMainCusProduct } from "tests/utils/cusProductUtils/cusProductUtils.js";
|
import { getMainCusProduct } from "@tests/utils/cusProductUtils/cusProductUtils.js";
|
||||||
import { createProducts } from "tests/utils/productUtils.js";
|
import { createProducts } from "@tests/utils/productUtils.js";
|
||||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||||
import { constructFeaturePriceItem } from "@/internal/products/product-items/productItemUtils.js";
|
import { constructFeaturePriceItem } from "@/internal/products/product-items/productItemUtils.js";
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { beforeAll, describe, expect, test } from "bun:test";
|
import { beforeAll, describe, expect, test } from "bun:test";
|
||||||
import { ApiVersion, type AppEnv, LegacyVersion } from "@autumn/shared";
|
import { ApiVersion, type AppEnv, LegacyVersion } from "@autumn/shared";
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import { TestFeature } from "tests/setup/v2Features.js";
|
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||||
import {
|
import {
|
||||||
getLifetimeFreeCusEnt,
|
getLifetimeFreeCusEnt,
|
||||||
getUsageCusEnt,
|
getUsageCusEnt,
|
||||||
} from "tests/utils/cusProductUtils/cusEntSearchUtils.js";
|
} from "@tests/utils/cusProductUtils/cusEntSearchUtils.js";
|
||||||
import { getMainCusProduct } from "tests/utils/cusProductUtils/cusProductUtils.js";
|
import { getMainCusProduct } from "@tests/utils/cusProductUtils/cusProductUtils.js";
|
||||||
import ctx from "tests/utils/testInitUtils/createTestContext.js";
|
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||||
import { timeout } from "@/utils/genUtils.js";
|
import { timeout } from "@/utils/genUtils.js";
|
||||||
|
|||||||
@@ -8,14 +8,14 @@ import {
|
|||||||
} from "@autumn/shared";
|
} from "@autumn/shared";
|
||||||
import { expect } from "chai";
|
import { expect } from "chai";
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import { setupBefore } from "tests/before.js";
|
import { setupBefore } from "@tests/before.js";
|
||||||
import { features } from "tests/global.js";
|
import { features } from "@tests/global.js";
|
||||||
import {
|
import {
|
||||||
getLifetimeFreeCusEnt,
|
getLifetimeFreeCusEnt,
|
||||||
getUsageCusEnt,
|
getUsageCusEnt,
|
||||||
} from "tests/utils/cusProductUtils/cusEntSearchUtils.js";
|
} from "@tests/utils/cusProductUtils/cusEntSearchUtils.js";
|
||||||
import { getMainCusProduct } from "tests/utils/cusProductUtils/cusProductUtils.js";
|
import { getMainCusProduct } from "@tests/utils/cusProductUtils/cusProductUtils.js";
|
||||||
import { createProduct } from "tests/utils/productUtils.js";
|
import { createProduct } from "@tests/utils/productUtils.js";
|
||||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ import { beforeAll, describe, expect, test } from "bun:test";
|
|||||||
import { ApiVersion, type AppEnv } from "@autumn/shared";
|
import { ApiVersion, type AppEnv } from "@autumn/shared";
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import { addMonths } from "date-fns";
|
import { addMonths } from "date-fns";
|
||||||
import { TestFeature } from "tests/setup/v2Features.js";
|
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||||
import {
|
import {
|
||||||
getLifetimeFreeCusEnt,
|
getLifetimeFreeCusEnt,
|
||||||
getUsageCusEnt,
|
getUsageCusEnt,
|
||||||
} from "tests/utils/cusProductUtils/cusEntSearchUtils.js";
|
} from "@tests/utils/cusProductUtils/cusEntSearchUtils.js";
|
||||||
import { getMainCusProduct } from "tests/utils/cusProductUtils/cusProductUtils.js";
|
import { getMainCusProduct } from "@tests/utils/cusProductUtils/cusProductUtils.js";
|
||||||
import ctx from "tests/utils/testInitUtils/createTestContext.js";
|
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||||
import { timeout } from "@/utils/genUtils.js";
|
import { timeout } from "@/utils/genUtils.js";
|
||||||
|
|||||||
@@ -9,15 +9,15 @@ import {
|
|||||||
import { expect } from "chai";
|
import { expect } from "chai";
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import { addMonths } from "date-fns";
|
import { addMonths } from "date-fns";
|
||||||
import { setupBefore } from "tests/before.js";
|
import { setupBefore } from "@tests/before.js";
|
||||||
import { features } from "tests/global.js";
|
import { features } from "@tests/global.js";
|
||||||
import {
|
import {
|
||||||
getLifetimeFreeCusEnt,
|
getLifetimeFreeCusEnt,
|
||||||
getUsageCusEnt,
|
getUsageCusEnt,
|
||||||
} from "tests/utils/cusProductUtils/cusEntSearchUtils.js";
|
} from "@tests/utils/cusProductUtils/cusEntSearchUtils.js";
|
||||||
import { getMainCusProduct } from "tests/utils/cusProductUtils/cusProductUtils.js";
|
import { getMainCusProduct } from "@tests/utils/cusProductUtils/cusProductUtils.js";
|
||||||
import { createProducts } from "tests/utils/productUtils.js";
|
import { createProducts } from "@tests/utils/productUtils.js";
|
||||||
import { advanceTestClock } from "tests/utils/stripeUtils.js";
|
import { advanceTestClock } from "@tests/utils/stripeUtils.js";
|
||||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ import {
|
|||||||
import { assert } from "chai";
|
import { assert } from "chai";
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import type { Stripe } from "stripe";
|
import type { Stripe } from "stripe";
|
||||||
import { setupBefore } from "tests/before.js";
|
import { setupBefore } from "@tests/before.js";
|
||||||
import { expectProductV1Attached } from "tests/utils/expectUtils/expectProductAttached.js";
|
import { expectProductV1Attached } from "@tests/utils/expectUtils/expectProductAttached.js";
|
||||||
import { advanceTestClock } from "tests/utils/stripeUtils.js";
|
import { advanceTestClock } from "@tests/utils/stripeUtils.js";
|
||||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||||
import AutumnError, { AutumnInt } from "@/external/autumn/autumnCli.js";
|
import AutumnError, { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||||
|
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ import {
|
|||||||
import { beforeAll, describe, expect, test } from "bun:test";
|
import { beforeAll, describe, expect, test } from "bun:test";
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import type { Stripe } from "stripe";
|
import type { Stripe } from "stripe";
|
||||||
import { expectProductV1Attached } from "tests/utils/expectUtils/expectProductAttached.js";
|
import { expectProductV1Attached } from "@tests/utils/expectUtils/expectProductAttached.js";
|
||||||
import { advanceTestClock } from "tests/utils/stripeUtils.js";
|
import { advanceTestClock } from "@tests/utils/stripeUtils.js";
|
||||||
import ctx from "tests/utils/testInitUtils/createTestContext.js";
|
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||||
import AutumnError, { AutumnInt } from "@/external/autumn/autumnCli.js";
|
import AutumnError, { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||||
import { CusService } from "@/internal/customers/CusService.js";
|
import { CusService } from "@/internal/customers/CusService.js";
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ import {
|
|||||||
import { assert } from "chai";
|
import { assert } from "chai";
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import type { Stripe } from "stripe";
|
import type { Stripe } from "stripe";
|
||||||
import { setupBefore } from "tests/before.js";
|
import { setupBefore } from "@tests/before.js";
|
||||||
import { expectProductV1Attached } from "tests/utils/expectUtils/expectProductAttached.js";
|
import { expectProductV1Attached } from "@tests/utils/expectUtils/expectProductAttached.js";
|
||||||
import { advanceTestClock } from "tests/utils/stripeUtils.js";
|
import { advanceTestClock } from "@tests/utils/stripeUtils.js";
|
||||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||||
import AutumnError, { AutumnInt } from "@/external/autumn/autumnCli.js";
|
import AutumnError, { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||||
import { CusService } from "@/internal/customers/CusService.js";
|
import { CusService } from "@/internal/customers/CusService.js";
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ import {
|
|||||||
import { beforeAll, describe, expect, test } from "bun:test";
|
import { beforeAll, describe, expect, test } from "bun:test";
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import type { Stripe } from "stripe";
|
import type { Stripe } from "stripe";
|
||||||
import { expectProductV1Attached } from "tests/utils/expectUtils/expectProductAttached.js";
|
import { expectProductV1Attached } from "@tests/utils/expectUtils/expectProductAttached.js";
|
||||||
import { advanceTestClock } from "tests/utils/stripeUtils.js";
|
import { advanceTestClock } from "@tests/utils/stripeUtils.js";
|
||||||
import ctx from "tests/utils/testInitUtils/createTestContext.js";
|
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||||
import AutumnError, { AutumnInt } from "@/external/autumn/autumnCli.js";
|
import AutumnError, { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||||
import { CusService } from "@/internal/customers/CusService.js";
|
import { CusService } from "@/internal/customers/CusService.js";
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ import {
|
|||||||
import { assert } from "chai";
|
import { assert } from "chai";
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import type { Stripe } from "stripe";
|
import type { Stripe } from "stripe";
|
||||||
import { setupBefore } from "tests/before.js";
|
import { setupBefore } from "@tests/before.js";
|
||||||
import { expectProductV1Attached } from "tests/utils/expectUtils/expectProductAttached.js";
|
import { expectProductV1Attached } from "@tests/utils/expectUtils/expectProductAttached.js";
|
||||||
import { advanceTestClock } from "tests/utils/stripeUtils.js";
|
import { advanceTestClock } from "@tests/utils/stripeUtils.js";
|
||||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||||
import AutumnError, { AutumnInt } from "@/external/autumn/autumnCli.js";
|
import AutumnError, { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||||
import { CusService } from "@/internal/customers/CusService.js";
|
import { CusService } from "@/internal/customers/CusService.js";
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ import {
|
|||||||
import { beforeAll, describe, expect, test } from "bun:test";
|
import { beforeAll, describe, expect, test } from "bun:test";
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import type { Stripe } from "stripe";
|
import type { Stripe } from "stripe";
|
||||||
import { expectProductV1Attached } from "tests/utils/expectUtils/expectProductAttached.js";
|
import { expectProductV1Attached } from "@tests/utils/expectUtils/expectProductAttached.js";
|
||||||
import { advanceTestClock } from "tests/utils/stripeUtils.js";
|
import { advanceTestClock } from "@tests/utils/stripeUtils.js";
|
||||||
import ctx from "tests/utils/testInitUtils/createTestContext.js";
|
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||||
import AutumnError, { AutumnInt } from "@/external/autumn/autumnCli.js";
|
import AutumnError, { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||||
import { CusService } from "@/internal/customers/CusService.js";
|
import { CusService } from "@/internal/customers/CusService.js";
|
||||||
|
|||||||
@@ -10,12 +10,12 @@
|
|||||||
// import { assert } from "chai";
|
// import { assert } from "chai";
|
||||||
// import chalk from "chalk";
|
// import chalk from "chalk";
|
||||||
// import type { Stripe } from "stripe";
|
// import type { Stripe } from "stripe";
|
||||||
// import { setupBefore } from "tests/before.js";
|
// import { setupBefore } from "@tests/before.js";
|
||||||
// import { expectProductV1Attached } from "tests/utils/expectUtils/expectProductAttached.js";
|
// import { expectProductV1Attached } from "@tests/utils/expectUtils/expectProductAttached.js";
|
||||||
// import {
|
// import {
|
||||||
// advanceTestClock,
|
// advanceTestClock,
|
||||||
// completeCheckoutForm,
|
// completeCheckoutForm,
|
||||||
// } from "tests/utils/stripeUtils.js";
|
// } from "@tests/utils/stripeUtils.js";
|
||||||
// import type { DrizzleCli } from "@/db/initDrizzle.js";
|
// import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||||
// import AutumnError, { AutumnInt } from "@/external/autumn/autumnCli.js";
|
// import AutumnError, { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||||
// import { CusService } from "@/internal/customers/CusService.js";
|
// import { CusService } from "@/internal/customers/CusService.js";
|
||||||
|
|||||||
@@ -13,12 +13,12 @@
|
|||||||
// import { beforeAll, describe, expect, test } from "bun:test";
|
// import { beforeAll, describe, expect, test } from "bun:test";
|
||||||
// import chalk from "chalk";
|
// import chalk from "chalk";
|
||||||
// import type { Stripe } from "stripe";
|
// import type { Stripe } from "stripe";
|
||||||
// import { expectProductV1Attached } from "tests/utils/expectUtils/expectProductAttached.js";
|
// import { expectProductV1Attached } from "@tests/utils/expectUtils/expectProductAttached.js";
|
||||||
// import {
|
// import {
|
||||||
// advanceTestClock,
|
// advanceTestClock,
|
||||||
// completeCheckoutForm,
|
// completeCheckoutForm,
|
||||||
// } from "tests/utils/stripeUtils.js";
|
// } from "@tests/utils/stripeUtils.js";
|
||||||
// import ctx from "tests/utils/testInitUtils/createTestContext.js";
|
// import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||||
// import type { DrizzleCli } from "@/db/initDrizzle.js";
|
// import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||||
// import AutumnError, { AutumnInt } from "@/external/autumn/autumnCli.js";
|
// import AutumnError, { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||||
// import { CusService } from "@/internal/customers/CusService.js";
|
// import { CusService } from "@/internal/customers/CusService.js";
|
||||||
|
|||||||
@@ -15,11 +15,11 @@ import {
|
|||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import { addDays } from "date-fns";
|
import { addDays } from "date-fns";
|
||||||
import type { Stripe } from "stripe";
|
import type { Stripe } from "stripe";
|
||||||
import { TestFeature } from "tests/setup/v2Features.js";
|
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||||
import { timeout } from "tests/utils/genUtils.js";
|
import { timeout } from "@tests/utils/genUtils.js";
|
||||||
import { createReferralProgram } from "tests/utils/productUtils.js";
|
import { createReferralProgram } from "@tests/utils/productUtils.js";
|
||||||
import { advanceTestClock } from "tests/utils/stripeUtils.js";
|
import { advanceTestClock } from "@tests/utils/stripeUtils.js";
|
||||||
import ctx from "tests/utils/testInitUtils/createTestContext.js";
|
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||||
import AutumnError, { AutumnInt } from "@/external/autumn/autumnCli.js";
|
import AutumnError, { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||||
import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
|
import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
|
||||||
|
|||||||
@@ -16,11 +16,11 @@ import {
|
|||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import { addDays } from "date-fns";
|
import { addDays } from "date-fns";
|
||||||
import type { Stripe } from "stripe";
|
import type { Stripe } from "stripe";
|
||||||
import { TestFeature } from "tests/setup/v2Features.js";
|
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||||
import { timeout } from "tests/utils/genUtils.js";
|
import { timeout } from "@tests/utils/genUtils.js";
|
||||||
import { createReferralProgram } from "tests/utils/productUtils.js";
|
import { createReferralProgram } from "@tests/utils/productUtils.js";
|
||||||
import { advanceTestClock } from "tests/utils/stripeUtils.js";
|
import { advanceTestClock } from "@tests/utils/stripeUtils.js";
|
||||||
import ctx from "tests/utils/testInitUtils/createTestContext.js";
|
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||||
import AutumnError, { AutumnInt } from "@/external/autumn/autumnCli.js";
|
import AutumnError, { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||||
import { createStripeCli } from "@/external/connect/createStripeCli.js";
|
import { createStripeCli } from "@/external/connect/createStripeCli.js";
|
||||||
import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
|
import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
|
||||||
|
|||||||
@@ -11,10 +11,10 @@ import {
|
|||||||
RewardType,
|
RewardType,
|
||||||
} from "@autumn/shared";
|
} from "@autumn/shared";
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import { TestFeature } from "tests/setup/v2Features.js";
|
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||||
import { timeout } from "tests/utils/genUtils.js";
|
import { timeout } from "@tests/utils/genUtils.js";
|
||||||
import { createReferralProgram } from "tests/utils/productUtils.js";
|
import { createReferralProgram } from "@tests/utils/productUtils.js";
|
||||||
import ctx from "tests/utils/testInitUtils/createTestContext.js";
|
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||||
import AutumnError, { AutumnInt } from "@/external/autumn/autumnCli.js";
|
import AutumnError, { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||||
import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
|
import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
|
||||||
import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js";
|
import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js";
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user