diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..cb62855ed --- /dev/null +++ b/package-lock.json @@ -0,0 +1,11005 @@ +{ + "name": "autumn", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "autumn", + "workspaces": [ + "frontend", + "server", + "shared", + "tests", + "vite" + ] + }, + "node_modules/@ai-sdk/anthropic": { + "version": "1.2.12", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "1.1.3", + "@ai-sdk/provider-utils": "2.2.8" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.0.0" + } + }, + "node_modules/@ai-sdk/provider": { + "version": "1.1.3", + "license": "Apache-2.0", + "dependencies": { + "json-schema": "^0.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ai-sdk/provider-utils": { + "version": "2.2.8", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "1.1.3", + "nanoid": "^3.3.8", + "secure-json-parse": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.23.8" + } + }, + "node_modules/@ai-sdk/react": { + "version": "1.2.12", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider-utils": "2.2.8", + "@ai-sdk/ui-utils": "1.2.11", + "swr": "^2.2.5", + "throttleit": "2.1.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "react": "^18 || ^19 || ^19.0.0-rc", + "zod": "^3.23.8" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, + "node_modules/@ai-sdk/ui-utils": { + "version": "1.2.11", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "1.1.3", + "@ai-sdk/provider-utils": "2.2.8", + "zod-to-json-schema": "^3.24.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.23.8" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@anthropic-ai/sdk": { + "version": "0.32.1", + "license": "MIT", + "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" + } + }, + "node_modules/@anthropic-ai/sdk/node_modules/@types/node": { + "version": "18.19.103", + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@anthropic-ai/sdk/node_modules/undici-types": { + "version": "5.26.5", + "license": "MIT" + }, + "node_modules/@autumn/frontend2": { + "resolved": "vite", + "link": true + }, + "node_modules/@autumn/server": { + "resolved": "server", + "link": true + }, + "node_modules/@autumn/shared": { + "resolved": "shared", + "link": true + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.27.2", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.27.1", + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.1", + "@babel/helper-compilation-targets": "^7.27.1", + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helpers": "^7.27.1", + "@babel/parser": "^7.27.1", + "@babel/template": "^7.27.1", + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/debug": { + "version": "4.4.1", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@babel/core/node_modules/ms": { + "version": "2.1.3", + "license": "MIT" + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.27.1", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.27.1", + "@babel/types": "^7.27.1", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.27.1", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.27.1", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/traverse": "^7.27.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { + "version": "6.3.1", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.27.1", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.27.1", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.27.1", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.27.1", + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.27.1", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.27.1", + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.27.2", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.1" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.11", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.27.1", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.27.1", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.1", + "@babel/parser": "^7.27.1", + "@babel/template": "^7.27.1", + "@babel/types": "^7.27.1", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/debug": { + "version": "4.4.1", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/traverse/node_modules/ms": { + "version": "2.1.3", + "license": "MIT" + }, + "node_modules/@babel/types": { + "version": "7.27.1", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@clerk/backend": { + "version": "1.33.0", + "license": "MIT", + "dependencies": { + "@clerk/shared": "^3.9.3", + "@clerk/types": "^4.59.1", + "cookie": "1.0.2", + "snakecase-keys": "8.0.1", + "tslib": "2.8.1" + }, + "engines": { + "node": ">=18.17.0" + }, + "peerDependencies": { + "svix": "^1.62.0" + }, + "peerDependenciesMeta": { + "svix": { + "optional": true + } + } + }, + "node_modules/@clerk/clerk-react": { + "version": "5.31.6", + "license": "MIT", + "dependencies": { + "@clerk/shared": "^3.9.3", + "@clerk/types": "^4.59.1", + "tslib": "2.8.1" + }, + "engines": { + "node": ">=18.17.0" + }, + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0 || ^19.0.0-0", + "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-0" + } + }, + "node_modules/@clerk/express": { + "version": "1.5.0", + "license": "MIT", + "dependencies": { + "@clerk/backend": "^1.33.0", + "@clerk/shared": "^3.9.3", + "@clerk/types": "^4.59.1", + "tslib": "2.8.1" + }, + "engines": { + "node": ">=18.17.0" + }, + "peerDependencies": { + "express": "^4.17.0 || ^5.0.0" + } + }, + "node_modules/@clerk/shared": { + "version": "3.9.3", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@clerk/types": "^4.59.1", + "dequal": "2.0.3", + "glob-to-regexp": "0.4.1", + "js-cookie": "3.0.5", + "std-env": "^3.9.0", + "swr": "^2.3.3" + }, + "engines": { + "node": ">=18.17.0" + }, + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0 || ^19.0.0-0", + "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/@clerk/types": { + "version": "4.59.1", + "license": "MIT", + "dependencies": { + "csstype": "3.1.3" + }, + "engines": { + "node": ">=18.17.0" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@date-fns/tz": { + "version": "1.2.0", + "license": "MIT" + }, + "node_modules/@date-fns/utc": { + "version": "2.1.0", + "license": "MIT" + }, + "node_modules/@drizzle-team/brocli": { + "version": "0.10.2", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@emotion/is-prop-valid": { + "version": "0.7.3", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "0.7.1" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.7.1", + "license": "MIT" + }, + "node_modules/@esbuild-kit/core-utils": { + "version": "3.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.18.20", + "source-map-support": "^0.5.21" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/darwin-arm64": { + "version": "0.18.20", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/esbuild": { + "version": "0.18.20", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" + } + }, + "node_modules/@esbuild-kit/esm-loader": { + "version": "2.6.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@esbuild-kit/core-utils": "^3.3.2", + "get-tsconfig": "^4.7.0" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.4", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.20.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "1.1.11", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/config-array/node_modules/debug": { + "version": "4.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/config-array/node_modules/ms": { + "version": "2.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@eslint/config-helpers": { + "version": "0.2.2", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.14.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.11", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/debug": { + "version": "4.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ms": { + "version": "2.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@eslint/js": { + "version": "9.27.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.3.1", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.14.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.7.0", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.9" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.0", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.0", + "@floating-ui/utils": "^0.2.9" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.2", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.9", + "license": "MIT" + }, + "node_modules/@fortawesome/fontawesome-common-types": { + "version": "6.7.2", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/fontawesome-svg-core": { + "version": "6.7.2", + "license": "MIT", + "peer": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.7.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-brands-svg-icons": { + "version": "6.7.2", + "license": "(CC-BY-4.0 AND MIT)", + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.7.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/react-fontawesome": { + "version": "0.2.2", + "license": "MIT", + "dependencies": { + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "@fortawesome/fontawesome-svg-core": "~1 || ~6", + "react": ">=16.3" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@inquirer/checkbox": { + "version": "4.1.6", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.11", + "@inquirer/figures": "^1.0.11", + "@inquirer/type": "^3.0.6", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/confirm": { + "version": "5.1.10", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.11", + "@inquirer/type": "^3.0.6" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core": { + "version": "10.1.11", + "license": "MIT", + "dependencies": { + "@inquirer/figures": "^1.0.11", + "@inquirer/type": "^3.0.6", + "ansi-escapes": "^4.3.2", + "cli-width": "^4.1.0", + "mute-stream": "^2.0.0", + "signal-exit": "^4.1.0", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core/node_modules/ansi-regex": { + "version": "5.0.1", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@inquirer/core/node_modules/ansi-styles": { + "version": "4.3.0", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@inquirer/core/node_modules/emoji-regex": { + "version": "8.0.0", + "license": "MIT" + }, + "node_modules/@inquirer/core/node_modules/string-width": { + "version": "4.2.3", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@inquirer/core/node_modules/strip-ansi": { + "version": "6.0.1", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@inquirer/core/node_modules/wrap-ansi": { + "version": "6.2.0", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@inquirer/editor": { + "version": "4.2.11", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.11", + "@inquirer/type": "^3.0.6", + "external-editor": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/expand": { + "version": "4.0.13", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.11", + "@inquirer/type": "^3.0.6", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.11", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/input": { + "version": "4.1.10", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.11", + "@inquirer/type": "^3.0.6" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/number": { + "version": "3.0.13", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.11", + "@inquirer/type": "^3.0.6" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/password": { + "version": "4.0.13", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.11", + "@inquirer/type": "^3.0.6", + "ansi-escapes": "^4.3.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/prompts": { + "version": "7.5.1", + "license": "MIT", + "dependencies": { + "@inquirer/checkbox": "^4.1.6", + "@inquirer/confirm": "^5.1.10", + "@inquirer/editor": "^4.2.11", + "@inquirer/expand": "^4.0.13", + "@inquirer/input": "^4.1.10", + "@inquirer/number": "^3.0.13", + "@inquirer/password": "^4.0.13", + "@inquirer/rawlist": "^4.1.1", + "@inquirer/search": "^3.0.13", + "@inquirer/select": "^4.2.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/rawlist": { + "version": "4.1.1", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.11", + "@inquirer/type": "^3.0.6", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/search": { + "version": "3.0.13", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.11", + "@inquirer/figures": "^1.0.11", + "@inquirer/type": "^3.0.6", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/select": { + "version": "4.2.1", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.11", + "@inquirer/figures": "^1.0.11", + "@inquirer/type": "^3.0.6", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/type": { + "version": "3.0.6", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@ioredis/commands": { + "version": "1.2.0", + "license": "MIT" + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@logtail/core": { + "version": "0.5.4", + "license": "ISC", + "dependencies": { + "@logtail/tools": "^0.5.4", + "@logtail/types": "^0.5.3", + "serialize-error": "8.1.0" + } + }, + "node_modules/@logtail/node": { + "version": "0.5.5", + "license": "ISC", + "dependencies": { + "@logtail/core": "^0.5.4", + "@logtail/types": "^0.5.3", + "@msgpack/msgpack": "^2.5.1", + "@types/stack-trace": "^0.0.33", + "minimatch": "^9.0.5", + "stack-trace": "0.0.10" + } + }, + "node_modules/@logtail/tools": { + "version": "0.5.4", + "license": "ISC", + "dependencies": { + "@logtail/types": "^0.5.3" + } + }, + "node_modules/@logtail/types": { + "version": "0.5.3", + "license": "ISC" + }, + "node_modules/@msgpack/msgpack": { + "version": "2.8.0", + "license": "ISC", + "engines": { + "node": ">= 10" + } + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { + "version": "3.0.3", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@paralleldrive/cuid2": { + "version": "2.2.2", + "license": "MIT", + "dependencies": { + "@noble/hashes": "^1.1.5" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@puppeteer/browsers": { + "version": "2.10.5", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "debug": "^4.4.1", + "extract-zip": "^2.0.1", + "progress": "^2.0.3", + "proxy-agent": "^6.5.0", + "semver": "^7.7.2", + "tar-fs": "^3.0.8", + "yargs": "^17.7.2" + }, + "bin": { + "browsers": "lib/cjs/main-cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@puppeteer/browsers/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@puppeteer/browsers/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@puppeteer/browsers/node_modules/cliui": { + "version": "8.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@puppeteer/browsers/node_modules/debug": { + "version": "4.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@puppeteer/browsers/node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@puppeteer/browsers/node_modules/ms": { + "version": "2.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@puppeteer/browsers/node_modules/string-width": { + "version": "4.2.3", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@puppeteer/browsers/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@puppeteer/browsers/node_modules/wrap-ansi": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@puppeteer/browsers/node_modules/y18n": { + "version": "5.0.8", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/@puppeteer/browsers/node_modules/yargs": { + "version": "17.7.2", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@radix-ui/number": { + "version": "1.1.1", + "license": "MIT" + }, + "node_modules/@radix-ui/primitive": { + "version": "1.1.2", + "license": "MIT" + }, + "node_modules/@radix-ui/react-accordion": { + "version": "1.2.11", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-collapsible": "1.1.11", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-arrow": { + "version": "1.1.7", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3" + }, + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-checkbox": { + "version": "1.3.2", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" + }, + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collapsible": { + "version": "1.1.11", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collection": { + "version": "1.1.7", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3" + }, + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.1.14", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.10", + "@radix-ui/react-focus-guards": "1.1.2", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-direction": { + "version": "1.1.1", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.10", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-escape-keydown": "1.1.1" + }, + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dropdown-menu": { + "version": "2.1.15", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-menu": "2.1.15", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.2", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.7", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.1.1", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menu": { + "version": "2.1.15", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.10", + "@radix-ui/react-focus-guards": "1.1.2", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.7", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.10", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover": { + "version": "1.1.14", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.10", + "@radix-ui/react-focus-guards": "1.1.2", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.7", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popper": { + "version": "1.2.7", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-rect": "1.1.1", + "@radix-ui/react-use-size": "1.1.1", + "@radix-ui/rect": "1.1.1" + }, + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-portal": { + "version": "1.1.9", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.1.4", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-roving-focus": { + "version": "1.1.10", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-scroll-area": { + "version": "1.2.9", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.1", + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-select": { + "version": "2.2.5", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.1", + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.10", + "@radix-ui/react-focus-guards": "1.1.2", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.7", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-visually-hidden": "1.2.3", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-separator": { + "version": "1.1.7", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3" + }, + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-switch": { + "version": "1.2.5", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" + }, + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs": { + "version": "1.1.12", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.10", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip": { + "version": "1.2.7", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.10", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.7", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-visually-hidden": "1.2.3" + }, + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.1", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.2", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-effect-event": "0.0.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.2", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.1.1", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.1", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-previous": { + "version": "1.1.1", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-rect": { + "version": "1.1.1", + "license": "MIT", + "dependencies": { + "@radix-ui/rect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-size": { + "version": "1.1.1", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-visually-hidden": { + "version": "1.2.3", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3" + }, + "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" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/rect": { + "version": "1.1.1", + "license": "MIT" + }, + "node_modules/@react-email/render": { + "version": "1.0.6", + "license": "MIT", + "dependencies": { + "html-to-text": "9.0.5", + "prettier": "3.5.3", + "react-promise-suspense": "0.3.4" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.9", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.41.0", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@selderee/plugin-htmlparser2": { + "version": "0.11.0", + "license": "MIT", + "dependencies": { + "domhandler": "^5.0.3", + "selderee": "^0.11.0" + }, + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, + "node_modules/@stablelib/base64": { + "version": "1.0.1", + "license": "MIT" + }, + "node_modules/@supabase/auth-js": { + "version": "2.69.1", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/functions-js": { + "version": "2.4.4", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/node-fetch": { + "version": "2.6.15", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + } + }, + "node_modules/@supabase/postgrest-js": { + "version": "1.19.4", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/realtime-js": { + "version": "2.11.2", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14", + "@types/phoenix": "^1.5.4", + "@types/ws": "^8.5.10", + "ws": "^8.18.0" + } + }, + "node_modules/@supabase/ssr": { + "version": "0.5.2", + "license": "MIT", + "dependencies": { + "@types/cookie": "^0.6.0", + "cookie": "^0.7.0" + }, + "peerDependencies": { + "@supabase/supabase-js": "^2.43.4" + } + }, + "node_modules/@supabase/ssr/node_modules/cookie": { + "version": "0.7.2", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@supabase/storage-js": { + "version": "2.7.1", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/supabase-js": { + "version": "2.49.8", + "license": "MIT", + "dependencies": { + "@supabase/auth-js": "2.69.1", + "@supabase/functions-js": "2.4.4", + "@supabase/node-fetch": "2.6.15", + "@supabase/postgrest-js": "1.19.4", + "@supabase/realtime-js": "2.11.2", + "@supabase/storage-js": "2.7.1" + } + }, + "node_modules/@tailwindcss/node": { + "version": "4.1.7", + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.3.0", + "enhanced-resolve": "^5.18.1", + "jiti": "^2.4.2", + "lightningcss": "1.30.1", + "magic-string": "^0.30.17", + "source-map-js": "^1.2.1", + "tailwindcss": "4.1.7" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.1.7", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.4", + "tar": "^7.4.3" + }, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.1.7", + "@tailwindcss/oxide-darwin-arm64": "4.1.7", + "@tailwindcss/oxide-darwin-x64": "4.1.7", + "@tailwindcss/oxide-freebsd-x64": "4.1.7", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.7", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.7", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.7", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.7", + "@tailwindcss/oxide-linux-x64-musl": "4.1.7", + "@tailwindcss/oxide-wasm32-wasi": "4.1.7", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.7", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.7" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.1.7", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.1.7", + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.1.7", + "@tailwindcss/oxide": "4.1.7", + "tailwindcss": "4.1.7" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6" + } + }, + "node_modules/@tanstack/query-core": { + "version": "5.76.2", + "license": "MIT", + "peer": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.76.2", + "license": "MIT", + "peer": true, + "dependencies": { + "@tanstack/query-core": "5.76.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, + "node_modules/@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.5", + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/chai": { + "version": "5.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*" + } + }, + "node_modules/@types/chai-http": { + "version": "3.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "*", + "@types/node": "*", + "@types/superagent": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "license": "MIT" + }, + "node_modules/@types/cookiejar": { + "version": "2.1.5", + "license": "MIT" + }, + "node_modules/@types/cors": { + "version": "2.8.18", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/diff-match-patch": { + "version": "1.0.36", + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.7", + "license": "MIT" + }, + "node_modules/@types/express": { + "version": "5.0.2", + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.0.6", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/hast": { + "version": "3.0.4", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/lodash": { + "version": "4.17.17", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/lodash-es": { + "version": "4.17.12", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/lodash": "*" + } + }, + "node_modules/@types/luxon": { + "version": "3.4.2", + "license": "MIT" + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/methods": { + "version": "1.1.4", + "license": "MIT" + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "license": "MIT" + }, + "node_modules/@types/mocha": { + "version": "10.0.10", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.15.21", + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/node-fetch": { + "version": "2.6.12", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.0" + } + }, + "node_modules/@types/pg": { + "version": "8.15.2", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^4.0.1" + } + }, + "node_modules/@types/phoenix": { + "version": "1.6.6", + "license": "MIT" + }, + "node_modules/@types/prismjs": { + "version": "1.26.5", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/prop-types": { + "version": "15.7.14", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@types/qs": { + "version": "6.14.0", + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "18.3.22", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.7", + "devOptional": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, + "node_modules/@types/send": { + "version": "0.17.4", + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.7", + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, + "node_modules/@types/stack-trace": { + "version": "0.0.33", + "license": "MIT" + }, + "node_modules/@types/superagent": { + "version": "8.1.9", + "license": "MIT", + "dependencies": { + "@types/cookiejar": "^2.1.5", + "@types/methods": "^1.1.4", + "@types/node": "*", + "form-data": "^4.0.0" + } + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "license": "MIT" + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.32.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.32.1", + "@typescript-eslint/type-utils": "8.32.1", + "@typescript-eslint/utils": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.32.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.32.1", + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/typescript-estree": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/debug": { + "version": "4.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser/node_modules/ms": { + "version": "2.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.32.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.32.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "8.32.1", + "@typescript-eslint/utils": "8.32.1", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/debug": { + "version": "4.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/ms": { + "version": "2.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/types": { + "version": "8.32.1", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.32.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/debug": { + "version": "4.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/ms": { + "version": "2.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.32.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.32.1", + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/typescript-estree": "8.32.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.32.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.32.1", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "license": "ISC" + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.26.10", + "@babel/plugin-transform-react-jsx-self": "^7.25.9", + "@babel/plugin-transform-react-jsx-source": "^7.25.9", + "@rolldown/pluginutils": "1.0.0-beta.9", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" + } + }, + "node_modules/@wooorm/starry-night": { + "version": "3.7.0", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "import-meta-resolve": "^4.0.0", + "vscode-oniguruma": "^2.0.0", + "vscode-textmate": "^9.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.14.1", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/agentkeepalive": { + "version": "4.6.0", + "license": "MIT", + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/ai": { + "version": "4.3.16", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "1.1.3", + "@ai-sdk/provider-utils": "2.2.8", + "@ai-sdk/react": "1.2.12", + "@ai-sdk/ui-utils": "1.2.11", + "@opentelemetry/api": "1.9.0", + "jsondiffpatch": "0.6.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "react": "^18 || ^19 || ^19.0.0-rc", + "zod": "^3.23.8" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + } + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/aria-hidden": { + "version": "1.2.6", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "license": "MIT" + }, + "node_modules/array-union": { + "version": "2.1.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "license": "MIT" + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/ast-types": { + "version": "0.13.4", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/async-listen": { + "version": "3.1.0", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "license": "MIT" + }, + "node_modules/atomic-sleep": { + "version": "1.0.0", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/autumn-js": { + "version": "0.0.41", + "license": "MIT", + "dependencies": { + "rou3": "^0.6.1" + }, + "peerDependencies": { + "@tanstack/react-query": "^5.76.1", + "react": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + } + } + }, + "node_modules/axios": { + "version": "1.9.0", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/b4a": { + "version": "1.6.7", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "license": "MIT" + }, + "node_modules/bare-events": { + "version": "2.5.4", + "dev": true, + "license": "Apache-2.0", + "optional": true + }, + "node_modules/bare-fs": { + "version": "4.1.5", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "bare-events": "^2.5.4", + "bare-path": "^3.0.0", + "bare-stream": "^2.6.4" + }, + "engines": { + "bare": ">=1.16.0" + }, + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } + } + }, + "node_modules/bare-os": { + "version": "3.6.1", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "engines": { + "bare": ">=1.14.0" + } + }, + "node_modules/bare-path": { + "version": "3.0.0", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "bare-os": "^3.0.1" + } + }, + "node_modules/bare-stream": { + "version": "2.6.5", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "streamx": "^2.21.0" + }, + "peerDependencies": { + "bare-buffer": "*", + "bare-events": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + }, + "bare-events": { + "optional": true + } + } + }, + "node_modules/base-convert-int-array": { + "version": "1.0.1", + "license": "ISC" + }, + "node_modules/basic-ftp": { + "version": "5.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/body-parser": { + "version": "1.20.3", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "dev": true, + "license": "ISC" + }, + "node_modules/browserslist": { + "version": "4.24.5", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001716", + "electron-to-chromium": "^1.5.149", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/bullmq": { + "version": "5.53.0", + "license": "MIT", + "dependencies": { + "cron-parser": "^4.9.0", + "ioredis": "^5.4.1", + "msgpackr": "^1.11.2", + "node-abort-controller": "^3.1.1", + "semver": "^7.5.4", + "tslib": "^2.0.0", + "uuid": "^9.0.0" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001718", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/ccount": { + "version": "2.0.1", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chai": { + "version": "5.2.0", + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/chai-http": { + "version": "5.1.2", + "license": "MIT", + "dependencies": { + "@types/superagent": "^8.1.7", + "charset": "^1.0.1", + "cookiejar": "^2.1.4", + "is-ip": "^5.0.1", + "methods": "^1.1.2", + "qs": "^6.12.1", + "superagent": "^10.0.0" + }, + "engines": { + "node": ">=18.20.0" + } + }, + "node_modules/chalk": { + "version": "5.4.1", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "license": "MIT" + }, + "node_modules/charset": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/check-error": { + "version": "2.1.1", + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/chownr": { + "version": "3.0.0", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/chromium-bidi": { + "version": "5.1.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "mitt": "^3.0.1", + "zod": "^3.24.1" + }, + "peerDependencies": { + "devtools-protocol": "*" + } + }, + "node_modules/class-variance-authority": { + "version": "0.7.1", + "license": "Apache-2.0", + "dependencies": { + "clsx": "^2.1.1" + }, + "funding": { + "url": "https://polar.sh/cva" + } + }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "4.1.0", + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, + "node_modules/clone-regexp": { + "version": "3.0.0", + "license": "MIT", + "dependencies": { + "is-regexp": "^3.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/cluster-key-slot": { + "version": "1.1.2", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cmdk": { + "version": "1.1.1", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "^1.1.1", + "@radix-ui/react-dialog": "^1.1.6", + "@radix-ui/react-id": "^1.1.0", + "@radix-ui/react-primitive": "^2.0.2" + }, + "peerDependencies": { + "react": "^18 || ^19 || ^19.0.0-rc", + "react-dom": "^18 || ^19 || ^19.0.0-rc" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "license": "MIT" + }, + "node_modules/colorette": { + "version": "2.0.20", + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/commander": { + "version": "12.1.0", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/component-emitter": { + "version": "1.3.1", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "license": "MIT" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-hrtime": { + "version": "5.0.0", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "license": "MIT" + }, + "node_modules/cookie": { + "version": "1.0.2", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "license": "MIT" + }, + "node_modules/cookiejar": { + "version": "2.1.4", + "license": "MIT" + }, + "node_modules/core-js": { + "version": "3.42.0", + "hasInstallScript": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/cors": { + "version": "2.8.5", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cosmiconfig": { + "version": "9.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/cron": { + "version": "3.5.0", + "license": "MIT", + "dependencies": { + "@types/luxon": "~3.4.0", + "luxon": "~3.5.0" + } + }, + "node_modules/cron-parser": { + "version": "4.9.0", + "license": "MIT", + "dependencies": { + "luxon": "^3.2.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-jss": { + "version": "10.10.0", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "^10.10.0", + "jss-preset-default": "^10.10.0" + } + }, + "node_modules/css-vendor": { + "version": "2.0.8", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.3", + "is-in-browser": "^1.0.2" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "license": "MIT" + }, + "node_modules/csv-parse": { + "version": "5.6.0", + "license": "MIT" + }, + "node_modules/csv-parser": { + "version": "3.2.0", + "license": "MIT", + "bin": { + "csv-parser": "bin/csv-parser" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/currency-symbol-map": { + "version": "5.1.0", + "license": "BSD-2-Clause" + }, + "node_modules/data-uri-to-buffer": { + "version": "6.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/date-fns": { + "version": "3.6.0", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, + "node_modules/dateformat": { + "version": "4.6.3", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/decimal.js": { + "version": "10.5.0", + "license": "MIT" + }, + "node_modules/deep-eql": { + "version": "5.0.2", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/degenerator": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ast-types": "^0.13.4", + "escodegen": "^2.1.0", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/denque": { + "version": "2.1.0", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "2.0.4", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "license": "MIT" + }, + "node_modules/devlop": { + "version": "1.1.0", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/devtools-protocol": { + "version": "0.0.1439962", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/dezalgo": { + "version": "1.0.4", + "license": "ISC", + "dependencies": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "node_modules/diff": { + "version": "7.0.0", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff-match-patch": { + "version": "1.0.5", + "license": "Apache-2.0" + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dot-case": { + "version": "3.0.4", + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/dotenv": { + "version": "16.5.0", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/drizzle-kit": { + "version": "0.31.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@drizzle-team/brocli": "^0.10.2", + "@esbuild-kit/esm-loader": "^2.5.5", + "esbuild": "^0.25.2", + "esbuild-register": "^3.5.0" + }, + "bin": { + "drizzle-kit": "bin.cjs" + } + }, + "node_modules/drizzle-orm": { + "version": "0.43.1", + "license": "Apache-2.0", + "peerDependencies": { + "@aws-sdk/client-rds-data": ">=3", + "@cloudflare/workers-types": ">=4", + "@electric-sql/pglite": ">=0.2.0", + "@libsql/client": ">=0.10.0", + "@libsql/client-wasm": ">=0.10.0", + "@neondatabase/serverless": ">=0.10.0", + "@op-engineering/op-sqlite": ">=2", + "@opentelemetry/api": "^1.4.1", + "@planetscale/database": ">=1.13", + "@prisma/client": "*", + "@tidbcloud/serverless": "*", + "@types/better-sqlite3": "*", + "@types/pg": "*", + "@types/sql.js": "*", + "@vercel/postgres": ">=0.8.0", + "@xata.io/client": "*", + "better-sqlite3": ">=7", + "bun-types": "*", + "expo-sqlite": ">=14.0.0", + "gel": ">=2", + "knex": "*", + "kysely": "*", + "mysql2": ">=2", + "pg": ">=8", + "postgres": ">=3", + "sql.js": ">=1", + "sqlite3": ">=5" + }, + "peerDependenciesMeta": { + "@aws-sdk/client-rds-data": { + "optional": true + }, + "@cloudflare/workers-types": { + "optional": true + }, + "@electric-sql/pglite": { + "optional": true + }, + "@libsql/client": { + "optional": true + }, + "@libsql/client-wasm": { + "optional": true + }, + "@neondatabase/serverless": { + "optional": true + }, + "@op-engineering/op-sqlite": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@planetscale/database": { + "optional": true + }, + "@prisma/client": { + "optional": true + }, + "@tidbcloud/serverless": { + "optional": true + }, + "@types/better-sqlite3": { + "optional": true + }, + "@types/pg": { + "optional": true + }, + "@types/sql.js": { + "optional": true + }, + "@vercel/postgres": { + "optional": true + }, + "@xata.io/client": { + "optional": true + }, + "better-sqlite3": { + "optional": true + }, + "bun-types": { + "optional": true + }, + "expo-sqlite": { + "optional": true + }, + "gel": { + "optional": true + }, + "knex": { + "optional": true + }, + "kysely": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "pg": { + "optional": true + }, + "postgres": { + "optional": true + }, + "prisma": { + "optional": true + }, + "sql.js": { + "optional": true + }, + "sqlite3": { + "optional": true + } + } + }, + "node_modules/drizzle-zod": { + "version": "0.8.2", + "license": "Apache-2.0", + "peerDependencies": { + "drizzle-orm": ">=0.36.0", + "zod": "^3.25.1" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/ee-first": { + "version": "1.1.1", + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.157", + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "dev": true, + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.18.1", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es6-promise": { + "version": "4.2.8", + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.25.4", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "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" + } + }, + "node_modules/esbuild-register": { + "version": "3.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "peerDependencies": { + "esbuild": ">=0.12 <1" + } + }, + "node_modules/esbuild-register/node_modules/debug": { + "version": "4.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/esbuild-register/node_modules/ms": { + "version": "2.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/escalade": { + "version": "3.2.0", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/escodegen": { + "version": "2.1.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/eslint": { + "version": "9.27.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.20.0", + "@eslint/config-helpers": "^0.2.1", + "@eslint/core": "^0.14.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.27.0", + "@eslint/plugin-kit": "^0.3.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.3.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.20", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=8.40" + } + }, + "node_modules/eslint-scope": { + "version": "8.3.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.11", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/debug": { + "version": "4.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint/node_modules/ms": { + "version": "2.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/espree": { + "version": "10.3.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/express": { + "version": "4.21.2", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express/node_modules/cookie": { + "version": "0.7.1", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/external-editor": { + "version": "3.1.0", + "license": "MIT", + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/extract-zip/node_modules/debug": { + "version": "4.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/extract-zip/node_modules/get-stream": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/extract-zip/node_modules/ms": { + "version": "2.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-copy": { + "version": "3.0.2", + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-redact": { + "version": "3.5.0", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "license": "MIT" + }, + "node_modules/fast-sha256": { + "version": "1.3.0", + "license": "Unlicense" + }, + "node_modules/fastq": { + "version": "1.19.1", + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/fetch-retry": { + "version": "6.0.0", + "license": "MIT" + }, + "node_modules/fflate": { + "version": "0.4.8", + "license": "MIT" + }, + "node_modules/figures": { + "version": "6.1.0", + "license": "MIT", + "dependencies": { + "is-unicode-supported": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/figures/node_modules/is-unicode-supported": { + "version": "2.1.0", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "dev": true, + "license": "ISC" + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.2", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data-encoder": { + "version": "1.7.2", + "license": "MIT" + }, + "node_modules/formdata-node": { + "version": "4.4.1", + "license": "MIT", + "dependencies": { + "node-domexception": "1.0.0", + "web-streams-polyfill": "4.0.0-beta.3" + }, + "engines": { + "node": ">= 12.20" + } + }, + "node_modules/formidable": { + "version": "3.5.4", + "license": "MIT", + "dependencies": { + "@paralleldrive/cuid2": "^2.2.2", + "dezalgo": "^1.0.4", + "once": "^1.4.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/framer-motion": { + "version": "12.16.0", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.16.0.tgz", + "integrity": "sha512-xryrmD4jSBQrS2IkMdcTmiS4aSKckbS7kLDCuhUn9110SQKG1w3zlq1RTqCblewg+ZYe+m3sdtzQA6cRwo5g8Q==", + "license": "MIT", + "dependencies": { + "motion-dom": "^12.16.0", + "motion-utils": "^12.12.1", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function-timeout": { + "version": "0.1.1", + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.3.0", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-nonce": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-tsconfig": { + "version": "4.10.1", + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/get-uri": { + "version": "6.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "basic-ftp": "^5.0.2", + "data-uri-to-buffer": "^6.0.2", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/get-uri/node_modules/debug": { + "version": "4.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/get-uri/node_modules/ms": { + "version": "2.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/glob": { + "version": "10.4.5", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "license": "BSD-2-Clause" + }, + "node_modules/globals": { + "version": "15.15.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "license": "MIT", + "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" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/goober": { + "version": "2.1.16", + "license": "MIT", + "peerDependencies": { + "csstype": "^3.0.10" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "license": "ISC" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hast-util-to-html": { + "version": "9.0.5", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/he": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/help-me": { + "version": "5.0.0", + "license": "MIT" + }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/html-to-text": { + "version": "9.0.5", + "license": "MIT", + "dependencies": { + "@selderee/plugin-htmlparser2": "^0.11.0", + "deepmerge": "^4.3.1", + "dom-serializer": "^2.0.0", + "htmlparser2": "^8.0.2", + "selderee": "^0.11.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/htmlparser2": { + "version": "8.0.2", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/http-proxy-agent/node_modules/debug": { + "version": "4.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/http-proxy-agent/node_modules/ms": { + "version": "2.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/http-status-codes": { + "version": "2.3.0", + "license": "MIT" + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/https-proxy-agent/node_modules/ms": { + "version": "2.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "license": "MIT", + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/hyphenate-style-name": { + "version": "1.1.0", + "license": "BSD-3-Clause" + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/iframe-resizer": { + "version": "4.4.5", + "hasInstallScript": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + }, + "funding": { + "type": "individual", + "url": "https://iframe-resizer.com//pricing" + } + }, + "node_modules/iframe-resizer-react": { + "version": "1.1.1", + "license": "MIT", + "dependencies": { + "@babel/plugin-proposal-private-property-in-object": "^7.21.11", + "iframe-resizer": "^4.4.4", + "warning": "^4.0.3" + }, + "engines": { + "node": ">=16", + "npm": ">=5" + }, + "funding": { + "type": "individual", + "url": "https://iframe-resizer.com/licenses/" + }, + "peerDependencies": { + "prop-types": "^15.7.2", + "react": "^16.13.1 || ^18.0.0", + "react-dom": "^16.13.1 || ^18.0.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "license": "ISC" + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-meta-resolve": { + "version": "4.1.0", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "license": "ISC" + }, + "node_modules/inquirer": { + "version": "12.6.1", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.11", + "@inquirer/prompts": "^7.5.1", + "@inquirer/type": "^3.0.6", + "ansi-escapes": "^4.3.2", + "mute-stream": "^2.0.0", + "run-async": "^3.0.0", + "rxjs": "^7.8.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/ioredis": { + "version": "5.6.1", + "license": "MIT", + "dependencies": { + "@ioredis/commands": "^1.1.1", + "cluster-key-slot": "^1.1.0", + "debug": "^4.3.4", + "denque": "^2.1.0", + "lodash.defaults": "^4.2.0", + "lodash.isarguments": "^3.1.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0", + "standard-as-callback": "^2.1.0" + }, + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ioredis" + } + }, + "node_modules/ioredis/node_modules/debug": { + "version": "4.4.1", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/ioredis/node_modules/ms": { + "version": "2.1.3", + "license": "MIT" + }, + "node_modules/ip-address": { + "version": "9.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/ip-regex": { + "version": "5.0.0", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "dev": true, + "license": "MIT" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-in-browser": { + "version": "1.1.3", + "license": "MIT" + }, + "node_modules/is-interactive": { + "version": "2.0.0", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-ip": { + "version": "5.0.1", + "license": "MIT", + "dependencies": { + "ip-regex": "^5.0.0", + "super-regex": "^0.2.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-regexp": { + "version": "3.1.0", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jiti": { + "version": "2.4.2", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/joycon": { + "version": "3.1.1", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/js-cookie": { + "version": "3.0.5", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsbn": { + "version": "1.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema": { + "version": "0.4.0", + "license": "(AFL-2.1 OR BSD-3-Clause)" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsondiffpatch": { + "version": "0.6.0", + "license": "MIT", + "dependencies": { + "@types/diff-match-patch": "^1.0.36", + "chalk": "^5.3.0", + "diff-match-patch": "^1.0.5" + }, + "bin": { + "jsondiffpatch": "bin/jsondiffpatch.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/jss": { + "version": "10.10.0", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.3.1", + "csstype": "^3.0.2", + "is-in-browser": "^1.1.3", + "tiny-warning": "^1.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/jss" + } + }, + "node_modules/jss-plugin-camel-case": { + "version": "10.10.0", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.3.1", + "hyphenate-style-name": "^1.0.3", + "jss": "10.10.0" + } + }, + "node_modules/jss-plugin-compose": { + "version": "10.10.0", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0", + "tiny-warning": "^1.0.2" + } + }, + "node_modules/jss-plugin-default-unit": { + "version": "10.10.0", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0" + } + }, + "node_modules/jss-plugin-expand": { + "version": "10.10.0", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0" + } + }, + "node_modules/jss-plugin-extend": { + "version": "10.10.0", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0", + "tiny-warning": "^1.0.2" + } + }, + "node_modules/jss-plugin-global": { + "version": "10.10.0", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0" + } + }, + "node_modules/jss-plugin-nested": { + "version": "10.10.0", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0", + "tiny-warning": "^1.0.2" + } + }, + "node_modules/jss-plugin-props-sort": { + "version": "10.10.0", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0" + } + }, + "node_modules/jss-plugin-rule-value-function": { + "version": "10.10.0", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0", + "tiny-warning": "^1.0.2" + } + }, + "node_modules/jss-plugin-rule-value-observable": { + "version": "10.10.0", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0", + "symbol-observable": "^1.2.0" + } + }, + "node_modules/jss-plugin-template": { + "version": "10.10.0", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0", + "tiny-warning": "^1.0.2" + } + }, + "node_modules/jss-plugin-vendor-prefixer": { + "version": "10.10.0", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.3.1", + "css-vendor": "^2.0.8", + "jss": "10.10.0" + } + }, + "node_modules/jss-preset-default": { + "version": "10.10.0", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0", + "jss-plugin-camel-case": "10.10.0", + "jss-plugin-compose": "10.10.0", + "jss-plugin-default-unit": "10.10.0", + "jss-plugin-expand": "10.10.0", + "jss-plugin-extend": "10.10.0", + "jss-plugin-global": "10.10.0", + "jss-plugin-nested": "10.10.0", + "jss-plugin-props-sort": "10.10.0", + "jss-plugin-rule-value-function": "10.10.0", + "jss-plugin-rule-value-observable": "10.10.0", + "jss-plugin-template": "10.10.0", + "jss-plugin-vendor-prefixer": "10.10.0" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/ksuid": { + "version": "3.0.0", + "license": "MIT", + "dependencies": { + "base-convert-int-array": "^1.0.1" + } + }, + "node_modules/leac": { + "version": "0.6.0", + "license": "MIT", + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.30.1", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.30.1", + "lightningcss-darwin-x64": "1.30.1", + "lightningcss-freebsd-x64": "1.30.1", + "lightningcss-linux-arm-gnueabihf": "1.30.1", + "lightningcss-linux-arm64-gnu": "1.30.1", + "lightningcss-linux-arm64-musl": "1.30.1", + "lightningcss-linux-x64-gnu": "1.30.1", + "lightningcss-linux-x64-musl": "1.30.1", + "lightningcss-win32-arm64-msvc": "1.30.1", + "lightningcss-win32-x64-msvc": "1.30.1" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.30.1", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "license": "MIT" + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "license": "MIT" + }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "license": "MIT" + }, + "node_modules/lodash.isarguments": { + "version": "3.1.0", + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "dev": true, + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/loupe": { + "version": "3.1.3", + "license": "MIT" + }, + "node_modules/lower-case": { + "version": "2.0.2", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lucide-react": { + "version": "0.511.0", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/luxon": { + "version": "3.5.0", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/magic-string": { + "version": "0.30.17", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "dev": true, + "license": "ISC" + }, + "node_modules/map-obj": { + "version": "4.3.0", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.0", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "3.0.2", + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mitt": { + "version": "3.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/mkdirp": { + "version": "3.0.1", + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha": { + "version": "11.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "browser-stdout": "^1.3.1", + "chokidar": "^4.0.1", + "debug": "^4.3.5", + "diff": "^7.0.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "picocolors": "^1.1.1", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/mocha/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/mocha/node_modules/cliui": { + "version": "8.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/mocha/node_modules/debug": { + "version": "4.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/mocha/node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "5.1.6", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/mocha/node_modules/string-width": { + "version": "4.2.3", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/wrap-ansi": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/mocha/node_modules/y18n": { + "version": "5.0.8", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/yargs": { + "version": "17.7.2", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/motion": { + "version": "12.16.0", + "resolved": "https://registry.npmjs.org/motion/-/motion-12.16.0.tgz", + "integrity": "sha512-P3HA83fnPMEGBLfKdD5vDdjH1Aa3wM3jT3+HX3fCVpy/4/lJiqvABajLgZenBu+rzkFzmeaPkvT7ouf9Tq5tVQ==", + "license": "MIT", + "dependencies": { + "framer-motion": "^12.16.0", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/motion-dom": { + "version": "12.16.0", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.16.0.tgz", + "integrity": "sha512-Z2nGwWrrdH4egLEtgYMCEN4V2qQt1qxlKy/uV7w691ztyA41Q5Rbn0KNGbsNVDZr9E8PD2IOQ3hSccRnB6xWzw==", + "license": "MIT", + "dependencies": { + "motion-utils": "^12.12.1" + } + }, + "node_modules/motion-utils": { + "version": "12.12.1", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.12.1.tgz", + "integrity": "sha512-f9qiqUHm7hWSLlNW8gS9pisnsN7CRFRD58vNjptKdsqFLpkVnX00TNeD6Q0d27V9KzT7ySFyK1TZ/DShfVOv6w==", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.0.0", + "license": "MIT" + }, + "node_modules/msgpackr": { + "version": "1.11.4", + "license": "MIT", + "optionalDependencies": { + "msgpackr-extract": "^3.0.2" + } + }, + "node_modules/msgpackr-extract": { + "version": "3.0.3", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-gyp-build-optional-packages": "5.2.2" + }, + "bin": { + "download-msgpackr-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" + } + }, + "node_modules/mute-stream": { + "version": "2.0.0", + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/mylas": { + "version": "2.1.13", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/raouldeheer" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/netmask": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/next-themes": { + "version": "0.4.6", + "license": "MIT", + "peerDependencies": { + "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" + } + }, + "node_modules/no-case": { + "version": "3.0.4", + "license": "MIT", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/node-abort-controller": { + "version": "3.1.1", + "license": "MIT" + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-gyp-build-optional-packages": { + "version": "5.2.2", + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.1" + }, + "bin": { + "node-gyp-build-optional-packages": "bin.js", + "node-gyp-build-optional-packages-optional": "optional.js", + "node-gyp-build-optional-packages-test": "build-test.js" + } + }, + "node_modules/node-releases": { + "version": "2.0.19", + "license": "MIT" + }, + "node_modules/nodemon": { + "version": "3.1.10", + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/brace-expansion": { + "version": "1.1.11", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/nodemon/node_modules/chokidar": { + "version": "3.6.0", + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/nodemon/node_modules/debug": { + "version": "4.4.1", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/nodemon/node_modules/glob-parent": { + "version": "5.1.2", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/nodemon/node_modules/has-flag": { + "version": "3.0.0", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/nodemon/node_modules/minimatch": { + "version": "3.1.2", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/nodemon/node_modules/ms": { + "version": "2.1.3", + "license": "MIT" + }, + "node_modules/nodemon/node_modules/readdirp": { + "version": "3.6.0", + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/nodemon/node_modules/supports-color": { + "version": "5.5.0", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obuf": { + "version": "1.1.2", + "devOptional": true, + "license": "MIT" + }, + "node_modules/on-exit-leak-free": { + "version": "2.1.2", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/openai": { + "version": "4.103.0", + "license": "Apache-2.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" + }, + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "ws": "^8.18.0", + "zod": "^3.23.8" + }, + "peerDependenciesMeta": { + "ws": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/openai/node_modules/@types/node": { + "version": "18.19.103", + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/openai/node_modules/undici-types": { + "version": "5.26.5", + "license": "MIT" + }, + "node_modules/optionator": { + "version": "0.9.4", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ora": { + "version": "8.2.0", + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "cli-cursor": "^5.0.0", + "cli-spinners": "^2.9.2", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^2.0.0", + "log-symbols": "^6.0.0", + "stdin-discarder": "^0.2.2", + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/ansi-regex": { + "version": "6.1.0", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ora/node_modules/emoji-regex": { + "version": "10.4.0", + "license": "MIT" + }, + "node_modules/ora/node_modules/is-unicode-supported": { + "version": "2.1.0", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/log-symbols": { + "version": "6.0.0", + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "is-unicode-supported": "^1.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "1.3.0", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/string-width": { + "version": "7.2.0", + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/strip-ansi": { + "version": "7.1.0", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pac-proxy-agent": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/quickjs-emscripten": "^0.23.0", + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "get-uri": "^6.0.1", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.6", + "pac-resolver": "^7.0.1", + "socks-proxy-agent": "^8.0.5" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-proxy-agent/node_modules/debug": { + "version": "4.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/pac-proxy-agent/node_modules/ms": { + "version": "2.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/pac-resolver": { + "version": "7.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "degenerator": "^5.0.0", + "netmask": "^2.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parseley": { + "version": "0.12.1", + "license": "MIT", + "dependencies": { + "leac": "^0.6.0", + "peberminta": "^0.9.0" + }, + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "dev": true, + "license": "ISC" + }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pathval": { + "version": "2.0.0", + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, + "node_modules/peberminta": { + "version": "0.9.0", + "license": "MIT", + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/pg": { + "version": "8.16.0", + "license": "MIT", + "dependencies": { + "pg-connection-string": "^2.9.0", + "pg-pool": "^3.10.0", + "pg-protocol": "^1.10.0", + "pg-types": "2.2.0", + "pgpass": "1.0.5" + }, + "engines": { + "node": ">= 8.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.2.5" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.2.5", + "license": "MIT", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.9.0", + "license": "MIT" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "license": "ISC", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-numeric": { + "version": "1.0.2", + "devOptional": true, + "license": "ISC", + "engines": { + "node": ">=4" + } + }, + "node_modules/pg-pool": { + "version": "3.10.0", + "license": "MIT", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.10.0", + "license": "MIT" + }, + "node_modules/pg-types": { + "version": "4.0.2", + "devOptional": true, + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "pg-numeric": "1.0.2", + "postgres-array": "~3.0.1", + "postgres-bytea": "~3.0.0", + "postgres-date": "~2.1.0", + "postgres-interval": "^3.0.0", + "postgres-range": "^1.1.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/pg/node_modules/pg-types": { + "version": "2.2.0", + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pg/node_modules/postgres-array": { + "version": "2.0.0", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/pg/node_modules/postgres-bytea": { + "version": "1.0.0", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pg/node_modules/postgres-date": { + "version": "1.0.7", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pg/node_modules/postgres-interval": { + "version": "1.2.0", + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "license": "MIT", + "dependencies": { + "split2": "^4.1.0" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pino": { + "version": "9.7.0", + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.0", + "fast-redact": "^3.1.1", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^2.0.0", + "pino-std-serializers": "^7.0.0", + "process-warning": "^5.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^4.0.1", + "thread-stream": "^3.0.0" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/pino-abstract-transport": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "split2": "^4.0.0" + } + }, + "node_modules/pino-pretty": { + "version": "13.0.0", + "license": "MIT", + "dependencies": { + "colorette": "^2.0.7", + "dateformat": "^4.6.3", + "fast-copy": "^3.0.2", + "fast-safe-stringify": "^2.1.1", + "help-me": "^5.0.0", + "joycon": "^3.1.1", + "minimist": "^1.2.6", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^2.0.0", + "pump": "^3.0.0", + "secure-json-parse": "^2.4.0", + "sonic-boom": "^4.0.1", + "strip-json-comments": "^3.1.1" + }, + "bin": { + "pino-pretty": "bin.js" + } + }, + "node_modules/pino-std-serializers": { + "version": "7.0.0", + "license": "MIT" + }, + "node_modules/plimit-lit": { + "version": "1.6.1", + "license": "MIT", + "dependencies": { + "queue-lit": "^1.5.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/postcss": { + "version": "8.5.3", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postgres": { + "version": "3.4.7", + "license": "Unlicense", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/porsager" + } + }, + "node_modules/postgres-array": { + "version": "3.0.4", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/postgres-bytea": { + "version": "3.0.0", + "devOptional": true, + "license": "MIT", + "dependencies": { + "obuf": "~1.1.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/postgres-date": { + "version": "2.1.0", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/postgres-interval": { + "version": "3.0.0", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/postgres-range": { + "version": "1.1.4", + "devOptional": true, + "license": "MIT" + }, + "node_modules/posthog-js": { + "version": "1.246.0", + "license": "SEE LICENSE IN LICENSE", + "dependencies": { + "core-js": "^3.38.1", + "fflate": "^0.4.8", + "preact": "^10.19.3", + "web-vitals": "^4.2.4" + }, + "peerDependencies": { + "@rrweb/types": "2.0.0-alpha.17", + "rrweb-snapshot": "2.0.0-alpha.17" + }, + "peerDependenciesMeta": { + "@rrweb/types": { + "optional": true + }, + "rrweb-snapshot": { + "optional": true + } + } + }, + "node_modules/posthog-node": { + "version": "4.17.2", + "license": "MIT", + "dependencies": { + "axios": "^1.8.2" + }, + "engines": { + "node": ">=15.0.0" + } + }, + "node_modules/preact": { + "version": "10.26.6", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.5.3", + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prism-themes": { + "version": "1.9.0", + "license": "MIT" + }, + "node_modules/prismjs": { + "version": "1.30.0", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/process-warning": { + "version": "5.0.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "node_modules/progress": { + "version": "2.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/property-information": { + "version": "7.1.0", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-agent": { + "version": "6.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.1", + "https-proxy-agent": "^7.0.6", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.1.0", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.5" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/debug": { + "version": "4.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/proxy-agent/node_modules/lru-cache": { + "version": "7.18.3", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/proxy-agent/node_modules/ms": { + "version": "2.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "license": "MIT" + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "license": "MIT" + }, + "node_modules/pump": { + "version": "3.0.2", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/puppeteer": { + "version": "24.9.0", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@puppeteer/browsers": "2.10.5", + "chromium-bidi": "5.1.0", + "cosmiconfig": "^9.0.0", + "devtools-protocol": "0.0.1439962", + "puppeteer-core": "24.9.0", + "typed-query-selector": "^2.12.0" + }, + "bin": { + "puppeteer": "lib/cjs/puppeteer/node/cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/puppeteer-core": { + "version": "24.9.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@puppeteer/browsers": "2.10.5", + "chromium-bidi": "5.1.0", + "debug": "^4.4.1", + "devtools-protocol": "0.0.1439962", + "typed-query-selector": "^2.12.0", + "ws": "^8.18.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/puppeteer-core/node_modules/debug": { + "version": "4.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/puppeteer-core/node_modules/ms": { + "version": "2.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/qs": { + "version": "6.13.0", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "license": "MIT" + }, + "node_modules/queue-lit": { + "version": "1.5.2", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/quick-format-unescaped": { + "version": "4.0.4", + "license": "MIT" + }, + "node_modules/randombytes": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/react": { + "version": "18.3.1", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-confetti-explosion": { + "version": "2.1.2", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.21", + "react-jss": "^10.9.2" + }, + "peerDependencies": { + "react": "^18.x", + "react-dom": "^18.x" + } + }, + "node_modules/react-day-picker": { + "version": "8.10.1", + "license": "MIT", + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/gpbl" + }, + "peerDependencies": { + "date-fns": "^2.28.0 || ^3.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-display-name": { + "version": "0.2.5", + "license": "MIT" + }, + "node_modules/react-dom": { + "version": "18.3.1", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-hot-toast": { + "version": "2.5.2", + "license": "MIT", + "dependencies": { + "csstype": "^3.1.3", + "goober": "^2.1.16" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16" + } + }, + "node_modules/react-hotkeys-hook": { + "version": "4.6.2", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.1", + "react-dom": ">=16.8.1" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "license": "MIT" + }, + "node_modules/react-jss": { + "version": "10.10.0", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.3.1", + "@emotion/is-prop-valid": "^0.7.3", + "css-jss": "10.10.0", + "hoist-non-react-statics": "^3.2.0", + "is-in-browser": "^1.1.3", + "jss": "10.10.0", + "jss-preset-default": "10.10.0", + "prop-types": "^15.6.0", + "shallow-equal": "^1.2.0", + "theming": "^3.3.0", + "tiny-warning": "^1.0.2" + }, + "peerDependencies": { + "react": ">=16.8.6" + } + }, + "node_modules/react-promise-suspense": { + "version": "0.3.4", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^2.0.1" + } + }, + "node_modules/react-promise-suspense/node_modules/fast-deep-equal": { + "version": "2.0.1", + "license": "MIT" + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-remove-scroll": { + "version": "2.7.0", + "license": "MIT", + "dependencies": { + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.3", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.3" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.8", + "license": "MIT", + "dependencies": { + "react-style-singleton": "^2.2.2", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-router": { + "version": "7.6.0", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-style-singleton": { + "version": "2.2.3", + "license": "MIT", + "dependencies": { + "get-nonce": "^1.0.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/real-require": { + "version": "0.2.0", + "license": "MIT", + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/recaseai": { + "version": "0.0.37", + "license": "ISC", + "dependencies": { + "@anthropic-ai/sdk": "^0.32.1", + "@supabase/supabase-js": "^2.47.2", + "@types/cors": "^2.8.17", + "@types/express": "^5.0.0", + "@types/node": "^22.10.1", + "async-listen": "^3.0.1", + "axios": "^1.7.9", + "commander": "^12.1.0", + "cors": "^2.8.5", + "dotenv": "^16.4.7", + "express": "^4.21.2", + "figures": "^6.1.0", + "inquirer": "^12.1.0", + "ksuid": "^3.0.0", + "nanoid": "^5.0.9", + "openai": "^4.76.0", + "ora": "^8.1.1", + "picocolors": "^1.1.1", + "pino": "^9.5.0", + "tsx": "^4.19.2", + "typescript": "^5.7.2" + }, + "bin": { + "recase": "dist/cli.js" + } + }, + "node_modules/recaseai/node_modules/nanoid": { + "version": "5.1.5", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^18 || >=20" + } + }, + "node_modules/redis-errors": { + "version": "1.2.0", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/redis-parser": { + "version": "3.0.0", + "license": "MIT", + "dependencies": { + "redis-errors": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "license": "MIT" + }, + "node_modules/resend": { + "version": "4.5.1", + "license": "MIT", + "dependencies": { + "@react-email/render": "1.0.6" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor/node_modules/onetime": { + "version": "7.0.0", + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.41.0", + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.7" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.41.0", + "@rollup/rollup-android-arm64": "4.41.0", + "@rollup/rollup-darwin-arm64": "4.41.0", + "@rollup/rollup-darwin-x64": "4.41.0", + "@rollup/rollup-freebsd-arm64": "4.41.0", + "@rollup/rollup-freebsd-x64": "4.41.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.41.0", + "@rollup/rollup-linux-arm-musleabihf": "4.41.0", + "@rollup/rollup-linux-arm64-gnu": "4.41.0", + "@rollup/rollup-linux-arm64-musl": "4.41.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.41.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.41.0", + "@rollup/rollup-linux-riscv64-gnu": "4.41.0", + "@rollup/rollup-linux-riscv64-musl": "4.41.0", + "@rollup/rollup-linux-s390x-gnu": "4.41.0", + "@rollup/rollup-linux-x64-gnu": "4.41.0", + "@rollup/rollup-linux-x64-musl": "4.41.0", + "@rollup/rollup-win32-arm64-msvc": "4.41.0", + "@rollup/rollup-win32-ia32-msvc": "4.41.0", + "@rollup/rollup-win32-x64-msvc": "4.41.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/rou3": { + "version": "0.6.1", + "license": "MIT" + }, + "node_modules/run-async": { + "version": "3.0.0", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/scheduler": { + "version": "0.23.2", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/secure-json-parse": { + "version": "2.7.0", + "license": "BSD-3-Clause" + }, + "node_modules/selderee": { + "version": "0.11.0", + "license": "MIT", + "dependencies": { + "parseley": "^0.12.0" + }, + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, + "node_modules/semver": { + "version": "7.7.2", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.0", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "license": "MIT" + }, + "node_modules/serialize-error": { + "version": "8.1.0", + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/serialize-error/node_modules/type-fest": { + "version": "0.20.2", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-static": { + "version": "1.16.2", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "license": "MIT" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "license": "ISC" + }, + "node_modules/shallow-equal": { + "version": "1.2.1", + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/snake-case": { + "version": "3.0.4", + "license": "MIT", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/snakecase-keys": { + "version": "8.0.1", + "license": "MIT", + "dependencies": { + "map-obj": "^4.1.0", + "snake-case": "^3.0.4", + "type-fest": "^4.15.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/snakecase-keys/node_modules/type-fest": { + "version": "4.41.0", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/socks": { + "version": "2.8.4", + "dev": true, + "license": "MIT", + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/socks-proxy-agent/node_modules/debug": { + "version": "4.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socks-proxy-agent/node_modules/ms": { + "version": "2.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/sonic-boom": { + "version": "4.2.0", + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, + "node_modules/sonner": { + "version": "2.0.3", + "license": "MIT", + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/standard-as-callback": { + "version": "2.1.0", + "license": "MIT" + }, + "node_modules/statuses": { + "version": "2.0.1", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/std-env": { + "version": "3.9.0", + "license": "MIT" + }, + "node_modules/stdin-discarder": { + "version": "0.2.2", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/streamx": { + "version": "2.22.0", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + }, + "optionalDependencies": { + "bare-events": "^2.2.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stripe": { + "version": "17.7.0", + "license": "MIT", + "dependencies": { + "@types/node": ">=8.1.0", + "qs": "^6.11.0" + }, + "engines": { + "node": ">=12.*" + } + }, + "node_modules/super-regex": { + "version": "0.2.0", + "license": "MIT", + "dependencies": { + "clone-regexp": "^3.0.0", + "function-timeout": "^0.1.0", + "time-span": "^5.1.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/superagent": { + "version": "10.2.1", + "license": "MIT", + "dependencies": { + "component-emitter": "^1.3.0", + "cookiejar": "^2.1.4", + "debug": "^4.3.4", + "fast-safe-stringify": "^2.1.1", + "form-data": "^4.0.0", + "formidable": "^3.5.4", + "methods": "^1.1.2", + "mime": "2.6.0", + "qs": "^6.11.0" + }, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/superagent/node_modules/debug": { + "version": "4.4.1", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/superagent/node_modules/mime": { + "version": "2.6.0", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/superagent/node_modules/ms": { + "version": "2.1.3", + "license": "MIT" + }, + "node_modules/supports-color": { + "version": "8.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/svix": { + "version": "1.66.0", + "license": "MIT", + "dependencies": { + "@stablelib/base64": "^1.0.0", + "@types/node": "^22.7.5", + "es6-promise": "^4.2.8", + "fast-sha256": "^1.3.0", + "svix-fetch": "^3.0.0", + "url-parse": "^1.5.10" + } + }, + "node_modules/svix-fetch": { + "version": "3.0.0", + "license": "MIT", + "dependencies": { + "node-fetch": "^2.6.1", + "whatwg-fetch": "^3.4.1" + } + }, + "node_modules/svix-react": { + "version": "1.13.4", + "license": "MIT", + "dependencies": { + "iframe-resizer-react": "^1.1.0" + }, + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16", + "svix": ">=1.26.0" + } + }, + "node_modules/swr": { + "version": "2.3.3", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.3", + "use-sync-external-store": "^1.4.0" + }, + "peerDependencies": { + "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/symbol-observable": { + "version": "1.2.0", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tailwind-merge": { + "version": "3.3.0", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/tailwindcss": { + "version": "4.1.7", + "license": "MIT" + }, + "node_modules/tailwindcss-animate": { + "version": "1.0.7", + "license": "MIT", + "peerDependencies": { + "tailwindcss": ">=3.0.0 || insiders" + } + }, + "node_modules/tapable": { + "version": "2.2.2", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tar": { + "version": "7.4.3", + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tar-fs": { + "version": "3.0.9", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + }, + "optionalDependencies": { + "bare-fs": "^4.0.1", + "bare-path": "^3.0.0" + } + }, + "node_modules/tar-stream": { + "version": "3.1.7", + "dev": true, + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "5.0.0", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/text-decoder": { + "version": "1.2.3", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, + "node_modules/theming": { + "version": "3.3.0", + "license": "MIT", + "dependencies": { + "hoist-non-react-statics": "^3.3.0", + "prop-types": "^15.5.8", + "react-display-name": "^0.2.4", + "tiny-warning": "^1.0.2" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "react": ">=16.3" + } + }, + "node_modules/thread-stream": { + "version": "3.1.0", + "license": "MIT", + "dependencies": { + "real-require": "^0.2.0" + } + }, + "node_modules/throttleit": { + "version": "2.1.0", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/time-span": { + "version": "5.1.0", + "license": "MIT", + "dependencies": { + "convert-hrtime": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tiny-warning": { + "version": "1.0.3", + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.13", + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.4", + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.2", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/tmp": { + "version": "0.0.33", + "license": "MIT", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/touch": { + "version": "3.1.1", + "license": "ISC", + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "license": "MIT" + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/ts-api-utils": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/tsc-alias": { + "version": "1.8.16", + "license": "MIT", + "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" + }, + "engines": { + "node": ">=16.20.2" + } + }, + "node_modules/tsc-alias/node_modules/chokidar": { + "version": "3.6.0", + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/tsc-alias/node_modules/commander": { + "version": "9.5.0", + "license": "MIT", + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/tsc-alias/node_modules/glob-parent": { + "version": "5.1.2", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/tsc-alias/node_modules/readdirp": { + "version": "3.6.0", + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/tsconfig-paths": { + "version": "4.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "license": "0BSD" + }, + "node_modules/tsx": { + "version": "4.19.4", + "license": "MIT", + "dependencies": { + "esbuild": "~0.25.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typed-query-selector": { + "version": "2.12.0", + "dev": true, + "license": "MIT" + }, + "node_modules/typescript": { + "version": "5.8.3", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.32.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.32.1", + "@typescript-eslint/parser": "8.32.1", + "@typescript-eslint/utils": "8.32.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "6.21.0", + "license": "MIT" + }, + "node_modules/unist-util-is": { + "version": "6.0.0", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.0.0", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.1", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url-parse": { + "version": "1.5.10", + "license": "MIT", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/use-callback-ref": { + "version": "1.3.3", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sidecar": { + "version": "1.1.3", + "license": "MIT", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sync-external-store": { + "version": "1.5.0", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "9.0.1", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/vary": { + "version": "1.1.2", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vaul": { + "version": "1.1.2", + "license": "MIT", + "dependencies": { + "@radix-ui/react-dialog": "^1.1.1" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc" + } + }, + "node_modules/vfile": { + "version": "6.0.3", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.2", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vite": { + "version": "6.3.5", + "license": "MIT", + "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" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "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" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/fdir": { + "version": "6.4.4", + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.2", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/vscode-oniguruma": { + "version": "2.0.1", + "license": "MIT" + }, + "node_modules/vscode-textmate": { + "version": "9.2.0", + "license": "MIT" + }, + "node_modules/warning": { + "version": "4.0.3", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/web-streams-polyfill": { + "version": "4.0.0-beta.3", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/web-vitals": { + "version": "4.2.4", + "license": "Apache-2.0" + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-fetch": { + "version": "3.6.20", + "license": "MIT" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/workerpool": { + "version": "6.5.1", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.18.2", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "license": "ISC" + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser/node_modules/decamelize": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.2", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "3.25.23", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.24.5", + "license": "ISC", + "peerDependencies": { + "zod": "^3.24.1" + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "server": { + "name": "@autumn/server", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@ai-sdk/anthropic": "^1.2.10", + "@anthropic-ai/sdk": "^0.32.1", + "@autumn/shared": "*", + "@clerk/express": "^1.3.22", + "@date-fns/tz": "^1.2.0", + "@date-fns/utc": "^2.1.0", + "@logtail/node": "^0.5.2", + "@supabase/supabase-js": "^2.46.2", + "@types/cors": "^2.8.17", + "@types/express": "^5.0.0", + "ai": "^4.3.10", + "autumn-js": "^0.0.46", + "body-parser": "^1.20.3", + "bullmq": "^5.31.1", + "chai": "^5.1.2", + "chai-http": "^5.1.1", + "chalk": "^5.3.0", + "cors": "^2.8.5", + "cron": "^3.5.0", + "csv-parse": "^5.6.0", + "csv-parser": "^3.2.0", + "currency-symbol-map": "^5.1.0", + "date-fns": "^4.1.0", + "decimal.js": "^10.5.0", + "dotenv": "^16.5.0", + "drizzle-orm": "^0.43.1", + "express": "^4.21.1", + "fetch-retry": "^6.0.0", + "http-status-codes": "^2.3.0", + "ioredis": "^5.5.0", + "ksuid": "^3.0.0", + "lodash-es": "^4.17.21", + "nodemon": "^3.1.7", + "openai": "^4.85.2", + "pg": "^8.13.1", + "pino": "^9.6.0", + "pino-pretty": "^13.0.0", + "postgres": "^3.4.7", + "posthog-node": "^4.17.2", + "recaseai": "^0.0.37", + "resend": "^4.1.1", + "stripe": "^17.5.0", + "svix": "^1.45.1", + "tsc-alias": "^1.8.16", + "ws": "^8.18.0" + }, + "devDependencies": { + "@types/chai": "^5.0.1", + "@types/chai-http": "^3.0.5", + "@types/lodash-es": "^4.17.12", + "@types/mocha": "^10.0.10", + "@types/node": "^22.13.4", + "@types/pg": "^8.11.10", + "drizzle-kit": "^0.31.1", + "mocha": "^11.1.0", + "puppeteer": "^24.2.0", + "ts-node": "^10.9.2", + "tsconfig-paths": "^4.2.0", + "tsx": "^4.19.4", + "typescript": "^5.7.3" + } + }, + "server/node_modules/autumn-js": { + "version": "0.0.46", + "license": "MIT", + "dependencies": { + "rou3": "^0.6.1" + }, + "peerDependencies": { + "@tanstack/react-query": "^5.76.1", + "react": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + } + } + }, + "server/node_modules/date-fns": { + "version": "4.1.0", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, + "shared": { + "name": "@autumn/shared", + "version": "1.0.0", + "dependencies": { + "drizzle-orm": "^0.43.1", + "drizzle-zod": "^0.8.1", + "zod": "^3.25.23" + }, + "devDependencies": { + "tsx": "^4.19.4", + "typescript": "^5.7.2" + } + }, + "vite": { + "name": "@autumn/frontend2", + "version": "0.0.0", + "dependencies": { + "@autumn/shared": "*", + "@clerk/clerk-react": "^5.24.2", + "@fortawesome/free-brands-svg-icons": "^6.7.2", + "@fortawesome/react-fontawesome": "^0.2.2", + "@radix-ui/react-accordion": "^1.2.3", + "@radix-ui/react-checkbox": "^1.1.4", + "@radix-ui/react-dialog": "^1.1.14", + "@radix-ui/react-dropdown-menu": "^2.1.6", + "@radix-ui/react-popover": "^1.1.6", + "@radix-ui/react-scroll-area": "^1.2.3", + "@radix-ui/react-select": "^2.1.6", + "@radix-ui/react-separator": "^1.1.2", + "@radix-ui/react-slot": "^1.2.3", + "@radix-ui/react-switch": "^1.2.5", + "@radix-ui/react-tabs": "^1.1.3", + "@radix-ui/react-tooltip": "^1.1.8", + "@supabase/ssr": "^0.5.2", + "@tailwindcss/vite": "^4.0.13", + "@wooorm/starry-night": "^3.6.0", + "autumn-js": "^0.0.41", + "axios": "^1.8.3", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "cmdk": "^1.0.0", + "date-fns": "^3.6.0", + "hast-util-to-html": "^9.0.5", + "lodash": "^4.17.21", + "lucide-react": "^0.511.0", + "motion": "^12.16.0", + "next-themes": "^0.4.6", + "posthog-js": "^1.230.4", + "prism-themes": "^1.9.0", + "prismjs": "^1.29.0", + "react": "^18.2.0", + "react-confetti-explosion": "^2.1.2", + "react-day-picker": "^8.10.1", + "react-dom": "^18.2.0", + "react-hot-toast": "^2.5.2", + "react-hotkeys-hook": "^4.6.1", + "react-router": "^7.3.0", + "sonner": "^2.0.1", + "svix-react": "^1.13.3", + "tailwind-merge": "^3.0.2", + "tailwindcss": "^4.0.13", + "tailwindcss-animate": "^1.0.7", + "vaul": "^1.1.2" + }, + "devDependencies": { + "@eslint/js": "^9.21.0", + "@types/lodash": "^4.17.16", + "@types/node": "^22.13.10", + "@types/prismjs": "^1.26.5", + "@types/react": "^18.3.18", + "@types/react-dom": "^18.3.5", + "@vitejs/plugin-react": "^4.3.4", + "eslint": "^9.21.0", + "eslint-plugin-react-hooks": "^5.1.0", + "eslint-plugin-react-refresh": "^0.4.19", + "globals": "^15.15.0", + "typescript": "~5.7.2", + "typescript-eslint": "^8.24.1", + "vite": "^6.2.0" + } + }, + "vite/node_modules/typescript": { + "version": "5.7.3", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + } + } +} diff --git a/server/src/external/stripe/stripeInvoiceUtils.ts b/server/src/external/stripe/stripeInvoiceUtils.ts index 59f73f1f6..4a8b82e4a 100644 --- a/server/src/external/stripe/stripeInvoiceUtils.ts +++ b/server/src/external/stripe/stripeInvoiceUtils.ts @@ -53,30 +53,12 @@ export const payForInvoice = async ({ voidIfFailed = false, }: { stripeCli: Stripe; - paymentMethod?: Stripe.PaymentMethod | null; + paymentMethod: Stripe.PaymentMethod | null; invoiceId: string; logger: any; errorOnFail?: boolean; voidIfFailed?: boolean; }) => { - // const stripeCli = createStripeCli({ org: fullOrg, env: env as AppEnv }); - - // const paymentMethod = await getCusPaymentMethod({ - // stripeCli, - // stripeId: customer.processor.id, - // }); - - // if (!paymentMethod) { - // logger.warn(" ❌ No payment method found"); - // return { - // paid: false, - // error: new RecaseError({ - // message: "No payment method found", - // code: ErrCode.CustomerHasNoPaymentMethod, - // statusCode: 400, - // }), - // }; - // } if (!paymentMethod) { if (errorOnFail) { throw new RecaseError({ @@ -97,6 +79,16 @@ export const payForInvoice = async ({ } } + let invoice = await stripeCli.invoices.retrieve(invoiceId); + if (invoice.status == "paid") { + logger.info(`Invoice ${invoiceId} is already paid`); + return { + paid: true, + error: null, + invoice, + }; + } + try { const invoice = await stripeCli.invoices.pay(invoiceId, { payment_method: paymentMethod?.id, diff --git a/server/src/external/stripe/stripeSubUtils/updateStripeSub.ts b/server/src/external/stripe/stripeSubUtils/updateStripeSub.ts index 7a83f9bb4..1ef6a6d68 100644 --- a/server/src/external/stripe/stripeSubUtils/updateStripeSub.ts +++ b/server/src/external/stripe/stripeSubUtils/updateStripeSub.ts @@ -12,6 +12,8 @@ import { resetUsageBalances, } from "@/internal/customers/attach/attachFunctions/upgradeFlow/createUsageInvoiceItems.js"; import { attachParamToCusProducts } from "@/internal/customers/attach/attachUtils/convertAttachParams.js"; +import { getContUseInvoiceItems } from "@/internal/customers/attach/attachFunctions/upgradeFlow/getContUseInvoiceItems.js"; +import { createAndFilterContUseItems } from "@/internal/customers/attach/attachFunctions/upgradeFlow/createAndFilterContUseItems.js"; export const updateStripeSubscription = async ({ db, @@ -75,6 +77,7 @@ export const updateStripeSubscription = async ({ } const latestInvoice = sub.latest_invoice as Stripe.Invoice; + if (proration == ProrationBehavior.None) { return { preview: null, @@ -96,6 +99,14 @@ export const updateStripeSubscription = async ({ logger, }); + await createAndFilterContUseItems({ + attachParams, + curMainProduct: curMainProduct!, + stripeSubs, + latestInvoice, + logger, + }); + if (proration === ProrationBehavior.Immediately) { if (latestInvoice.id != curSub.latest_invoice) { sub.latest_invoice = latestInvoice.id; diff --git a/server/src/external/stripe/stripeSubUtils/updateStripeSub/createProrationinvoice.ts b/server/src/external/stripe/stripeSubUtils/updateStripeSub/createProrationinvoice.ts index 1d9b6deb0..ff63aef34 100644 --- a/server/src/external/stripe/stripeSubUtils/updateStripeSub/createProrationinvoice.ts +++ b/server/src/external/stripe/stripeSubUtils/updateStripeSub/createProrationinvoice.ts @@ -71,7 +71,7 @@ export const createProrationInvoice = async ({ try { const { invoice: subInvoice } = await payForInvoice({ stripeCli, - paymentMethod, + paymentMethod: paymentMethod || null, invoiceId: invoice.id, logger, voidIfFailed: true, diff --git a/server/src/internal/api/entities/entityRouter.ts b/server/src/internal/api/entities/entityRouter.ts index 560821b34..af6f40d99 100644 --- a/server/src/internal/api/entities/entityRouter.ts +++ b/server/src/internal/api/entities/entityRouter.ts @@ -6,7 +6,7 @@ import { CusService } from "@/internal/customers/CusService.js"; import RecaseError from "@/utils/errorUtils.js"; import { ErrCode } from "@autumn/shared"; import { handleGetEntity } from "./handlers/handleGetEntity.js"; -import { handlePostEntityRequest } from "./handlers/handleCreateEntity.js"; +import { handlePostEntityRequest } from "../../entities/handlers/handleCreateEntity/handleCreateEntity.js"; import { handleDeleteEntity } from "./handlers/handleDeleteEntity.js"; export const entityRouter = Router({ mergeParams: true }); diff --git a/server/src/internal/api/entities/handlers/handleCreateEntity.ts b/server/src/internal/api/entities/handlers/handleCreateEntity.ts deleted file mode 100644 index f28f5da5d..000000000 --- a/server/src/internal/api/entities/handlers/handleCreateEntity.ts +++ /dev/null @@ -1,553 +0,0 @@ -import { CusEntService } from "@/internal/customers/cusProducts/cusEnts/CusEntitlementService.js"; -import { FeatureService } from "@/internal/features/FeatureService.js"; -import { OrgService } from "@/internal/orgs/OrgService.js"; - -import RecaseError from "@/utils/errorUtils.js"; -import { EntityService } from "../EntityService.js"; -import { - APIVersion, - AppEnv, - CustomerData, - Entity, - ErrCode, - Feature, - Organization, -} from "@autumn/shared"; -import { generateId, notNullish, nullish } from "@/utils/genUtils.js"; -import { adjustAllowance } from "@/trigger/adjustAllowance.js"; -import { - getCusEntMasterBalance, - getRelatedCusPrice, -} from "@/internal/customers/cusProducts/cusEnts/cusEntUtils.js"; -import { getEntityResponse } from "../getEntityUtils.js"; -import { StatusCodes } from "http-status-codes"; -import { orgToVersion } from "@/utils/versionUtils.js"; -import { DrizzleCli } from "@/db/initDrizzle.js"; -import { routeHandler } from "@/utils/routerUtils.js"; -import { getOrCreateCustomer } from "@/internal/customers/cusUtils/getOrCreateCustomer.js"; -import { ExtendedRequest } from "@/utils/models/Request.js"; - -interface CreateEntityData { - id: string; - name?: string; - feature_id?: string; -} - -export const constructEntity = ({ - inputEntity, - feature, - internalCustomerId, - orgId, - env, - deleted = false, -}: { - inputEntity: any; - feature: any; - internalCustomerId: string; - orgId: string; - env: AppEnv; - deleted?: boolean; -}) => { - let entity: Entity = { - internal_id: generateId("ety"), - id: inputEntity.id, - name: inputEntity.name, - internal_customer_id: internalCustomerId, - feature_id: feature.id, - internal_feature_id: feature.internal_id, - org_id: orgId, - env, - deleted, - created_at: Date.now(), - }; - - return entity; -}; - -export const getEntityToAction = ({ - inputEntities, - existingEntities, - logger, - feature, - cusProducts, -}: { - inputEntities: any[]; - existingEntities: Entity[]; - logger: any; - feature: any; - cusProducts: any[]; -}) => { - // 1. GET ENTITY TO ACTION - let entityToAction: any = {}; - let createCount = 0; - let replacedEntities: string[] = []; - for (const inputEntity of inputEntities) { - let curEntity = existingEntities.find((e: any) => e.id === inputEntity.id); - - if (curEntity && curEntity.deleted) { - entityToAction[inputEntity.id] = { - action: "replace", - replace: curEntity, - entity: inputEntity, - }; - replacedEntities.push(curEntity.id); - continue; - } - - let replaced = false; - - for (const entity of existingEntities) { - if (entity.deleted && !replacedEntities.includes(entity.id)) { - replaced = true; - replacedEntities.push(entity.id); - - entityToAction[inputEntity.id] = { - action: "replace", - replace: entity, - entity: inputEntity, - }; - break; - } - - // If there's an entity with null ID and cur input entity has an ID, fill it up! - - if ( - nullish(entity.id) && - notNullish(inputEntity.id) && - !replacedEntities.includes(entity.internal_id) && - entity.feature_id === feature.id - ) { - entityToAction[inputEntity.id] = { - action: "replace", - replace: entity, - entity: inputEntity, - }; - - replacedEntities.push(entity.internal_id); - replaced = true; - break; - } - } - - if (!replaced) { - entityToAction[inputEntity.id] = { - action: "create", - entity: inputEntity, - }; - createCount++; - } - } - - return entityToAction; -}; - -export const logEntityToAction = ({ - entityToAction, - logger, -}: { - entityToAction: any; - logger: any; -}) => { - for (const id in entityToAction) { - logger.info( - `${id} - ${entityToAction[id].action}${ - entityToAction[id].replace - ? ` (replace ${ - entityToAction[id].replace.id || - entityToAction[id].replace.internal_id - })` - : "" - }`, - ); - } -}; - -export const validateAndGetInputEntities = async ({ - req, - db, - org, - features, - customerId, - customerData, - createEntityData, - env, - logger, -}: { - req: ExtendedRequest; - db: DrizzleCli; - org: Organization; - features: Feature[]; - customerId: string; - customerData?: CustomerData; - env: AppEnv; - createEntityData: CreateEntityData[] | CreateEntityData; - logger: any; -}) => { - // 1. Get customer, features and orgs - let customer = await getOrCreateCustomer({ - req, - customerId, - customerData, - withEntities: true, - }); - - if (!customer) { - throw new RecaseError({ - message: `Customer ${customerId} not found`, - code: ErrCode.CustomerNotFound, - }); - } - - // 2. Get input entities - let inputEntities: any[] = []; - if (Array.isArray(createEntityData)) { - inputEntities = createEntityData; - } else { - inputEntities = [createEntityData]; - } - - let featureIds = [...new Set(inputEntities.map((e: any) => e.feature_id))]; - if (featureIds.length > 1) { - throw new RecaseError({ - message: "Multiple features not supported", - code: ErrCode.InvalidInputs, - statusCode: StatusCodes.BAD_REQUEST, - }); - } - - let feature_id = featureIds[0]; - let feature = features.find((f: any) => f.id === feature_id); - - if (!feature_id) { - throw new RecaseError({ - message: - "Feature ID is required to create entity. Did you forget to pass it into entity_data?", - code: ErrCode.InvalidInputs, - statusCode: StatusCodes.BAD_REQUEST, - }); - } - - if (!feature) { - throw new RecaseError({ - message: `Create entity failed: feature ${feature_id} not found`, - code: ErrCode.FeatureNotFound, - statusCode: StatusCodes.NOT_FOUND, - }); - } - - let cusProducts = await customer.customer_products; - let existingEntities = customer.entities; - - logger.info("Existing entities:"); - logger.info( - existingEntities.map( - (e: any) => `${e.id} - ${e.name}, deleted: ${e.deleted}`, - ), - ); - - let noIdEntities = existingEntities.filter((e: any) => !e.id); - let noIdNewEntities = inputEntities.filter((e: any) => !e.id); - if (noIdEntities.length + noIdNewEntities.length > 1) { - throw new RecaseError({ - message: "Can only have one entity with no ID", - code: ErrCode.EntityIdRequired, - statusCode: StatusCodes.BAD_REQUEST, - }); - } - - for (const entity of existingEntities) { - if (inputEntities.some((e: any) => e.id === entity.id) && !entity.deleted) { - throw new RecaseError({ - message: `Entity ${entity.id} already exists`, - code: "ENTITY_ALREADY_EXISTS", - data: { - entity, - }, - statusCode: StatusCodes.CONFLICT, - }); - } - } - - return { - customer, - features, - inputEntities, - feature_id, - feature, - cusProducts, - existingEntities, - }; -}; - -export const createEntities = async ({ - req, - db, - env, - org, - features, - logger, - customerId, - customerData, - createEntityData, - withAutumnId = false, - apiVersion, - fromAutoCreate = false, -}: { - req: ExtendedRequest; - db: DrizzleCli; - org: Organization; - customerData?: CustomerData; - features: Feature[]; - env: AppEnv; - logger: any; - customerId: string; - createEntityData: CreateEntityData[] | CreateEntityData; - withAutumnId?: boolean; - apiVersion?: APIVersion; - fromAutoCreate?: boolean; -}) => { - const { - customer, - inputEntities, - feature_id, - feature, - cusProducts, - existingEntities, - } = await validateAndGetInputEntities({ - req, - db, - customerId, - org, - customerData, - env, - logger, - createEntityData, - features, - }); - - const entityToAction = getEntityToAction({ - inputEntities, - existingEntities, - logger, - feature, - cusProducts, - }); - - logEntityToAction({ - entityToAction, - logger, - }); - - // 3. CREATE LINKED CUSTOMER ENTITLEMENTS - for (const cusProduct of cusProducts) { - let cusEnts = cusProduct.customer_entitlements; - let product = cusProduct.product; - - let mainCusEnt = cusEnts.find( - (e: any) => e.entitlement.feature.id === feature_id, - ); - - // Get linked features - let linkedCusEnts = cusEnts.filter( - (e: any) => e.entitlement.entity_feature_id === feature.id, - ); - - if (linkedCusEnts.length > 0 && inputEntities.some((e: any) => !e.id)) { - throw new RecaseError({ - message: "Entity ID is required", - code: ErrCode.EntityIdRequired, - statusCode: StatusCodes.BAD_REQUEST, - }); - } - - // 1. Pay for new seats - let replacedCount = Object.keys(entityToAction).filter( - (id) => entityToAction[id].action === "replace", - ).length; - - let newCount = Object.keys(entityToAction).filter( - (id) => entityToAction[id].action === "create", - ).length; - - if (mainCusEnt) { - if (fromAutoCreate) { - let cusPrice = getRelatedCusPrice( - mainCusEnt, - cusProduct.customer_prices || [], - ); - - if (cusPrice) { - return []; - } - } - - let { unused } = getCusEntMasterBalance({ - cusEnt: mainCusEnt, - entities: existingEntities, - }); - - let mainCusEntBalance = mainCusEnt.balance || 0; - const originalBalance = mainCusEntBalance + (unused || 0); - const newBalance = - mainCusEntBalance - (newCount + replacedCount) + (unused || 0); - - await adjustAllowance({ - db, - - env, - org, - cusPrices: cusProducts.flatMap((p: any) => p.customer_prices), - customer, - affectedFeature: feature, - cusEnt: { ...mainCusEnt, customer_product: cusProduct }, - originalBalance, - newBalance, - deduction: newCount + replacedCount, - product, - replacedCount, - fromEntities: true, - }); - - await CusEntService.update({ - db, - id: mainCusEnt.id, - updates: { balance: mainCusEntBalance - newCount }, - }); - } - - for (const linkedCusEnt of linkedCusEnts) { - let allowance = linkedCusEnt?.entitlement.allowance; - let newEntities = linkedCusEnt?.entities || {}; - - for (const entity of inputEntities) { - let entityAction = entityToAction[entity.id]; - - if (entityAction.action === "create") { - newEntities[entity.id] = { - id: entity.id, - balance: allowance!, - adjustment: 0, - }; - } else if (entityAction.action === "replace") { - let tmp = newEntities[entityAction.replace.id]; - delete newEntities[entityAction.replace.id]; - newEntities[entity.id] = { - ...tmp, - id: entity.id, - }; - } - } - - await CusEntService.update({ - db, - id: linkedCusEnt.id, - updates: { entities: newEntities }, - }); - } - } - - // 4. CREATE ENTITIES - let newEntities: Entity[] = []; - for (const id in entityToAction) { - let { action, entity, replace } = entityToAction[id]; - - // Create and add to customer entitlement? - if (action === "create") { - let results = await EntityService.insert({ - db, - data: constructEntity({ - inputEntity: entity, - feature, - internalCustomerId: customer.internal_id, - orgId: org.id, - env, - }), - }); - - newEntities.push(results[0]); - } else if (action === "replace") { - let updatedEntity = await EntityService.update({ - db, - internalId: replace.internal_id, - update: { - id: entity.id, - name: entity.name, - deleted: false, - }, - }); - - newEntities.push(updatedEntity); - } - } - - if (fromAutoCreate) { - return newEntities; - } - - let { entities } = await getEntityResponse({ - db, - entityIds: inputEntities.map((e: any) => e.id), - org, - env, - customerId: customer.id || customer.internal_id, - withAutumnId, - apiVersion: apiVersion!, - }); - - return entities; -}; - -export const handlePostEntityRequest = async (req: any, res: any) => - routeHandler({ - req, - res, - action: "create entity", - handler: async (req: any, res: any) => { - const { env, db, logtail: logger } = req; - - const [org, features] = await Promise.all([ - OrgService.getFromReq(req), - FeatureService.getFromReq(req), - ]); - - let apiVersion = orgToVersion({ - org, - reqApiVersion: req.apiVersion, - }); - - let customerData = - Array.isArray(req.body) && req.body.length > 0 - ? req.body[0].customer_data - : req.body.customer_data; - - const entities = await createEntities({ - req, - db, - org, - features, - logger, - env, - customerId: req.params.customer_id, - createEntityData: req.body, - customerData, - withAutumnId: req.query.with_autumn_id === "true", - apiVersion, - }); - - logger.info(` Created / replaced entities!`); - - if (apiVersion < APIVersion.v1_2) { - res.status(200).json({ - success: true, - }); - return; - } - if (Array.isArray(req.body)) { - res.status(200).json({ - list: entities, - }); - } else { - res.status(200).json(entities[0]); - } - }, - }); diff --git a/server/src/internal/api/entities/handlers/handleDeleteEntity.ts b/server/src/internal/api/entities/handlers/handleDeleteEntity.ts index d694a7985..208f7b282 100644 --- a/server/src/internal/api/entities/handlers/handleDeleteEntity.ts +++ b/server/src/internal/api/entities/handlers/handleDeleteEntity.ts @@ -4,29 +4,28 @@ import { StatusCodes } from "http-status-codes"; import { CusProductStatus, ErrCode } from "@autumn/shared"; import { CusService } from "@/internal/customers/CusService.js"; import { adjustAllowance } from "@/trigger/adjustAllowance.js"; -import { OrgService } from "@/internal/orgs/OrgService.js"; import { handleCustomerRaceCondition } from "@/external/redis/redisUtils.js"; -import { - getCusEntMasterBalance, - getRelatedCusPrice, -} from "@/internal/customers/cusProducts/cusEnts/cusEntUtils.js"; -import { createStripeCli } from "@/external/stripe/utils.js"; -import { removeEntityFromCusEnt } from "../entityUtils.js"; import { CusEntService } from "@/internal/customers/cusProducts/cusEnts/CusEntitlementService.js"; -import { getStripeSubs } from "@/external/stripe/stripeSubUtils.js"; -import { cancelCurSubs } from "@/internal/customers/change-product/handleDowngrade/cancelCurSubs.js"; -import { removeScheduledProduct } from "../../../customers/handlers/handleCusProductExpired.js"; -import { cusProductsToCusEnts } from "@/internal/customers/cusProducts/cusProductUtils/convertCusProduct.js"; +import { + findLinkedCusEnts, + findMainCusEntForFeature, +} from "@/internal/customers/cusProducts/cusEnts/cusEntUtils/findCusEntUtils.js"; +import { RepService } from "@/internal/customers/cusProducts/cusEnts/RepService.js"; +import { + deleteEntityFromCusEnt, + replaceEntityInCusEnt, +} from "@/internal/customers/cusProducts/cusEnts/cusEntUtils/linkedCusEntUtils.js"; +import { cancelSubsForEntity } from "@/internal/entities/handlers/handleDeleteEntity/cancelSubsForEntity.js"; export const handleDeleteEntity = async (req: any, res: any) => { try { - const { orgId, env, db, logtail: logger } = req; + const { org, env, db, logtail: logger, features } = req; const { customer_id, entity_id } = req.params; await handleCustomerRaceCondition({ action: "entity", customerId: customer_id, - orgId, + orgId: org.id, env, res, logger, @@ -55,7 +54,6 @@ export const handleDeleteEntity = async (req: any, res: any) => { const existingEntities = customer.entities; const cusProducts = customer.customer_products; - const entity = existingEntities.find((e: any) => e.id === entity_id); if (!entity) { @@ -72,146 +70,201 @@ export const handleDeleteEntity = async (req: any, res: any) => { }); } - const org = await OrgService.getFromReq(req); - - let cusPriceExists = false; + const feature = features.find((f: any) => f.id === entity?.feature_id); for (const cusProduct of cusProducts) { let cusEnts = cusProduct.customer_entitlements; - let product = cusProduct.product; - let cusEnt = cusEnts.find( - (e: any) => - e.entitlement.feature.internal_id === entity.internal_feature_id, - ); + let mainCusEnt = findMainCusEntForFeature({ + cusEnts, + feature, + }); - if (!cusEnt) { + if (!mainCusEnt) { continue; } - let relatedCusPrice = getRelatedCusPrice( - cusEnt, - cusProduct.customer_prices, - ); - - if (relatedCusPrice) { - cusPriceExists = true; - } - - let { unused } = getCusEntMasterBalance({ - cusEnt, - entities: existingEntities, - }); - - let newBalance = (cusEnt.balance || 0) + 1 + (unused || 0); - - await adjustAllowance({ + const { newReplaceables } = await adjustAllowance({ db, env, org, cusPrices: cusProducts.flatMap((p: any) => p.customer_prices), customer, - affectedFeature: cusEnt.entitlement.feature, - cusEnt: { ...cusEnt, customer_product: cusProduct }, - originalBalance: (cusEnt.balance || 0) + (unused || 0), - newBalance, - deduction: 1, - product, - fromEntities: true, + affectedFeature: mainCusEnt.entitlement.feature, + cusEnt: { ...mainCusEnt, customer_product: cusProduct }, + originalBalance: mainCusEnt.balance!, + newBalance: mainCusEnt.balance! + 1, + logger, + + // deduction: 1, + // product, + // fromEntities: true, }); - } - if (!cusPriceExists || org.config.prorate_unused) { - // Completely remove entity - let cusEnts = cusProductsToCusEnts({ cusProducts }); + let linkedCusEnts = findLinkedCusEnts({ + cusEnts: cusProduct.customer_entitlements, + feature: mainCusEnt.entitlement.feature, + }); - // TODO: Charge for unused feature IDs... - - for (const cusEnt of cusEnts) { - let relatedCusPrice = getRelatedCusPrice( - cusEnt, - cusProducts.flatMap((p: any) => p.customer_prices), - ); - await removeEntityFromCusEnt({ + if (newReplaceables) { + let replaceable = newReplaceables[0]; + await RepService.update({ db, - cusEnt, - entity, - logger, - cusPrice: relatedCusPrice, - customer, - org, - env, + id: replaceable.id, + data: { + from_entity_id: entity.id, + }, }); } - try { - let stripeCli = createStripeCli({ org, env }); - let curSubs = await getStripeSubs({ - stripeCli, - subIds: cusProducts.flatMap((p: any) => p.subscription_ids), - }); + let replaceable = newReplaceables ? newReplaceables[0] : null; - for (const cusProduct of cusProducts) { - if (cusProduct.internal_entity_id !== entity.internal_id) { - continue; - } - - if (cusProduct.status == CusProductStatus.Scheduled) { - await removeScheduledProduct({ - req, - db, - cusProduct, - cusProducts, - org, - env, - logger, - renewCurProduct: false, - }); - } else { - await cancelCurSubs({ - curCusProduct: cusProduct, - curSubs, - stripeCli, - }); - } + // Update linked cus ents with replaceables... + for (const linkedCusEnt of linkedCusEnts) { + let newEntities; + if (replaceable) { + let { newEntities: newEntities_ } = replaceEntityInCusEnt({ + cusEnt: linkedCusEnt, + entityId: entity.id, + replaceable, + }); + newEntities = newEntities_; + } else { + let { newEntities: newEntities_ } = deleteEntityFromCusEnt({ + cusEnt: linkedCusEnt, + entityId: entity.id, + }); + newEntities = newEntities_; } - } catch (error) { - logger.error("FAILED TO CANCEL SUBS FOR DELETED ENTITY", error); - } - // Perform deduction on cus ent - let updateCusEnt = cusEnts.find( - (e: any) => e.entitlement.feature.id === entity.feature_id, - ); - if (updateCusEnt) { - await CusEntService.increment({ + await CusEntService.update({ db, - id: updateCusEnt.id, - amount: 1, + id: linkedCusEnt.id, + updates: { + entities: newEntities, + }, }); } - await EntityService.deleteInInternalIds({ + await CusEntService.increment({ db, - internalIds: [entity.internal_id], - orgId: req.orgId, - env: req.env, - }); - } else { - await EntityService.update({ - db, - internalId: entity.internal_id, - update: { - deleted: true, - }, + id: mainCusEnt.id, + amount: 1, }); } + // Cancel any subs + await cancelSubsForEntity({ + req, + cusProducts, + entity, + }); + + await EntityService.deleteInInternalIds({ + db, + internalIds: [entity.internal_id], + orgId: req.orgId, + env: req.env, + }); + logger.info(` ✅ Finished deleting entity ${entity_id}`); - res.status(200).json({ + return res.status(200).json({ success: true, }); + + // const linkedCusEnts = findLinkedCusEnts({ + // cusEnts: cusEnt.customer_product.customer_entitlements, + // feature: cusEnt.entitlement.feature, + // }); + + // if (!cusPriceExists || org.config.prorate_unused) { + // let cusEnts = cusProductsToCusEnts({ cusProducts }); + // for (const cusEnt of cusEnts) { + // let relatedCusPrice = getRelatedCusPrice( + // cusEnt, + // cusProducts.flatMap((p: any) => p.customer_prices), + // ); + // await removeEntityFromCusEnt({ + // db, + // cusEnt, + // entity, + // logger, + // cusPrice: relatedCusPrice, + // customer, + // org, + // env, + // }); + // } + + // try { + // let stripeCli = createStripeCli({ org, env }); + // let curSubs = await getStripeSubs({ + // stripeCli, + // subIds: cusProducts.flatMap((p: any) => p.subscription_ids), + // }); + + // for (const cusProduct of cusProducts) { + // if (cusProduct.internal_entity_id !== entity.internal_id) { + // continue; + // } + + // if (cusProduct.status == CusProductStatus.Scheduled) { + // await removeScheduledProduct({ + // req, + // db, + // cusProduct, + // cusProducts, + // org, + // env, + // logger, + // renewCurProduct: false, + // }); + // } else { + // await cancelCurSubs({ + // curCusProduct: cusProduct, + // curSubs, + // stripeCli, + // }); + // } + // } + // } catch (error) { + // logger.error("FAILED TO CANCEL SUBS FOR DELETED ENTITY", error); + // } + + // // Perform deduction on cus ent + // let updateCusEnt = cusEnts.find( + // (e: any) => e.entitlement.feature.id === entity.feature_id, + // ); + // if (updateCusEnt) { + // await CusEntService.increment({ + // db, + // id: updateCusEnt.id, + // amount: 1, + // }); + // } + + // await EntityService.deleteInInternalIds({ + // db, + // internalIds: [entity.internal_id], + // orgId: req.orgId, + // env: req.env, + // }); + // } else { + // await EntityService.update({ + // db, + // internalId: entity.internal_id, + // update: { + // deleted: true, + // }, + // }); + // } + + // logger.info(` ✅ Finished deleting entity ${entity_id}`); + + // res.status(200).json({ + // success: true, + // }); } catch (error) { handleRequestError({ error, req, res, action: "delete entity" }); } diff --git a/server/src/internal/api/entities/handlers/handleDeleteEntity_old.ts b/server/src/internal/api/entities/handlers/handleDeleteEntity_old.ts new file mode 100644 index 000000000..e1d53274c --- /dev/null +++ b/server/src/internal/api/entities/handlers/handleDeleteEntity_old.ts @@ -0,0 +1,217 @@ +import RecaseError, { handleRequestError } from "@/utils/errorUtils.js"; +import { EntityService } from "../EntityService.js"; +import { StatusCodes } from "http-status-codes"; +import { CusProductStatus, ErrCode } from "@autumn/shared"; +import { CusService } from "@/internal/customers/CusService.js"; +import { adjustAllowance } from "@/trigger/adjustAllowance.js"; +import { OrgService } from "@/internal/orgs/OrgService.js"; +import { handleCustomerRaceCondition } from "@/external/redis/redisUtils.js"; +import { + getCusEntMasterBalance, + getRelatedCusPrice, +} from "@/internal/customers/cusProducts/cusEnts/cusEntUtils.js"; +import { createStripeCli } from "@/external/stripe/utils.js"; +import { removeEntityFromCusEnt } from "../entityUtils.js"; +import { CusEntService } from "@/internal/customers/cusProducts/cusEnts/CusEntitlementService.js"; +import { getStripeSubs } from "@/external/stripe/stripeSubUtils.js"; +import { cancelCurSubs } from "@/internal/customers/change-product/handleDowngrade/cancelCurSubs.js"; +import { removeScheduledProduct } from "../../../customers/handlers/handleCusProductExpired.js"; +import { cusProductsToCusEnts } from "@/internal/customers/cusProducts/cusProductUtils/convertCusProduct.js"; + +export const handleDeleteEntity = async (req: any, res: any) => { + try { + const { orgId, env, db, logtail: logger } = req; + const { customer_id, entity_id } = req.params; + + await handleCustomerRaceCondition({ + action: "entity", + customerId: customer_id, + orgId, + env, + res, + logger, + }); + + const customer = await CusService.getFull({ + db, + idOrInternalId: customer_id, + orgId: req.orgId, + env: req.env, + withEntities: true, + inStatuses: [ + CusProductStatus.Active, + CusProductStatus.PastDue, + CusProductStatus.Scheduled, + ], + }); + + if (!customer) { + throw new RecaseError({ + message: `Customer ${customer_id} not found`, + code: ErrCode.CustomerNotFound, + statusCode: StatusCodes.NOT_FOUND, + }); + } + + const existingEntities = customer.entities; + const cusProducts = customer.customer_products; + const entity = existingEntities.find((e: any) => e.id === entity_id); + + if (!entity) { + throw new RecaseError({ + message: `Entity ${entity_id} not found`, + code: ErrCode.EntityNotFound, + statusCode: StatusCodes.NOT_FOUND, + }); + } else if (entity.deleted) { + throw new RecaseError({ + message: `Entity ${entity_id} already deleted`, + code: ErrCode.EntityAlreadyDeleted, + statusCode: StatusCodes.BAD_REQUEST, + }); + } + + const org = await OrgService.getFromReq(req); + + let cusPriceExists = false; + + for (const cusProduct of cusProducts) { + let cusEnts = cusProduct.customer_entitlements; + let product = cusProduct.product; + + let cusEnt = cusEnts.find( + (e: any) => + e.entitlement.feature.internal_id === entity.internal_feature_id, + ); + + if (!cusEnt) { + continue; + } + + let relatedCusPrice = getRelatedCusPrice( + cusEnt, + cusProduct.customer_prices, + ); + + if (relatedCusPrice) { + cusPriceExists = true; + } + + let { unused } = getCusEntMasterBalance({ + cusEnt, + entities: existingEntities, + }); + + let newBalance = (cusEnt.balance || 0) + 1 + (unused || 0); + + await adjustAllowance({ + db, + env, + org, + cusPrices: cusProducts.flatMap((p: any) => p.customer_prices), + customer, + affectedFeature: cusEnt.entitlement.feature, + cusEnt: { ...cusEnt, customer_product: cusProduct }, + originalBalance: (cusEnt.balance || 0) + (unused || 0), + newBalance, + deduction: 1, + product, + fromEntities: true, + }); + } + + if (!cusPriceExists || org.config.prorate_unused) { + // Completely remove entity + let cusEnts = cusProductsToCusEnts({ cusProducts }); + + // TODO: Charge for unused feature IDs... + + for (const cusEnt of cusEnts) { + let relatedCusPrice = getRelatedCusPrice( + cusEnt, + cusProducts.flatMap((p: any) => p.customer_prices), + ); + await removeEntityFromCusEnt({ + db, + cusEnt, + entity, + logger, + cusPrice: relatedCusPrice, + customer, + org, + env, + }); + } + + try { + let stripeCli = createStripeCli({ org, env }); + let curSubs = await getStripeSubs({ + stripeCli, + subIds: cusProducts.flatMap((p: any) => p.subscription_ids), + }); + + for (const cusProduct of cusProducts) { + if (cusProduct.internal_entity_id !== entity.internal_id) { + continue; + } + + if (cusProduct.status == CusProductStatus.Scheduled) { + await removeScheduledProduct({ + req, + db, + cusProduct, + cusProducts, + org, + env, + logger, + renewCurProduct: false, + }); + } else { + await cancelCurSubs({ + curCusProduct: cusProduct, + curSubs, + stripeCli, + }); + } + } + } catch (error) { + logger.error("FAILED TO CANCEL SUBS FOR DELETED ENTITY", error); + } + + // Perform deduction on cus ent + let updateCusEnt = cusEnts.find( + (e: any) => e.entitlement.feature.id === entity.feature_id, + ); + if (updateCusEnt) { + await CusEntService.increment({ + db, + id: updateCusEnt.id, + amount: 1, + }); + } + + await EntityService.deleteInInternalIds({ + db, + internalIds: [entity.internal_id], + orgId: req.orgId, + env: req.env, + }); + } else { + await EntityService.update({ + db, + internalId: entity.internal_id, + update: { + deleted: true, + }, + }); + } + + logger.info(` ✅ Finished deleting entity ${entity_id}`); + + res.status(200).json({ + success: true, + }); + } catch (error) { + handleRequestError({ error, req, res, action: "delete entity" }); + } +}; diff --git a/server/src/internal/api/entitled/checkUtils.ts b/server/src/internal/api/entitled/checkUtils.ts index 7bed9449e..a622b5aca 100644 --- a/server/src/internal/api/entitled/checkUtils.ts +++ b/server/src/internal/api/entitled/checkUtils.ts @@ -1,5 +1,5 @@ import { itemToPriceOrTiers } from "@/internal/products/product-items/productItemUtils.js"; -import { isFeaturePriceItem } from "@/internal/products/product-items/getItemType.js"; +import { isFeaturePriceItem } from "@/internal/products/product-items/productItemUtils/getItemType.js"; import { APIVersion, diff --git a/server/src/internal/api/products/handleUpdateProduct.ts b/server/src/internal/api/products/handleUpdateProduct.ts index 55723d84c..5767995f9 100644 --- a/server/src/internal/api/products/handleUpdateProduct.ts +++ b/server/src/internal/api/products/handleUpdateProduct.ts @@ -17,7 +17,7 @@ import { CusProductService } from "@/internal/customers/cusProducts/CusProductSe import { handleVersionProductV2 } from "./handleVersionProduct.js"; import { routeHandler } from "@/utils/routerUtils.js"; -import { handleNewProductItems } from "@/internal/products/product-items/productItemInitUtils.js"; +import { handleNewProductItems } from "@/internal/products/product-items/productItemUtils/handleNewProductItems.js"; import { RewardProgramService } from "@/internal/rewards/RewardProgramService.js"; import { DrizzleCli } from "@/db/initDrizzle.js"; diff --git a/server/src/internal/api/products/handleVersionProduct.ts b/server/src/internal/api/products/handleVersionProduct.ts index d0dd88a90..95598ae9c 100644 --- a/server/src/internal/api/products/handleVersionProduct.ts +++ b/server/src/internal/api/products/handleVersionProduct.ts @@ -11,7 +11,7 @@ import { } from "@autumn/shared"; import { FullProduct } from "@autumn/shared"; -import { handleNewProductItems } from "@/internal/products/product-items/productItemInitUtils.js"; +import { handleNewProductItems } from "@/internal/products/product-items/productItemUtils/handleNewProductItems.js"; import { validateProductItems } from "@/internal/products/product-items/validateProductItems.js"; import { EntitlementService } from "@/internal/products/entitlements/EntitlementService.js"; import { PriceService } from "@/internal/products/prices/PriceService.js"; diff --git a/server/src/internal/api/products/handlers/handleCreateProduct.ts b/server/src/internal/api/products/handlers/handleCreateProduct.ts index 80e3a01ad..e07731d34 100644 --- a/server/src/internal/api/products/handlers/handleCreateProduct.ts +++ b/server/src/internal/api/products/handlers/handleCreateProduct.ts @@ -21,7 +21,7 @@ import { import { ProductService } from "@/internal/products/ProductService.js"; import { constructProduct } from "@/internal/products/productUtils.js"; -import { handleNewProductItems } from "@/internal/products/product-items/productItemInitUtils.js"; +import { handleNewProductItems } from "@/internal/products/product-items/productItemUtils/handleNewProductItems.js"; import { FeatureService } from "@/internal/features/FeatureService.js"; import { ExtendedRequest } from "@/utils/models/Request.js"; diff --git a/server/src/internal/customers/attach/attachFunctions/updateEntsFlow/handleUpdateEntsFunction.ts b/server/src/internal/customers/attach/attachFunctions/updateEntsFlow/handleUpdateEntsFunction.ts index e51d77abb..2dcd4d188 100644 --- a/server/src/internal/customers/attach/attachFunctions/updateEntsFlow/handleUpdateEntsFunction.ts +++ b/server/src/internal/customers/attach/attachFunctions/updateEntsFlow/handleUpdateEntsFunction.ts @@ -21,7 +21,7 @@ import { cusProductToProduct } from "@/internal/customers/cusProducts/cusProduct import { isFeaturePriceItem, isPriceItem, -} from "@/internal/products/product-items/getItemType.js"; +} from "@/internal/products/product-items/productItemUtils/getItemType.js"; import { getStripeSubItems } from "@/external/stripe/stripeSubUtils/getStripeSubItems.js"; import { subToAutumnInterval } from "@/external/stripe/utils.js"; import { addSubItemsToRemove } from "../attachFuncUtils.js"; diff --git a/server/src/internal/customers/attach/attachFunctions/upgradeFlow/createAndFilterContUseItems.ts b/server/src/internal/customers/attach/attachFunctions/upgradeFlow/createAndFilterContUseItems.ts new file mode 100644 index 000000000..ceb1ac0a0 --- /dev/null +++ b/server/src/internal/customers/attach/attachFunctions/upgradeFlow/createAndFilterContUseItems.ts @@ -0,0 +1,80 @@ +import { DrizzleCli } from "@/db/initDrizzle.js"; +import { AttachParams } from "@/internal/customers/cusProducts/AttachParams.js"; +import { BillingType, FullCusProduct } from "@autumn/shared"; +import Stripe from "stripe"; +import { getContUseInvoiceItems } from "./getContUseInvoiceItems.js"; +import { findPriceInStripeItems } from "@/external/stripe/stripeSubUtils/stripeSubItemUtils.js"; +import { cusProductToPrices } from "@/internal/customers/cusProducts/cusProductUtils/convertCusProduct.js"; + +export const createAndFilterContUseItems = async ({ + attachParams, + curMainProduct, + stripeSubs, + latestInvoice, + logger, +}: { + attachParams: AttachParams; + curMainProduct: FullCusProduct; + stripeSubs: Stripe.Subscription[]; + latestInvoice: Stripe.Invoice; + logger: any; +}) => { + let { newItems, oldItems, replaceables } = await getContUseInvoiceItems({ + attachParams, + cusProduct: curMainProduct!, + stripeSubs, + logger, + }); + + const { stripeCli, customer, org } = attachParams; + + const curPrices = cusProductToPrices({ + cusProduct: curMainProduct, + }); + const latestInvoiceItems = await stripeCli.invoices.listUpcomingLines({ + subscription: stripeSubs[0].id, + }); + + for (const item of latestInvoiceItems.data) { + let price = findPriceInStripeItems({ + prices: curPrices, + subItem: item as any, + billingType: BillingType.InArrearProrated, + }); + + if (!price) continue; + + logger.info(`Deleting item: ${item.description}, ${item.amount}`); + try { + await stripeCli.invoiceItems.del(item.id); + } catch (error) { + logger.error(`Failed to delete: ${error}`); + } + } + return; + + const items = [...oldItems, ...newItems]; + for (const item of items) { + if (!item.amount || item.amount === 0) { + continue; + } + logger.info( + `Adding invoice item: ${item.description}, ${item.description}`, + ); + } + + for (const item of items) { + if (!item.amount || item.amount === 0) { + continue; + } + + await stripeCli.invoiceItems.create({ + customer: customer.processor?.id!, + amount: Math.round(item.amount * 100), + description: item.description, + currency: org.default_currency || "usd", + }); + } + + return { newItems, oldItems, replaceables }; +}; diff --git a/server/src/internal/customers/attach/attachFunctions/upgradeFlow/getContUseInvoiceItems.ts b/server/src/internal/customers/attach/attachFunctions/upgradeFlow/getContUseInvoiceItems.ts new file mode 100644 index 000000000..65504d8c7 --- /dev/null +++ b/server/src/internal/customers/attach/attachFunctions/upgradeFlow/getContUseInvoiceItems.ts @@ -0,0 +1,314 @@ +import { DrizzleCli } from "@/db/initDrizzle.js"; +import { AttachParams } from "@/internal/customers/cusProducts/AttachParams.js"; +import { cusProductsToCusPrices } from "@/internal/customers/cusProducts/cusProductUtils/convertCusProduct.js"; +import { getItemsForCurProduct } from "@/internal/invoices/previewItemUtils/getItemsForCurProduct.js"; +import { + BillingType, + Entitlement, + FullCusProduct, + FullCustomerEntitlement, + FullCustomerPrice, + FullEntitlement, + getFeatureInvoiceDescription, + InvoiceItem, + OnIncrease, + PreviewLineItem, + Price, + UsagePriceConfig, + usageToFeatureName, +} from "@autumn/shared"; +import Stripe from "stripe"; +import { attachParamsToProduct } from "../../attachUtils/convertAttachParams.js"; +import { + getBillingType, + getPriceEntitlement, +} from "@/internal/products/prices/priceUtils.js"; +import { findCusEntByFeatureId } from "@/internal/customers/cusProducts/cusEnts/cusEntUtils/findCusEntUtils.js"; +import { + getExistingUsageFromCusProducts, + getRelatedCusPrice, +} from "@/internal/customers/cusProducts/cusEnts/cusEntUtils.js"; +import { getPrevAndNewPriceForUpgrade } from "@/trigger/arrearProratedUsage/handleProratedUpgrade.js"; +import { getUpgradeProrationInvoiceItem } from "@/trigger/arrearProratedUsage/createUpgradeProrationInvoice.js"; +import { priceToInvoiceAmount } from "@/internal/products/prices/priceUtils/getAmountForPrice.js"; +import { subToAutumnInterval } from "@/external/stripe/utils.js"; +import { getCycleWillReset } from "../../attachUtils/attachUtils.js"; +import { Decimal } from "decimal.js"; +import { shouldProrate } from "@/internal/products/prices/priceUtils/prorationConfigUtils.js"; +import { getNewContUsageAmount } from "@/internal/products/prices/priceUtils/arrearProratedUtils/getContUsageUpgradeItem.js"; +import { formatUnixToDate } from "@/utils/genUtils.js"; +import { constructPreviewItem } from "@/internal/invoices/previewItemUtils/constructPreviewItem.js"; +import { getReplaceables } from "@/internal/products/prices/priceUtils/arrearProratedUtils/getContUsageDowngradeItem.js"; +import { getUsageDiffLineItem } from "@/internal/products/prices/priceUtils/arrearProratedUtils/getUsageDiffLineItem.js"; +import { getCurContUseItems } from "@/internal/invoices/previewItemUtils/getCurContUseItems.js"; + +export const getContUsageItems = async ({ + price, + ent, + prevCusEnt, + prevCusPrice, + attachParams, + sub, + logger, + curItems, +}: { + price: Price; + ent: FullEntitlement; + prevCusEnt?: FullCustomerEntitlement; + prevCusPrice?: FullCustomerPrice; + attachParams: AttachParams; + sub: Stripe.Subscription | undefined; + logger: any; + curItems: PreviewLineItem[]; +}) => { + const { cusProducts, entities, internalEntityId, now } = attachParams; + const product = attachParamsToProduct({ attachParams }); + const prevEnt = prevCusEnt?.entitlement; + const billingUnits = (price.config as UsagePriceConfig).billing_units || 1; + + // 1. Get current usage + let curUsage = getExistingUsageFromCusProducts({ + entitlement: ent, + cusProducts, + entities, + carryExistingUsages: true, + internalEntityId, + }); + + let newBalance = ent.allowance! - curUsage; + let prevBalance = prevEnt ? prevEnt.allowance! - curUsage : 0; + + // 2. Get + const { prevRoundedOverage, newRoundedOverage, newRoundedUsage, newUsage } = + getPrevAndNewPriceForUpgrade({ + ent, + numReplaceables: prevCusEnt?.replaceables.length || 0, + price, + newBalance, + prevBalance, + logger, + }); + + let prevInvoiceItem = curItems.find( + (item) => item.price_id === prevCusPrice?.price.id, + ); + + let newOverageAmount = priceToInvoiceAmount({ + price, + overage: prevRoundedOverage, + now: now || Date.now(), + proration: sub + ? { + start: sub.current_period_start * 1000, + end: sub.current_period_end * 1000, + } + : undefined, + }); + + let newUsageAmount = getNewContUsageAmount({ + price, + ent, + newBalance, + prevBalance, + stripeSub: sub, + now: now || Date.now(), + }); + + logger.info(`Prev item amount: ${prevInvoiceItem?.amount}`); + logger.info(`New item amount: ${newOverageAmount}`); + logger.info(`New usage amount: ${newUsageAmount}`); + + let replaceables = getReplaceables({ + cusEnt: prevCusEnt!, + prevOverage: prevRoundedOverage, + newOverage: newRoundedOverage, + }); + + let prevItemAmount = new Decimal( + prevInvoiceItem?.amount ?? 0, + ).toDecimalPlaces(2); + let newItemAmount = new Decimal(newOverageAmount).toDecimalPlaces(2); + + const onlyUsageItems = prevItemAmount.add(newItemAmount).eq(0); + const isIncrease = newBalance <= prevBalance; + const willProrate = isIncrease + ? shouldProrate(price.proration_config?.on_increase) + : shouldProrate(price.proration_config?.on_decrease); + + // Case 1: Prev amount = new amount + + const newUsageLineItem = getUsageDiffLineItem({ + prevBalance, + newBalance, + price, + ent, + product, + newUsageAmount, + org: attachParams.org, + }); + + if (onlyUsageItems) { + return { + oldItem: null, + newItems: [newUsageLineItem], + replaceables, + }; + } + + // Case 2: Proration involved + let oldItem = prevInvoiceItem; + let newItems = []; + let start = formatUnixToDate(now); + + if (willProrate) { + const description = getFeatureInvoiceDescription({ + feature: ent.feature, + usage: newRoundedUsage, + billingUnits: (price.config as UsagePriceConfig).billing_units, + prodName: product.name, + }); + + newItems.push( + constructPreviewItem({ + price, + org: attachParams.org, + amount: new Decimal(newOverageAmount).plus(newUsageAmount).toNumber(), + description: `${description} (from ${start})`, + }), + ); + } else { + const usageDiff = new Decimal(prevBalance).sub(newBalance).toNumber(); + const usage = new Decimal(newUsage).sub(usageDiff).toNumber(); + const newRoundedUsage = new Decimal(usage) + .div(billingUnits) + .ceil() + .mul(billingUnits) + .toNumber(); + + // logger.info(`Usage diff: ${usageDiff}`); + // logger.info(`New rounded usage: ${newRoundedUsage}`); + + let overageDescription = getFeatureInvoiceDescription({ + feature: ent.feature, + usage: usage, + billingUnits: billingUnits, + prodName: product.name, + }); + + let newPreviewItem = constructPreviewItem({ + price, + org: attachParams.org, + amount: newOverageAmount, + description: `${overageDescription} (from ${start})`, + }); + + newItems = [newPreviewItem, newUsageLineItem]; + } + + return { + oldItem, + newItems, + replaceables, + }; +}; + +export const getContUseInvoiceItems = async ({ + cusProduct, + stripeSubs, + attachParams, + logger, +}: { + cusProduct: FullCusProduct; + stripeSubs: Stripe.Subscription[]; + attachParams: AttachParams; + logger: any; +}) => { + let cusPrices = cusProductsToCusPrices({ + cusProducts: [cusProduct], + billingType: BillingType.InArrearProrated, + }); + const now = attachParams.now || Date.now(); + const cusEnts = cusProduct.customer_entitlements; + const product = attachParamsToProduct({ attachParams }); + + const cycleWillReset = getCycleWillReset({ + attachParams, + stripeSubs, + }); + + const curItems = await getCurContUseItems({ + stripeSubs, + attachParams, + now: now || Date.now(), + }); + + let newEnts = product.entitlements; + + let oldItems = []; + let newItems = []; + let replaceables = []; + + for (const price of product.prices) { + let billingType = getBillingType(price.config); + if (billingType !== BillingType.InArrearProrated) { + continue; + } + + let ent = getPriceEntitlement(price, newEnts); + + let prevCusEnt = findCusEntByFeatureId({ + cusEnts, + feature: ent.feature, + }); + + let prevCusPrice = prevCusEnt + ? getRelatedCusPrice(prevCusEnt!, cusPrices) + : undefined; + + let sub = !cycleWillReset + ? stripeSubs.find( + (sub) => subToAutumnInterval(sub) === price.config.interval, + ) + : undefined; + + let { + oldItem, + newItems: itemsToAdd, + replaceables: replaceablesToAdd, + } = await getContUsageItems({ + price, + ent, + prevCusEnt, + prevCusPrice, + attachParams, + sub, + logger, + curItems, + }); + + if (oldItem) { + oldItems.push(oldItem); + } + + newItems.push(...itemsToAdd.filter((item) => item.amount !== 0)); + + replaceables.push(...(replaceablesToAdd || [])); + + // if (oldItem) { + // logger.info(`${oldItem.description} | Amount: ${oldItem.amount}`); + // } + + // for (const item of newItems) { + // logger.info(`${item.description} | Amount: ${item.amount}`); + // } + + // if (replaceables) { + // logger.info(`Number of replaceables: ${replaceables.length}`); + // } + } + return { + oldItems, + newItems, + replaceables, + }; +}; diff --git a/server/src/internal/customers/attach/attachFunctions/upgradeFlow/handleUpgradeFunction.ts b/server/src/internal/customers/attach/attachFunctions/upgradeFlow/handleUpgradeFunction.ts index 9140a7a04..21c9247ea 100644 --- a/server/src/internal/customers/attach/attachFunctions/upgradeFlow/handleUpgradeFunction.ts +++ b/server/src/internal/customers/attach/attachFunctions/upgradeFlow/handleUpgradeFunction.ts @@ -49,6 +49,7 @@ export const handleUpgradeFunction = async ({ const stripeSubs = await getStripeSubs({ stripeCli, subIds: curCusProduct.subscription_ids, + expand: ["items.data.price.tiers"], }); logger.info("1. Updating current subscriptions in Stripe"); diff --git a/server/src/internal/customers/attach/attachFunctions/upgradeFlow/updateStripeSubs.ts b/server/src/internal/customers/attach/attachFunctions/upgradeFlow/updateStripeSubs.ts index db7697320..155d4cd9f 100644 --- a/server/src/internal/customers/attach/attachFunctions/upgradeFlow/updateStripeSubs.ts +++ b/server/src/internal/customers/attach/attachFunctions/upgradeFlow/updateStripeSubs.ts @@ -8,10 +8,6 @@ import { AttachConfig, FullCusProduct } from "@autumn/shared"; import { subItemInCusProduct } from "@/external/stripe/stripeSubUtils/stripeSubItemUtils.js"; import { updateCurSchedules } from "./updateCurSchedules.js"; import { getStripeSubItems } from "@/external/stripe/stripeSubUtils/getStripeSubItems.js"; -import { - createUsageInvoiceItems, - resetUsageBalances, -} from "./createUsageInvoiceItems.js"; // UPGRADE FUNCTIONS diff --git a/server/src/internal/customers/attach/attachUtils/attachUtils.ts b/server/src/internal/customers/attach/attachUtils/attachUtils.ts new file mode 100644 index 000000000..d2dc8999c --- /dev/null +++ b/server/src/internal/customers/attach/attachUtils/attachUtils.ts @@ -0,0 +1,18 @@ +import { AttachParams } from "@/internal/customers/cusProducts/AttachParams.js"; +import { getFirstInterval } from "@/internal/products/prices/priceUtils/priceIntervalUtils.js"; +import { subToAutumnInterval } from "@/external/stripe/utils.js"; +import Stripe from "stripe"; +import { attachParamsToProduct } from "./convertAttachParams.js"; + +export const getCycleWillReset = ({ + attachParams, + stripeSubs, +}: { + attachParams: AttachParams; + stripeSubs: Stripe.Subscription[]; +}) => { + const product = attachParamsToProduct({ attachParams }); + const firstInterval = getFirstInterval({ prices: product.prices }); + const prevInterval = subToAutumnInterval(stripeSubs[0]); + return prevInterval !== firstInterval; +}; diff --git a/server/src/internal/customers/attach/attachUtils/processAttachBody.ts b/server/src/internal/customers/attach/attachUtils/processAttachBody.ts index 5822a37cd..6f98dcfeb 100644 --- a/server/src/internal/customers/attach/attachUtils/processAttachBody.ts +++ b/server/src/internal/customers/attach/attachUtils/processAttachBody.ts @@ -23,7 +23,7 @@ import { cusProductToEnts, cusProductToPrices, } from "../../cusProducts/cusProductUtils/convertCusProduct.js"; -import { handleNewProductItems } from "@/internal/products/product-items/productItemInitUtils.js"; +import { handleNewProductItems } from "@/internal/products/product-items/productItemUtils/handleNewProductItems.js"; import { getEntsWithFeature } from "@/internal/products/entitlements/entitlementUtils.js"; import { isMainProduct } from "@/internal/products/productUtils/classifyProduct.js"; diff --git a/server/src/internal/customers/attach/handleAttachPreview/getUpgradeProductPreview.ts b/server/src/internal/customers/attach/handleAttachPreview/getUpgradeProductPreview.ts index f2b9a6c5e..21dd2e58f 100644 --- a/server/src/internal/customers/attach/handleAttachPreview/getUpgradeProductPreview.ts +++ b/server/src/internal/customers/attach/handleAttachPreview/getUpgradeProductPreview.ts @@ -100,7 +100,7 @@ export const getUpgradeProductPreview = async ({ expand: ["items.data.price.tiers"], }); - let curPreviewItems = getItemsForCurProduct({ + let curPreviewItems = await getItemsForCurProduct({ stripeSubs, attachParams, now, diff --git a/server/src/internal/customers/cusProducts/attachUtils.ts b/server/src/internal/customers/cusProducts/attachUtils.ts index 15298d791..b8fa198d0 100644 --- a/server/src/internal/customers/cusProducts/attachUtils.ts +++ b/server/src/internal/customers/cusProducts/attachUtils.ts @@ -32,7 +32,7 @@ import { StatusCodes } from "http-status-codes"; import { getExistingCusProducts } from "./cusProductUtils/getExistingCusProducts.js"; import { getPricesForCusProduct } from "../change-product/scheduleUtils.js"; import { getOrCreateCustomer } from "@/internal/customers/cusUtils/getOrCreateCustomer.js"; -import { handleNewProductItems } from "@/internal/products/product-items/productItemInitUtils.js"; +import { handleNewProductItems } from "@/internal/products/product-items/productItemUtils/handleNewProductItems.js"; import { getBillingType } from "@/internal/products/prices/priceUtils.js"; import { Decimal } from "decimal.js"; import { DrizzleCli } from "@/db/initDrizzle.js"; diff --git a/server/src/internal/customers/cusProducts/cusEnts/CusEntitlementService.ts b/server/src/internal/customers/cusProducts/cusEnts/CusEntitlementService.ts index 12d4248b4..fab6d251d 100644 --- a/server/src/internal/customers/cusProducts/cusEnts/CusEntitlementService.ts +++ b/server/src/internal/customers/cusProducts/cusEnts/CusEntitlementService.ts @@ -11,6 +11,7 @@ import { features, FullCusEntWithProduct, FullCustomerEntitlement, + Replaceable, } from "@autumn/shared"; import { customerEntitlements } from "@autumn/shared"; import { StatusCodes } from "http-status-codes"; @@ -84,6 +85,7 @@ export class CusEntService { feature: item.features, }, customer_product: item.customer_products, + replaceables: [], })) as FullCusEntWithProduct[]; } @@ -126,6 +128,7 @@ export class CusEntService { feature: true, }, }, + replaceables: true, customer_product: withCusProduct || undefined, customer: true, }, @@ -147,6 +150,7 @@ export class CusEntService { return data as FullCustomerEntitlement & { customer: Customer; customer_product?: CusProduct; + replaceables?: Replaceable[]; }; } @@ -167,4 +171,22 @@ export class CusEntService { return data; } + + static async decrement({ + db, + id, + amount, + }: { + db: DrizzleCli; + id: string; + amount: number; + }) { + const data = await db + .update(customerEntitlements) + .set({ balance: sql`${customerEntitlements.balance} - ${amount}` }) + .where(eq(customerEntitlements.id, id)) + .returning(); + + return data; + } } diff --git a/server/src/internal/customers/cusProducts/cusEnts/RepService.ts b/server/src/internal/customers/cusProducts/cusEnts/RepService.ts new file mode 100644 index 000000000..706689588 --- /dev/null +++ b/server/src/internal/customers/cusProducts/cusEnts/RepService.ts @@ -0,0 +1,41 @@ +import { DrizzleCli } from "@/db/initDrizzle.js"; +import { InsertReplaceable, Replaceable, replaceables } from "@autumn/shared"; +import { eq, inArray } from "drizzle-orm"; + +export class RepService { + static async insert({ + db, + data, + }: { + db: DrizzleCli; + data: InsertReplaceable[]; + }) { + const inserted = await db.insert(replaceables).values(data).returning(); + return inserted as Replaceable[]; + } + + static async update({ + db, + id, + data, + }: { + db: DrizzleCli; + id: string; + data: any; + }) { + const updated = await db + .update(replaceables) + .set(data) + .where(eq(replaceables.id, id)) + .returning(); + return updated as Replaceable[]; + } + + static async deleteInIds({ db, ids }: { db: DrizzleCli; ids: string[] }) { + const deleted = await db + .delete(replaceables) + .where(inArray(replaceables.id, ids)) + .returning(); + return deleted as Replaceable[]; + } +} diff --git a/server/src/internal/customers/cusProducts/cusEnts/cusEntUtils/cusEntBalanceUtils.ts b/server/src/internal/customers/cusProducts/cusEnts/cusEntUtils/cusEntBalanceUtils.ts new file mode 100644 index 000000000..e69de29bb diff --git a/server/src/internal/customers/cusProducts/cusEnts/cusEntUtils/findCusEntUtils.ts b/server/src/internal/customers/cusProducts/cusEnts/cusEntUtils/findCusEntUtils.ts new file mode 100644 index 000000000..82ed26520 --- /dev/null +++ b/server/src/internal/customers/cusProducts/cusEnts/cusEntUtils/findCusEntUtils.ts @@ -0,0 +1,39 @@ +import { Feature, FullCustomerEntitlement } from "@autumn/shared"; + +export const findMainCusEntForFeature = ({ + cusEnts, + feature, +}: { + cusEnts: FullCustomerEntitlement[]; + feature: Feature; +}) => { + let mainCusEnt = cusEnts.find( + (e: any) => e.entitlement.feature.internal_id === feature.internal_id, + ); + + return mainCusEnt; +}; + +export const findLinkedCusEnts = ({ + cusEnts, + feature, +}: { + cusEnts: FullCustomerEntitlement[]; + feature: Feature; +}) => { + return cusEnts.filter( + (e: any) => e.entitlement.entity_feature_id === feature.id, + ); +}; + +export const findCusEntByFeatureId = ({ + cusEnts, + feature, +}: { + cusEnts: FullCustomerEntitlement[]; + feature: Feature; +}) => { + return cusEnts.find( + (e: any) => e.entitlement.feature.internal_id === feature.internal_id, + ); +}; diff --git a/server/src/internal/customers/cusProducts/cusEnts/cusEntUtils/linkedCusEntUtils.ts b/server/src/internal/customers/cusProducts/cusEnts/cusEntUtils/linkedCusEntUtils.ts new file mode 100644 index 000000000..830e895b1 --- /dev/null +++ b/server/src/internal/customers/cusProducts/cusEnts/cusEntUtils/linkedCusEntUtils.ts @@ -0,0 +1,31 @@ +import { FullCustomerEntitlement, Replaceable } from "@autumn/shared"; + +export const replaceEntityInCusEnt = ({ + cusEnt, + entityId, + replaceable, +}: { + cusEnt: FullCustomerEntitlement; + entityId: string; + replaceable: Replaceable; +}) => { + let newEntities = structuredClone(cusEnt.entities) || {}; + newEntities[replaceable.id] = newEntities[entityId]; + + delete newEntities[entityId]; + + return { newEntities }; +}; + +export const deleteEntityFromCusEnt = ({ + cusEnt, + entityId, +}: { + cusEnt: FullCustomerEntitlement; + entityId: string; +}) => { + let newEntities = structuredClone(cusEnt.entities) || {}; + delete newEntities[entityId]; + + return { newEntities }; +}; diff --git a/server/src/internal/customers/cusProducts/cusProductUtils/convertCusProduct.ts b/server/src/internal/customers/cusProducts/cusProductUtils/convertCusProduct.ts index 97f13760b..32799fa5b 100644 --- a/server/src/internal/customers/cusProducts/cusProductUtils/convertCusProduct.ts +++ b/server/src/internal/customers/cusProducts/cusProductUtils/convertCusProduct.ts @@ -19,9 +19,11 @@ import { getBillingType } from "@/internal/products/prices/priceUtils.js"; export const cusProductsToCusPrices = ({ cusProducts, inStatuses = [CusProductStatus.Active], + billingType, }: { cusProducts: FullCusProduct[]; inStatuses?: CusProductStatus[]; + billingType?: BillingType; }) => { const cusPrices: FullCustomerPrice[] = []; @@ -30,7 +32,14 @@ export const cusProductsToCusPrices = ({ continue; } - cusPrices.push(...cusProduct.customer_prices); + let prices = cusProduct.customer_prices; + if (billingType) { + prices = prices.filter( + (cp) => getBillingType(cp.price.config) === billingType, + ); + } + + cusPrices.push(...prices); } return cusPrices; @@ -122,3 +131,16 @@ export const cusProductsToSchedules = ({ scheduleIds, }); }; + +export const cusProductsToStripeSubs = ({ + cusProducts, + stripeCli, +}: { + cusProducts: FullCusProduct[]; + stripeCli: Stripe; +}) => { + return getStripeSubs({ + stripeCli, + subIds: cusProducts.flatMap((p: any) => p.subscription_ids || []), + }); +}; diff --git a/server/src/internal/customers/cusUtils/getOrCreateCustomer.ts b/server/src/internal/customers/cusUtils/getOrCreateCustomer.ts index 10e888dfb..af7f9a264 100644 --- a/server/src/internal/customers/cusUtils/getOrCreateCustomer.ts +++ b/server/src/internal/customers/cusUtils/getOrCreateCustomer.ts @@ -14,7 +14,7 @@ import { Organization, } from "@autumn/shared"; -import { createEntities } from "@/internal/api/entities/handlers/handleCreateEntity.js"; +import { createEntities } from "@/internal/entities/handlers/handleCreateEntity/handleCreateEntity.js"; import RecaseError from "@/utils/errorUtils.js"; import { StatusCodes } from "http-status-codes"; import { DrizzleCli } from "@/db/initDrizzle.js"; @@ -122,16 +122,12 @@ export const getOrCreateCustomer = async ({ let newEntities = await createEntities({ req, - db, - org, customerId, createEntityData: { id: entityId, name: entityData?.name, feature_id: entityData?.feature_id, }, - features, - env, logger, fromAutoCreate: true, }); diff --git a/server/src/internal/customers/getFullCusQuery.ts b/server/src/internal/customers/getFullCusQuery.ts index 24a84c25d..b6dc80cce 100644 --- a/server/src/internal/customers/getFullCusQuery.ts +++ b/server/src/internal/customers/getFullCusQuery.ts @@ -25,10 +25,6 @@ const buildCusProductsCTE = (inStatuses?: CusProductStatus[]) => { }; const withCusEntitlements = () => { - // SELECT COALESCE( - // json_agg(entitlement_data) FILTER (WHERE entitlement_data IS NOT NULL), - // '[]'::json - // ) return sql` SELECT json_agg(entitlement_data) FROM ( @@ -44,7 +40,15 @@ const buildCusProductsCTE = (inStatuses?: CusProductStatus[]) => { JOIN features f ON e.internal_feature_id = f.internal_id WHERE e.id = ce.entitlement_id ) AS ent_with_feature - ) AS entitlement + ) AS entitlement, + ( + SELECT COALESCE( + json_agg(row_to_json(r)) FILTER (WHERE r.id IS NOT NULL), + '[]'::json + ) + FROM replaceables r + WHERE r.cus_ent_id = ce.id + ) AS replaceables FROM customer_entitlements ce WHERE ce.customer_product_id = cp.id ) AS entitlement_data diff --git a/server/src/internal/customers/previews/checkProductUtils.ts b/server/src/internal/customers/previews/checkProductUtils.ts index 1fffd565d..7986704e5 100644 --- a/server/src/internal/customers/previews/checkProductUtils.ts +++ b/server/src/internal/customers/previews/checkProductUtils.ts @@ -5,11 +5,11 @@ import { import { isFeatureItem, isPriceItem, -} from "@/internal/products/product-items/getItemType.js"; +} from "@/internal/products/product-items/productItemUtils/getItemType.js"; import { Feature, Organization, ProductItem, ProductV2 } from "@autumn/shared"; import { formatCurrency, formatTiers } from "./previewUtils.js"; -import { isFeaturePriceItem } from "@/internal/products/product-items/getItemType.js"; +import { isFeaturePriceItem } from "@/internal/products/product-items/productItemUtils/getItemType.js"; import { notNullish } from "@/utils/genUtils.js"; import { getFeatureNameWithCapital } from "@/internal/features/utils/displayUtils.js"; diff --git a/server/src/internal/customers/previews/getNewProductPreview.ts b/server/src/internal/customers/previews/getNewProductPreview.ts index 5759de40e..6934eea7f 100644 --- a/server/src/internal/customers/previews/getNewProductPreview.ts +++ b/server/src/internal/customers/previews/getNewProductPreview.ts @@ -5,7 +5,7 @@ import { isOneOff } from "@/internal/products/productUtils.js"; import { isFeatureItem, isPriceItem, -} from "@/internal/products/product-items/getItemType.js"; +} from "@/internal/products/product-items/productItemUtils/getItemType.js"; import { getPricecnPrice, diff --git a/server/src/internal/customers/previews/getUpgradePreview.ts b/server/src/internal/customers/previews/getUpgradePreview.ts index c8f0b08d1..a834e9188 100644 --- a/server/src/internal/customers/previews/getUpgradePreview.ts +++ b/server/src/internal/customers/previews/getUpgradePreview.ts @@ -28,7 +28,7 @@ import { getOptions } from "@/internal/api/entitled/checkUtils.js"; import { isFeaturePriceItem, isPriceItem, -} from "@/internal/products/product-items/getItemType.js"; +} from "@/internal/products/product-items/productItemUtils/getItemType.js"; import { DrizzleCli } from "@/db/initDrizzle.js"; import { getBillingType } from "@/internal/products/prices/priceUtils.js"; import { cusProductToPrices } from "../cusProducts/cusProductUtils/convertCusProduct.js"; diff --git a/server/src/internal/entities/entityUtils/entityUtils.ts b/server/src/internal/entities/entityUtils/entityUtils.ts new file mode 100644 index 000000000..940230437 --- /dev/null +++ b/server/src/internal/entities/entityUtils/entityUtils.ts @@ -0,0 +1,33 @@ +import { generateId } from "@/utils/genUtils.js"; +import { AppEnv, Entity } from "@autumn/shared"; + +export const constructEntity = ({ + inputEntity, + feature, + internalCustomerId, + orgId, + env, + deleted = false, +}: { + inputEntity: any; + feature: any; + internalCustomerId: string; + orgId: string; + env: AppEnv; + deleted?: boolean; +}) => { + let entity: Entity = { + internal_id: generateId("ety"), + id: inputEntity.id, + name: inputEntity.name, + internal_customer_id: internalCustomerId, + feature_id: feature.id, + internal_feature_id: feature.internal_id, + org_id: orgId, + env, + deleted, + created_at: Date.now(), + }; + + return entity; +}; diff --git a/server/src/internal/entities/handlers/handleCreateEntity/createEntityForCusProduct.ts b/server/src/internal/entities/handlers/handleCreateEntity/createEntityForCusProduct.ts new file mode 100644 index 000000000..ae23746ca --- /dev/null +++ b/server/src/internal/entities/handlers/handleCreateEntity/createEntityForCusProduct.ts @@ -0,0 +1,140 @@ +import { DrizzleCli } from "@/db/initDrizzle.js"; +import { CusEntService } from "@/internal/customers/cusProducts/cusEnts/CusEntitlementService.js"; +import { getResetBalance } from "@/internal/customers/cusProducts/cusEnts/cusEntUtils.js"; +import { findLinkedCusEnts } from "@/internal/customers/cusProducts/cusEnts/cusEntUtils/findCusEntUtils.js"; +import { adjustAllowance } from "@/trigger/adjustAllowance.js"; +import { ExtendedRequest } from "@/utils/models/Request.js"; +import { + CreateEntity, + Feature, + FullCusProduct, + FullCustomer, + FullCustomerEntitlement, + Replaceable, +} from "@autumn/shared"; + +export const updateLinkedCusEnt = async ({ + db, + linkedCusEnt, + inputEntities, + entityToReplacement, +}: { + db: DrizzleCli; + linkedCusEnt: FullCustomerEntitlement; + inputEntities: CreateEntity[]; + entityToReplacement: Record; +}) => { + let newEntities = structuredClone(linkedCusEnt.entities) || {}; + for (const entity of inputEntities) { + let replaceableId = entityToReplacement[entity.id]; + let replaceableInEntities = replaceableId + ? newEntities[replaceableId] + : null; + + if (replaceableInEntities) { + newEntities[entity.id] = { + ...replaceableInEntities, + id: entity.id, + }; + delete newEntities[replaceableId]; + } else { + let balance = linkedCusEnt.entitlement.allowance!; + newEntities[entity.id] = { + id: entity.id, + balance, + adjustment: 0, + }; + } + + await CusEntService.update({ + db, + id: linkedCusEnt.id, + updates: { + entities: newEntities, + }, + }); + } +}; + +export const createEntityForCusProduct = async ({ + req, + feature, + customer, + cusProduct, + inputEntities, + logger, +}: { + req: ExtendedRequest; + feature: Feature; + customer: FullCustomer; + cusProduct: FullCusProduct; + inputEntities: CreateEntity[]; + logger: any; +}) => { + const { db, env, org } = req; + + const cusEnts = cusProduct.customer_entitlements; + const cusPrices = cusProduct.customer_prices; + const mainCusEnt = cusEnts.find( + (e: any) => e.entitlement.feature.id === feature.id, + ); + + // 1. If main cus ent: + let deletedReplaceables: Replaceable[] = []; + if (mainCusEnt) { + const originalBalance = mainCusEnt.balance || 0; + const newBalance = originalBalance - inputEntities.length; + + const { deletedReplaceables: deletedReplaceables_, invoice } = + await adjustAllowance({ + db, + env, + org, + cusPrices, + customer, + affectedFeature: feature, + cusEnt: { ...mainCusEnt, customer_product: cusProduct }, + originalBalance, + newBalance, + logger, + errorIfIncomplete: true, + // deduction: newCount + replacedCount, + // deduction: newEntities.length, + // product, + // replacedCount, + // fromEntities: true, + }); + + deletedReplaceables = deletedReplaceables_ || []; + + await CusEntService.decrement({ + db, + id: mainCusEnt.id, + amount: inputEntities.length, + }); + } + + const entityToReplacement: Record = {}; + for (let i = 0; i < deletedReplaceables.length; i++) { + const replaceable = deletedReplaceables[i]; + entityToReplacement[inputEntities[i].id] = replaceable.id; + + if (i >= inputEntities.length) { + break; + } + } + + const linkedCusEnts = findLinkedCusEnts({ + cusEnts, + feature, + }); + + for (const linkedCusEnt of linkedCusEnts) { + await updateLinkedCusEnt({ + db, + linkedCusEnt, + inputEntities, + entityToReplacement, + }); + } +}; diff --git a/server/src/internal/entities/handlers/handleCreateEntity/entityToAction.ts b/server/src/internal/entities/handlers/handleCreateEntity/entityToAction.ts new file mode 100644 index 000000000..a2a011a69 --- /dev/null +++ b/server/src/internal/entities/handlers/handleCreateEntity/entityToAction.ts @@ -0,0 +1,100 @@ +import { notNullish } from "@/utils/genUtils.js"; +import { nullish } from "@/utils/genUtils.js"; +import { Entity } from "@autumn/shared"; + +export const logEntityToAction = ({ + entityToAction, + logger, +}: { + entityToAction: any; + logger: any; +}) => { + for (const id in entityToAction) { + logger.info( + `${id} - ${entityToAction[id].action}${ + entityToAction[id].replace + ? ` (replace ${ + entityToAction[id].replace.id || + entityToAction[id].replace.internal_id + })` + : "" + }`, + ); + } +}; + +// Probably going to change +export const getEntityToAction = ({ + inputEntities, + existingEntities, + feature, + logger, +}: { + inputEntities: any[]; + existingEntities: Entity[]; + feature: any; + logger: any; +}) => { + let entityToAction: any = {}; + let createCount = 0; + let replacedEntities: string[] = []; + for (const inputEntity of inputEntities) { + let curEntity = existingEntities.find((e: any) => e.id === inputEntity.id); + + if (curEntity && curEntity.deleted) { + entityToAction[inputEntity.id] = { + action: "replace", + replace: curEntity, + entity: inputEntity, + }; + replacedEntities.push(curEntity.id); + continue; + } + + let replaced = false; + + for (const entity of existingEntities) { + if (entity.deleted && !replacedEntities.includes(entity.id)) { + replaced = true; + replacedEntities.push(entity.id); + + entityToAction[inputEntity.id] = { + action: "replace", + replace: entity, + entity: inputEntity, + }; + break; + } + + // If there's an entity with null ID and cur input entity has an ID, fill it up! + if ( + nullish(entity.id) && + notNullish(inputEntity.id) && + !replacedEntities.includes(entity.internal_id) && + entity.feature_id === feature.id + ) { + entityToAction[inputEntity.id] = { + action: "replace", + replace: entity, + entity: inputEntity, + }; + + replacedEntities.push(entity.internal_id); + replaced = true; + break; + } + } + + if (!replaced) { + entityToAction[inputEntity.id] = { + action: "create", + entity: inputEntity, + }; + createCount++; + } + } + + logEntityToAction({ entityToAction, logger }); + + return entityToAction; +}; diff --git a/server/src/internal/entities/handlers/handleCreateEntity/getInputEntities.ts b/server/src/internal/entities/handlers/handleCreateEntity/getInputEntities.ts new file mode 100644 index 000000000..9dc0fbdde --- /dev/null +++ b/server/src/internal/entities/handlers/handleCreateEntity/getInputEntities.ts @@ -0,0 +1,110 @@ +import { getOrCreateCustomer } from "@/internal/customers/cusUtils/getOrCreateCustomer.js"; +import RecaseError from "@/utils/errorUtils.js"; +import { ExtendedRequest } from "@/utils/models/Request.js"; +import { CreateEntity, CustomerData, Entity, ErrCode } from "@autumn/shared"; +import { StatusCodes } from "http-status-codes"; + +export const validateAndGetInputEntities = async ({ + req, + customerId, + customerData, + createEntityData, + logger, +}: { + req: ExtendedRequest; + customerId: string; + customerData?: CustomerData; + createEntityData: CreateEntity[] | CreateEntity; + logger: any; +}) => { + const { features } = req; + + // 1. Get customer + let customer = await getOrCreateCustomer({ + req, + customerId, + customerData, + withEntities: true, + }); + + if (!customer) { + throw new RecaseError({ + message: `Customer ${customerId} not found`, + code: ErrCode.CustomerNotFound, + }); + } + + // 2. Get input entities + let inputEntities: any[] = []; + if (Array.isArray(createEntityData)) { + inputEntities = createEntityData; + } else { + inputEntities = [createEntityData]; + } + + let featureIds = [...new Set(inputEntities.map((e: any) => e.feature_id))]; + if (featureIds.length > 1) { + throw new RecaseError({ + message: "Multiple features not supported", + code: ErrCode.InvalidInputs, + statusCode: StatusCodes.BAD_REQUEST, + }); + } + + let feature_id = featureIds[0]; + let feature = features.find((f: any) => f.id === feature_id); + + if (!feature_id) { + throw new RecaseError({ + message: + "Feature ID is required to create entity. Did you forget to pass it into entity_data?", + code: ErrCode.InvalidInputs, + statusCode: StatusCodes.BAD_REQUEST, + }); + } + + if (!feature) { + throw new RecaseError({ + message: `Create entity failed: feature ${feature_id} not found`, + code: ErrCode.FeatureNotFound, + statusCode: StatusCodes.NOT_FOUND, + }); + } + + let cusProducts = customer.customer_products; + let existingEntities = customer.entities; + + let noIdEntities = existingEntities.filter((e: Entity) => !e.id); + let noIdNewEntities = inputEntities.filter((e: CreateEntity) => !e.id); + + if (noIdEntities.length + noIdNewEntities.length > 1) { + throw new RecaseError({ + message: "Can only have one entity with no ID", + code: ErrCode.EntityIdRequired, + statusCode: StatusCodes.BAD_REQUEST, + }); + } + + for (const entity of existingEntities) { + if (inputEntities.some((e: any) => e.id === entity.id) && !entity.deleted) { + throw new RecaseError({ + message: `Entity ${entity.id} already exists`, + code: "ENTITY_ALREADY_EXISTS", + data: { + entity, + }, + statusCode: StatusCodes.CONFLICT, + }); + } + } + + return { + customer, + features, + inputEntities, + feature_id, + feature, + cusProducts, + existingEntities, + }; +}; diff --git a/server/src/internal/entities/handlers/handleCreateEntity/handleCreateEntity.ts b/server/src/internal/entities/handlers/handleCreateEntity/handleCreateEntity.ts new file mode 100644 index 000000000..25ecd9128 --- /dev/null +++ b/server/src/internal/entities/handlers/handleCreateEntity/handleCreateEntity.ts @@ -0,0 +1,304 @@ +import { CusEntService } from "@/internal/customers/cusProducts/cusEnts/CusEntitlementService.js"; +import { FeatureService } from "@/internal/features/FeatureService.js"; +import { OrgService } from "@/internal/orgs/OrgService.js"; +import RecaseError from "@/utils/errorUtils.js"; +import { EntityService } from "../../../api/entities/EntityService.js"; +import { + APIVersion, + CreateEntity, + CustomerData, + Entity, + ErrCode, +} from "@autumn/shared"; +import { notNullish, nullish } from "@/utils/genUtils.js"; +import { adjustAllowance } from "@/trigger/adjustAllowance.js"; +import { + getCusEntMasterBalance, + getRelatedCusPrice, +} from "@/internal/customers/cusProducts/cusEnts/cusEntUtils.js"; +import { getEntityResponse } from "../../../api/entities/getEntityUtils.js"; +import { StatusCodes } from "http-status-codes"; +import { orgToVersion } from "@/utils/versionUtils.js"; + +import { routeHandler } from "@/utils/routerUtils.js"; + +import { ExtendedRequest } from "@/utils/models/Request.js"; +import { constructEntity } from "../../entityUtils/entityUtils.js"; +import { validateAndGetInputEntities } from "./getInputEntities.js"; +import { getEntityToAction } from "./entityToAction.js"; +import { createEntityForCusProduct } from "./createEntityForCusProduct.js"; + +export const createEntities = async ({ + req, + logger, + customerId, + customerData, + createEntityData, + withAutumnId = false, + apiVersion, + fromAutoCreate = false, +}: { + req: ExtendedRequest; + customerData?: CustomerData; + logger: any; + customerId: string; + createEntityData: CreateEntity[] | CreateEntity; + withAutumnId?: boolean; + apiVersion?: APIVersion; + fromAutoCreate?: boolean; +}) => { + const { db, org, env } = req; + + // 1. Get data + const { + customer, + inputEntities, + feature_id, + feature, + cusProducts, + existingEntities, + } = await validateAndGetInputEntities({ + req, + customerId, + customerData, + createEntityData, + logger, + }); + + // const entityToAction = getEntityToAction({ + // inputEntities, + // existingEntities, + // feature, + // logger, + // }); + + for (const cusProduct of cusProducts) { + await createEntityForCusProduct({ + req, + feature, + customer, + cusProduct, + inputEntities, + logger, + }); + // let cusEnts = cusProduct.customer_entitlements; + // let product = cusProduct.product; + + // let mainCusEnt = cusEnts.find( + // (e: any) => e.entitlement.feature.id === feature_id, + // ); + + // let linkedCusEnts = cusEnts.filter( + // (e: any) => e.entitlement.entity_feature_id === feature.id, + // ); + + // if (linkedCusEnts.length > 0 && inputEntities.some((e: any) => !e.id)) { + // throw new RecaseError({ + // message: "Entity ID is required", + // code: ErrCode.EntityIdRequired, + // statusCode: StatusCodes.BAD_REQUEST, + // }); + // } + + // let replacedCount = Object.keys(entityToAction).filter( + // (id) => entityToAction[id].action === "replace", + // ).length; + + // let newCount = Object.keys(entityToAction).filter( + // (id) => entityToAction[id].action === "create", + // ).length; + + // if (mainCusEnt) { + // if (fromAutoCreate) { + // let cusPrice = getRelatedCusPrice( + // mainCusEnt, + // cusProduct.customer_prices || [], + // ); + + // if (cusPrice) { + // return []; + // } + // } + + // let { unused } = getCusEntMasterBalance({ + // cusEnt: mainCusEnt, + // entities: existingEntities, + // }); + + // let mainCusEntBalance = mainCusEnt.balance || 0; + // const originalBalance = mainCusEntBalance + (unused || 0); + // const newBalance = + // mainCusEntBalance - (newCount + replacedCount) + (unused || 0); + + // await adjustAllowance({ + // db, + + // env, + // org, + // cusPrices: cusProducts.flatMap((p: any) => p.customer_prices), + // customer, + // affectedFeature: feature, + // cusEnt: { ...mainCusEnt, customer_product: cusProduct }, + // originalBalance, + // newBalance, + // deduction: newCount + replacedCount, + // product, + // replacedCount, + // fromEntities: true, + // }); + + // await CusEntService.update({ + // db, + // id: mainCusEnt.id, + // updates: { balance: mainCusEntBalance - newCount }, + // }); + // } + + // // Linked cus ent stuff... + // for (const linkedCusEnt of linkedCusEnts) { + // let allowance = linkedCusEnt?.entitlement.allowance; + // let newEntities = linkedCusEnt?.entities || {}; + + // for (const entity of inputEntities) { + // let entityAction = entityToAction[entity.id]; + + // if (entityAction.action === "create") { + // newEntities[entity.id] = { + // id: entity.id, + // balance: allowance!, + // adjustment: 0, + // }; + // } else if (entityAction.action === "replace") { + // let tmp = newEntities[entityAction.replace.id]; + // delete newEntities[entityAction.replace.id]; + // newEntities[entity.id] = { + // ...tmp, + // id: entity.id, + // }; + // } + // } + + // await CusEntService.update({ + // db, + // id: linkedCusEnt.id, + // updates: { entities: newEntities }, + // }); + // } + } + + let data = inputEntities.map((e: any) => + constructEntity({ + inputEntity: e, + feature, + internalCustomerId: customer.internal_id, + orgId: org.id, + env, + }), + ); + + let newEntities = await EntityService.insert({ + db, + data, + }); + + // 4. CREATE ENTITIES + // let newEntities: Entity[] = []; + // for (const id in entityToAction) { + // let { action, entity, replace } = entityToAction[id]; + + // // Create and add to customer entitlement? + // if (action === "create") { + // let results = await EntityService.insert({ + // db, + // data: constructEntity({ + // inputEntity: entity, + // feature, + // internalCustomerId: customer.internal_id, + // orgId: org.id, + // env, + // }), + // }); + + // newEntities.push(results[0]); + // } else if (action === "replace") { + // let updatedEntity = await EntityService.update({ + // db, + // internalId: replace.internal_id, + // update: { + // id: entity.id, + // name: entity.name, + // deleted: false, + // }, + // }); + + // newEntities.push(updatedEntity); + // } + // } + + if (fromAutoCreate) { + return newEntities; + } + + let { entities } = await getEntityResponse({ + db, + entityIds: inputEntities.map((e: any) => e.id), + org, + env, + customerId: customer.id || customer.internal_id, + withAutumnId, + apiVersion: apiVersion!, + }); + + return entities; +}; + +export const handlePostEntityRequest = async (req: any, res: any) => + routeHandler({ + req, + res, + action: "create entity", + handler: async (req: any, res: any) => { + const { env, db, logtail: logger } = req; + + const [org, features] = await Promise.all([ + OrgService.getFromReq(req), + FeatureService.getFromReq(req), + ]); + + let apiVersion = orgToVersion({ + org, + reqApiVersion: req.apiVersion, + }); + + let customerData = + Array.isArray(req.body) && req.body.length > 0 + ? req.body[0].customer_data + : req.body.customer_data; + + const entities = await createEntities({ + req, + logger, + customerId: req.params.customer_id, + createEntityData: req.body, + customerData, + withAutumnId: req.query.with_autumn_id === "true", + apiVersion, + }); + + logger.info(` Created / replaced entities!`); + + if (apiVersion < APIVersion.v1_2) { + res.status(200).json({ + success: true, + }); + return; + } + if (Array.isArray(req.body)) { + res.status(200).json({ + list: entities, + }); + } else { + res.status(200).json(entities[0]); + } + }, + }); diff --git a/server/src/internal/entities/handlers/handleDeleteEntity/cancelSubsForEntity.ts b/server/src/internal/entities/handlers/handleDeleteEntity/cancelSubsForEntity.ts new file mode 100644 index 000000000..2e29f531f --- /dev/null +++ b/server/src/internal/entities/handlers/handleDeleteEntity/cancelSubsForEntity.ts @@ -0,0 +1,52 @@ +import { createStripeCli } from "@/external/stripe/utils.js"; +import { cancelCurSubs } from "@/internal/customers/change-product/handleDowngrade/cancelCurSubs.js"; +import { cusProductsToStripeSubs } from "@/internal/customers/cusProducts/cusProductUtils/convertCusProduct.js"; +import { removeScheduledProduct } from "@/internal/customers/handlers/handleCusProductExpired.js"; +import { ExtendedRequest } from "@/utils/models/Request.js"; +import { CusProductStatus, Entity, FullCusProduct } from "@autumn/shared"; + +export const cancelSubsForEntity = async ({ + req, + cusProducts, + entity, +}: { + req: ExtendedRequest; + cusProducts: FullCusProduct[]; + entity: Entity; +}) => { + const { org, env, db, logtail: logger } = req; + try { + let stripeCli = createStripeCli({ org, env }); + let curSubs = await cusProductsToStripeSubs({ + cusProducts, + stripeCli, + }); + + for (const cusProduct of cusProducts) { + if (cusProduct.internal_entity_id !== entity.internal_id) { + continue; + } + + if (cusProduct.status == CusProductStatus.Scheduled) { + await removeScheduledProduct({ + req, + db, + cusProduct, + cusProducts, + org, + env, + logger, + renewCurProduct: false, + }); + } else { + await cancelCurSubs({ + curCusProduct: cusProduct, + curSubs, + stripeCli, + }); + } + } + } catch (error) { + logger.error("Failed to cancel subs for deleted entity", { error }); + } +}; diff --git a/server/src/internal/invoices/invoiceItemUtils/invoiceItemUtils.ts b/server/src/internal/invoices/invoiceItemUtils/invoiceItemUtils.ts new file mode 100644 index 000000000..3c0c85f2b --- /dev/null +++ b/server/src/internal/invoices/invoiceItemUtils/invoiceItemUtils.ts @@ -0,0 +1,59 @@ +import { Organization, Price, Product, UsagePriceConfig } from "@autumn/shared"; +import { Decimal } from "decimal.js"; +import Stripe from "stripe"; + +export const constructStripeInvoiceItem = ({ + product, + amount, + org, + price, + description, + stripeSubId, + stripeCustomerId, + periodStart, + periodEnd, +}: { + product: Product; + amount: number; + org: Organization; + price: Price; + description: string; + stripeSubId: string; + stripeCustomerId: string; + periodStart: number; + periodEnd: number; +}) => { + const config = price.config as UsagePriceConfig; + + const amountInCents = Math.floor( + new Decimal(amount).mul(100).round().toNumber(), + ); + + let priceData = + amountInCents > 0 + ? { + price_data: { + unit_amount: amountInCents, + currency: org.default_currency || "usd", + product: config.stripe_product_id || product.processor?.id!, + }, + } + : { + amount: amountInCents, + currency: org.default_currency || "usd", + }; + + let invoiceItem: Stripe.InvoiceItemCreateParams = { + subscription: stripeSubId, + ...priceData, + + description, + customer: stripeCustomerId, + period: { + start: periodStart, + end: periodEnd, + }, + }; + + return invoiceItem; +}; diff --git a/server/src/internal/invoices/invoiceUtils/createAndFinalizeInvoice.ts b/server/src/internal/invoices/invoiceUtils/createAndFinalizeInvoice.ts new file mode 100644 index 000000000..b9146228c --- /dev/null +++ b/server/src/internal/invoices/invoiceUtils/createAndFinalizeInvoice.ts @@ -0,0 +1,35 @@ +import Stripe from "stripe"; + +export const createAndFinalizeInvoice = async ({ + stripeCli, + stripeCusId, + stripeSubId, + invoiceItems, +}: { + stripeCli: Stripe; + stripeCusId: string; + stripeSubId: string; + invoiceItems?: Stripe.InvoiceItemCreateParams[]; +}) => { + const invoice = await stripeCli.invoices.create({ + customer: stripeCusId, + auto_advance: false, + subscription: stripeSubId, + }); + + if (invoiceItems) { + for (const item of invoiceItems) { + await stripeCli.invoiceItems.create({ + ...item, + invoice: invoice.id, + customer: stripeCusId, + }); + } + } + + let finalInvoice = await stripeCli.invoices.finalizeInvoice(invoice.id, { + auto_advance: false, + }); + + return { invoice: finalInvoice }; +}; diff --git a/server/src/internal/invoices/previewItemUtils/constructPreviewItem.ts b/server/src/internal/invoices/previewItemUtils/constructPreviewItem.ts new file mode 100644 index 000000000..3b52dadce --- /dev/null +++ b/server/src/internal/invoices/previewItemUtils/constructPreviewItem.ts @@ -0,0 +1,34 @@ +import { priceToUsageModel } from "@/internal/products/prices/priceUtils/convertPrice.js"; +import { formatAmount } from "@/utils/formatUtils.js"; +import { Organization, Price } from "@autumn/shared"; + +export const constructPreviewItem = ({ + priceStr, + price, + org, + amount, + description, +}: { + priceStr?: string; + price: Price; + org: Organization; + amount?: number; + description: string; +}) => { + if (amount) { + priceStr = formatAmount({ + org, + amount, + }); + } else { + priceStr = priceStr; + } + + return { + price: priceStr!, + description, + amount, + usage_model: priceToUsageModel(price), + price_id: price.id!, + }; +}; diff --git a/server/src/internal/invoices/previewItemUtils/getCurContUseItems.ts b/server/src/internal/invoices/previewItemUtils/getCurContUseItems.ts new file mode 100644 index 000000000..aedc1a393 --- /dev/null +++ b/server/src/internal/invoices/previewItemUtils/getCurContUseItems.ts @@ -0,0 +1,109 @@ +import { getSubItemAmount } from "@/external/stripe/stripeSubUtils/getSubItemAmount.js"; +import { findPriceInStripeItems } from "@/external/stripe/stripeSubUtils/stripeSubItemUtils.js"; +import { AttachParams } from "@/internal/customers/cusProducts/AttachParams.js"; + +import { + BillingType, + getFeatureInvoiceDescription, + PreviewLineItem, + UsagePriceConfig, +} from "@autumn/shared"; +import { Decimal } from "decimal.js"; +import Stripe from "stripe"; +import { getProration } from "./getItemsForNewProduct.js"; +import { calculateProrationAmount } from "../prorationUtils.js"; +import { formatUnixToDate } from "@/utils/genUtils.js"; +import { formatAmount } from "@/utils/formatUtils.js"; +import { + priceToFeature, + priceToUsageModel, +} from "@/internal/products/prices/priceUtils/convertPrice.js"; +import { attachParamToCusProducts } from "@/internal/customers/attach/attachUtils/convertAttachParams.js"; +import { + cusProductToEnts, + cusProductToPrices, +} from "@/internal/customers/cusProducts/cusProductUtils/convertCusProduct.js"; +import { getExistingUsageFromCusProducts } from "@/internal/customers/cusProducts/cusEnts/cusEntUtils.js"; +import { getPriceEntitlement } from "@/internal/products/prices/priceUtils.js"; + +export const getCurContUseItems = async ({ + stripeSubs, + attachParams, + now, +}: { + stripeSubs: Stripe.Subscription[]; + attachParams: AttachParams; + now: number; +}) => { + const { features } = attachParams; + const { curMainProduct } = attachParamToCusProducts({ attachParams }); + const curCusProduct = curMainProduct!; + const curPrices = cusProductToPrices({ cusProduct: curCusProduct }); + const curEnts = cusProductToEnts({ cusProduct: curCusProduct }); + + let items: PreviewLineItem[] = []; + + for (const sub of stripeSubs) { + for (const item of sub.items.data) { + const price = findPriceInStripeItems({ + prices: curPrices, + subItem: item, + billingType: BillingType.InArrearProrated, + }); + + if (!price) continue; + + const totalAmountCents = getSubItemAmount({ subItem: item }); + const totalAmount = new Decimal(totalAmountCents).div(100).toNumber(); + const periodEnd = sub.current_period_end * 1000; + const ent = getPriceEntitlement(price, curEnts); + + if (now < periodEnd) { + const finalProration = getProration({ + now, + interval: price.config.interval!, + anchorToUnix: sub.current_period_end * 1000, + })!; + + const proratedAmount = -calculateProrationAmount({ + periodEnd: finalProration?.end, + periodStart: finalProration?.start, + now, + amount: totalAmount, + }); + + const existingUsage = getExistingUsageFromCusProducts({ + entitlement: ent, + cusProducts: [curCusProduct], + entities: attachParams.entities, + carryExistingUsages: true, + internalEntityId: attachParams.internalEntityId, + }); + + const feature = priceToFeature({ price, features }); + + let description = getFeatureInvoiceDescription({ + feature: feature!, + usage: existingUsage, + billingUnits: (price.config as UsagePriceConfig).billing_units, + prodName: curMainProduct?.product.name, + }); + + description = `Unused ${description} (from ${formatUnixToDate(now)})`; + + items.push({ + price: formatAmount({ + org: attachParams.org, + amount: proratedAmount, + }), + description, + amount: proratedAmount, + usage_model: priceToUsageModel(price), + price_id: price.id!, + }); + } + } + } + + return items; +}; diff --git a/server/src/internal/invoices/previewItemUtils/getItemsForCurProduct.ts b/server/src/internal/invoices/previewItemUtils/getItemsForCurProduct.ts index 0112d9e26..cbe400f00 100644 --- a/server/src/internal/invoices/previewItemUtils/getItemsForCurProduct.ts +++ b/server/src/internal/invoices/previewItemUtils/getItemsForCurProduct.ts @@ -1,29 +1,22 @@ +import Stripe from "stripe"; import { findPriceInStripeItems } from "@/external/stripe/stripeSubUtils/stripeSubItemUtils.js"; import { attachParamToCusProducts } from "@/internal/customers/attach/attachUtils/convertAttachParams.js"; import { cusProductToPrices } from "@/internal/customers/cusProducts/cusProductUtils/convertCusProduct.js"; import { AttachParams } from "@/internal/customers/cusProducts/AttachParams.js"; -import Stripe from "stripe"; import { getSubItemAmount } from "@/external/stripe/stripeSubUtils/getSubItemAmount.js"; import { Decimal } from "decimal.js"; import { calculateProrationAmount } from "../prorationUtils.js"; import { getBillingType } from "@/internal/products/prices/priceUtils.js"; -import { - BillingType, - FeatureUsageType, - PreviewItem, - UsageModel, -} from "@autumn/shared"; +import { BillingType, PreviewLineItem } from "@autumn/shared"; import { priceToInvoiceDescription } from "../invoiceFormatUtils.js"; -import { formatUnixToDate, formatUnixToDateTime } from "@/utils/genUtils.js"; +import { formatUnixToDate } from "@/utils/genUtils.js"; import { formatAmount } from "@/utils/formatUtils.js"; import { getProration } from "./getItemsForNewProduct.js"; import { getCusPriceUsage } from "@/internal/customers/cusProducts/cusPrices/cusPriceUtils.js"; -import { - priceToFeature, - priceToUsageModel, -} from "@/internal/products/prices/priceUtils/convertPrice.js"; +import { priceToUsageModel } from "@/internal/products/prices/priceUtils/convertPrice.js"; +import { getContUseInvoiceItems } from "@/internal/customers/attach/attachFunctions/upgradeFlow/getContUseInvoiceItems.js"; -export const getItemsForCurProduct = ({ +export const getItemsForCurProduct = async ({ stripeSubs, attachParams, now, @@ -38,7 +31,7 @@ export const getItemsForCurProduct = ({ const curCusProduct = curMainProduct!; const curPrices = cusProductToPrices({ cusProduct: curCusProduct }); - const items: PreviewItem[] = []; + let items: PreviewLineItem[] = []; for (const sub of stripeSubs) { for (const item of sub.items.data) { const price = findPriceInStripeItems({ @@ -49,7 +42,11 @@ export const getItemsForCurProduct = ({ if (!price) continue; const billingType = getBillingType(price.config); - if (billingType == BillingType.UsageInArrear) continue; + if ( + billingType == BillingType.UsageInArrear || + billingType == BillingType.InArrearProrated + ) + continue; const totalAmountCents = getSubItemAmount({ subItem: item }); const totalAmount = new Decimal(totalAmountCents).div(100).toNumber(); @@ -90,11 +87,21 @@ export const getItemsForCurProduct = ({ description, amount: proratedAmount, usage_model: priceToUsageModel(price), + price_id: price.id!, }); } } } + let { oldItems, newItems } = await getContUseInvoiceItems({ + stripeSubs, + attachParams, + logger, + cusProduct: curCusProduct, + }); + + items = [...items, ...(oldItems || []), ...(newItems || [])]; + for (const price of curPrices) { let billingType = getBillingType(price.config); @@ -114,6 +121,8 @@ export const getItemsForCurProduct = ({ }), description, amount, + price_id: price.id!, + usage_model: priceToUsageModel(price), }); } } diff --git a/server/src/internal/invoices/previewItemUtils/getItemsForNewProduct.ts b/server/src/internal/invoices/previewItemUtils/getItemsForNewProduct.ts index ef8558b00..e8a0eefea 100644 --- a/server/src/internal/invoices/previewItemUtils/getItemsForNewProduct.ts +++ b/server/src/internal/invoices/previewItemUtils/getItemsForNewProduct.ts @@ -30,6 +30,7 @@ import { subtractBillingIntervalUnix, } from "../../products/prices/billingIntervalUtils.js"; import { priceToUsageModel } from "@/internal/products/prices/priceUtils/convertPrice.js"; +import { getContUseInvoiceItems } from "@/internal/customers/attach/attachFunctions/upgradeFlow/getContUseInvoiceItems.js"; const getDefaultPriceStr = ({ org, @@ -147,13 +148,13 @@ export const getItemsForNewProduct = ({ product: newProduct, }); - if (proration) { + if (finalProration) { description = `${description} (from ${formatUnixToDate(now)})`; } items.push({ - // price: formatAmount({ org, amount }), - price: "", + price: formatAmount({ org, amount }), + // price: "", description, amount, usage_model: priceToUsageModel(price), @@ -161,20 +162,24 @@ export const getItemsForNewProduct = ({ continue; } - if (billingType == BillingType.UsageInAdvance) continue; - - if (billingType == BillingType.UsageInArrear) { - items.push({ - price: getDefaultPriceStr({ org, price, ent, features }), - description: newPriceToInvoiceDescription({ - org, - price, - product: newProduct, - }), - usage_model: priceToUsageModel(price), - }); + if ( + billingType == BillingType.UsageInAdvance || + billingType == BillingType.InArrearProrated + ) continue; - } + + // if (billingType == BillingType.UsageInArrear) { + // items.push({ + // price: getDefaultPriceStr({ org, price, ent, features }), + // description: newPriceToInvoiceDescription({ + // org, + // price, + // product: newProduct, + // }), + // usage_model: priceToUsageModel(price), + // }); + // continue; + // } const usage = getExistingUsageFromCusProducts({ entitlement: ent, @@ -219,12 +224,6 @@ export const getItemsForNewProduct = ({ usage_model: priceToUsageModel(price), }); } - - // // const finalAmount = cycleWillReset ? amount : proratedAmount; - - // console.log("Item:", description); - // console.log("Amount: ", amount); - // console.log("--------------------------------"); } for (const item of items) { diff --git a/server/src/internal/invoices/prorationUtils.ts b/server/src/internal/invoices/prorationUtils.ts index 8443b9d8a..5e5eadb15 100644 --- a/server/src/internal/invoices/prorationUtils.ts +++ b/server/src/internal/invoices/prorationUtils.ts @@ -10,17 +10,20 @@ export const calculateProrationAmount = ({ periodStart, now, amount, + allowNegative = false, }: { periodEnd: number; periodStart: number; now: number; amount: number; + allowNegative?: boolean; }) => { const num = new Decimal(periodEnd).minus(now); const denom = new Decimal(periodEnd).minus(periodStart); const proratedAmount = num.div(denom).mul(amount); - if (proratedAmount.lte(0)) { + + if (proratedAmount.lte(0) && !allowNegative) { return 0; } diff --git a/server/src/internal/orgs/onboarding/parseChatProducts.ts b/server/src/internal/orgs/onboarding/parseChatProducts.ts index 0d4813315..9fa4cb79b 100644 --- a/server/src/internal/orgs/onboarding/parseChatProducts.ts +++ b/server/src/internal/orgs/onboarding/parseChatProducts.ts @@ -1,5 +1,5 @@ import { DrizzleCli } from "@/db/initDrizzle.js"; -import { handleNewProductItems } from "@/internal/products/product-items/productItemInitUtils.js"; +import { handleNewProductItems } from "@/internal/products/product-items/productItemUtils/handleNewProductItems.js"; import { constructProduct } from "@/internal/products/productUtils.js"; import { AppEnv, diff --git a/server/src/internal/products/compareProductUtils.ts b/server/src/internal/products/compareProductUtils.ts index c2f689eae..04068c41e 100644 --- a/server/src/internal/products/compareProductUtils.ts +++ b/server/src/internal/products/compareProductUtils.ts @@ -15,7 +15,7 @@ import { freeTrialsAreSame } from "./free-trials/freeTrialUtils.js"; import { isFeaturePriceItem, isPriceItem, -} from "./product-items/getItemType.js"; +} from "./product-items/productItemUtils/getItemType.js"; export const productsAreSame = ({ newProductV1, diff --git a/server/src/internal/products/internalProductRouter.ts b/server/src/internal/products/internalProductRouter.ts index a5256a9dc..f1c725028 100644 --- a/server/src/internal/products/internalProductRouter.ts +++ b/server/src/internal/products/internalProductRouter.ts @@ -12,7 +12,7 @@ import { CusProdReadService } from "../customers/cusProducts/CusProdReadService. import { MigrationService } from "../migrations/MigrationService.js"; import { RewardProgramService } from "../rewards/RewardProgramService.js"; import { mapToProductV2 } from "./productV2Utils.js"; -import { isFeaturePriceItem } from "./product-items/getItemType.js"; +import { isFeaturePriceItem } from "./product-items/productItemUtils/getItemType.js"; import RecaseError, { handleFrontendReqError } from "@/utils/errorUtils.js"; import { createOrgResponse } from "../orgs/orgUtils.js"; diff --git a/server/src/internal/products/pricecn/pricecnUtils.ts b/server/src/internal/products/pricecn/pricecnUtils.ts index e9de32e36..bca992b81 100644 --- a/server/src/internal/products/pricecn/pricecnUtils.ts +++ b/server/src/internal/products/pricecn/pricecnUtils.ts @@ -10,8 +10,8 @@ import { CusProductStatus, } from "@autumn/shared"; import { features } from "process"; -import { isPriceItem } from "../product-items/getItemType.js"; -import { isFeaturePriceItem } from "../product-items/getItemType.js"; +import { isPriceItem } from "../product-items/productItemUtils/getItemType.js"; +import { isFeaturePriceItem } from "../product-items/productItemUtils/getItemType.js"; import RecaseError from "@/utils/errorUtils.js"; import { nullish } from "@/utils/genUtils.js"; import { numberWithCommas } from "tests/utils/general/numberUtils.js"; diff --git a/server/src/internal/products/prices/priceInitUtils.ts b/server/src/internal/products/prices/priceInitUtils.ts index e933620b9..fa616952a 100644 --- a/server/src/internal/products/prices/priceInitUtils.ts +++ b/server/src/internal/products/prices/priceInitUtils.ts @@ -193,30 +193,27 @@ export const pricesAreSame = ( (t) => `${t.to} (${t.amount})`, )} !== ${usageConfig2.usage_tiers.map((t) => `${t.to} (${t.amount})`)}`, }, - // stripe_price_id: { - // condition: - // usageConfig1.stripe_price_id !== usageConfig2.stripe_price_id, - // message: `Stripe price ID different: ${usageConfig1.stripe_price_id} !== ${usageConfig2.stripe_price_id}`, - // }, - // stripe_placeholder_price_id: { - // condition: - // usageConfig1.stripe_placeholder_price_id !== - // usageConfig2.stripe_placeholder_price_id, - // message: `Stripe placeholder price ID different: ${usageConfig1.stripe_placeholder_price_id} !== ${usageConfig2.stripe_placeholder_price_id}`, - // }, - // stripe_meter_id: { - // condition: - // usageConfig1.stripe_meter_id !== usageConfig2.stripe_meter_id, - // message: `Stripe meter ID different: ${usageConfig1.stripe_meter_id} !== ${usageConfig2.stripe_meter_id}`, - // }, - // stripe_product_id: { - // condition: - // usageConfig1.stripe_product_id !== usageConfig2.stripe_product_id, - // message: `Stripe product ID different: ${usageConfig1.stripe_product_id} !== ${usageConfig2.stripe_product_id}`, - // }, }; - let pricesAreDiff = Object.values(diffs).some((d) => d.condition); + const prorationConfig1 = price1.proration_config; + const prorationConfig2 = price2.proration_config; + + const prorationConfigDiff = { + on_increase: { + condition: + prorationConfig1?.on_increase != prorationConfig2?.on_increase, + message: `On increase different: ${prorationConfig1?.on_increase} != ${prorationConfig2?.on_increase}`, + }, + on_decrease: { + condition: + prorationConfig1?.on_decrease != prorationConfig2?.on_decrease, + message: `On decrease different: ${prorationConfig1?.on_decrease} != ${prorationConfig2?.on_decrease}`, + }, + }; + + let pricesAreDiff = + Object.values(diffs).some((d) => d.condition) || + Object.values(prorationConfigDiff).some((d) => d.condition); if (pricesAreDiff && logDifferences) { console.log(`Usage price different: ${usageConfig1.feature_id}`); @@ -225,6 +222,9 @@ export const pricesAreSame = ( Object.values(diffs) .filter((d) => d.condition) .map((d) => d.message), + Object.values(prorationConfigDiff) + .filter((d) => d.condition) + .map((d) => d.message), ); } diff --git a/server/src/internal/products/prices/priceUtils/arrearProratedUtils/getContUsageDowngradeItem.ts b/server/src/internal/products/prices/priceUtils/arrearProratedUtils/getContUsageDowngradeItem.ts new file mode 100644 index 000000000..8be7ca97c --- /dev/null +++ b/server/src/internal/products/prices/priceUtils/arrearProratedUtils/getContUsageDowngradeItem.ts @@ -0,0 +1,94 @@ +import { DrizzleCli } from "@/db/initDrizzle.js"; +import { handleCreateReplaceables } from "@/trigger/arrearProratedUsage/handleCreateReplaceables.js"; +import { generateId } from "@/utils/genUtils.js"; +import { + Feature, + FullCusEntWithFullCusProduct, + FullCustomerEntitlement, + getFeatureInvoiceDescription, + InsertReplaceableSchema, + OnDecrease, + Price, + UsagePriceConfig, +} from "@autumn/shared"; +import Decimal from "decimal.js"; + +export const getReplaceables = ({ + cusEnt, + prevOverage, + newOverage, +}: { + cusEnt: FullCustomerEntitlement; + prevOverage: number; + newOverage: number; +}) => { + if (prevOverage <= newOverage) { + return; + } + + let numReplaceables = prevOverage - newOverage; + let newReplaceables = Array.from({ length: numReplaceables }, (_, i) => + InsertReplaceableSchema.parse({ + id: generateId("rep"), + cus_ent_id: cusEnt.id, + created_at: Date.now(), + delete_next_cycle: true, + }), + ); + + return newReplaceables; +}; + +export const getContUsageDowngradeItem = ({ + price, + cusEnt, + prevOverage, + newOverage, +}: { + price: Price; + cusEnt: FullCustomerEntitlement; + prevOverage: number; + newOverage: number; +}) => { + let noProration = price.proration_config?.on_decrease == OnDecrease.None; + + if (noProration) { + let newReplaceables = getReplaceables({ + cusEnt, + prevOverage, + newOverage, + }); + + // let description = getFeatureInvoiceDescription({ + // feature, + // usage: newRoundedUsage, + // billingUnits: (price.config as UsagePriceConfig).billing_units, + // prodName: product.name, + // }); + + return { + newReplaceables, + amount: null, + }; + } else { + } + + // let shouldProrate = + // price.config.proration_config?.on_decrease == OnDecrease.Prorate; + + // if (shouldProrate) { + // invoice = await createDowngradeProrationInvoice({ + // org, + // cusPrice, + // stripeCli, + // sub, + // newPrice, + // prevPrice, + // newRoundedUsage, + // feature, + // product, + // onDecrease, + // logger, + // }); + // } +}; diff --git a/server/src/internal/products/prices/priceUtils/arrearProratedUtils/getContUsageUpgradeItem.ts b/server/src/internal/products/prices/priceUtils/arrearProratedUtils/getContUsageUpgradeItem.ts new file mode 100644 index 000000000..748b6b5ad --- /dev/null +++ b/server/src/internal/products/prices/priceUtils/arrearProratedUtils/getContUsageUpgradeItem.ts @@ -0,0 +1,141 @@ +import { + Feature, + FullEntitlement, + getFeatureInvoiceDescription, + OnIncrease, + Organization, + Price, + Product, + UsagePriceConfig, +} from "@autumn/shared"; +import Stripe from "stripe"; +import { Decimal } from "decimal.js"; +import { shouldProrate } from "../prorationConfigUtils.js"; +import { calculateProrationAmount } from "@/internal/invoices/prorationUtils.js"; +import { formatUnixToDate } from "@/utils/genUtils.js"; +import { constructStripeInvoiceItem } from "@/internal/invoices/invoiceItemUtils/invoiceItemUtils.js"; +import { priceToInvoiceAmount } from "../getAmountForPrice.js"; +import { getPrevAndNewPriceForUpgrade } from "./getPrevAndNewUsages.js"; + +export const getNewContUsageAmount = ({ + price, + ent, + newBalance, + prevBalance, + numReplaceables = 0, + stripeSub, + now, +}: { + price: Price; + ent: FullEntitlement; + newBalance: number; + prevBalance: number; + numReplaceables?: number; + stripeSub?: Stripe.Subscription; + now: number; +}) => { + let { overageWithReplaceables: prevRoundedOverage, newRoundedOverage } = + getPrevAndNewPriceForUpgrade({ + ent, + numReplaceables, + price, + newBalance, + prevBalance, + }); + + // Get price for usage... + let prevPrice = priceToInvoiceAmount({ + price, + overage: prevRoundedOverage, + }); + + let newPrice = priceToInvoiceAmount({ + price, + overage: newRoundedOverage, + }); + + let invoiceAmount = new Decimal(newPrice).minus(prevPrice).toNumber(); + + let onIncrease = price.proration_config?.on_increase; + if (shouldProrate(onIncrease) && stripeSub) { + // console.log( + // "Period start: ", + // formatUnixToDate(stripeSub.current_period_start * 1000), + // ); + // console.log( + // "Period end: ", + // formatUnixToDate(stripeSub.current_period_end * 1000), + // ); + + invoiceAmount = calculateProrationAmount({ + periodStart: stripeSub.current_period_start * 1000, + periodEnd: stripeSub.current_period_end * 1000, + now, + amount: invoiceAmount, + allowNegative: true, + }); + } + + return invoiceAmount; +}; + +export const getContUsageUpgradeItem = ({ + prevPrice, + newPrice, + now, + feature, + newRoundedUsage, + price, + org, + onIncrease, + product, + stripeSub, +}: { + prevPrice: number; + newPrice: number; + now: number; + feature: Feature; + newRoundedUsage: number; + price: Price; + org: Organization; + onIncrease: OnIncrease; + product: Product; + stripeSub: Stripe.Subscription; +}) => { + const billingUnits = (price.config as UsagePriceConfig).billing_units; + let invoiceAmount = new Decimal(newPrice).minus(prevPrice).toNumber(); + + let invoiceDescription = getFeatureInvoiceDescription({ + feature, + usage: newRoundedUsage, + billingUnits, + prodName: product.name, + }); + + if (shouldProrate(onIncrease)) { + invoiceAmount = calculateProrationAmount({ + periodStart: stripeSub.current_period_start * 1000, + periodEnd: stripeSub.current_period_end * 1000, + now, + amount: invoiceAmount, + }); + + let start = formatUnixToDate(now); + let end = formatUnixToDate(stripeSub.current_period_end * 1000); + invoiceDescription = `${invoiceDescription} (from ${start} to ${end})`; + } + + let invoiceItem = constructStripeInvoiceItem({ + product, + amount: invoiceAmount, + org, + price: price, + description: invoiceDescription, + stripeSubId: stripeSub.id, + stripeCustomerId: stripeSub.customer as string, + periodStart: stripeSub.current_period_start, + periodEnd: Math.floor(now / 1000), + }); + + return invoiceItem; +}; diff --git a/server/src/internal/products/prices/priceUtils/arrearProratedUtils/getPrevAndNewUsages.ts b/server/src/internal/products/prices/priceUtils/arrearProratedUtils/getPrevAndNewUsages.ts new file mode 100644 index 000000000..ec29f1730 --- /dev/null +++ b/server/src/internal/products/prices/priceUtils/arrearProratedUtils/getPrevAndNewUsages.ts @@ -0,0 +1,101 @@ +import { Entitlement, Price, UsagePriceConfig } from "@autumn/shared"; +import { Decimal } from "decimal.js"; +import { priceToInvoiceAmount } from "../getAmountForPrice.js"; + +export const getUsageFromBalance = ({ + ent, + price, + balance, +}: { + ent: Entitlement; + price: Price; + balance: number; +}) => { + let config = price.config as UsagePriceConfig; + let billingUnits = config.billing_units || 1; + + let overage = -Math.min(0, balance); + let roundedOverage = new Decimal(overage) + .div(billingUnits) + .ceil() + .mul(billingUnits) + .toNumber(); + + let usage = new Decimal(ent.allowance!).sub(balance).toNumber(); + + let roundedUsage = usage; + if (overage > 0) { + roundedUsage = new Decimal(usage) + .div(billingUnits) + .ceil() + .mul(billingUnits) + .toNumber(); + } + + return { usage, roundedUsage, overage, roundedOverage }; +}; + +export const getPrevAndNewPriceForUpgrade = ({ + ent, + numReplaceables, + price, + newBalance, + prevBalance, +}: { + ent: Entitlement; + numReplaceables: number; + price: Price; + newBalance: number; + prevBalance: number; +}) => { + const { + usage: newUsage, + roundedUsage: newRoundedUsage, + roundedOverage: newRoundedOverage, + } = getUsageFromBalance({ + ent, + price, + balance: newBalance, + }); + + const { + usage: prevUsage, + overage: prevOverage, + roundedOverage: prevRoundedOverage, + } = getUsageFromBalance({ + ent, + price, + balance: prevBalance, + }); + + const { roundedOverage: overageWithReplaceables } = getUsageFromBalance({ + ent, + price, + balance: prevBalance - numReplaceables, + }); + + // Get price for usage... + let prevPrice = priceToInvoiceAmount({ + price, + overage: overageWithReplaceables, + }); + + let newPrice = priceToInvoiceAmount({ + price, + overage: newRoundedOverage, + }); + + return { + prevPrice, + newPrice, + + prevUsage, + newUsage, + + overageWithReplaceables, + newRoundedOverage, + // newRoundedUsage, + // prevRoundedOverage, + // prevOverage, + }; +}; diff --git a/server/src/internal/products/prices/priceUtils/arrearProratedUtils/getUsageDiffLineItem.ts b/server/src/internal/products/prices/priceUtils/arrearProratedUtils/getUsageDiffLineItem.ts new file mode 100644 index 000000000..5d12eb35a --- /dev/null +++ b/server/src/internal/products/prices/priceUtils/arrearProratedUtils/getUsageDiffLineItem.ts @@ -0,0 +1,72 @@ +import { constructPreviewItem } from "@/internal/invoices/previewItemUtils/constructPreviewItem.js"; +import { + FullEntitlement, + getFeatureInvoiceDescription, + Organization, + Price, + Product, + UsagePriceConfig, + usageToFeatureName, +} from "@autumn/shared"; +import { Decimal } from "decimal.js"; +import { shouldProrate } from "../prorationConfigUtils.js"; + +export const getUsageDiffLineItem = ({ + prevBalance, + newBalance, + org, + price, + newUsageAmount, + ent, + product, +}: { + prevBalance: number; + newBalance: number; + org: Organization; + price: Price; + newUsageAmount: number; + ent: FullEntitlement; + product: Product; +}) => { + const usageDiff = new Decimal(prevBalance).sub(newBalance).abs().toNumber(); + const isIncrease = newBalance <= prevBalance; + const willProrate = isIncrease + ? shouldProrate(price.proration_config?.on_increase) + : shouldProrate(price.proration_config?.on_decrease); + + let description = getFeatureInvoiceDescription({ + feature: ent.feature, + usage: usageDiff, + billingUnits: (price.config as UsagePriceConfig).billing_units, + }); + + if (isIncrease) { + description = `${product.name} - Additional ${description}`; + } else { + description = `${product.name} - Unused ${description}`; + } + + let previewLineItem = constructPreviewItem({ + price, + org, + amount: newUsageAmount, + description, + }); + + if (!isIncrease && !willProrate) { + let featureName = usageToFeatureName({ + usage: usageDiff, + feature: ent.feature, + }); + + previewLineItem = constructPreviewItem({ + priceStr: `${usageDiff} free ${featureName}`, + price, + org, + // description: `${product.name} - ${usageDiff} free ${featureName}`, + description: `${product.name} - ${featureName}`, + }); + } + + return previewLineItem; +}; diff --git a/server/src/internal/products/prices/priceUtils/getAmountForPrice.ts b/server/src/internal/products/prices/priceUtils/getAmountForPrice.ts index a0c716bcd..155c342a9 100644 --- a/server/src/internal/products/prices/priceUtils/getAmountForPrice.ts +++ b/server/src/internal/products/prices/priceUtils/getAmountForPrice.ts @@ -10,6 +10,11 @@ import { import { isFixedPrice } from "./usagePriceUtils.js"; import { getBillingType } from "../priceUtils.js"; import { Decimal } from "decimal.js"; +import { nullish } from "@/utils/genUtils.js"; +import { + calculateProrationAmount, + Proration, +} from "@/internal/invoices/prorationUtils.js"; export const getAmountForQuantity = ({ price, @@ -65,28 +70,46 @@ export const priceToInvoiceAmount = ({ price, overage, quantity, + proration, + now, }: { price: Price; overage?: number; quantity?: number; + proration?: Proration; + now?: number; }) => { // 1. If fixed price, just return amount + + let amount = 0; if (isFixedPrice({ price })) { - return (price.config as FixedPriceConfig).amount; + amount = (price.config as FixedPriceConfig).amount; } const config = price.config as UsagePriceConfig; let billingType = getBillingType(config); - if (!quantity && !overage) { + if (!nullish(quantity) && !nullish(overage)) { throw new Error( `getAmountForPrice: quantity or overage is required, autumn price: ${price.id}`, ); } if (billingType == BillingType.UsageInAdvance) { - return getAmountForQuantity({ price, quantity: quantity! }); + amount = getAmountForQuantity({ price, quantity: quantity! }); } else { - return getAmountForQuantity({ price, quantity: overage! }); + amount = getAmountForQuantity({ price, quantity: overage! }); } + + if (proration) { + return calculateProrationAmount({ + periodEnd: proration.end, + periodStart: proration.start, + now: now || Date.now(), + amount, + allowNegative: true, + }); + } + + return amount; }; diff --git a/server/src/internal/products/prices/priceUtils/prorationConfigUtils.ts b/server/src/internal/products/prices/priceUtils/prorationConfigUtils.ts new file mode 100644 index 000000000..78d4cd10a --- /dev/null +++ b/server/src/internal/products/prices/priceUtils/prorationConfigUtils.ts @@ -0,0 +1,28 @@ +import { OnDecrease, OnIncrease } from "@autumn/shared"; + +export const shouldCreateInvoiceItem = (onIncrease: OnIncrease) => { + return ( + onIncrease === OnIncrease.BillImmediately || + onIncrease === OnIncrease.ProrateImmediately || + onIncrease === OnIncrease.ProrateNextCycle + ); +}; + +export const shouldBillNow = (onIncrease: OnIncrease) => { + return ( + onIncrease === OnIncrease.BillImmediately || + onIncrease === OnIncrease.ProrateImmediately + ); +}; + +export const shouldProrate = (onIncrease?: OnIncrease | OnDecrease) => { + if (!onIncrease) { + return true; + } + + return ( + onIncrease === OnIncrease.ProrateNextCycle || + onIncrease === OnIncrease.ProrateImmediately || + onIncrease === OnDecrease.Prorate + ); +}; diff --git a/server/src/internal/products/prices/priceUtils/usagePriceUtils.ts b/server/src/internal/products/prices/priceUtils/usagePriceUtils.ts index 5ac37f750..9765648a5 100644 --- a/server/src/internal/products/prices/priceUtils/usagePriceUtils.ts +++ b/server/src/internal/products/prices/priceUtils/usagePriceUtils.ts @@ -1,6 +1,14 @@ -import { APIVersion, BillingType, FullCusProduct, Price } from "@autumn/shared"; +import { + APIVersion, + BillingType, + FullCusProduct, + OnDecrease, + OnIncrease, + Price, +} from "@autumn/shared"; import { getBillingType } from "../priceUtils.js"; import { notNullish } from "@/utils/genUtils.js"; +import Stripe from "stripe"; export const isUsagePrice = ({ price }: { price: Price }) => { let billingType = getBillingType(price.config); @@ -41,3 +49,32 @@ export const isV4Usage = ({ notNullish(cusProduct.internal_entity_id)) ); }; + +// export const +export const onIncreaseToStripeProration = ({ + onIncrease, +}: { + onIncrease: OnIncrease; +}) => { + let behavior = "none"; + if (onIncrease === OnIncrease.ProrateImmediately) { + behavior = "always_invoice"; + } else if (onIncrease === OnIncrease.ProrateNextCycle) { + behavior = "create_prorations"; + } + + return behavior as Stripe.SubscriptionItemUpdateParams.ProrationBehavior; +}; + +export const onDecreaseToStripeProration = ({ + onDecrease, +}: { + onDecrease: OnDecrease; +}) => { + let behavior = "none"; + if (onDecrease === OnDecrease.Prorate) { + behavior = "always_invoice"; + } + + return behavior as Stripe.SubscriptionItemUpdateParams.ProrationBehavior; +}; diff --git a/server/src/internal/products/product-items/compareItemUtils.ts b/server/src/internal/products/product-items/compareItemUtils.ts index 414b1ab46..d9a372a78 100644 --- a/server/src/internal/products/product-items/compareItemUtils.ts +++ b/server/src/internal/products/product-items/compareItemUtils.ts @@ -1,5 +1,4 @@ import { - ErrCode, Feature, FeatureItem, FeatureItemSchema, @@ -14,7 +13,7 @@ import { isFeatureItem, isFeaturePriceItem, isPriceItem, -} from "./getItemType.js"; +} from "./productItemUtils/getItemType.js"; import RecaseError from "@/utils/errorUtils.js"; import { itemToFeature } from "./productItemUtils/convertItem.js"; @@ -196,7 +195,7 @@ export const itemsAreSame = ({ features: features || [], }); - if (feature?.config?.type === FeatureUsageType.Continuous) { + if (feature?.config?.usage_type === FeatureUsageType.Continuous) { pricesChanged = true; } else { pricesChanged = pricesChanged_; diff --git a/server/src/internal/products/product-items/createFeaturesFromItems.ts b/server/src/internal/products/product-items/createFeaturesFromItems.ts index a7d930e1f..d4d21952c 100644 --- a/server/src/internal/products/product-items/createFeaturesFromItems.ts +++ b/server/src/internal/products/product-items/createFeaturesFromItems.ts @@ -7,8 +7,7 @@ import { ProductItem, ProductItemFeatureType, } from "@autumn/shared"; -import { SupabaseClient } from "@supabase/supabase-js"; -import { isBooleanFeatureItem } from "./getItemType.js"; +import { isBooleanFeatureItem } from "./productItemUtils/getItemType.js"; import { validateFeatureId } from "@/internal/features/featureUtils.js"; import { constructBooleanFeature, diff --git a/server/src/internal/products/product-items/mapToItem.ts b/server/src/internal/products/product-items/mapToItem.ts index 3d0e5c409..fc2772e2c 100644 --- a/server/src/internal/products/product-items/mapToItem.ts +++ b/server/src/internal/products/product-items/mapToItem.ts @@ -96,6 +96,7 @@ export const toFeaturePriceItem = ({ price_id: price.id, price_config: price.config, + config: price.proration_config || undefined, }; return item; diff --git a/server/src/internal/products/product-items/productItemUtils.ts b/server/src/internal/products/product-items/productItemUtils.ts index ab6a16cb1..03ea53c10 100644 --- a/server/src/internal/products/product-items/productItemUtils.ts +++ b/server/src/internal/products/product-items/productItemUtils.ts @@ -11,7 +11,7 @@ import { Feature, FeatureType, } from "@autumn/shared"; -import { isFeatureItem } from "./getItemType.js"; +import { isFeatureItem } from "./productItemUtils/getItemType.js"; import { billingToItemInterval, entToItemInterval, diff --git a/server/src/internal/products/product-items/productItemUtils/classifyItem.ts b/server/src/internal/products/product-items/productItemUtils/classifyItem.ts new file mode 100644 index 000000000..2ea5e1473 --- /dev/null +++ b/server/src/internal/products/product-items/productItemUtils/classifyItem.ts @@ -0,0 +1,26 @@ +import { UsageModel } from "@autumn/shared"; +import { ProductItemFeatureType } from "@autumn/shared"; +import { Feature } from "@autumn/shared"; +import { ProductItem } from "@autumn/shared"; +import { itemToUsageType } from "./convertItem.js"; + +export const itemCanBeProrated = ({ + item, + features, +}: { + item: ProductItem; + features: Feature[]; +}) => { + const usageType = itemToUsageType({ item, features }); + + if ( + usageType == ProductItemFeatureType.SingleUse && + item.usage_model == UsageModel.Prepaid + ) { + return true; + } else if (usageType == ProductItemFeatureType.ContinuousUse) { + return true; + } + + return false; +}; diff --git a/server/src/internal/products/product-items/productItemUtils/convertItem.ts b/server/src/internal/products/product-items/productItemUtils/convertItem.ts index 68dbd4c19..8507a7bf4 100644 --- a/server/src/internal/products/product-items/productItemUtils/convertItem.ts +++ b/server/src/internal/products/product-items/productItemUtils/convertItem.ts @@ -1,4 +1,10 @@ -import { Feature, ProductItem } from "@autumn/shared"; +import { + Feature, + FeatureType, + ProductItem, + ProductItemFeatureType, + UsageModel, +} from "@autumn/shared"; export const itemToFeature = ({ item, @@ -9,9 +15,26 @@ export const itemToFeature = ({ }) => { const feature = features.find((f) => f.id === item.feature_id); - if (!feature) { - return null; - } - return feature; }; + +export const itemToUsageType = ({ + item, + features, +}: { + item: ProductItem; + features: Feature[]; +}) => { + const feature = itemToFeature({ item, features }); + if (!feature) return null; + + if (feature.type == FeatureType.Boolean) { + return ProductItemFeatureType.Static; + } + + if (feature.type == FeatureType.CreditSystem) { + return ProductItemFeatureType.SingleUse; + } + + return feature.config!.usage_type as ProductItemFeatureType; +}; diff --git a/server/src/internal/products/product-items/getItemType.ts b/server/src/internal/products/product-items/productItemUtils/getItemType.ts similarity index 100% rename from server/src/internal/products/product-items/getItemType.ts rename to server/src/internal/products/product-items/productItemUtils/getItemType.ts diff --git a/server/src/internal/products/product-items/productItemInitUtils.ts b/server/src/internal/products/product-items/productItemUtils/handleNewProductItems.ts similarity index 96% rename from server/src/internal/products/product-items/productItemInitUtils.ts rename to server/src/internal/products/product-items/productItemUtils/handleNewProductItems.ts index 98e968746..9c3e73f7a 100644 --- a/server/src/internal/products/product-items/productItemInitUtils.ts +++ b/server/src/internal/products/product-items/productItemUtils/handleNewProductItems.ts @@ -6,10 +6,10 @@ import { Product, ProductItem, } from "@autumn/shared"; -import { itemToPriceAndEnt } from "./mapFromItem.js"; +import { itemToPriceAndEnt } from "./itemToPriceAndEnt.js"; import { PriceService } from "@/internal/products/prices/PriceService.js"; -import { EntitlementService } from "../entitlements/EntitlementService.js"; -import { validateProductItems } from "./validateProductItems.js"; +import { EntitlementService } from "../../entitlements/EntitlementService.js"; +import { validateProductItems } from "../validateProductItems.js"; import { FeatureService } from "@/internal/features/FeatureService.js"; import { isFeatureItem } from "./getItemType.js"; import { DrizzleCli } from "@/db/initDrizzle.js"; @@ -224,6 +224,7 @@ export const handleNewProductItems = async ({ curEnt, isCustom, newVersion, + features, }); if (newPrice) { diff --git a/server/src/internal/products/product-items/mapFromItem.ts b/server/src/internal/products/product-items/productItemUtils/itemToPriceAndEnt.ts similarity index 93% rename from server/src/internal/products/product-items/mapFromItem.ts rename to server/src/internal/products/product-items/productItemUtils/itemToPriceAndEnt.ts index 937164e2a..aaecc1f4b 100644 --- a/server/src/internal/products/product-items/mapFromItem.ts +++ b/server/src/internal/products/product-items/productItemUtils/itemToPriceAndEnt.ts @@ -16,17 +16,20 @@ import { UsageModel, TierInfinite, UsagePriceConfig, + OnIncrease, + OnDecrease, } from "@autumn/shared"; import { generateId, notNullish } from "@/utils/genUtils.js"; import { pricesAreSame } from "@/internal/products/prices/priceInitUtils.js"; -import { entsAreSame } from "../entitlements/entitlementUtils.js"; +import { entsAreSame } from "../../entitlements/entitlementUtils.js"; import { getBillingType } from "@/internal/products/prices/priceUtils.js"; import RecaseError from "@/utils/errorUtils.js"; import { isFeatureItem, isPriceItem } from "./getItemType.js"; import { itemToBillingInterval, itemToEntInterval, -} from "./itemIntervalUtils.js"; +} from "../itemIntervalUtils.js"; +import { itemCanBeProrated } from "./classifyItem.js"; // ITEM TO PRICE AND ENTITLEMENT export const toPrice = ({ @@ -55,6 +58,7 @@ export const toPrice = ({ internal_product_id: internalProductId, is_custom: isCustom, config, + proration_config: null, }; if (isCustom || newVersion) { @@ -131,6 +135,7 @@ export const toFeatureAndPrice = ({ curPrice, curEnt, newVersion, + features, }: { item: ProductItem; orgId: string; @@ -140,6 +145,7 @@ export const toFeatureAndPrice = ({ curPrice?: Price; curEnt?: Entitlement; newVersion?: boolean; + features: Feature[]; }) => { let resetUsage = item.reset_usage_when_enabled || false; @@ -197,6 +203,14 @@ export const toFeatureAndPrice = ({ interval: itemToBillingInterval(item) as BillingInterval, }; + let prorationConfig = null; + if (itemCanBeProrated({ item, features })) { + prorationConfig = { + on_increase: item.config?.on_increase || OnIncrease.ProrateImmediately, + on_decrease: item.config?.on_decrease || OnDecrease.Prorate, + }; + } + let price: Price = { id: item.price_id || generateId("pr"), created_at: item.created_at || Date.now(), @@ -205,6 +219,7 @@ export const toFeatureAndPrice = ({ is_custom: isCustom, config, entitlement_id: ent.id, + proration_config: prorationConfig, }; let billingType = getBillingType(price.config!); @@ -224,8 +239,6 @@ export const toFeatureAndPrice = ({ (curPrice && !pricesAreSame(curPrice, price, true)) || (curEnt && !entsAreSame(curEnt, ent)); - // console.log("Cur price exists: ", notNullish(curPrice)); - // console.log("Price or ent different: ", priceOrEntDifferent); if (curPrice && (priceOrEntDifferent || newVersion)) { let newConfig = price.config as UsagePriceConfig; let curConfig = curPrice.config as UsagePriceConfig; @@ -254,6 +267,7 @@ export const itemToPriceAndEnt = ({ curEnt, isCustom, newVersion, + features, }: { item: ProductItem; orgId: string; @@ -263,6 +277,7 @@ export const itemToPriceAndEnt = ({ curEnt?: Entitlement; isCustom: boolean; newVersion?: boolean; + features: Feature[]; }) => { let newPrice: Price | null = null; let newEnt: Entitlement | null = null; @@ -335,6 +350,7 @@ export const itemToPriceAndEnt = ({ curPrice, curEnt, newVersion, + features, }); let entSame = curEnt && entsAreSame(curEnt, ent); diff --git a/server/src/internal/products/product-items/validateProductItems.ts b/server/src/internal/products/product-items/validateProductItems.ts index fbc78058d..17479933d 100644 --- a/server/src/internal/products/product-items/validateProductItems.ts +++ b/server/src/internal/products/product-items/validateProductItems.ts @@ -17,9 +17,10 @@ import { isFeatureItem, isFeaturePriceItem, isPriceItem, -} from "./getItemType.js"; +} from "./productItemUtils/getItemType.js"; import { itemToEntInterval } from "./itemIntervalUtils.js"; import { createFeaturesFromItems } from "./createFeaturesFromItems.js"; + const validateProductItem = ({ item, features, diff --git a/server/src/internal/products/productUtils/classifyProduct.ts b/server/src/internal/products/productUtils/classifyProduct.ts index ad2f0d233..3a3035e54 100644 --- a/server/src/internal/products/productUtils/classifyProduct.ts +++ b/server/src/internal/products/productUtils/classifyProduct.ts @@ -1,6 +1,6 @@ import { FullProduct, Price, ProductV2 } from "@autumn/shared"; import { pricesOnlyOneOff } from "../prices/priceUtils.js"; -import { isFeatureItem } from "../product-items/getItemType.js"; +import { isFeatureItem } from "../product-items/productItemUtils/getItemType.js"; export const prodIsAddOn = ({ product }: { product: FullProduct }) => { return product.is_add_on; diff --git a/server/src/internal/products/productV2Utils.ts b/server/src/internal/products/productV2Utils.ts index 03a60b73b..b88bf0526 100644 --- a/server/src/internal/products/productV2Utils.ts +++ b/server/src/internal/products/productV2Utils.ts @@ -15,7 +15,7 @@ import { getPriceEntitlement } from "./prices/priceUtils.js"; import { toProductItem } from "./product-items/mapToItem.js"; import { getItemFeatureType } from "./product-items/productItemUtils.js"; import { sortProductItems } from "./pricecn/pricecnUtils.js"; -import { getItemType } from "./product-items/getItemType.js"; +import { getItemType } from "./product-items/productItemUtils/getItemType.js"; export const mapToProductItems = ({ prices, diff --git a/server/src/trigger/adjustAllowance.ts b/server/src/trigger/adjustAllowance.ts index ba67fdaf2..bb0cbbb15 100644 --- a/server/src/trigger/adjustAllowance.ts +++ b/server/src/trigger/adjustAllowance.ts @@ -1,14 +1,12 @@ import { - ErrCode, - FullCusProduct, - FullCustomerEntitlement, - InvoiceItem, - Product, + Entitlement, + FullCusEntWithFullCusProduct, + FullCusEntWithProduct, + Price, } from "@autumn/shared"; import { AppEnv, BillingType, - CusProduct, Customer, Feature, FullCustomerPrice, @@ -17,31 +15,48 @@ import { } from "@autumn/shared"; import { getRelatedCusPrice } from "@/internal/customers/cusProducts/cusEnts/cusEntUtils.js"; -import { - getBillingType, - getPriceForOverage, -} from "@/internal/products/prices/priceUtils.js"; - +import { getBillingType } from "@/internal/products/prices/priceUtils.js"; import { getUsageBasedSub } from "@/external/stripe/stripeSubUtils.js"; import { createStripeCli } from "@/external/stripe/utils.js"; - import { Decimal } from "decimal.js"; - -import { createLogtailWithContext } from "@/external/logtail/logtailUtils.js"; -import { LoggerAction } from "@autumn/shared"; -import { - getInvoiceExpansion, - payForInvoice, -} from "@/external/stripe/stripeInvoiceUtils.js"; -import { isTrialing } from "@/internal/customers/cusProducts/cusProductUtils.js"; -import { ProductService } from "@/internal/products/ProductService.js"; -import { InvoiceService } from "@/internal/invoices/InvoiceService.js"; -import RecaseError from "@/utils/errorUtils.js"; import { DrizzleCli } from "@/db/initDrizzle.js"; -import { getCusPaymentMethod } from "@/external/stripe/stripeCusUtils.js"; +import { findStripeItemForPrice } from "@/external/stripe/stripeSubUtils/stripeSubItemUtils.js"; +import { handleProratedUpgrade } from "./arrearProratedUsage/handleProratedUpgrade.js"; +import Stripe from "stripe"; +import { handleProratedDowngrade } from "./arrearProratedUsage/handleProratedDowngrade.js"; -type CusEntWithCusProduct = FullCustomerEntitlement & { - customer_product: CusProduct; +export const getUsageFromBalance = ({ + ent, + price, + balance, +}: { + ent: Entitlement; + price: Price; + balance: number; +}) => { + let config = price.config as UsagePriceConfig; + let billingUnits = config.billing_units || 1; + + // Should get overage... + let overage = -Math.min(0, balance); + let roundedOverage = new Decimal(overage) + .div(billingUnits) + .ceil() + .mul(billingUnits) + .toNumber(); + + let usage = new Decimal(ent.allowance!).sub(balance).toNumber(); + + let roundedUsage = usage; + if (overage > 0) { + roundedUsage = new Decimal(usage) + .div(billingUnits) + .ceil() + .mul(billingUnits) + .toNumber(); + } + + return { usage, roundedUsage, overage, roundedOverage }; }; export const adjustAllowance = async ({ @@ -54,66 +69,45 @@ export const adjustAllowance = async ({ customer, originalBalance, newBalance, - deduction, - product, - replacedCount, - fromEntities = false, + logger, + errorIfIncomplete = false, + // deduction, + // product, + // fromEntities = false, }: { db: DrizzleCli; env: AppEnv; affectedFeature: Feature; org: Organization; - cusEnt: CusEntWithCusProduct; + cusEnt: FullCusEntWithFullCusProduct; cusPrices: FullCustomerPrice[]; customer: Customer; originalBalance: number; newBalance: number; - deduction: number; - product?: Product; - replacedCount?: number; - fromEntities?: boolean; + logger: any; + errorIfIncomplete?: boolean; + // deduction: number; + // product?: Product; + // replacedCount?: number; + // fromEntities?: boolean; }) => { - // Get customer entitlement - - if (originalBalance == newBalance) { - return; - } - - // 1. Check if price is prorated in arrear, if not skip... - let logger = createLogtailWithContext({ - org_id: org.id, - org_slug: org.slug, - customer, - action: LoggerAction.AdjustAllowance, - }); - let cusPrice = getRelatedCusPrice(cusEnt, cusPrices); let billingType = cusPrice ? getBillingType(cusPrice.price.config!) : null; let cusProduct = cusEnt.customer_product; - if (!cusPrice || billingType !== BillingType.InArrearProrated) { - return; + if ( + !cusProduct || + !cusPrice || + billingType !== BillingType.InArrearProrated || + originalBalance == newBalance + ) { + return { newReplaceables: null, invoice: null, deletedReplaceables: null }; } - logger.info(`Updating prorated in arrear usage for ${affectedFeature.name}`); - logger.info(` - Customer: ${customer.name}, Org: ${org.slug}`); - logger.info( - ` - Balance: ${originalBalance} -> ${newBalance}${ - replacedCount ? ` (replaced ${replacedCount})` : "" - }`, - ); - - if (!cusProduct) { - logger.error("❗️ Error: can't adjust allowance, no customer product found"); - return; - } + logger.info(`Updating arrear prorated usage: ${affectedFeature.name}`); + logger.info(`Customer: ${customer.name}, Org: ${org.slug}`); let stripeCli = createStripeCli({ org, env }); - let paymentMethod = await getCusPaymentMethod({ - stripeCli, - stripeId: customer.processor.id, - }); - let sub = await getUsageBasedSub({ db, stripeCli, @@ -122,181 +116,243 @@ export const adjustAllowance = async ({ }); if (!sub) { - logger.error("❗️ Error: can't adjust allowance, no usage-based sub found"); - return; + logger.error("adjustAllowance: no usage-based sub found"); + return { newReplaceables: null, invoice: null, deletedReplaceables: null }; } - // Update sub item - let config = cusPrice.price.config as UsagePriceConfig; - - let subItem = sub.items.data.find( - (item) => item.price.id === config.stripe_price_id, - ); + let subItem = findStripeItemForPrice({ + price: cusPrice.price, + stripeItems: sub.items.data, + }); if (!subItem) { - logger.error("❗️ Error: can't adjust allowance, no sub item found"); - return; + logger.error("adjustAllowance: no sub item found"); + return { newReplaceables: null, invoice: null, deletedReplaceables: null }; } - let paidUsage = -newBalance; - if (paidUsage > 0) { - let roundedPaidUsage = new Decimal(paidUsage) - .div(config.billing_units || 1) - .ceil() - .mul(config.billing_units || 1) - .toNumber(); + let isUpgrade = newBalance < originalBalance; - paidUsage = roundedPaidUsage; - } - - let quantity = new Decimal(paidUsage) - .add(cusEnt.entitlement.allowance!) - .toNumber(); - - logger.info( - ` - New quantity = ${paidUsage} (paid) + ${cusEnt.entitlement.allowance} (allowance) = ${quantity} `, - ); - - let prorationBehaviour = org.config.bill_upgrade_immediately - ? "always_invoice" - : "create_prorations"; - - // If prorate unused is false, then remove end of cycle - if (!org.config.prorate_unused) { - prorationBehaviour = "none"; - - const downgrade = quantity < (subItem.quantity || 0); - if (!downgrade && !isTrialing(cusProduct as FullCusProduct)) { - let entitlement = cusEnt.entitlement; - let newUsage = entitlement.allowance! - newBalance; - let oldUsage = - entitlement.allowance! - originalBalance + (replacedCount || 0); - - let newAmount = getPriceForOverage(cusPrice.price, newUsage); - let oldAmount = getPriceForOverage(cusPrice.price, oldUsage); - - const stripeAmount = new Decimal(newAmount) - .sub(oldAmount) - .mul(100) - .round() - .toNumber(); - - logger.info(` - Stripe amount: ${stripeAmount}`); - - if (stripeAmount > 0) { - const invoice = await stripeCli.invoices.create({ - customer: customer.processor.id, - auto_advance: false, - subscription: sub.id, - }); - - if (!product) { - product = await ProductService.getByInternalId({ - db, - internalId: cusProduct.internal_product_id, - }); - } - - await stripeCli.invoiceItems.create({ - customer: customer.processor.id, - invoice: invoice.id, - quantity: 1, - description: `${product!.name} - ${ - affectedFeature.name - } x ${Math.round(newUsage - oldUsage)}`, - - price_data: { - product: config.stripe_product_id!, - unit_amount: stripeAmount, - currency: org.default_currency, - }, - }); - - const { paid, error } = await payForInvoice({ - stripeCli, - paymentMethod, - invoiceId: invoice.id, - logger, - }); - - if (!paid) { - try { - await stripeCli.invoices.voidInvoice(invoice.id); - } catch (error: any) {} - - throw new RecaseError({ - message: "Failed to pay for invoice", - code: ErrCode.PayInvoiceFailed, - data: { - invoiceId: invoice.id, - error, - }, - }); - } - - const latestInvoice = await stripeCli.invoices.retrieve(invoice.id, { - ...getInvoiceExpansion(), - }); - - let invoiceItems: InvoiceItem[] = []; - try { - invoiceItems = [ - { - price_id: cusPrice.price.id!, - stripe_id: latestInvoice.id, - internal_feature_id: affectedFeature.internal_id || null, - description: `${product!.name} - ${ - affectedFeature.name - } x ${Math.round(newUsage - oldUsage)}`, - period_start: Date.now(), - period_end: sub.current_period_end * 1000, - }, - ]; - } catch (error) {} - - await InvoiceService.createInvoiceFromStripe({ - db, - stripeInvoice: latestInvoice, - internalCustomerId: customer.internal_id, - internalEntityId: cusProduct.internal_entity_id || undefined, - org, - productIds: [product!.id], - internalProductIds: [product!.internal_id], - items: invoiceItems, - }); - } - } - } - - if (quantity < 0) { - quantity = 0; - logger.warn("❗️ Warning: quantity is negative, setting to 0"); - logger.warn( - `❗️ Allowance: ${cusEnt.entitlement.allowance}, New Balance: ${newBalance}`, - ); - } - - try { - await stripeCli.subscriptionItems.update(subItem.id, { - quantity: quantity, - proration_behavior: prorationBehaviour as any, - payment_behavior: fromEntities ? "error_if_incomplete" : undefined, + if (isUpgrade) { + return await handleProratedUpgrade({ + db, + stripeCli, + cusEnt, + cusPrice, + sub, + subItem: subItem as Stripe.SubscriptionItem, + newBalance, + prevBalance: originalBalance, + org, + logger, + }); + } else { + return await handleProratedDowngrade({ + db, + org, + stripeCli, + cusEnt, + cusPrice, + sub, + subItem: subItem as Stripe.SubscriptionItem, + newBalance, + prevBalance: originalBalance, + logger, }); - logger.info(` ✅ Adjusted sub item ${subItem.id} to ${quantity}`); - } catch (error: any) { - if (fromEntities) { - throw new RecaseError({ - message: `Failed to update subscription subscription: ${error.message}`, - code: ErrCode.StripeUpdateSubscriptionFailed, - statusCode: error.statusCode, - }); - } else { - logger.error( - `❗️ adjustAllowance: Error updating subscription item (from event)`, - ); - logger.error(error); - } } - return; + // let isUpgrade = newBalance < originalBalance; + + // if (isUpgrade) { + // let onIncrease = + // cusPrice.price.proration_config?.on_increase || + // OnIncrease.ProrateImmediately; + + // if (onIncrease === OnIncrease.BillImmediately) { + // // Create invoice for new usage + // } + + // let stripeProration = onIncreaseToStripeProration({ + // onIncrease, + // }); + + // // Old quantity = old usage + replaced count + + // await stripeCli.subscriptionItems.update(subItem.id, { + // quantity: roundedUsage, + // proration_behavior: "none", + // // proration_behavior: stripeProration, + // // payment_behavior: errorIfIncomplete ? "error_if_incomplete" : undefined, + // }); + + // logger.info(`Updated sub item ${subItem.id} to quantity: ${roundedUsage}`); + // } else { + // let onDecrease = + // cusPrice.price.proration_config?.on_decrease || OnDecrease.Prorate; + + // let stripeProration = onDecreaseToStripeProration({ + // onDecrease, + // }); + + // await stripeCli.subscriptionItems.update(subItem.id, { + // quantity: roundedUsage, + // proration_behavior: stripeProration, + // payment_behavior: errorIfIncomplete ? "error_if_incomplete" : undefined, + // }); + // } + + // // throw new Error("test"); + + // let prorationBehaviour = org.config.bill_upgrade_immediately + // ? "always_invoice" + // : "create_prorations"; + + // // If prorate unused is false, then remove end of cycle + + // if (quantity < 0) { + // quantity = 0; + // } + + // try { + // await stripeCli.subscriptionItems.update(subItem.id, { + // quantity: quantity, + // proration_behavior: prorationBehaviour as any, + // // payment_behavior: fromEntities ? "error_if_incomplete" : undefined, + // }); + // logger.info(` ✅ Adjusted sub item ${subItem.id} to ${quantity}`); + // } catch (error: any) { + // // if (fromEntities) { + // // throw new RecaseError({ + // // message: `Failed to update subscription subscription: ${error.message}`, + // // code: ErrCode.StripeUpdateSubscriptionFailed, + // // statusCode: error.statusCode, + // // }); + // // } else { + // // logger.error( + // // `❗️ adjustAllowance: Error updating subscription item (from event)`, + // // ); + // // logger.error(error); + // // } + // } + + // return; }; + +// if (!org.config.prorate_unused) { +// prorationBehaviour = "none"; + +// const downgrade = quantity < (subItem.quantity || 0); +// if (!downgrade && !isTrialing(cusProduct as FullCusProduct)) { +// let entitlement = cusEnt.entitlement; +// let newUsage = entitlement.allowance! - newBalance; +// let oldUsage = +// entitlement.allowance! - originalBalance + (replacedCount || 0); + +// let newAmount = getPriceForOverage(cusPrice.price, newUsage); +// let oldAmount = getPriceForOverage(cusPrice.price, oldUsage); + +// const stripeAmount = new Decimal(newAmount) +// .sub(oldAmount) +// .mul(100) +// .round() +// .toNumber(); + +// logger.info(` - Stripe amount: ${stripeAmount}`); + +// if (stripeAmount > 0) { +// const invoice = await stripeCli.invoices.create({ +// customer: customer.processor.id, +// auto_advance: false, +// subscription: sub.id, +// }); + +// if (!product) { +// product = await ProductService.getByInternalId({ +// db, +// internalId: cusProduct.internal_product_id, +// }); +// } + +// await stripeCli.invoiceItems.create({ +// customer: customer.processor.id, +// invoice: invoice.id, +// quantity: 1, +// description: `${product!.name} - ${ +// affectedFeature.name +// } x ${Math.round(newUsage - oldUsage)}`, + +// price_data: { +// product: config.stripe_product_id!, +// unit_amount: stripeAmount, +// currency: org.default_currency, +// }, +// }); + +// const { paid, error } = await payForInvoice({ +// stripeCli, +// paymentMethod, +// invoiceId: invoice.id, +// logger, +// }); + +// if (!paid) { +// try { +// await stripeCli.invoices.voidInvoice(invoice.id); +// } catch (error: any) {} + +// throw new RecaseError({ +// message: "Failed to pay for invoice", +// code: ErrCode.PayInvoiceFailed, +// data: { +// invoiceId: invoice.id, +// error, +// }, +// }); +// } + +// const latestInvoice = await stripeCli.invoices.retrieve(invoice.id, { +// ...getInvoiceExpansion(), +// }); + +// let invoiceItems: InvoiceItem[] = []; +// try { +// invoiceItems = [ +// { +// price_id: cusPrice.price.id!, +// stripe_id: latestInvoice.id, +// internal_feature_id: affectedFeature.internal_id || null, +// description: `${product!.name} - ${ +// affectedFeature.name +// } x ${Math.round(newUsage - oldUsage)}`, +// period_start: Date.now(), +// period_end: sub.current_period_end * 1000, +// }, +// ]; +// } catch (error) {} + +// await InvoiceService.createInvoiceFromStripe({ +// db, +// stripeInvoice: latestInvoice, +// internalCustomerId: customer.internal_id, +// internalEntityId: cusProduct.internal_entity_id || undefined, +// org, +// productIds: [product!.id], +// internalProductIds: [product!.internal_id], +// items: invoiceItems, +// }); +// } +// } +// } + +// let paidUsage = -newBalance; +// if (paidUsage > 0) { +// let roundedPaidUsage = new Decimal(paidUsage) +// .div(config.billing_units || 1) +// .ceil() +// .mul(config.billing_units || 1) +// .toNumber(); +// paidUsage = roundedPaidUsage; +// } + +// let quantity = new Decimal(paidUsage) +// .add(cusEnt.entitlement.allowance!) +// .toNumber(); diff --git a/server/src/trigger/adjustAllowance_old.ts b/server/src/trigger/adjustAllowance_old.ts new file mode 100644 index 000000000..a6851177e --- /dev/null +++ b/server/src/trigger/adjustAllowance_old.ts @@ -0,0 +1,302 @@ +import { + ErrCode, + FullCusProduct, + FullCustomerEntitlement, + InvoiceItem, + Product, +} from "@autumn/shared"; +import { + AppEnv, + BillingType, + CusProduct, + Customer, + Feature, + FullCustomerPrice, + Organization, + UsagePriceConfig, +} from "@autumn/shared"; + +import { getRelatedCusPrice } from "@/internal/customers/cusProducts/cusEnts/cusEntUtils.js"; +import { + getBillingType, + getPriceForOverage, +} from "@/internal/products/prices/priceUtils.js"; + +import { getUsageBasedSub } from "@/external/stripe/stripeSubUtils.js"; +import { createStripeCli } from "@/external/stripe/utils.js"; + +import { Decimal } from "decimal.js"; + +import { createLogtailWithContext } from "@/external/logtail/logtailUtils.js"; +import { LoggerAction } from "@autumn/shared"; +import { + getInvoiceExpansion, + payForInvoice, +} from "@/external/stripe/stripeInvoiceUtils.js"; +import { isTrialing } from "@/internal/customers/cusProducts/cusProductUtils.js"; +import { ProductService } from "@/internal/products/ProductService.js"; +import { InvoiceService } from "@/internal/invoices/InvoiceService.js"; +import RecaseError from "@/utils/errorUtils.js"; +import { DrizzleCli } from "@/db/initDrizzle.js"; +import { getCusPaymentMethod } from "@/external/stripe/stripeCusUtils.js"; + +type CusEntWithCusProduct = FullCustomerEntitlement & { + customer_product: CusProduct; +}; + +export const adjustAllowance = async ({ + db, + env, + org, + affectedFeature, + cusEnt, + cusPrices, + customer, + originalBalance, + newBalance, + // deduction, + // product, + // replacedCount, + // fromEntities = false, +}: { + db: DrizzleCli; + env: AppEnv; + affectedFeature: Feature; + org: Organization; + cusEnt: CusEntWithCusProduct; + cusPrices: FullCustomerPrice[]; + customer: Customer; + originalBalance: number; + newBalance: number; + // deduction: number; + // product?: Product; + // replacedCount?: number; + // fromEntities?: boolean; +}) => { + // Get customer entitlement + + if (originalBalance == newBalance) { + return; + } + + // 1. Check if price is prorated in arrear, if not skip... + let logger = createLogtailWithContext({ + org_id: org.id, + org_slug: org.slug, + customer, + action: LoggerAction.AdjustAllowance, + }); + + let cusPrice = getRelatedCusPrice(cusEnt, cusPrices); + let billingType = cusPrice ? getBillingType(cusPrice.price.config!) : null; + let cusProduct = cusEnt.customer_product; + + if (!cusPrice || billingType !== BillingType.InArrearProrated) { + return; + } + + logger.info(`Updating prorated in arrear usage for ${affectedFeature.name}`); + // logger.info(` - Customer: ${customer.name}, Org: ${org.slug}`); + // logger.info( + // ` - Balance: ${originalBalance} -> ${newBalance}${ + // replacedCount ? ` (replaced ${replacedCount})` : "" + // }`, + // ); + + if (!cusProduct) { + logger.error("❗️ Error: can't adjust allowance, no customer product found"); + return; + } + + let stripeCli = createStripeCli({ org, env }); + let paymentMethod = await getCusPaymentMethod({ + stripeCli, + stripeId: customer.processor.id, + }); + + let sub = await getUsageBasedSub({ + db, + stripeCli, + subIds: cusProduct.subscription_ids!, + feature: affectedFeature, + }); + + if (!sub) { + logger.error("❗️ Error: can't adjust allowance, no usage-based sub found"); + return; + } + + // Update sub item + let config = cusPrice.price.config as UsagePriceConfig; + + let subItem = sub.items.data.find( + (item) => item.price.id === config.stripe_price_id, + ); + + if (!subItem) { + logger.error("❗️ Error: can't adjust allowance, no sub item found"); + return; + } + + let paidUsage = -newBalance; + if (paidUsage > 0) { + let roundedPaidUsage = new Decimal(paidUsage) + .div(config.billing_units || 1) + .ceil() + .mul(config.billing_units || 1) + .toNumber(); + + paidUsage = roundedPaidUsage; + } + + let quantity = new Decimal(paidUsage) + .add(cusEnt.entitlement.allowance!) + .toNumber(); + + logger.info( + ` - New quantity = ${paidUsage} (paid) + ${cusEnt.entitlement.allowance} (allowance) = ${quantity} `, + ); + + let prorationBehaviour = org.config.bill_upgrade_immediately + ? "always_invoice" + : "create_prorations"; + + // If prorate unused is false, then remove end of cycle + if (!org.config.prorate_unused) { + prorationBehaviour = "none"; + + const downgrade = quantity < (subItem.quantity || 0); + if (!downgrade && !isTrialing(cusProduct as FullCusProduct)) { + let entitlement = cusEnt.entitlement; + let newUsage = entitlement.allowance! - newBalance; + let oldUsage = + entitlement.allowance! - originalBalance + (replacedCount || 0); + + let newAmount = getPriceForOverage(cusPrice.price, newUsage); + let oldAmount = getPriceForOverage(cusPrice.price, oldUsage); + + const stripeAmount = new Decimal(newAmount) + .sub(oldAmount) + .mul(100) + .round() + .toNumber(); + + logger.info(` - Stripe amount: ${stripeAmount}`); + + if (stripeAmount > 0) { + const invoice = await stripeCli.invoices.create({ + customer: customer.processor.id, + auto_advance: false, + subscription: sub.id, + }); + + if (!product) { + product = await ProductService.getByInternalId({ + db, + internalId: cusProduct.internal_product_id, + }); + } + + await stripeCli.invoiceItems.create({ + customer: customer.processor.id, + invoice: invoice.id, + quantity: 1, + description: `${product!.name} - ${ + affectedFeature.name + } x ${Math.round(newUsage - oldUsage)}`, + + price_data: { + product: config.stripe_product_id!, + unit_amount: stripeAmount, + currency: org.default_currency, + }, + }); + + const { paid, error } = await payForInvoice({ + stripeCli, + paymentMethod, + invoiceId: invoice.id, + logger, + }); + + if (!paid) { + try { + await stripeCli.invoices.voidInvoice(invoice.id); + } catch (error: any) {} + + throw new RecaseError({ + message: "Failed to pay for invoice", + code: ErrCode.PayInvoiceFailed, + data: { + invoiceId: invoice.id, + error, + }, + }); + } + + const latestInvoice = await stripeCli.invoices.retrieve(invoice.id, { + ...getInvoiceExpansion(), + }); + + let invoiceItems: InvoiceItem[] = []; + try { + invoiceItems = [ + { + price_id: cusPrice.price.id!, + stripe_id: latestInvoice.id, + internal_feature_id: affectedFeature.internal_id || null, + description: `${product!.name} - ${ + affectedFeature.name + } x ${Math.round(newUsage - oldUsage)}`, + period_start: Date.now(), + period_end: sub.current_period_end * 1000, + }, + ]; + } catch (error) {} + + await InvoiceService.createInvoiceFromStripe({ + db, + stripeInvoice: latestInvoice, + internalCustomerId: customer.internal_id, + internalEntityId: cusProduct.internal_entity_id || undefined, + org, + productIds: [product!.id], + internalProductIds: [product!.internal_id], + items: invoiceItems, + }); + } + } + } + + if (quantity < 0) { + quantity = 0; + logger.warn("❗️ Warning: quantity is negative, setting to 0"); + logger.warn( + `❗️ Allowance: ${cusEnt.entitlement.allowance}, New Balance: ${newBalance}`, + ); + } + + try { + await stripeCli.subscriptionItems.update(subItem.id, { + quantity: quantity, + proration_behavior: prorationBehaviour as any, + payment_behavior: fromEntities ? "error_if_incomplete" : undefined, + }); + logger.info(` ✅ Adjusted sub item ${subItem.id} to ${quantity}`); + } catch (error: any) { + if (fromEntities) { + throw new RecaseError({ + message: `Failed to update subscription subscription: ${error.message}`, + code: ErrCode.StripeUpdateSubscriptionFailed, + statusCode: error.statusCode, + }); + } else { + logger.error( + `❗️ adjustAllowance: Error updating subscription item (from event)`, + ); + logger.error(error); + } + } + + return; +}; diff --git a/server/src/trigger/arrearProratedUsage/createUpgradeProrationInvoice.ts b/server/src/trigger/arrearProratedUsage/createUpgradeProrationInvoice.ts new file mode 100644 index 000000000..cd41c5e13 --- /dev/null +++ b/server/src/trigger/arrearProratedUsage/createUpgradeProrationInvoice.ts @@ -0,0 +1,166 @@ +import { + Feature, + FullCustomerPrice, + OnIncrease, + Organization, + Price, + Product, + UsagePriceConfig, +} from "@autumn/shared"; +import { Decimal } from "decimal.js"; +import { getFeatureInvoiceDescription } from "@autumn/shared"; +import Stripe from "stripe"; +import { getCusPaymentMethod } from "@/external/stripe/stripeCusUtils.js"; +import { payForInvoice } from "@/external/stripe/stripeInvoiceUtils.js"; +import { constructStripeInvoiceItem } from "@/internal/invoices/invoiceItemUtils/invoiceItemUtils.js"; +import { createAndFinalizeInvoice } from "@/internal/invoices/invoiceUtils/createAndFinalizeInvoice.js"; +import { calculateProrationAmount } from "@/internal/invoices/prorationUtils.js"; +import { + shouldProrate, + shouldBillNow, +} from "@/internal/products/prices/priceUtils/prorationConfigUtils.js"; +import { formatUnixToDate } from "@/utils/genUtils.js"; +import { getStripeNow } from "@/utils/scriptUtils/testClockUtils.js"; + +export const getUpgradeProrationInvoiceItem = ({ + prevPrice, + newPrice, + now, + feature, + newRoundedUsage, + price, + org, + onIncrease, + product, + stripeSub, +}: { + prevPrice: number; + newPrice: number; + now: number; + feature: Feature; + newRoundedUsage: number; + price: Price; + org: Organization; + onIncrease: OnIncrease; + product: Product; + stripeSub: Stripe.Subscription; +}) => { + const billingUnits = (price.config as UsagePriceConfig).billing_units; + let invoiceAmount = new Decimal(newPrice).minus(prevPrice).toNumber(); + let invoiceDescription = getFeatureInvoiceDescription({ + feature, + usage: newRoundedUsage, + billingUnits, + prodName: product.name, + }); + + if (shouldProrate(onIncrease)) { + invoiceAmount = calculateProrationAmount({ + periodStart: stripeSub.current_period_start * 1000, + periodEnd: stripeSub.current_period_end * 1000, + now, + amount: invoiceAmount, + }); + + let start = formatUnixToDate(now); + let end = formatUnixToDate(stripeSub.current_period_end * 1000); + invoiceDescription = `${invoiceDescription} (from ${start} to ${end})`; + } + + let invoiceItem = constructStripeInvoiceItem({ + product, + amount: invoiceAmount, + org, + price: price, + description: invoiceDescription, + stripeSubId: stripeSub.id, + stripeCustomerId: stripeSub.customer as string, + periodStart: stripeSub.current_period_start, + periodEnd: Math.floor(now / 1000), + }); + + return invoiceItem; +}; + +export const createUpgradeProrationInvoice = async ({ + org, + cusPrice, + stripeCli, + sub, + newPrice, + prevPrice, + newRoundedUsage, + feature, + product, + config, + onIncrease, + logger, +}: { + org: Organization; + cusPrice: FullCustomerPrice; + stripeCli: Stripe; + sub: Stripe.Subscription; + newPrice: number; + prevPrice: number; + newRoundedUsage: number; + feature: Feature; + product: Product; + config: UsagePriceConfig; + onIncrease: OnIncrease; + logger: any; +}) => { + let now = await getStripeNow({ stripeCli, stripeSub: sub }); + + const paymentMethod = await getCusPaymentMethod({ + stripeCli, + stripeId: sub.customer as string, + }); + + let invoiceItem = getUpgradeProrationInvoiceItem({ + prevPrice, + newPrice, + now, + feature, + newRoundedUsage, + price: cusPrice.price, + org, + onIncrease, + product, + stripeSub: sub, + }); + + let invoiceAmount = + invoiceItem?.amount || invoiceItem?.price_data?.unit_amount || 0; + + let invoiceDescription = invoiceItem?.description || ""; + + if (invoiceAmount == 0) return; + + logger.info( + `🚀 Creating invoice item: ${invoiceDescription} - ${invoiceAmount.toFixed(2)}`, + ); + + await stripeCli.invoiceItems.create(invoiceItem); + + if (shouldBillNow(onIncrease)) { + const { invoice: finalInvoice } = await createAndFinalizeInvoice({ + stripeCli, + stripeCusId: sub.customer as string, + stripeSubId: sub.id, + }); + + const { invoice: paidInvoice } = await payForInvoice({ + stripeCli, + paymentMethod, + invoiceId: finalInvoice.id, + logger, + errorOnFail: true, + voidIfFailed: true, + }); + + logger.info(`Paid for invoice ${paidInvoice?.id}`); + return paidInvoice; + } + + return null; +}; diff --git a/server/src/trigger/arrearProratedUsage/handleCreateReplaceables.ts b/server/src/trigger/arrearProratedUsage/handleCreateReplaceables.ts new file mode 100644 index 000000000..0bbb514a1 --- /dev/null +++ b/server/src/trigger/arrearProratedUsage/handleCreateReplaceables.ts @@ -0,0 +1,44 @@ +import { DrizzleCli } from "@/db/initDrizzle.js"; +import { findLinkedCusEnts } from "@/internal/customers/cusProducts/cusEnts/cusEntUtils/findCusEntUtils.js"; +import { RepService } from "@/internal/customers/cusProducts/cusEnts/RepService.js"; +import { generateId } from "@/utils/genUtils.js"; +import { + FullCusEntWithFullCusProduct, + FullCusEntWithProduct, + InsertReplaceableSchema, +} from "@autumn/shared"; + +export const handleCreateReplaceables = async ({ + db, + prevOverage, + newOverage, + cusEnt, + logger, +}: { + db: DrizzleCli; + prevOverage: number; + newOverage: number; + cusEnt: FullCusEntWithFullCusProduct; + logger: any; +}) => { + if (prevOverage <= newOverage) { + logger.info("No replaceables needed"); + return; + } + logger.info(`Prev overage: ${prevOverage}, New overage: ${newOverage}`); + + let numReplaceables = prevOverage - newOverage; + let newReplaceables = Array.from({ length: numReplaceables }, (_, i) => + InsertReplaceableSchema.parse({ + id: generateId("rep"), + cus_ent_id: cusEnt.id, + created_at: Date.now(), + delete_next_cycle: true, + }), + ); + + return await RepService.insert({ + db, + data: newReplaceables, + }); +}; diff --git a/server/src/trigger/arrearProratedUsage/handleProratedDowngrade.ts b/server/src/trigger/arrearProratedUsage/handleProratedDowngrade.ts new file mode 100644 index 000000000..847019c0e --- /dev/null +++ b/server/src/trigger/arrearProratedUsage/handleProratedDowngrade.ts @@ -0,0 +1,203 @@ +import { + FullCusEntWithFullCusProduct, + FullCusEntWithProduct, + FullCustomerPrice, + OnDecrease, +} from "@autumn/shared"; +import Stripe from "stripe"; + +import { + Feature, + Organization, + Product, + UsagePriceConfig, +} from "@autumn/shared"; +import { Decimal } from "decimal.js"; +import { getFeatureInvoiceDescription } from "@autumn/shared"; +import { getCusPaymentMethod } from "@/external/stripe/stripeCusUtils.js"; +import { constructStripeInvoiceItem } from "@/internal/invoices/invoiceItemUtils/invoiceItemUtils.js"; +import { createAndFinalizeInvoice } from "@/internal/invoices/invoiceUtils/createAndFinalizeInvoice.js"; +import { calculateProrationAmount } from "@/internal/invoices/prorationUtils.js"; +import { formatUnixToDate } from "@/utils/genUtils.js"; +import { getStripeNow } from "@/utils/scriptUtils/testClockUtils.js"; +import { priceToInvoiceAmount } from "@/internal/products/prices/priceUtils/getAmountForPrice.js"; +import { handleCreateReplaceables } from "./handleCreateReplaceables.js"; +import { DrizzleCli } from "@/db/initDrizzle.js"; +import { getUsageFromBalance } from "../adjustAllowance.js"; + +export const createDowngradeProrationInvoice = async ({ + org, + cusPrice, + stripeCli, + sub, + newPrice, + prevPrice, + newRoundedUsage, + feature, + product, + onDecrease, + logger, +}: { + org: Organization; + cusPrice: FullCustomerPrice; + stripeCli: Stripe; + sub: Stripe.Subscription; + newPrice: number; + prevPrice: number; + newRoundedUsage: number; + feature: Feature; + product: Product; + onDecrease: OnDecrease; + logger: any; +}) => { + const config = cusPrice.price.config as UsagePriceConfig; + + let now = await getStripeNow({ stripeCli, stripeSub: sub }); + let invoiceAmount = new Decimal(newPrice).minus(prevPrice).toNumber(); + + logger.info(`Prev price: ${prevPrice}, New price: ${newPrice}`); + logger.info(`Invoice amount: ${invoiceAmount}`); + + let invoiceDescription = getFeatureInvoiceDescription({ + feature, + usage: newRoundedUsage, + billingUnits: config.billing_units, + prodName: product.name, + }); + + invoiceAmount = calculateProrationAmount({ + periodStart: sub.current_period_start * 1000, + periodEnd: sub.current_period_end * 1000, + now, + amount: invoiceAmount, + allowNegative: true, + }); + + let start = formatUnixToDate(now); + let end = formatUnixToDate(sub.current_period_end * 1000); + invoiceDescription = `${invoiceDescription} (from ${start} to ${end})`; + + if (invoiceAmount == 0) return; + + logger.info( + `🚀 Creating invoice item: ${invoiceDescription} - ${invoiceAmount.toFixed(2)}`, + ); + + const { invoice } = await createAndFinalizeInvoice({ + stripeCli, + stripeCusId: sub.customer as string, + stripeSubId: sub.id, + invoiceItems: [ + constructStripeInvoiceItem({ + product, + amount: invoiceAmount, + org, + price: cusPrice.price, + description: invoiceDescription, + stripeSubId: sub.id, + stripeCustomerId: sub.customer as string, + periodStart: sub.current_period_start, + periodEnd: Math.floor(now / 1000), + }), + ], + }); + + return invoice; +}; + +export const handleProratedDowngrade = async ({ + db, + org, + stripeCli, + cusEnt, + cusPrice, + sub, + subItem, + newBalance, + prevBalance, + logger, +}: { + db: DrizzleCli; + org: Organization; + stripeCli: Stripe; + cusEnt: FullCusEntWithFullCusProduct; + cusPrice: FullCustomerPrice; + sub: Stripe.Subscription; + subItem: Stripe.SubscriptionItem; + newBalance: number; + prevBalance: number; + logger: any; +}) => { + logger.info(`Handling quantity decrease`); + + const { + roundedUsage: newRoundedUsage, + roundedOverage: newRoundedOverage, + overage: newOverage, + } = getUsageFromBalance({ + ent: cusEnt.entitlement, + price: cusPrice.price, + balance: newBalance, + }); + + const { roundedOverage: prevRoundedOverage, overage: prevOverage } = + getUsageFromBalance({ + ent: cusEnt.entitlement, + price: cusPrice.price, + balance: prevBalance, + }); + + let onDecrease = + cusPrice.price.proration_config?.on_decrease || OnDecrease.Prorate; + + const feature = cusEnt.entitlement.feature; + const product = cusEnt.customer_product.product; + + let invoice = null; + let newReplaceables = null; + + if (onDecrease === OnDecrease.Prorate) { + let prevPrice = priceToInvoiceAmount({ + price: cusPrice.price, + overage: prevRoundedOverage, + }); + + let newPrice = priceToInvoiceAmount({ + price: cusPrice.price, + overage: newRoundedOverage, + }); + + invoice = await createDowngradeProrationInvoice({ + org, + cusPrice, + stripeCli, + sub, + newPrice, + prevPrice, + newRoundedUsage, + feature, + product, + onDecrease, + logger, + }); + } else { + newReplaceables = await handleCreateReplaceables({ + db, + prevOverage, + newOverage, + cusEnt, + logger, + }); + + logger.info("New replaceables", { + newReplaceables, + }); + } + + await stripeCli.subscriptionItems.update(subItem.id, { + quantity: newRoundedUsage, + proration_behavior: "none", + }); + + return { invoice, newReplaceables, deletedReplaceables: null }; +}; diff --git a/server/src/trigger/arrearProratedUsage/handleProratedUpgrade.ts b/server/src/trigger/arrearProratedUsage/handleProratedUpgrade.ts new file mode 100644 index 000000000..904e46b37 --- /dev/null +++ b/server/src/trigger/arrearProratedUsage/handleProratedUpgrade.ts @@ -0,0 +1,181 @@ +import { priceToInvoiceAmount } from "@/internal/products/prices/priceUtils/getAmountForPrice.js"; +import { shouldCreateInvoiceItem } from "@/internal/products/prices/priceUtils/prorationConfigUtils.js"; + +import { + Entitlement, + FullCusEntWithFullCusProduct, + FullCusEntWithProduct, + FullCustomerPrice, + OnIncrease, + Organization, + Price, + UsagePriceConfig, +} from "@autumn/shared"; + +import Stripe from "stripe"; +import { createUpgradeProrationInvoice } from "./createUpgradeProrationInvoice.js"; +import { getUsageFromBalance } from "../adjustAllowance.js"; +import { RepService } from "@/internal/customers/cusProducts/cusEnts/RepService.js"; +import { DrizzleCli } from "@/db/initDrizzle.js"; + +interface UsageValues { + prevRoundedUsage: number; + newRoundedUsage: number; + prevRoundedOverage: number; + newRoundedOverage: number; +} + +export const getPrevAndNewPriceForUpgrade = ({ + ent, + numReplaceables, + price, + newBalance, + prevBalance, + logger, +}: { + ent: Entitlement; + numReplaceables: number; + price: Price; + newBalance: number; + prevBalance: number; + logger: any; +}) => { + const { + usage: newUsage, + roundedUsage: newRoundedUsage, + roundedOverage: newRoundedOverage, + } = getUsageFromBalance({ + ent, + price, + balance: newBalance, + }); + + const { + usage: prevUsage, + roundedUsage: prevRoundedUsage, + overage: prevOverage, + roundedOverage: prevRoundedOverage, + } = getUsageFromBalance({ + ent, + price, + balance: prevBalance, + }); + + const { roundedOverage: overageWithReplaceables } = getUsageFromBalance({ + ent, + price, + balance: prevBalance - numReplaceables, + }); + + // logger.info(`Handling quantity increase`); + // logger.info( + // `Prev overage: ${prevOverage} -> ${newOverage}, [Replaceables: ${numReplaceables}]`, + // ); + // logger.info(`Usage: ${prevRoundedUsage} -> ${newRoundedUsage}`); + + // Get price for usage... + let prevPrice = priceToInvoiceAmount({ + price, + overage: overageWithReplaceables, + }); + + let newPrice = priceToInvoiceAmount({ + price, + overage: newRoundedOverage, + }); + + return { + newUsage, + prevUsage, + + prevRoundedUsage, + newRoundedUsage, + + prevPrice, + newPrice, + prevOverage, + prevRoundedOverage, + newRoundedOverage, + }; +}; + +export const handleProratedUpgrade = async ({ + db, + stripeCli, + cusEnt, + org, + cusPrice, + sub, + subItem, + newBalance, + prevBalance, + logger, +}: { + db: DrizzleCli; + stripeCli: Stripe; + org: Organization; + cusEnt: FullCusEntWithFullCusProduct; + cusPrice: FullCustomerPrice; + sub: Stripe.Subscription; + subItem: Stripe.SubscriptionItem; + newBalance: number; + prevBalance: number; + logger: any; +}) => { + logger.info(`Handling quantity increase`); + + const { prevPrice, newPrice, newRoundedUsage, newUsage, prevUsage } = + getPrevAndNewPriceForUpgrade({ + ent: cusEnt.entitlement, + numReplaceables: cusEnt.replaceables.length, + price: cusPrice.price, + newBalance, + prevBalance, + logger, + }); + + const config = cusPrice.price.config as UsagePriceConfig; + const product = cusEnt.customer_product.product; + const feature = cusEnt.entitlement.feature; + + let onIncrease = + cusPrice.price.proration_config?.on_increase || + OnIncrease.ProrateImmediately; + + logger.info(`Prev price: ${prevPrice}, New price: ${newPrice}`); + + let invoice = null; + if (shouldCreateInvoiceItem(onIncrease)) { + invoice = await createUpgradeProrationInvoice({ + org, + cusPrice, + stripeCli, + sub, + newPrice, + prevPrice, + newRoundedUsage, + feature, + product, + config, + onIncrease, + logger, + }); + } + + // Replaceables used + let usageDiff = newUsage - prevUsage; + let deletedReplaceables = cusEnt.replaceables.slice(0, usageDiff); + + let deleted = await RepService.deleteInIds({ + db, + ids: deletedReplaceables.map((r) => r.id), + }); + + await stripeCli.subscriptionItems.update(subItem.id, { + quantity: newRoundedUsage, + proration_behavior: "none", + }); + + logger.info(`Updated sub item ${subItem.id} to quantity: ${newRoundedUsage}`); + return { deletedReplaceables: deleted, invoice }; +}; diff --git a/server/src/utils/scriptUtils/constructItem.ts b/server/src/utils/scriptUtils/constructItem.ts index 0d2c635ef..f1211b7ee 100644 --- a/server/src/utils/scriptUtils/constructItem.ts +++ b/server/src/utils/scriptUtils/constructItem.ts @@ -1,5 +1,23 @@ import { ProductItem, ProductItemInterval, UsageModel } from "@autumn/shared"; +export const constructFeatureItem = ({ + featureId, + includedUsage = 150, + entityFeatureId, +}: { + featureId: string; + includedUsage?: number; + entityFeatureId?: string; +}) => { + let item: ProductItem = { + feature_id: featureId, + included_usage: includedUsage, + entity_feature_id: entityFeatureId, + }; + + return item; +}; + export const constructPrepaidItem = ({ featureId, price, @@ -51,14 +69,16 @@ export const constructArrearItem = ({ export const constructArrearProratedItem = ({ featureId, pricePerUnit, + includedUsage = 1, }: { featureId: string; pricePerUnit: number; + includedUsage?: number; }) => { let item: ProductItem = { feature_id: featureId, usage_model: UsageModel.PayPerUse, - included_usage: 1, + included_usage: includedUsage, price: pricePerUnit, billing_units: 1, interval: ProductItemInterval.Month, diff --git a/server/tests/attach/entities/entity1.ts b/server/tests/attach/entities/entity1.ts new file mode 100644 index 000000000..2d07a74a9 --- /dev/null +++ b/server/tests/attach/entities/entity1.ts @@ -0,0 +1,104 @@ +import { AutumnInt } from "@/external/autumn/autumnCli.js"; +import { initCustomer } from "@/utils/scriptUtils/initCustomer.js"; +import { APIVersion, AppEnv, Organization } from "@autumn/shared"; +import chalk from "chalk"; +import Stripe from "stripe"; +import { DrizzleCli } from "@/db/initDrizzle.js"; +import { setupBefore } from "tests/before.js"; +import { createProducts } from "tests/utils/productUtils.js"; +import { addPrefixToProducts, runAttachTest } from "../utils.js"; +import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js"; +import { + constructArrearProratedItem, + constructFeatureItem, +} from "@/utils/scriptUtils/constructItem.js"; +import { TestFeature } from "tests/setup/v2Features.js"; + +export let pro = constructProduct({ + items: [ + constructArrearProratedItem({ + featureId: TestFeature.Users, + pricePerUnit: 50, + includedUsage: 0, + }), + constructFeatureItem({ + featureId: TestFeature.Messages, + includedUsage: 100, + entityFeatureId: TestFeature.Users, + }), + ], + type: "pro", +}); + +const testCase = "entity1"; + +describe(`${chalk.yellowBright(`${testCase}: Testing entities`)}`, () => { + let customerId = testCase; + let autumn: AutumnInt = new AutumnInt({ version: APIVersion.v1_4 }); + let testClockId: string; + let db: DrizzleCli, org: Organization, env: AppEnv; + let stripeCli: Stripe; + + let curUnix = new Date().getTime(); + + before(async function () { + await setupBefore(this); + const { autumnJs } = this; + db = this.db; + org = this.org; + env = this.env; + + stripeCli = this.stripeCli; + + addPrefixToProducts({ + products: [pro], + prefix: testCase, + }); + + await createProducts({ + autumn, + products: [pro], + customerId, + db, + orgId: org.id, + env, + }); + + const { testClockId: testClockId1 } = await initCustomer({ + autumn: autumnJs, + customerId, + db, + org, + env, + attachPm: "success", + }); + + testClockId = testClockId1!; + }); + + it("should create entity, then attach pro product", async function () { + await autumn.entities.create(customerId, [ + { + id: "1", + name: "test", + featureId: TestFeature.Users, + }, + ]); + + await runAttachTest({ + autumn, + customerId, + product: pro, + stripeCli, + db, + org, + env, + usage: [ + { + featureId: TestFeature.Users, + value: 1, + }, + ], + }); + }); +}); diff --git a/server/tests/attach/newVersion/newVersion1.ts b/server/tests/attach/newVersion/newVersion1.ts index 6004286c3..cc826d989 100644 --- a/server/tests/attach/newVersion/newVersion1.ts +++ b/server/tests/attach/newVersion/newVersion1.ts @@ -1,3 +1,4 @@ +import { expect } from "chai"; import { AutumnInt } from "@/external/autumn/autumnCli.js"; import { initCustomer } from "@/utils/scriptUtils/initCustomer.js"; import { @@ -27,7 +28,6 @@ import { timeout } from "@/utils/genUtils.js"; import { advanceTestClock } from "tests/utils/stripeUtils.js"; import { addHours, addMonths, addWeeks } from "date-fns"; import { hoursToFinalizeInvoice } from "tests/utils/constants.js"; -import { expect } from "chai"; export let pro = constructProduct({ items: [constructArrearItem({ featureId: TestFeature.Words })], diff --git a/server/tests/template.ts b/server/tests/template.ts index b613c0425..bc93655eb 100644 --- a/server/tests/template.ts +++ b/server/tests/template.ts @@ -8,21 +8,30 @@ // import { createProducts } from "tests/utils/productUtils.js"; // import { addPrefixToProducts } from "../utils.js"; // import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js"; +// import { +// constructArrearProratedItem, +// constructFeatureItem, +// } from "@/utils/scriptUtils/constructItem.js"; +// import { TestFeature } from "tests/setup/v2Features.js"; // export let pro = constructProduct({ // items: [ -// constructPrepaidItem({ +// constructArrearProratedItem({ // featureId: TestFeature.Users, -// price: 12, -// billingUnits: 1, +// pricePerUnit: 50, +// }), +// constructFeatureItem({ +// featureId: TestFeature.Messages, +// includedUsage: 100, +// entityFeatureId: TestFeature.Users, // }), // ], // type: "pro", // }); -// const testCase = "upgrade3"; +// const testCase = "entity1"; -// describe(`${chalk.yellowBright(`${testCase}: INSERT TEST NAME`)}`, () => { +// describe(`${chalk.yellowBright(`${testCase}: Testing entities`)}`, () => { // let customerId = testCase; // let autumn: AutumnInt = new AutumnInt({ version: APIVersion.v1_4 }); // let testClockId: string; @@ -41,14 +50,17 @@ // stripeCli = this.stripeCli; // addPrefixToProducts({ -// products: [free, pro, premium], +// products: [pro], // prefix: testCase, // }); // await createProducts({ // autumn, -// products: [pro, premium], +// products: [pro], // customerId, +// db, +// orgId: org.id, +// env, // }); // const { testClockId: testClockId1 } = await initCustomer({ diff --git a/shared/db/schema.ts b/shared/db/schema.ts index 63789ea05..996c6b65b 100644 --- a/shared/db/schema.ts +++ b/shared/db/schema.ts @@ -65,31 +65,7 @@ import { migrationErrorRelations } from "../models/migrationModels/migrationErro // Analytics Tables import { actions } from "../models/analyticsModels/actionTable.js"; import { events } from "../models/eventModels/eventTable.js"; - -// const relations = { -// organizationsRelations, -// entitlementsRelations, -// featureRelations, -// priceRelations, -// productRelations, -// freeTrialRelations, -// customerProductsRelations, -// customerPricesRelations, -// customerEntitlementsRelations, - -// // Customer Relations -// customersRelations, -// entitiesRelations, -// apiKeyRelations, - -// // Reward Relations -// rewardProgramRelations, -// referralCodeRelations, -// rewardRedemptionRelations, - -// // Migration Relations -// migrationErrorRelations, -// }; +import { replaceables } from "../models/cusProductModels/cusEntModels/replaceableTable.js"; export { // Tables @@ -117,6 +93,7 @@ export { migrationErrors, actions, events, + replaceables, // Relations organizationsRelations, diff --git a/shared/drizzle/0003_yummy_la_nuit.sql b/shared/drizzle/0003_yummy_la_nuit.sql new file mode 100644 index 000000000..22ed59c39 --- /dev/null +++ b/shared/drizzle/0003_yummy_la_nuit.sql @@ -0,0 +1,4 @@ +ALTER TABLE "events" ALTER COLUMN "timestamp" SET DATA TYPE timestamp with time zone;--> statement-breakpoint +ALTER TABLE "customer_products" ADD COLUMN "api_version" numeric;--> statement-breakpoint +ALTER TABLE "events" ADD COLUMN "org_slug" text NOT NULL;--> statement-breakpoint +ALTER TABLE "prices" ADD COLUMN "proration_config" jsonb DEFAULT null; \ No newline at end of file diff --git a/shared/drizzle/0004_broad_shocker.sql b/shared/drizzle/0004_broad_shocker.sql new file mode 100644 index 000000000..e6c1ba06d --- /dev/null +++ b/shared/drizzle/0004_broad_shocker.sql @@ -0,0 +1,9 @@ +CREATE TABLE "replaceables" ( + "id" text COLLATE "C" PRIMARY KEY NOT NULL, + "cus_ent_id" text NOT NULL, + "created_at" bigint NOT NULL, + "from_entity_id" text, + "delete_next_cycle" boolean DEFAULT false NOT NULL +); +--> statement-breakpoint +ALTER TABLE "replaceables" ADD CONSTRAINT "replaceables_cus_ent_id_fkey" FOREIGN KEY ("cus_ent_id") REFERENCES "public"."customer_entitlements"("id") ON DELETE cascade ON UPDATE no action; \ No newline at end of file diff --git a/shared/drizzle/meta/0003_snapshot.json b/shared/drizzle/meta/0003_snapshot.json new file mode 100644 index 000000000..78722fe91 --- /dev/null +++ b/shared/drizzle/meta/0003_snapshot.json @@ -0,0 +1,2525 @@ +{ + "id": "eff9957d-e375-4fb2-bcc9-b30c6b4810cc", + "prevId": "bd68ad51-9560-4872-aa55-3fe710632cb2", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.actions": { + "name": "actions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text COLLATE \"C\"", + "primaryKey": true, + "notNull": true + }, + "request_id": { + "name": "request_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_slug": { + "name": "org_slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "customer_id": { + "name": "customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "internal_customer_id": { + "name": "internal_customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "internal_entity_id": { + "name": "internal_entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "auth_type": { + "name": "auth_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "method": { + "name": "method", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "timestamp": { + "name": "timestamp", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "properties": { + "name": "properties", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "actions_org_id_fkey": { + "name": "actions_org_id_fkey", + "tableFrom": "actions", + "tableTo": "organizations", + "columnsFrom": [ + "org_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "actions_customer_id_fkey": { + "name": "actions_customer_id_fkey", + "tableFrom": "actions", + "tableTo": "customers", + "columnsFrom": [ + "internal_customer_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "actions_entity_id_fkey": { + "name": "actions_entity_id_fkey", + "tableFrom": "actions", + "tableTo": "entities", + "columnsFrom": [ + "internal_entity_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": true + }, + "public.api_keys": { + "name": "api_keys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text COLLATE \"C\"", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "default": "ROUND(date_part('epoch', NOW()) * 1000)::BIGINT" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "prefix": { + "name": "prefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "hashed_key": { + "name": "hashed_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "meta": { + "name": "meta", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "api_keys_org_id_fkey": { + "name": "api_keys_org_id_fkey", + "tableFrom": "api_keys", + "tableTo": "organizations", + "columnsFrom": [ + "org_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "api_keys_hashed_key_key": { + "name": "api_keys_hashed_key_key", + "nullsNotDistinct": false, + "columns": [ + "hashed_key" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_results": { + "name": "chat_results", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text COLLATE \"C\"", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": true + }, + "public.customer_entitlements": { + "name": "customer_entitlements", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text COLLATE \"C\"", + "primaryKey": true, + "notNull": true + }, + "customer_product_id": { + "name": "customer_product_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entitlement_id": { + "name": "entitlement_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "internal_customer_id": { + "name": "internal_customer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "internal_feature_id": { + "name": "internal_feature_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "unlimited": { + "name": "unlimited", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "balance": { + "name": "balance", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "next_reset_at": { + "name": "next_reset_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "usage_allowed": { + "name": "usage_allowed", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "adjustment": { + "name": "adjustment", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "entities": { + "name": "entities", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "customer_id": { + "name": "customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "feature_id": { + "name": "feature_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "entitlements_internal_feature_id_fkey": { + "name": "entitlements_internal_feature_id_fkey", + "tableFrom": "customer_entitlements", + "tableTo": "features", + "columnsFrom": [ + "internal_feature_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "customer_entitlements_customer_product_id_fkey": { + "name": "customer_entitlements_customer_product_id_fkey", + "tableFrom": "customer_entitlements", + "tableTo": "customer_products", + "columnsFrom": [ + "customer_product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "entitlements_id_key": { + "name": "entitlements_id_key", + "nullsNotDistinct": false, + "columns": [ + "id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.customer_prices": { + "name": "customer_prices", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "price_id": { + "name": "price_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "options": { + "name": "options", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "internal_customer_id": { + "name": "internal_customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customer_product_id": { + "name": "customer_product_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "customer_prices_customer_product_id_fkey": { + "name": "customer_prices_customer_product_id_fkey", + "tableFrom": "customer_prices", + "tableTo": "customer_products", + "columnsFrom": [ + "customer_product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "customer_prices_internal_customer_id_fkey": { + "name": "customer_prices_internal_customer_id_fkey", + "tableFrom": "customer_prices", + "tableTo": "customers", + "columnsFrom": [ + "internal_customer_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "customer_prices_price_id_fkey": { + "name": "customer_prices_price_id_fkey", + "tableFrom": "customer_prices", + "tableTo": "prices", + "columnsFrom": [ + "price_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.customer_products": { + "name": "customer_products", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text COLLATE \"C\"", + "primaryKey": true, + "notNull": true + }, + "internal_customer_id": { + "name": "internal_customer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "internal_product_id": { + "name": "internal_product_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "internal_entity_id": { + "name": "internal_entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "processor": { + "name": "processor", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "canceled_at": { + "name": "canceled_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "ended_at": { + "name": "ended_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "starts_at": { + "name": "starts_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "options": { + "name": "options", + "type": "jsonb[]", + "primaryKey": false, + "notNull": false + }, + "product_id": { + "name": "product_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "free_trial_id": { + "name": "free_trial_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "trial_ends_at": { + "name": "trial_ends_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "collection_method": { + "name": "collection_method", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'charge_automatically'" + }, + "subscription_ids": { + "name": "subscription_ids", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "scheduled_ids": { + "name": "scheduled_ids", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "quantity": { + "name": "quantity", + "type": "numeric", + "primaryKey": false, + "notNull": false, + "default": 1 + }, + "is_custom": { + "name": "is_custom", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "customer_id": { + "name": "customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "api_version": { + "name": "api_version", + "type": "numeric", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "customer_products_free_trial_id_fkey": { + "name": "customer_products_free_trial_id_fkey", + "tableFrom": "customer_products", + "tableTo": "free_trials", + "columnsFrom": [ + "free_trial_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "customer_products_internal_customer_id_fkey": { + "name": "customer_products_internal_customer_id_fkey", + "tableFrom": "customer_products", + "tableTo": "customers", + "columnsFrom": [ + "internal_customer_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "customer_products_internal_product_id_fkey": { + "name": "customer_products_internal_product_id_fkey", + "tableFrom": "customer_products", + "tableTo": "products", + "columnsFrom": [ + "internal_product_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "customer_products_internal_entity_id_fkey": { + "name": "customer_products_internal_entity_id_fkey", + "tableFrom": "customer_products", + "tableTo": "entities", + "columnsFrom": [ + "internal_entity_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.customers": { + "name": "customers", + "schema": "", + "columns": { + "internal_id": { + "name": "internal_id", + "type": "text COLLATE \"C\"", + "primaryKey": true, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id": { + "name": "id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "fingerprint": { + "name": "fingerprint", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "null" + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "processor": { + "name": "processor", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "customers_org_id_fkey": { + "name": "customers_org_id_fkey", + "tableFrom": "customers", + "tableTo": "organizations", + "columnsFrom": [ + "org_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "cus_id_constraint": { + "name": "cus_id_constraint", + "nullsNotDistinct": false, + "columns": [ + "org_id", + "id", + "env" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": true + }, + "public.entities": { + "name": "entities", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "internal_id": { + "name": "internal_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "internal_customer_id": { + "name": "internal_customer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "internal_feature_id": { + "name": "internal_feature_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "feature_id": { + "name": "feature_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "entities_internal_customer_id_fkey": { + "name": "entities_internal_customer_id_fkey", + "tableFrom": "entities", + "tableTo": "customers", + "columnsFrom": [ + "internal_customer_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "entities_internal_feature_id_fkey": { + "name": "entities_internal_feature_id_fkey", + "tableFrom": "entities", + "tableTo": "features", + "columnsFrom": [ + "internal_feature_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "entities_org_id_fkey": { + "name": "entities_org_id_fkey", + "tableFrom": "entities", + "tableTo": "organizations", + "columnsFrom": [ + "org_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "entity_id_constraint": { + "name": "entity_id_constraint", + "nullsNotDistinct": false, + "columns": [ + "org_id", + "env", + "internal_customer_id", + "id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.entitlements": { + "name": "entitlements", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text COLLATE \"C\"", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "internal_feature_id": { + "name": "internal_feature_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "internal_product_id": { + "name": "internal_product_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_custom": { + "name": "is_custom", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "allowance_type": { + "name": "allowance_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "allowance": { + "name": "allowance", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "interval": { + "name": "interval", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "carry_from_previous": { + "name": "carry_from_previous", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "entity_feature_id": { + "name": "entity_feature_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "null" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "feature_id": { + "name": "feature_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "entitlements_internal_feature_id_fkey": { + "name": "entitlements_internal_feature_id_fkey", + "tableFrom": "entitlements", + "tableTo": "features", + "columnsFrom": [ + "internal_feature_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "entitlements_internal_product_id_fkey": { + "name": "entitlements_internal_product_id_fkey", + "tableFrom": "entitlements", + "tableTo": "products", + "columnsFrom": [ + "internal_product_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "entitlements_id_key": { + "name": "entitlements_id_key", + "nullsNotDistinct": false, + "columns": [ + "id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.events": { + "name": "events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_slug": { + "name": "org_slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "internal_customer_id": { + "name": "internal_customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "timestamp": { + "name": "timestamp", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "event_name": { + "name": "event_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "null" + }, + "value": { + "name": "value", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "set_usage": { + "name": "set_usage", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customer_id": { + "name": "customer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "properties": { + "name": "properties", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "events_internal_customer_id_fkey": { + "name": "events_internal_customer_id_fkey", + "tableFrom": "events", + "tableTo": "customers", + "columnsFrom": [ + "internal_customer_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "unique_event_constraint": { + "name": "unique_event_constraint", + "nullsNotDistinct": false, + "columns": [ + "org_id", + "env", + "customer_id", + "event_name", + "idempotency_key" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.features": { + "name": "features", + "schema": "", + "columns": { + "internal_id": { + "name": "internal_id", + "type": "text COLLATE \"C\"", + "primaryKey": true, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id": { + "name": "id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "display": { + "name": "display", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "null" + } + }, + "indexes": {}, + "foreignKeys": { + "features_org_id_fkey": { + "name": "features_org_id_fkey", + "tableFrom": "features", + "tableTo": "organizations", + "columnsFrom": [ + "org_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "feature_id_constraint": { + "name": "feature_id_constraint", + "nullsNotDistinct": false, + "columns": [ + "org_id", + "id", + "env" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.free_trials": { + "name": "free_trials", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "internal_product_id": { + "name": "internal_product_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "duration": { + "name": "duration", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'day'" + }, + "length": { + "name": "length", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "unique_fingerprint": { + "name": "unique_fingerprint", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "is_custom": { + "name": "is_custom", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "free_trials_internal_product_id_fkey": { + "name": "free_trials_internal_product_id_fkey", + "tableFrom": "free_trials", + "tableTo": "products", + "columnsFrom": [ + "internal_product_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.invoices": { + "name": "invoices", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text COLLATE \"C\"", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "default": "ROUND(date_part('epoch', NOW()) * 1000)::BIGINT" + }, + "product_ids": { + "name": "product_ids", + "type": "text[]", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "internal_product_ids": { + "name": "internal_product_ids", + "type": "text[]", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "internal_customer_id": { + "name": "internal_customer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "internal_entity_id": { + "name": "internal_entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_id": { + "name": "stripe_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'draft'" + }, + "hosted_invoice_url": { + "name": "hosted_invoice_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "total": { + "name": "total", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "currency": { + "name": "currency", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'usd'" + }, + "discounts": { + "name": "discounts", + "type": "jsonb[]", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "items": { + "name": "items", + "type": "jsonb[]", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + } + }, + "indexes": {}, + "foreignKeys": { + "invoices_internal_customer_id_fkey": { + "name": "invoices_internal_customer_id_fkey", + "tableFrom": "invoices", + "tableTo": "customers", + "columnsFrom": [ + "internal_customer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "invoices_internal_entity_id_fkey": { + "name": "invoices_internal_entity_id_fkey", + "tableFrom": "invoices", + "tableTo": "entities", + "columnsFrom": [ + "internal_entity_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.metadata": { + "name": "metadata", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "default": "ROUND(date_part('epoch', NOW()) * 1000)::BIGINT" + }, + "expires_at": { + "name": "expires_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.migration_errors": { + "name": "migration_errors", + "schema": "", + "columns": { + "internal_customer_id": { + "name": "internal_customer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "migration_job_id": { + "name": "migration_job_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "migration_customers_internal_customer_id_fkey": { + "name": "migration_customers_internal_customer_id_fkey", + "tableFrom": "migration_errors", + "tableTo": "customers", + "columnsFrom": [ + "internal_customer_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "migration_customers_migration_job_id_fkey": { + "name": "migration_customers_migration_job_id_fkey", + "tableFrom": "migration_errors", + "tableTo": "migration_jobs", + "columnsFrom": [ + "migration_job_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "migration_errors_pkey": { + "name": "migration_errors_pkey", + "columns": [ + "internal_customer_id", + "migration_job_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.migration_jobs": { + "name": "migration_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "current_step": { + "name": "current_step", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "from_internal_product_id": { + "name": "from_internal_product_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "to_internal_product_id": { + "name": "to_internal_product_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "step_details": { + "name": "step_details", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "migration_jobs_from_internal_product_id_fkey": { + "name": "migration_jobs_from_internal_product_id_fkey", + "tableFrom": "migration_jobs", + "tableTo": "products", + "columnsFrom": [ + "from_internal_product_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "migration_jobs_org_id_fkey": { + "name": "migration_jobs_org_id_fkey", + "tableFrom": "migration_jobs", + "tableTo": "organizations", + "columnsFrom": [ + "org_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "migration_jobs_to_internal_product_id_fkey": { + "name": "migration_jobs_to_internal_product_id_fkey", + "tableFrom": "migration_jobs", + "tableTo": "products", + "columnsFrom": [ + "to_internal_product_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organizations": { + "name": "organizations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "default_currency": { + "name": "default_currency", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'usd'" + }, + "stripe_connected": { + "name": "stripe_connected", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "stripe_config": { + "name": "stripe_config", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "test_pkey": { + "name": "test_pkey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "live_pkey": { + "name": "live_pkey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "svix_config": { + "name": "svix_config", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "organizations_test_pkey_key": { + "name": "organizations_test_pkey_key", + "nullsNotDistinct": false, + "columns": [ + "test_pkey" + ] + }, + "organizations_live_pkey_key": { + "name": "organizations_live_pkey_key", + "nullsNotDistinct": false, + "columns": [ + "live_pkey" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.prices": { + "name": "prices", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text COLLATE \"C\"", + "primaryKey": true, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "internal_product_id": { + "name": "internal_product_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "billing_type": { + "name": "billing_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_custom": { + "name": "is_custom", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "entitlement_id": { + "name": "entitlement_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "null" + }, + "proration_config": { + "name": "proration_config", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "null" + } + }, + "indexes": {}, + "foreignKeys": { + "prices_entitlement_id_fkey": { + "name": "prices_entitlement_id_fkey", + "tableFrom": "prices", + "tableTo": "entitlements", + "columnsFrom": [ + "entitlement_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "prices_internal_product_id_fkey": { + "name": "prices_internal_product_id_fkey", + "tableFrom": "prices", + "tableTo": "products", + "columnsFrom": [ + "internal_product_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "prices_id_key": { + "name": "prices_id_key", + "nullsNotDistinct": false, + "columns": [ + "id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.products": { + "name": "products", + "schema": "", + "columns": { + "internal_id": { + "name": "internal_id", + "type": "text COLLATE \"C\"", + "primaryKey": true, + "notNull": true + }, + "id": { + "name": "id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "default": "ROUND(date_part('epoch', NOW()) * 1000)::BIGINT" + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_add_on": { + "name": "is_add_on", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_default": { + "name": "is_default", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "group": { + "name": "group", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "''" + }, + "version": { + "name": "version", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "processor": { + "name": "processor", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "null" + } + }, + "indexes": {}, + "foreignKeys": { + "products_org_id_fkey": { + "name": "products_org_id_fkey", + "tableFrom": "products", + "tableTo": "organizations", + "columnsFrom": [ + "org_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "unique_product": { + "name": "unique_product", + "nullsNotDistinct": false, + "columns": [ + "org_id", + "id", + "env", + "version" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.referral_codes": { + "name": "referral_codes", + "schema": "", + "columns": { + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "internal_customer_id": { + "name": "internal_customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "internal_reward_program_id": { + "name": "internal_reward_program_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id": { + "name": "id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "referral_codes_internal_customer_id_fkey": { + "name": "referral_codes_internal_customer_id_fkey", + "tableFrom": "referral_codes", + "tableTo": "customers", + "columnsFrom": [ + "internal_customer_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "referral_codes_internal_reward_program_id_fkey": { + "name": "referral_codes_internal_reward_program_id_fkey", + "tableFrom": "referral_codes", + "tableTo": "reward_programs", + "columnsFrom": [ + "internal_reward_program_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "referral_codes_org_id_fkey": { + "name": "referral_codes_org_id_fkey", + "tableFrom": "referral_codes", + "tableTo": "organizations", + "columnsFrom": [ + "org_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "referral_codes_pkey": { + "name": "referral_codes_pkey", + "columns": [ + "code", + "org_id", + "env" + ] + } + }, + "uniqueConstraints": { + "referral_codes_id_key": { + "name": "referral_codes_id_key", + "nullsNotDistinct": false, + "columns": [ + "id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reward_programs": { + "name": "reward_programs", + "schema": "", + "columns": { + "internal_id": { + "name": "internal_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "id": { + "name": "id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "internal_reward_id": { + "name": "internal_reward_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "max_redemptions": { + "name": "max_redemptions", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "unlimited_redemptions": { + "name": "unlimited_redemptions", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "when": { + "name": "when", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'immediately'" + }, + "product_ids": { + "name": "product_ids", + "type": "text[]", + "primaryKey": false, + "notNull": false, + "default": "'{\"\"}'" + }, + "exclude_trial": { + "name": "exclude_trial", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "received_by": { + "name": "received_by", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "reward_triggers_internal_reward_id_fkey": { + "name": "reward_triggers_internal_reward_id_fkey", + "tableFrom": "reward_programs", + "tableTo": "rewards", + "columnsFrom": [ + "internal_reward_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "reward_triggers_org_id_fkey": { + "name": "reward_triggers_org_id_fkey", + "tableFrom": "reward_programs", + "tableTo": "organizations", + "columnsFrom": [ + "org_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reward_redemptions": { + "name": "reward_redemptions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "internal_customer_id": { + "name": "internal_customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "triggered": { + "name": "triggered", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "internal_reward_program_id": { + "name": "internal_reward_program_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applied": { + "name": "applied", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "referral_code_id": { + "name": "referral_code_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "reward_redemptions_internal_customer_id_fkey": { + "name": "reward_redemptions_internal_customer_id_fkey", + "tableFrom": "reward_redemptions", + "tableTo": "customers", + "columnsFrom": [ + "internal_customer_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "reward_redemptions_internal_reward_program_id_fkey": { + "name": "reward_redemptions_internal_reward_program_id_fkey", + "tableFrom": "reward_redemptions", + "tableTo": "reward_programs", + "columnsFrom": [ + "internal_reward_program_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "reward_redemptions_referral_code_id_fkey": { + "name": "reward_redemptions_referral_code_id_fkey", + "tableFrom": "reward_redemptions", + "tableTo": "referral_codes", + "columnsFrom": [ + "referral_code_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.rewards": { + "name": "rewards", + "schema": "", + "columns": { + "internal_id": { + "name": "internal_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "id": { + "name": "id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "discount_config": { + "name": "discount_config", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "free_product_id": { + "name": "free_product_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "promo_codes": { + "name": "promo_codes", + "type": "jsonb[]", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "coupons_org_id_fkey": { + "name": "coupons_org_id_fkey", + "tableFrom": "rewards", + "tableTo": "organizations", + "columnsFrom": [ + "org_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.subscriptions": { + "name": "subscriptions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stripe_id": { + "name": "stripe_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_schedule_id": { + "name": "stripe_schedule_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "default": "ROUND(date_part('epoch', NOW()) * 1000)::BIGINT" + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{}'::jsonb" + }, + "usage_features": { + "name": "usage_features", + "type": "text[]", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "current_period_start": { + "name": "current_period_start", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "current_period_end": { + "name": "current_period_end", + "type": "numeric", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "subscriptions_org_id_fkey": { + "name": "subscriptions_org_id_fkey", + "tableFrom": "subscriptions", + "tableTo": "organizations", + "columnsFrom": [ + "org_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "subscriptions_stripe_id_key": { + "name": "subscriptions_stripe_id_key", + "nullsNotDistinct": false, + "columns": [ + "stripe_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/shared/drizzle/meta/0004_snapshot.json b/shared/drizzle/meta/0004_snapshot.json new file mode 100644 index 000000000..70ef1a225 --- /dev/null +++ b/shared/drizzle/meta/0004_snapshot.json @@ -0,0 +1,2583 @@ +{ + "id": "e40b8996-e527-41a2-a823-9dddd2325689", + "prevId": "eff9957d-e375-4fb2-bcc9-b30c6b4810cc", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.actions": { + "name": "actions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text COLLATE \"C\"", + "primaryKey": true, + "notNull": true + }, + "request_id": { + "name": "request_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_slug": { + "name": "org_slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "customer_id": { + "name": "customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "internal_customer_id": { + "name": "internal_customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "internal_entity_id": { + "name": "internal_entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "auth_type": { + "name": "auth_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "method": { + "name": "method", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "timestamp": { + "name": "timestamp", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "properties": { + "name": "properties", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "actions_org_id_fkey": { + "name": "actions_org_id_fkey", + "tableFrom": "actions", + "tableTo": "organizations", + "columnsFrom": [ + "org_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "actions_customer_id_fkey": { + "name": "actions_customer_id_fkey", + "tableFrom": "actions", + "tableTo": "customers", + "columnsFrom": [ + "internal_customer_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "actions_entity_id_fkey": { + "name": "actions_entity_id_fkey", + "tableFrom": "actions", + "tableTo": "entities", + "columnsFrom": [ + "internal_entity_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": true + }, + "public.api_keys": { + "name": "api_keys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text COLLATE \"C\"", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "default": "ROUND(date_part('epoch', NOW()) * 1000)::BIGINT" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "prefix": { + "name": "prefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "hashed_key": { + "name": "hashed_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "meta": { + "name": "meta", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "api_keys_org_id_fkey": { + "name": "api_keys_org_id_fkey", + "tableFrom": "api_keys", + "tableTo": "organizations", + "columnsFrom": [ + "org_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "api_keys_hashed_key_key": { + "name": "api_keys_hashed_key_key", + "nullsNotDistinct": false, + "columns": [ + "hashed_key" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_results": { + "name": "chat_results", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text COLLATE \"C\"", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": true + }, + "public.customer_entitlements": { + "name": "customer_entitlements", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text COLLATE \"C\"", + "primaryKey": true, + "notNull": true + }, + "customer_product_id": { + "name": "customer_product_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entitlement_id": { + "name": "entitlement_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "internal_customer_id": { + "name": "internal_customer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "internal_feature_id": { + "name": "internal_feature_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "unlimited": { + "name": "unlimited", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "balance": { + "name": "balance", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "next_reset_at": { + "name": "next_reset_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "usage_allowed": { + "name": "usage_allowed", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "adjustment": { + "name": "adjustment", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "entities": { + "name": "entities", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "customer_id": { + "name": "customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "feature_id": { + "name": "feature_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "entitlements_internal_feature_id_fkey": { + "name": "entitlements_internal_feature_id_fkey", + "tableFrom": "customer_entitlements", + "tableTo": "features", + "columnsFrom": [ + "internal_feature_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "customer_entitlements_customer_product_id_fkey": { + "name": "customer_entitlements_customer_product_id_fkey", + "tableFrom": "customer_entitlements", + "tableTo": "customer_products", + "columnsFrom": [ + "customer_product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "entitlements_id_key": { + "name": "entitlements_id_key", + "nullsNotDistinct": false, + "columns": [ + "id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.customer_prices": { + "name": "customer_prices", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "price_id": { + "name": "price_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "options": { + "name": "options", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "internal_customer_id": { + "name": "internal_customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customer_product_id": { + "name": "customer_product_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "customer_prices_customer_product_id_fkey": { + "name": "customer_prices_customer_product_id_fkey", + "tableFrom": "customer_prices", + "tableTo": "customer_products", + "columnsFrom": [ + "customer_product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "customer_prices_internal_customer_id_fkey": { + "name": "customer_prices_internal_customer_id_fkey", + "tableFrom": "customer_prices", + "tableTo": "customers", + "columnsFrom": [ + "internal_customer_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "customer_prices_price_id_fkey": { + "name": "customer_prices_price_id_fkey", + "tableFrom": "customer_prices", + "tableTo": "prices", + "columnsFrom": [ + "price_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.customer_products": { + "name": "customer_products", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text COLLATE \"C\"", + "primaryKey": true, + "notNull": true + }, + "internal_customer_id": { + "name": "internal_customer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "internal_product_id": { + "name": "internal_product_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "internal_entity_id": { + "name": "internal_entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "processor": { + "name": "processor", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "canceled_at": { + "name": "canceled_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "ended_at": { + "name": "ended_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "starts_at": { + "name": "starts_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "options": { + "name": "options", + "type": "jsonb[]", + "primaryKey": false, + "notNull": false + }, + "product_id": { + "name": "product_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "free_trial_id": { + "name": "free_trial_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "trial_ends_at": { + "name": "trial_ends_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "collection_method": { + "name": "collection_method", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'charge_automatically'" + }, + "subscription_ids": { + "name": "subscription_ids", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "scheduled_ids": { + "name": "scheduled_ids", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "quantity": { + "name": "quantity", + "type": "numeric", + "primaryKey": false, + "notNull": false, + "default": 1 + }, + "is_custom": { + "name": "is_custom", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "customer_id": { + "name": "customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "api_version": { + "name": "api_version", + "type": "numeric", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "customer_products_free_trial_id_fkey": { + "name": "customer_products_free_trial_id_fkey", + "tableFrom": "customer_products", + "tableTo": "free_trials", + "columnsFrom": [ + "free_trial_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "customer_products_internal_customer_id_fkey": { + "name": "customer_products_internal_customer_id_fkey", + "tableFrom": "customer_products", + "tableTo": "customers", + "columnsFrom": [ + "internal_customer_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "customer_products_internal_product_id_fkey": { + "name": "customer_products_internal_product_id_fkey", + "tableFrom": "customer_products", + "tableTo": "products", + "columnsFrom": [ + "internal_product_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "customer_products_internal_entity_id_fkey": { + "name": "customer_products_internal_entity_id_fkey", + "tableFrom": "customer_products", + "tableTo": "entities", + "columnsFrom": [ + "internal_entity_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.customers": { + "name": "customers", + "schema": "", + "columns": { + "internal_id": { + "name": "internal_id", + "type": "text COLLATE \"C\"", + "primaryKey": true, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id": { + "name": "id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "fingerprint": { + "name": "fingerprint", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "null" + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "processor": { + "name": "processor", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "customers_org_id_fkey": { + "name": "customers_org_id_fkey", + "tableFrom": "customers", + "tableTo": "organizations", + "columnsFrom": [ + "org_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "cus_id_constraint": { + "name": "cus_id_constraint", + "nullsNotDistinct": false, + "columns": [ + "org_id", + "id", + "env" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": true + }, + "public.entities": { + "name": "entities", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "internal_id": { + "name": "internal_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "internal_customer_id": { + "name": "internal_customer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "internal_feature_id": { + "name": "internal_feature_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "feature_id": { + "name": "feature_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "entities_internal_customer_id_fkey": { + "name": "entities_internal_customer_id_fkey", + "tableFrom": "entities", + "tableTo": "customers", + "columnsFrom": [ + "internal_customer_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "entities_internal_feature_id_fkey": { + "name": "entities_internal_feature_id_fkey", + "tableFrom": "entities", + "tableTo": "features", + "columnsFrom": [ + "internal_feature_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "entities_org_id_fkey": { + "name": "entities_org_id_fkey", + "tableFrom": "entities", + "tableTo": "organizations", + "columnsFrom": [ + "org_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "entity_id_constraint": { + "name": "entity_id_constraint", + "nullsNotDistinct": false, + "columns": [ + "org_id", + "env", + "internal_customer_id", + "id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.entitlements": { + "name": "entitlements", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text COLLATE \"C\"", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "internal_feature_id": { + "name": "internal_feature_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "internal_product_id": { + "name": "internal_product_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_custom": { + "name": "is_custom", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "allowance_type": { + "name": "allowance_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "allowance": { + "name": "allowance", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "interval": { + "name": "interval", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "carry_from_previous": { + "name": "carry_from_previous", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "entity_feature_id": { + "name": "entity_feature_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "null" + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "feature_id": { + "name": "feature_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "entitlements_internal_feature_id_fkey": { + "name": "entitlements_internal_feature_id_fkey", + "tableFrom": "entitlements", + "tableTo": "features", + "columnsFrom": [ + "internal_feature_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "entitlements_internal_product_id_fkey": { + "name": "entitlements_internal_product_id_fkey", + "tableFrom": "entitlements", + "tableTo": "products", + "columnsFrom": [ + "internal_product_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "entitlements_id_key": { + "name": "entitlements_id_key", + "nullsNotDistinct": false, + "columns": [ + "id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.events": { + "name": "events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_slug": { + "name": "org_slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "internal_customer_id": { + "name": "internal_customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "timestamp": { + "name": "timestamp", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "event_name": { + "name": "event_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "null" + }, + "value": { + "name": "value", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "set_usage": { + "name": "set_usage", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customer_id": { + "name": "customer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "properties": { + "name": "properties", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "events_internal_customer_id_fkey": { + "name": "events_internal_customer_id_fkey", + "tableFrom": "events", + "tableTo": "customers", + "columnsFrom": [ + "internal_customer_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "unique_event_constraint": { + "name": "unique_event_constraint", + "nullsNotDistinct": false, + "columns": [ + "org_id", + "env", + "customer_id", + "event_name", + "idempotency_key" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.features": { + "name": "features", + "schema": "", + "columns": { + "internal_id": { + "name": "internal_id", + "type": "text COLLATE \"C\"", + "primaryKey": true, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id": { + "name": "id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "display": { + "name": "display", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "null" + } + }, + "indexes": {}, + "foreignKeys": { + "features_org_id_fkey": { + "name": "features_org_id_fkey", + "tableFrom": "features", + "tableTo": "organizations", + "columnsFrom": [ + "org_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "feature_id_constraint": { + "name": "feature_id_constraint", + "nullsNotDistinct": false, + "columns": [ + "org_id", + "id", + "env" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.free_trials": { + "name": "free_trials", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "internal_product_id": { + "name": "internal_product_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "duration": { + "name": "duration", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'day'" + }, + "length": { + "name": "length", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "unique_fingerprint": { + "name": "unique_fingerprint", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "is_custom": { + "name": "is_custom", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "free_trials_internal_product_id_fkey": { + "name": "free_trials_internal_product_id_fkey", + "tableFrom": "free_trials", + "tableTo": "products", + "columnsFrom": [ + "internal_product_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.invoices": { + "name": "invoices", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text COLLATE \"C\"", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "default": "ROUND(date_part('epoch', NOW()) * 1000)::BIGINT" + }, + "product_ids": { + "name": "product_ids", + "type": "text[]", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "internal_product_ids": { + "name": "internal_product_ids", + "type": "text[]", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "internal_customer_id": { + "name": "internal_customer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "internal_entity_id": { + "name": "internal_entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_id": { + "name": "stripe_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'draft'" + }, + "hosted_invoice_url": { + "name": "hosted_invoice_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "total": { + "name": "total", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "currency": { + "name": "currency", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'usd'" + }, + "discounts": { + "name": "discounts", + "type": "jsonb[]", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "items": { + "name": "items", + "type": "jsonb[]", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + } + }, + "indexes": {}, + "foreignKeys": { + "invoices_internal_customer_id_fkey": { + "name": "invoices_internal_customer_id_fkey", + "tableFrom": "invoices", + "tableTo": "customers", + "columnsFrom": [ + "internal_customer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "invoices_internal_entity_id_fkey": { + "name": "invoices_internal_entity_id_fkey", + "tableFrom": "invoices", + "tableTo": "entities", + "columnsFrom": [ + "internal_entity_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.metadata": { + "name": "metadata", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "default": "ROUND(date_part('epoch', NOW()) * 1000)::BIGINT" + }, + "expires_at": { + "name": "expires_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.migration_errors": { + "name": "migration_errors", + "schema": "", + "columns": { + "internal_customer_id": { + "name": "internal_customer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "migration_job_id": { + "name": "migration_job_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "migration_customers_internal_customer_id_fkey": { + "name": "migration_customers_internal_customer_id_fkey", + "tableFrom": "migration_errors", + "tableTo": "customers", + "columnsFrom": [ + "internal_customer_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "migration_customers_migration_job_id_fkey": { + "name": "migration_customers_migration_job_id_fkey", + "tableFrom": "migration_errors", + "tableTo": "migration_jobs", + "columnsFrom": [ + "migration_job_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "migration_errors_pkey": { + "name": "migration_errors_pkey", + "columns": [ + "internal_customer_id", + "migration_job_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.migration_jobs": { + "name": "migration_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "current_step": { + "name": "current_step", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "from_internal_product_id": { + "name": "from_internal_product_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "to_internal_product_id": { + "name": "to_internal_product_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "step_details": { + "name": "step_details", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "migration_jobs_from_internal_product_id_fkey": { + "name": "migration_jobs_from_internal_product_id_fkey", + "tableFrom": "migration_jobs", + "tableTo": "products", + "columnsFrom": [ + "from_internal_product_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "migration_jobs_org_id_fkey": { + "name": "migration_jobs_org_id_fkey", + "tableFrom": "migration_jobs", + "tableTo": "organizations", + "columnsFrom": [ + "org_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "migration_jobs_to_internal_product_id_fkey": { + "name": "migration_jobs_to_internal_product_id_fkey", + "tableFrom": "migration_jobs", + "tableTo": "products", + "columnsFrom": [ + "to_internal_product_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organizations": { + "name": "organizations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "default_currency": { + "name": "default_currency", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'usd'" + }, + "stripe_connected": { + "name": "stripe_connected", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "stripe_config": { + "name": "stripe_config", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "test_pkey": { + "name": "test_pkey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "live_pkey": { + "name": "live_pkey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "svix_config": { + "name": "svix_config", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "organizations_test_pkey_key": { + "name": "organizations_test_pkey_key", + "nullsNotDistinct": false, + "columns": [ + "test_pkey" + ] + }, + "organizations_live_pkey_key": { + "name": "organizations_live_pkey_key", + "nullsNotDistinct": false, + "columns": [ + "live_pkey" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.prices": { + "name": "prices", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text COLLATE \"C\"", + "primaryKey": true, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "internal_product_id": { + "name": "internal_product_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "billing_type": { + "name": "billing_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_custom": { + "name": "is_custom", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "entitlement_id": { + "name": "entitlement_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "null" + }, + "proration_config": { + "name": "proration_config", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "null" + } + }, + "indexes": {}, + "foreignKeys": { + "prices_entitlement_id_fkey": { + "name": "prices_entitlement_id_fkey", + "tableFrom": "prices", + "tableTo": "entitlements", + "columnsFrom": [ + "entitlement_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "prices_internal_product_id_fkey": { + "name": "prices_internal_product_id_fkey", + "tableFrom": "prices", + "tableTo": "products", + "columnsFrom": [ + "internal_product_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "prices_id_key": { + "name": "prices_id_key", + "nullsNotDistinct": false, + "columns": [ + "id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.products": { + "name": "products", + "schema": "", + "columns": { + "internal_id": { + "name": "internal_id", + "type": "text COLLATE \"C\"", + "primaryKey": true, + "notNull": true + }, + "id": { + "name": "id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "default": "ROUND(date_part('epoch', NOW()) * 1000)::BIGINT" + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_add_on": { + "name": "is_add_on", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_default": { + "name": "is_default", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "group": { + "name": "group", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "''" + }, + "version": { + "name": "version", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "processor": { + "name": "processor", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "null" + } + }, + "indexes": {}, + "foreignKeys": { + "products_org_id_fkey": { + "name": "products_org_id_fkey", + "tableFrom": "products", + "tableTo": "organizations", + "columnsFrom": [ + "org_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "unique_product": { + "name": "unique_product", + "nullsNotDistinct": false, + "columns": [ + "org_id", + "id", + "env", + "version" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.referral_codes": { + "name": "referral_codes", + "schema": "", + "columns": { + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "internal_customer_id": { + "name": "internal_customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "internal_reward_program_id": { + "name": "internal_reward_program_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id": { + "name": "id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "referral_codes_internal_customer_id_fkey": { + "name": "referral_codes_internal_customer_id_fkey", + "tableFrom": "referral_codes", + "tableTo": "customers", + "columnsFrom": [ + "internal_customer_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "referral_codes_internal_reward_program_id_fkey": { + "name": "referral_codes_internal_reward_program_id_fkey", + "tableFrom": "referral_codes", + "tableTo": "reward_programs", + "columnsFrom": [ + "internal_reward_program_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "referral_codes_org_id_fkey": { + "name": "referral_codes_org_id_fkey", + "tableFrom": "referral_codes", + "tableTo": "organizations", + "columnsFrom": [ + "org_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "referral_codes_pkey": { + "name": "referral_codes_pkey", + "columns": [ + "code", + "org_id", + "env" + ] + } + }, + "uniqueConstraints": { + "referral_codes_id_key": { + "name": "referral_codes_id_key", + "nullsNotDistinct": false, + "columns": [ + "id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.replaceables": { + "name": "replaceables", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text COLLATE \"C\"", + "primaryKey": true, + "notNull": true + }, + "cus_ent_id": { + "name": "cus_ent_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "from_entity_id": { + "name": "from_entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "delete_next_cycle": { + "name": "delete_next_cycle", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "replaceables_cus_ent_id_fkey": { + "name": "replaceables_cus_ent_id_fkey", + "tableFrom": "replaceables", + "tableTo": "customer_entitlements", + "columnsFrom": [ + "cus_ent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reward_programs": { + "name": "reward_programs", + "schema": "", + "columns": { + "internal_id": { + "name": "internal_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "id": { + "name": "id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "internal_reward_id": { + "name": "internal_reward_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "max_redemptions": { + "name": "max_redemptions", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "unlimited_redemptions": { + "name": "unlimited_redemptions", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "when": { + "name": "when", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'immediately'" + }, + "product_ids": { + "name": "product_ids", + "type": "text[]", + "primaryKey": false, + "notNull": false, + "default": "'{\"\"}'" + }, + "exclude_trial": { + "name": "exclude_trial", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "received_by": { + "name": "received_by", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "reward_triggers_internal_reward_id_fkey": { + "name": "reward_triggers_internal_reward_id_fkey", + "tableFrom": "reward_programs", + "tableTo": "rewards", + "columnsFrom": [ + "internal_reward_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "reward_triggers_org_id_fkey": { + "name": "reward_triggers_org_id_fkey", + "tableFrom": "reward_programs", + "tableTo": "organizations", + "columnsFrom": [ + "org_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reward_redemptions": { + "name": "reward_redemptions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "internal_customer_id": { + "name": "internal_customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "triggered": { + "name": "triggered", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "internal_reward_program_id": { + "name": "internal_reward_program_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applied": { + "name": "applied", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "referral_code_id": { + "name": "referral_code_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "reward_redemptions_internal_customer_id_fkey": { + "name": "reward_redemptions_internal_customer_id_fkey", + "tableFrom": "reward_redemptions", + "tableTo": "customers", + "columnsFrom": [ + "internal_customer_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "reward_redemptions_internal_reward_program_id_fkey": { + "name": "reward_redemptions_internal_reward_program_id_fkey", + "tableFrom": "reward_redemptions", + "tableTo": "reward_programs", + "columnsFrom": [ + "internal_reward_program_id" + ], + "columnsTo": [ + "internal_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "reward_redemptions_referral_code_id_fkey": { + "name": "reward_redemptions_referral_code_id_fkey", + "tableFrom": "reward_redemptions", + "tableTo": "referral_codes", + "columnsFrom": [ + "referral_code_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.rewards": { + "name": "rewards", + "schema": "", + "columns": { + "internal_id": { + "name": "internal_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "id": { + "name": "id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "discount_config": { + "name": "discount_config", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "free_product_id": { + "name": "free_product_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "promo_codes": { + "name": "promo_codes", + "type": "jsonb[]", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "coupons_org_id_fkey": { + "name": "coupons_org_id_fkey", + "tableFrom": "rewards", + "tableTo": "organizations", + "columnsFrom": [ + "org_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.subscriptions": { + "name": "subscriptions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stripe_id": { + "name": "stripe_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_schedule_id": { + "name": "stripe_schedule_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "default": "ROUND(date_part('epoch', NOW()) * 1000)::BIGINT" + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{}'::jsonb" + }, + "usage_features": { + "name": "usage_features", + "type": "text[]", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "current_period_start": { + "name": "current_period_start", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "current_period_end": { + "name": "current_period_end", + "type": "numeric", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "subscriptions_org_id_fkey": { + "name": "subscriptions_org_id_fkey", + "tableFrom": "subscriptions", + "tableTo": "organizations", + "columnsFrom": [ + "org_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "subscriptions_stripe_id_key": { + "name": "subscriptions_stripe_id_key", + "nullsNotDistinct": false, + "columns": [ + "stripe_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/shared/drizzle/meta/_journal.json b/shared/drizzle/meta/_journal.json index 59d9231a4..e8e4fff8b 100644 --- a/shared/drizzle/meta/_journal.json +++ b/shared/drizzle/meta/_journal.json @@ -22,6 +22,20 @@ "when": 1748520847010, "tag": "0002_foamy_tombstone", "breakpoints": true + }, + { + "idx": 3, + "version": "7", + "when": 1749201302399, + "tag": "0003_yummy_la_nuit", + "breakpoints": true + }, + { + "idx": 4, + "version": "7", + "when": 1749201355061, + "tag": "0004_broad_shocker", + "breakpoints": true } ] } \ No newline at end of file diff --git a/shared/index.ts b/shared/index.ts index c849d60b4..4621c58f0 100644 --- a/shared/index.ts +++ b/shared/index.ts @@ -48,16 +48,19 @@ export * from "./models/productV2Models/productItemModels/prodItemResponseModels export * from "./models/productV2Models/productItemModels/featureItem.js"; export * from "./models/productV2Models/productItemModels/featurePriceItem.js"; export * from "./models/productV2Models/productItemModels/priceItem.js"; +export * from "./models/productV2Models/productItemModels/productItemEnums.js"; // 7. Cus Product Models export * from "./models/cusProductModels/cusProductModels.js"; export * from "./models/cusProductModels/cusProductTable.js"; export * from "./models/cusProductModels/cusProductEnums.js"; +export * from "./models/cusProductModels/cusPriceModels/cusPriceModels.js"; +export * from "./models/cusProductModels/cusPriceModels/cusPriceTable.js"; + export * from "./models/cusProductModels/cusEntModels/cusEntModels.js"; export * from "./models/cusProductModels/cusEntModels/cusEntWithProduct.js"; export * from "./models/cusProductModels/cusEntModels/cusEntTable.js"; -export * from "./models/cusProductModels/cusPriceModels/cusPriceModels.js"; -export * from "./models/cusProductModels/cusPriceModels/cusPriceTable.js"; +export * from "./models/cusProductModels/cusEntModels/replaceableTable.js"; // 8. Customer Models export * from "./models/cusModels/cusModels.js"; diff --git a/shared/models/attachModels/attachPreviewModels.ts b/shared/models/attachModels/attachPreviewModels.ts index 1bc46792d..eeef8e26f 100644 --- a/shared/models/attachModels/attachPreviewModels.ts +++ b/shared/models/attachModels/attachPreviewModels.ts @@ -1,10 +1,13 @@ import { FreeTrial } from "../productModels/freeTrialModels/freeTrialModels.js"; +import { UsageModel } from "../productV2Models/productItemModels/productItemModels.js"; import { AttachBranch } from "./attachEnums/AttachBranch.js"; export interface PreviewLineItem { - amount: number; + amount: number | undefined; description: string; price: string; + price_id: string; + usage_model?: UsageModel; } export interface AttachPreview { diff --git a/shared/models/cusModels/entityModels/entityModels.ts b/shared/models/cusModels/entityModels/entityModels.ts index 2790a4313..75d8b1126 100644 --- a/shared/models/cusModels/entityModels/entityModels.ts +++ b/shared/models/cusModels/entityModels/entityModels.ts @@ -14,10 +14,9 @@ export const EntitySchema = z.object({ }); export const CreateEntitySchema = z.object({ - id: z.string(), // ID of entity - name: z.string(), // Name of entity - customer_id: z.string(), // Customer ID of entity - feature_id: z.string(), // Feature ID of entity + id: z.string(), + name: z.string().nullish(), + feature_id: z.string().nullish(), }); export const EntityDataSchema = z.object({ diff --git a/shared/models/cusProductModels/cusEntModels/cusEntModels.ts b/shared/models/cusProductModels/cusEntModels/cusEntModels.ts index b40085f41..d675122f8 100644 --- a/shared/models/cusProductModels/cusEntModels/cusEntModels.ts +++ b/shared/models/cusProductModels/cusEntModels/cusEntModels.ts @@ -1,6 +1,7 @@ import { z } from "zod"; import { EntitlementWithFeatureSchema } from "../../productModels/entModels/entModels.js"; -import { CusProductSchema } from "../cusProductModels.js"; +import { Replaceable } from "./replaceableTable.js"; +import { ReplaceableSchema } from "./replaceableSchema.js"; export const EntityBalanceSchema = z.object({ id: z.string(), @@ -34,6 +35,7 @@ export const CustomerEntitlementSchema = z.object({ export const FullCustomerEntitlementSchema = CustomerEntitlementSchema.extend({ entitlement: EntitlementWithFeatureSchema, + replaceables: z.array(ReplaceableSchema), }); export type EntityBalance = z.infer; diff --git a/shared/models/cusProductModels/cusEntModels/cusEntRelations.ts b/shared/models/cusProductModels/cusEntModels/cusEntRelations.ts index 6f9f8e3e8..8dd3d61ba 100644 --- a/shared/models/cusProductModels/cusEntModels/cusEntRelations.ts +++ b/shared/models/cusProductModels/cusEntModels/cusEntRelations.ts @@ -5,6 +5,7 @@ import { customers } from "../../cusModels/cusTable.js"; import { features } from "../../featureModels/featureTable.js"; import { customerEntitlements } from "./cusEntTable.js"; import { customerProducts } from "../cusProductTable.js"; +import { replaceables } from "./replaceableTable.js"; export const customerEntitlementsRelations = relations( customerEntitlements, @@ -25,5 +26,6 @@ export const customerEntitlementsRelations = relations( fields: [customerEntitlements.internal_feature_id], references: [features.internal_id], }), + replaceables: many(replaceables), }), ); diff --git a/shared/models/cusProductModels/cusEntModels/cusEntWithProduct.ts b/shared/models/cusProductModels/cusEntModels/cusEntWithProduct.ts index 36ccd1887..53b4620b4 100644 --- a/shared/models/cusProductModels/cusEntModels/cusEntWithProduct.ts +++ b/shared/models/cusProductModels/cusEntModels/cusEntWithProduct.ts @@ -1,4 +1,4 @@ -import { CusProductSchema } from "../cusProductModels.js"; +import { CusProductSchema, FullCusProductSchema } from "../cusProductModels.js"; import { FullCustomerEntitlementSchema } from "./cusEntModels.js"; import { z } from "zod"; @@ -7,4 +7,13 @@ export const FullCusEntWithProductSchema = FullCustomerEntitlementSchema.extend( customer_product: CusProductSchema, }, ); + +export const FullCusEntWithFullCusProductSchema = + FullCustomerEntitlementSchema.extend({ + customer_product: FullCusProductSchema, + }); + export type FullCusEntWithProduct = z.infer; +export type FullCusEntWithFullCusProduct = z.infer< + typeof FullCusEntWithFullCusProductSchema +>; diff --git a/shared/models/cusProductModels/cusEntModels/replaceableSchema.ts b/shared/models/cusProductModels/cusEntModels/replaceableSchema.ts new file mode 100644 index 000000000..ec55eb982 --- /dev/null +++ b/shared/models/cusProductModels/cusEntModels/replaceableSchema.ts @@ -0,0 +1,11 @@ +import { z } from "zod"; + +export const ReplaceableSchema = z.object({ + id: z.string(), + cus_ent_id: z.string(), + created_at: z.number(), + from_entity_id: z.string(), + delete_next_cycle: z.boolean(), +}); + +export type Replaceable = z.infer; diff --git a/shared/models/cusProductModels/cusEntModels/replaceableTable.ts b/shared/models/cusProductModels/cusEntModels/replaceableTable.ts new file mode 100644 index 000000000..fd5db1f47 --- /dev/null +++ b/shared/models/cusProductModels/cusEntModels/replaceableTable.ts @@ -0,0 +1,35 @@ +import { + pgTable, + boolean, + foreignKey, + text, + bigint, +} from "drizzle-orm/pg-core"; + +import { collatePgColumn } from "../../../db/utils.js"; +import { customerEntitlements } from "./cusEntTable.js"; +import { createInsertSchema } from "drizzle-zod"; + +export const replaceables = pgTable( + "replaceables", + { + id: text().primaryKey().notNull(), + cus_ent_id: text().notNull(), + created_at: bigint({ mode: "number" }).notNull(), + from_entity_id: text(), + delete_next_cycle: boolean("delete_next_cycle").notNull().default(false), + }, + (table) => [ + foreignKey({ + columns: [table.cus_ent_id], + foreignColumns: [customerEntitlements.id], + name: "replaceables_cus_ent_id_fkey", + }).onDelete("cascade"), + ], +).enableRLS(); + +collatePgColumn(replaceables.id, "C"); + +export const InsertReplaceableSchema = createInsertSchema(replaceables); +export type Replaceable = typeof replaceables.$inferSelect; +export type InsertReplaceable = typeof replaceables.$inferInsert; diff --git a/shared/models/cusProductModels/cusProductModels.ts b/shared/models/cusProductModels/cusProductModels.ts index 4e4be1c3e..027469c22 100644 --- a/shared/models/cusProductModels/cusProductModels.ts +++ b/shared/models/cusProductModels/cusProductModels.ts @@ -2,12 +2,18 @@ import { z } from "zod"; import { ProcessorType } from "../genModels/genEnums.js"; import { ProductSchema } from "../productModels/productModels.js"; import { PriceSchema } from "../productModels/priceModels/priceModels.js"; -import { CustomerEntitlementSchema } from "./cusEntModels/cusEntModels.js"; +import { + CustomerEntitlementSchema, + FullCustomerEntitlementSchema, +} from "./cusEntModels/cusEntModels.js"; import { EntitlementSchema } from "../productModels/entModels/entModels.js"; import { FeatureSchema } from "../featureModels/featureModels.js"; import { CustomerSchema } from "../cusModels/cusModels.js"; import { FreeTrialSchema } from "../productModels/freeTrialModels/freeTrialModels.js"; -import { CustomerPriceSchema } from "./cusPriceModels/cusPriceModels.js"; +import { + CustomerPriceSchema, + FullCustomerPriceSchema, +} from "./cusPriceModels/cusPriceModels.js"; import { CollectionMethod } from "./cusProductEnums.js"; import { CusProductStatus } from "./cusProductEnums.js"; @@ -66,19 +72,8 @@ export const CusProductSchema = z.object({ }); export const FullCusProductSchema = CusProductSchema.extend({ - customer_prices: z.array( - CustomerPriceSchema.extend({ - price: PriceSchema, - }), - ), - - customer_entitlements: z.array( - CustomerEntitlementSchema.extend({ - entitlement: EntitlementSchema.extend({ - feature: FeatureSchema, - }), - }), - ), + customer_prices: z.array(FullCustomerPriceSchema), + customer_entitlements: z.array(FullCustomerEntitlementSchema), customer: CustomerSchema.optional(), product: ProductSchema, diff --git a/shared/models/orgModels/orgConfig.ts b/shared/models/orgModels/orgConfig.ts index 32ff88065..27b6a1918 100644 --- a/shared/models/orgModels/orgConfig.ts +++ b/shared/models/orgModels/orgConfig.ts @@ -5,6 +5,7 @@ export const OrgConfigSchema = z.object({ convert_to_charge_automatically: z.boolean().default(true), anchor_start_of_month: z.boolean().default(false), // If true, the billing cycle will start on the first day of the month cancel_on_past_due: z.boolean().default(false), + prorate_unused: z.boolean().default(true), api_version: z.number().default(0.2), diff --git a/shared/models/productModels/priceModels/priceEnums.ts b/shared/models/productModels/priceModels/priceEnums.ts index 26a5c048a..fbc36b181 100644 --- a/shared/models/productModels/priceModels/priceEnums.ts +++ b/shared/models/productModels/priceModels/priceEnums.ts @@ -5,3 +5,18 @@ export enum BillingInterval { SemiAnnual = "semi_annual", Year = "year", } + +export enum PriceType { + Fixed = "fixed", + Usage = "usage", +} + +export enum BillingType { + OneOff = "one_off", + FixedCycle = "fixed_cycle", + + UsageBelowThreshold = "usage_below_threshold", + UsageInAdvance = "usage_in_advance", + UsageInArrear = "usage_in_arrear", + InArrearProrated = "in_arrear_prorated", +} diff --git a/shared/models/productModels/priceModels/priceModels.ts b/shared/models/productModels/priceModels/priceModels.ts index b02ecbf5a..bb36e90ba 100644 --- a/shared/models/productModels/priceModels/priceModels.ts +++ b/shared/models/productModels/priceModels/priceModels.ts @@ -1,21 +1,14 @@ import { z } from "zod"; import { UsagePriceConfigSchema } from "./priceConfig/usagePriceConfig.js"; import { FixedPriceConfigSchema } from "./priceConfig/fixedPriceConfig.js"; +import { BillingType } from "./priceEnums.js"; +import { OnDecrease } from "../../productV2Models/productItemModels/productItemEnums.js"; +import { OnIncrease } from "../../productV2Models/productItemModels/productItemEnums.js"; -export enum PriceType { - Fixed = "fixed", - Usage = "usage", -} - -export enum BillingType { - OneOff = "one_off", - FixedCycle = "fixed_cycle", - - UsageBelowThreshold = "usage_below_threshold", - UsageInAdvance = "usage_in_advance", - UsageInArrear = "usage_in_arrear", - InArrearProrated = "in_arrear_prorated", -} +const ProrationConfigSchema = z.object({ + on_increase: z.nativeEnum(OnIncrease).default(OnIncrease.ProrateImmediately), + on_decrease: z.nativeEnum(OnDecrease).default(OnDecrease.Prorate), +}); export const PriceSchema = z.object({ id: z.string().optional(), @@ -24,17 +17,16 @@ export const PriceSchema = z.object({ created_at: z.number().optional(), billing_type: z.nativeEnum(BillingType).nullish(), is_custom: z.boolean().optional(), - - // name: z.string().optional(), config: FixedPriceConfigSchema.or(UsagePriceConfigSchema), entitlement_id: z.string().nullish(), + + proration_config: ProrationConfigSchema.nullable(), +}); + +export const CreatePriceSchema = z.object({ + config: FixedPriceConfigSchema.or(UsagePriceConfigSchema), }); export type Price = z.infer; - -export const CreatePriceSchema = z.object({ - // name: z.string().nonempty(), - config: FixedPriceConfigSchema.or(UsagePriceConfigSchema), -}); - export type CreatePrice = z.infer; +export type ProrationConfig = z.infer; diff --git a/shared/models/productModels/priceModels/priceTable.ts b/shared/models/productModels/priceModels/priceTable.ts index ae205feec..4faadb7f9 100644 --- a/shared/models/productModels/priceModels/priceTable.ts +++ b/shared/models/productModels/priceModels/priceTable.ts @@ -14,6 +14,7 @@ import { FixedPriceConfig } from "./priceConfig/fixedPriceConfig.js"; import { UsagePriceConfig } from "./priceConfig/usagePriceConfig.js"; import { sql } from "drizzle-orm"; import { collatePgColumn } from "../../../db/utils.js"; +import { ProrationConfig } from "./priceModels.js"; export const prices = pgTable( "prices", @@ -26,6 +27,9 @@ export const prices = pgTable( billing_type: text("billing_type"), is_custom: boolean("is_custom").default(false), entitlement_id: text("entitlement_id").default(sql`null`), + proration_config: jsonb("proration_config") + .$type() + .default(sql`null`), }, (table) => [ foreignKey({ diff --git a/shared/models/productV2Models/productItemModels/productItemEnums.ts b/shared/models/productV2Models/productItemModels/productItemEnums.ts new file mode 100644 index 000000000..3a3ecbad3 --- /dev/null +++ b/shared/models/productV2Models/productItemModels/productItemEnums.ts @@ -0,0 +1,11 @@ +export enum OnIncrease { + BillImmediately = "bill_immediately", + ProrateImmediately = "prorate_immediately", + ProrateNextCycle = "prorate_next_cycle", + BillNextCycle = "bill_next_cycle", +} + +export enum OnDecrease { + Prorate = "prorate", + None = "none", +} diff --git a/shared/models/productV2Models/productItemModels/productItemModels.ts b/shared/models/productV2Models/productItemModels/productItemModels.ts index 18acc6e69..242b182ba 100644 --- a/shared/models/productV2Models/productItemModels/productItemModels.ts +++ b/shared/models/productV2Models/productItemModels/productItemModels.ts @@ -1,5 +1,7 @@ import { z } from "zod"; import { Infinite } from "../../productModels/productEnums.js"; +import { OnIncrease } from "./productItemEnums.js"; +import { OnDecrease } from "./productItemEnums.js"; export const TierInfinite = "inf"; @@ -41,6 +43,14 @@ export enum ProductItemFeatureType { Static = "static", } +const ProductItemConfigSchema = z.object({ + on_increase: z + .nativeEnum(OnIncrease) + .optional() + .default(OnIncrease.BillImmediately), + on_decrease: z.nativeEnum(OnDecrease).optional().default(OnDecrease.None), +}); + export const ProductItemSchema = z.object({ // Feature stuff feature_id: z.string().nullish(), @@ -63,6 +73,8 @@ export const ProductItemSchema = z.object({ // carry_over_usage: z.boolean().nullish(), reset_usage_when_enabled: z.boolean().nullish(), + config: ProductItemConfigSchema.nullish(), + // Stored in backend created_at: z.number().nullish(), entitlement_id: z.string().nullish(), diff --git a/shared/utils/displayUtils.ts b/shared/utils/displayUtils.ts index ca513f3b8..073960ea6 100644 --- a/shared/utils/displayUtils.ts +++ b/shared/utils/displayUtils.ts @@ -1,4 +1,5 @@ import { Feature } from "../models/featureModels/featureModels.js"; +import { Decimal } from "decimal.js"; export const getFeatureName = ({ feature, @@ -59,23 +60,54 @@ export const numberWithCommas = (x: number) => { return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); }; +export const usageToFeatureName = ({ + usage, + feature, +}: { + usage: number; + feature: Feature; +}) => { + const { singular, plural } = getSingularAndPlural({ feature }); + + if (usage == 1) { + return singular; + } + + return plural; +}; + export const getFeatureInvoiceDescription = ({ feature, usage, billingUnits = 1, + prodName, + isPrepaid = false, }: { feature: Feature; usage: number; - billingUnits?: number; + billingUnits?: number | null; + prodName?: string; + isPrepaid?: boolean; }) => { const { singular, plural } = getSingularAndPlural({ feature }); const usageStr = numberWithCommas(usage); - if (billingUnits == 1) { - if (usage == 1) - return `${usageStr} ${singular}`; // eg. 1 credit - else return `${usageStr} ${plural}`; // eg. 4 credits + + let result = ""; + + if (isPrepaid && billingUnits && billingUnits > 1) { + result = `${usageStr} x ${billingUnits} ${plural}`; // eg. 4 x 100 credits } else { - return `${usageStr} x ${billingUnits} ${plural}`; // eg. 4 x 100 credits + if (usage == 1) { + result = `${usageStr} ${singular}`; // eg. 1 credit + } else { + result = `${usageStr} ${plural}`; // eg. 4 credits + } } + + if (prodName) { + result = `${prodName} - ${result}`; + } + + return result; }; diff --git a/vite/eslint.config.js b/vite/eslint.config.js index b1a0d774f..08a8e0ac7 100644 --- a/vite/eslint.config.js +++ b/vite/eslint.config.js @@ -26,6 +26,7 @@ export default tseslint.config( '@typescript-eslint/no-explicit-any': 'off', // Disable the "any" type warning '@typescript-eslint/no-unused-vars': 'off', 'no-unused-vars': 'off', // Also disable the core ESLint unused vars rule + 'react-hooks/exhaustive-deps': 'off', // Disable exhaustive deps rule project-wide }, }, diff --git a/vite/package.json b/vite/package.json index 660bd4d4a..872109e23 100644 --- a/vite/package.json +++ b/vite/package.json @@ -38,6 +38,7 @@ "hast-util-to-html": "^9.0.5", "lodash": "^4.17.21", "lucide-react": "^0.511.0", + "motion": "^12.16.0", "next-themes": "^0.4.6", "posthog-js": "^1.230.4", "prism-themes": "^1.9.0", @@ -72,8 +73,6 @@ "typescript-eslint": "^8.24.1", "vite": "^6.2.0" }, - - "resolutions": { "@rollup/rollup-linux-x64-gnu": "npm:@rollup/rollup-linux-x64-musl" }, diff --git a/vite/src/utils/product/productItemUtils.ts b/vite/src/utils/product/productItemUtils.ts index 40d03188e..c7590fb19 100644 --- a/vite/src/utils/product/productItemUtils.ts +++ b/vite/src/utils/product/productItemUtils.ts @@ -1,13 +1,16 @@ import { BillingInterval, EntInterval, + Feature, Infinite, ProductItem, - ProductItemInterval, + ProductItemFeatureType, ProductItemType, + UsageModel, } from "@autumn/shared"; import { notNullish, nullish } from "../genUtils"; -import { isFeatureItem, isPriceItem } from "./getItemType"; +import { isFeatureItem, isFeaturePriceItem, isPriceItem } from "./getItemType"; +import { itemToUsageType } from "./productItemUtils/convertItem"; export const itemIsUnlimited = (item: ProductItem) => { return item.included_usage == Infinite; @@ -67,3 +70,26 @@ export const getShowParams = (item: ProductItem | null) => { cycle: true, }; }; + +export const shouldShowProrationConfig = ({ + item, + features, +}: { + item: ProductItem; + features: Feature[]; +}) => { + if (!isFeaturePriceItem(item)) return false; + + // If pay per use single use + const usageType = itemToUsageType({ item, features }); + + if ( + usageType == ProductItemFeatureType.SingleUse && + item.usage_model == UsageModel.Prepaid + ) { + return true; + } else if (usageType == ProductItemFeatureType.ContinuousUse) { + return true; + } + return false; +}; diff --git a/vite/src/utils/product/productItemUtils/convertItem.ts b/vite/src/utils/product/productItemUtils/convertItem.ts new file mode 100644 index 000000000..c45cb9052 --- /dev/null +++ b/vite/src/utils/product/productItemUtils/convertItem.ts @@ -0,0 +1,37 @@ +import { + Feature, + FeatureType, + ProductItem, + ProductItemFeatureType, +} from "@autumn/shared"; + +export const itemToFeature = ({ + item, + features, +}: { + item: ProductItem; + features: Feature[]; +}) => { + return features.find((f) => f.id === item.feature_id); +}; + +export const itemToUsageType = ({ + item, + features, +}: { + item: ProductItem; + features: Feature[]; +}) => { + const feature = itemToFeature({ item, features }); + if (!feature) return null; + + if (feature.type == FeatureType.Boolean) { + return ProductItemFeatureType.Static; + } + + if (feature.type == FeatureType.CreditSystem) { + return ProductItemFeatureType.SingleUse; + } + + return feature.config!.usage_type as ProductItemFeatureType; +}; diff --git a/vite/src/views/customers/customer/product/components/attach-preview/DueNextCycle.tsx b/vite/src/views/customers/customer/product/components/attach-preview/DueNextCycle.tsx index e977c39b4..989577aa5 100644 --- a/vite/src/views/customers/customer/product/components/attach-preview/DueNextCycle.tsx +++ b/vite/src/views/customers/customer/product/components/attach-preview/DueNextCycle.tsx @@ -51,6 +51,7 @@ export const DueNextCycle = () => { )!, usage: quantity || 0, billingUnits: option.billing_units, + isPrepaid: true, }); return ( diff --git a/vite/src/views/products/product/product-item/ProductItemConfig.tsx b/vite/src/views/products/product/product-item/ProductItemConfig.tsx index d7e9ddeaf..0750adb6d 100644 --- a/vite/src/views/products/product/product-item/ProductItemConfig.tsx +++ b/vite/src/views/products/product/product-item/ProductItemConfig.tsx @@ -3,6 +3,7 @@ import { FeatureType, FeatureUsageType, Infinite, + OnIncrease, ProductItemFeatureType, ProductItemInterval, TierInfinite, @@ -14,7 +15,10 @@ import { PlusIcon } from "lucide-react"; import { cn } from "@/lib/utils"; import { useProductItemContext } from "./ProductItemContext"; -import { getShowParams } from "@/utils/product/productItemUtils"; +import { + getShowParams, + shouldShowProrationConfig, +} from "@/utils/product/productItemUtils"; import { ConfigWithFeature } from "./components/ConfigWithFeature"; import FixedPriceConfig from "./components/ConfigFixedPrice"; import { getFeature } from "@/utils/product/entitlementUtils"; @@ -35,7 +39,7 @@ export const ProductItemConfig = () => { const [show, setShow] = useState(getShowParams(item)); const selectedFeature = features.find( - (f: Feature) => f.id == item.feature_id + (f: Feature) => f.id == item.feature_id, ); const handleAddPrice = () => { @@ -57,7 +61,7 @@ export const ProductItemConfig = () => { }, []); useEffect(() => { - let feature = features.find((f: any) => f.id == item.feature_id); + const feature = features.find((f: Feature) => f.id == item.feature_id); if (feature) { if (feature.type == FeatureType.Boolean) { setItem({ @@ -75,7 +79,15 @@ export const ProductItemConfig = () => { }); } } - }, [item.feature_id]); + const showProration = shouldShowProrationConfig({ item, features }); + if (!showProration) { + console.log("Setting item proration config to null"); + setItem({ + ...item, + config: null, + }); + } + }, [item.feature_id, item.usage_model]); useEffect(() => { if (!show.perEntity) { @@ -100,7 +112,7 @@ export const ProductItemConfig = () => { "flex flex-col gap-6 w-lg transition-all ease-in-out duration-300", //modal animations !show.feature && "w-xs", show.feature && show.price && "w-xl", - show.price && show.feature && item.tiers?.length > 1 && "w-2xl" + show.price && show.feature && item.tiers?.length > 1 && "w-2xl", )} > {!show.feature ? ( @@ -129,7 +141,7 @@ export const ProductItemConfig = () => { getFeature(item.feature_id, features)?.type != FeatureType.Boolean ? "w-full max-w-32 mr-0 p-2" - : "w-0 max-w-0 p-0 border-none" + : "w-0 max-w-0 p-0 border-none", )} > @@ -140,7 +152,7 @@ export const ProductItemConfig = () => { "w-0 max-w-0 p-0 overflow-hidden transition-all duration-200 ease-in-out -ml-2", !show.feature && !isUpdate ? "w-full max-w-32 mr-0 p-2" - : "w-0 max-w-0 p-0 border-none" + : "w-0 max-w-0 p-0 border-none", )} variant="outline" onClick={() => { diff --git a/vite/src/views/products/product/product-item/components/ConfigWithFeature.tsx b/vite/src/views/products/product/product-item/components/ConfigWithFeature.tsx index cf5749c2d..92631a889 100644 --- a/vite/src/views/products/product/product-item/components/ConfigWithFeature.tsx +++ b/vite/src/views/products/product/product-item/components/ConfigWithFeature.tsx @@ -20,6 +20,7 @@ import { SelectCycle } from "./SelectCycle"; import MoreMenuButton, { MoreMenuPriceButton } from "../MoreMenuButton"; import { getFeature } from "@/utils/product/entitlementUtils"; +import { ProrationConfig } from "./ProrationConfig"; export const ConfigWithFeature = ({ show, @@ -186,7 +187,8 @@ export const ConfigWithFeature = ({ - {/* */} + + diff --git a/vite/src/views/products/product/product-item/components/ProrationConfig.tsx b/vite/src/views/products/product/product-item/components/ProrationConfig.tsx new file mode 100644 index 000000000..4c28c1f40 --- /dev/null +++ b/vite/src/views/products/product/product-item/components/ProrationConfig.tsx @@ -0,0 +1,51 @@ +import { + Feature, + ProductItem, + ProductItemFeatureType, + UsageModel, +} from "@autumn/shared"; +import { useProductItemContext } from "../ProductItemContext"; +import { isFeaturePriceItem } from "@/utils/product/getItemType"; +import { useProductContext } from "../../ProductContext"; +import { itemToUsageType } from "@/utils/product/productItemUtils/convertItem"; + +import { OnIncreaseSelect } from "./prorationConfig/OnIncreaseSelect"; +import { OnDecreaseSelect } from "./prorationConfig/OnDecreaseSelect"; +import { useEffect, useState } from "react"; +import { ChevronRight } from "lucide-react"; +import { shouldShowProrationConfig } from "@/utils/product/productItemUtils"; + +export const ProrationConfig = () => { + const { features } = useProductContext(); + const { item, setItem } = useProductItemContext(); + const [isOpen, setIsOpen] = useState(false); + + if (!shouldShowProrationConfig({ item, features })) return null; + + return ( +
+ + +
+
+ + +
+
+
+ ); +}; diff --git a/vite/src/views/products/product/product-item/components/prorationConfig/OnDecreaseSelect.tsx b/vite/src/views/products/product/product-item/components/prorationConfig/OnDecreaseSelect.tsx new file mode 100644 index 000000000..a3707b0dc --- /dev/null +++ b/vite/src/views/products/product/product-item/components/prorationConfig/OnDecreaseSelect.tsx @@ -0,0 +1,55 @@ +import { OnDecrease, OnIncrease, UsageModel } from "@autumn/shared"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { useProductItemContext } from "../../ProductItemContext"; +import { useEffect } from "react"; +import { ProrationSelect } from "./ProrationSelect"; + +const optionToText = (option: OnDecrease) => { + switch (option) { + case OnDecrease.Prorate: + return "Refund prorated amount"; + case OnDecrease.None: + return "No proration (usage will be kept till next cycle)"; + } +}; + +export const OnDecreaseSelect = () => { + const { item, setItem } = useProductItemContext(); + + const value = item.config?.on_decrease; + + useEffect(() => { + if (!item.config?.on_decrease) { + setItem({ + ...item, + config: { ...item.config, on_decrease: OnDecrease.Prorate }, + }); + } + }, [item]); + + const text = + item.usage_model == UsageModel.PayPerUse + ? "On usage decrease" + : "On quantity decrease"; + + return ( +
+

{text}

+ + + setItem({ ...item, config: { ...item.config, on_decrease: value } }) + } + optionToText={optionToText} + options={Object.values(OnDecrease)} + /> +
+ ); +}; diff --git a/vite/src/views/products/product/product-item/components/prorationConfig/OnIncreaseSelect.tsx b/vite/src/views/products/product/product-item/components/prorationConfig/OnIncreaseSelect.tsx new file mode 100644 index 000000000..20d2e8e3c --- /dev/null +++ b/vite/src/views/products/product/product-item/components/prorationConfig/OnIncreaseSelect.tsx @@ -0,0 +1,64 @@ +import { OnIncrease, ProductItemFeatureType, UsageModel } from "@autumn/shared"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { useProductItemContext } from "../../ProductItemContext"; +import { useEffect } from "react"; +import { ProrationSelect } from "./ProrationSelect"; +import { itemToUsageType } from "@/utils/product/productItemUtils/convertItem"; +import { useProductContext } from "../../../ProductContext"; + +const optionToText = (option: OnIncrease) => { + switch (option) { + case OnIncrease.BillImmediately: + return "Pay full amount immediately"; + case OnIncrease.ProrateImmediately: + return "Pay for prorated amount immediately"; + case OnIncrease.ProrateNextCycle: + return "Add prorated amount to next invoice"; + case OnIncrease.BillNextCycle: + return "Pay for full amount next cycle"; + } +}; + +export const OnIncreaseSelect = () => { + const { item, setItem } = useProductItemContext(); + + const value = item.config?.on_increase; + + useEffect(() => { + if (!item.config?.on_increase) { + console.log("Setting item on increase to default"); + setItem({ + ...item, + config: { + ...item.proration_config, + on_increase: OnIncrease.ProrateImmediately, + }, + }); + } + }, [item]); + + const text = + item.usage_model == UsageModel.PayPerUse + ? "On usage increase" + : "On quantity increase"; + + return ( +
+

{text}

+ + setItem({ ...item, config: { ...item.config, on_increase: value } }) + } + optionToText={optionToText} + options={Object.values(OnIncrease)} + /> +
+ ); +}; diff --git a/vite/src/views/products/product/product-item/components/prorationConfig/ProrationSelect.tsx b/vite/src/views/products/product/product-item/components/prorationConfig/ProrationSelect.tsx new file mode 100644 index 000000000..f32952af8 --- /dev/null +++ b/vite/src/views/products/product/product-item/components/prorationConfig/ProrationSelect.tsx @@ -0,0 +1,42 @@ +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { OnDecrease, OnIncrease } from "@autumn/shared"; + +export const ProrationSelect = ({ + value, + setValue, + optionToText, + options, +}: { + value: OnDecrease | OnIncrease; + setValue: (value: OnDecrease | OnIncrease) => void; + optionToText: any; + options: (OnDecrease | OnIncrease)[]; +}) => { + return ( + + ); +};